polycode
MCP server providing 13 tools to control opencode, Gemini CLI, and Qwen Code AI coding agents with session continuity and auth checking.
README
<p align="center"> <img src="assets/banner.svg" alt="polycode" width="100%" /> </p>
A production-grade MCP (Model Context Protocol) server exposing 13 tools that let Claude Code (or any MCP client) control three AI coding agents — opencode, Gemini CLI, and Qwen Code — with full session continuity, auth checking, and structured error handling.
How It Works
Claude Code (or Gemini CLI / Qwen Code)
│ calls tools (MCP stdio)
▼
polycode server (this package — auto-started by the MCP client)
│
├── spawns opencode serve → talks to any of 182 models via opencode
├── invokes gemini CLI → Gemini API with session continuity
└── invokes qwen CLI → Qwen API with session continuity
The MCP client (Claude Code, Gemini CLI, Qwen Code) auto-starts this server when the session begins. You never start it manually.
Requirements
-
Python 3.11+
python --version -
opencode CLI — for the
opencode_*toolsnpm install -g opencode-ai opencode --version # should print 1.x.x -
Gemini CLI — for the
gemini_*tools (optional)npm install -g @google/gemini-cli gemini --version # should print 0.36.x or higher -
Qwen Code CLI — for the
qwen_*tools (optional)npm install -g @qwen-code/qwen-code qwen --version # should print 0.14.x or higher -
A model provider for opencode — for the default
ollama/qwen3.5:cloud, Ollama must be running locally. See Changing the Model for alternatives.
Installation
pip install polycode
Verify the CLI is accessible:
polycode --help
Windows note: If
polycodeis not found after install, find the full path withwhere polycodein PowerShell and use it in the MCP config below.
MCP Client Setup
All three supported MCP clients use the same config format — only the config file path differs.
macOS / Linux:
{
"mcpServers": {
"polycode": {
"command": "polycode",
"env": {
"OPENCODE_DEFAULT_MODEL": "ollama/qwen3.5:cloud"
}
}
}
}
Windows — use the full path to the binary. Find it by running where polycode in PowerShell, then paste the result as the command value:
{
"mcpServers": {
"polycode": {
"command": "C:\\Users\\YourName\\AppData\\Local\\Programs\\Python\\Python313\\Scripts\\polycode.exe",
"env": {
"OPENCODE_DEFAULT_MODEL": "ollama/qwen3.5:cloud"
}
}
}
}
| MCP Client | Config file |
|---|---|
| Claude Code | ~/.claude.json |
| Gemini CLI | ~/.gemini/settings.json |
| Qwen Code | ~/.qwen/settings.json |
Restart your MCP client after saving. All 13 tools appear automatically.
Tools Reference
opencode tools (8)
These tools control opencode — a multi-provider AI coding agent. Sessions are stateful — messages within a session share full context across any of the 180+ supported models.
opencode_start_session
Start a new opencode session. Must be called before opencode_send_message.
| Parameter | Type | Required | Description |
|---|---|---|---|
project_dir |
string | No | Absolute path to the project. Defaults to current working directory. |
model |
string | No | Model in provider/model format. Defaults to OPENCODE_DEFAULT_MODEL. |
Returns:
{
"session_id": "ses_2a29...",
"model": "ollama/qwen3.5:cloud",
"project_dir": "/path/to/project"
}
opencode_send_message
Send a prompt to an active session. Blocks until opencode finishes responding.
| Parameter | Type | Required | Description |
|---|---|---|---|
session_id |
string | Yes | From opencode_start_session. |
message |
string | Yes | Your prompt. |
timeout_seconds |
int | No | Default: 120. |
Returns:
{
"response": "Here is the updated function...",
"session_id": "ses_2a29...",
"message_index": 1,
"partial": false
}
opencode_get_history
Retrieve the full message history for a session (tracked in-process).
| Parameter | Type | Required |
|---|---|---|
session_id |
string | Yes |
Returns:
{
"session_id": "ses_2a29...",
"messages": [
{"role": "user", "content": "...", "timestamp": "2026-04-05T19:00:00Z"},
{"role": "assistant", "content": "...", "timestamp": "2026-04-05T19:00:05Z"}
]
}
opencode_list_sessions
List all active opencode sessions.
Returns:
{
"sessions": [
{
"session_id": "ses_2a29...",
"model": "ollama/qwen3.5:cloud",
"project_dir": "/path/to/project",
"message_count": 4,
"created_at": "2026-04-05T19:00:00Z"
}
]
}
opencode_end_session
Close a session and free its resources.
| Parameter | Type | Required |
|---|---|---|
session_id |
string | Yes |
Returns: {"session_id": "ses_2a29...", "closed": true}
opencode_list_models
List all models available in opencode across all authenticated providers, grouped by provider. Only providers you are authenticated/connected to will show models.
Returns:
{
"models": ["ollama/qwen3.5:cloud", "openai/gpt-4o", "google/gemini-2.5-flash", "..."],
"by_provider": {
"ollama": ["ollama/qwen3.5:cloud", "..."],
"openai": ["openai/gpt-4o", "..."],
"google": ["google/gemini-2.5-flash", "..."]
},
"total": 182,
"default_model": "ollama/qwen3.5:cloud"
}
opencode_set_model
Change the default model for new sessions (takes effect immediately for all subsequent opencode_start_session calls).
| Parameter | Type | Required | Example |
|---|---|---|---|
model |
string | Yes | ollama/qwen3.5:cloud |
Returns: {"previous_model": "ollama/...", "new_model": "openai/gpt-4o"}
opencode_shutdown
Gracefully stop the opencode server and close all active sessions.
Returns: {"stopped": true, "sessions_closed": 2}
Gemini CLI tools (4)
These tools invoke the gemini CLI directly. Sessions are persisted to disk by the CLI — pass session_id to continue a conversation across calls.
Requires: gemini CLI installed and authenticated (OAuth or GEMINI_API_KEY).
gemini_check_auth
Check whether the Gemini CLI is authenticated before making prompt calls.
| Parameter | Type | Required | Default |
|---|---|---|---|
timeout_seconds |
int | No | 15 |
Returns:
{
"authenticated": true,
"method": "api_key_or_oauth",
"detail": "OK — model: gemini-2.5-flash-lite",
"suggestion": ""
}
If authenticated is false, suggestion tells you how to fix it.
gemini_prompt
Send a prompt to Gemini CLI. Returns the response and a session_id that can be passed back to continue the conversation.
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | The prompt to send. |
session_id |
string | No | Resume a previous session. Leave empty to start a new one. |
model |
string | No | E.g. gemini-2.5-flash. Defaults to the CLI's configured model. |
timeout_seconds |
int | No | Default: 120. |
project_dir |
string | No | Working directory. Defaults to current directory. |
Returns:
{
"response": "The word you asked me to remember is BLUEBIRD.",
"model": "gemini-2.5-flash-lite",
"session_id": "69cfc177-319c-484c-9..."
}
Multi-turn example:
# Turn 1 — new session
gemini_prompt(prompt="Remember the word BLUEBIRD")
→ { session_id: "69cfc177-..." }
# Turn 2 — continue session
gemini_prompt(prompt="What word did I ask you to remember?", session_id="69cfc177-...")
→ { response: "BLUEBIRD" }
gemini_list_sessions
List saved Gemini CLI sessions for the current project.
| Parameter | Type | Required | Description |
|---|---|---|---|
project_dir |
string | No | Defaults to current directory. |
timeout_seconds |
int | No | Default: 10. |
Returns:
{
"sessions": [
{"raw": "0: [2026-04-05] Remember the word BLUEBIRD"},
{"raw": "1: [2026-04-05] Explain the polycode architecture"}
]
}
Qwen Code CLI tools (3)
These tools invoke the qwen CLI directly. Sessions are persisted to disk by the CLI — pass session_id to continue a conversation across calls.
Requires: qwen CLI installed and authenticated (qwen auth qwen-oauth or qwen auth coding-plan).
qwen_check_auth
Check whether the Qwen Code CLI is authenticated before making prompt calls.
| Parameter | Type | Required | Default |
|---|---|---|---|
timeout_seconds |
int | No | 15 |
Returns:
{
"authenticated": true,
"method": "qwen-oauth",
"detail": "=== Authentication Status ===\n✓ Authentication Method: Qwen OAuth\n Type: Free tier",
"suggestion": ""
}
If authenticated is false, suggestion tells you the exact command to run.
qwen_prompt
Send a prompt to Qwen Code CLI. Returns the response and a session_id that can be passed back to continue the conversation.
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | The prompt to send. |
session_id |
string | No | Resume a previous session. Leave empty to start a new one. |
model |
string | No | E.g. qwen-plus. Defaults to the CLI's configured model. |
timeout_seconds |
int | No | Default: 120. |
project_dir |
string | No | Working directory. Defaults to current directory. |
Returns:
{
"response": "The word you asked me to remember was REDPANDA.",
"model": "coder-model",
"session_id": "ead03e7a-afff-4ccd-a..."
}
Multi-turn example:
# Turn 1 — new session
qwen_prompt(prompt="Remember the word REDPANDA")
→ { session_id: "ead03e7a-..." }
# Turn 2 — continue session
qwen_prompt(prompt="What word did I ask you to remember?", session_id="ead03e7a-...")
→ { response: "REDPANDA" }
Changing the opencode Model
The model format is provider/model-name. Set it via env var:
"env": {
"OPENCODE_DEFAULT_MODEL": "openai/gpt-4o"
}
Or call opencode_set_model at runtime. Call opencode_list_models to see all 182 available models across your connected providers.
Common models:
| Provider | Model string |
|---|---|
| Ollama (local) | ollama/qwen3.5:cloud |
| OpenAI | openai/gpt-4o |
| Anthropic | anthropic/claude-sonnet-4-5 |
google/gemini-2.5-flash |
|
| GitHub Copilot | github-copilot/claude-sonnet-4.6 |
Configuration
| Variable | Default | Description |
|---|---|---|
OPENCODE_DEFAULT_MODEL |
ollama/qwen3.5:cloud |
Default model for new opencode sessions |
OPENCODE_PORT |
0 (random) |
Port for the opencode server |
OPENCODE_STARTUP_TIMEOUT |
10 |
Seconds to wait for opencode to start |
OPENCODE_REQUEST_TIMEOUT |
120 |
Seconds before a generation times out |
OPENCODE_LOG_LEVEL |
INFO |
Log level: DEBUG, INFO, WARNING, ERROR |
OPENCODE_SERVER_PASSWORD |
(unset) | Optional HTTP Basic Auth password for the opencode server |
Error Handling
Every tool always returns a structured response — never a raw exception:
{
"error": "OpencodeBinaryNotFoundError",
"message": "gemini CLI not found on PATH. Install: npm install -g @google/gemini-cli",
"detail": {},
"recoverable": false,
"suggestion": "Install opencode via: npm install -g opencode-ai"
}
| Field | Description |
|---|---|
error |
Exception class name |
message |
What went wrong |
detail |
Structured context (stderr, attempted values, etc.) |
recoverable |
Whether retrying makes sense |
suggestion |
Exact next step to fix it |
Common errors:
| Error | Cause | Fix |
|---|---|---|
OpencodeBinaryNotFoundError |
CLI not on PATH | Install the CLI listed in suggestion |
OpencodeStartupError |
opencode failed to start | Increase OPENCODE_STARTUP_TIMEOUT; check opencode serve manually |
OpencodeTimeoutError |
Generation took too long | Increase OPENCODE_REQUEST_TIMEOUT or simplify the prompt |
OpencodeSessionError |
Session ID not found | Call opencode_list_sessions to see active sessions |
OpencodeValidationError |
Bad input or auth error | Read the suggestion field — includes exact CLI auth command if needed |
OpencodeProtocolError |
Unexpected CLI output shape | Update the CLI to the latest version |
Troubleshooting
"polycode not found" on Windows
Use the full path in your MCP config. Find it with:
where polycode
opencode server times out on startup
Cloud models do a network handshake on first use. Increase the timeout:
"env": { "OPENCODE_STARTUP_TIMEOUT": "30" }
Tools appear but calls hang
On Windows, subprocesses can inherit a blocked stdin from the MCP stdio pipe. This package sets stdin=DEVNULL on all subprocesses — ensure you are on polycode >= 0.1.0.
gemini_prompt or qwen_prompt returns an auth error
Run the auth check first:
gemini_check_auth→ readssuggestionfield for the fixqwen_check_auth→ readssuggestionfield for the fix
Then authenticate interactively (gemini or qwen auth qwen-oauth) and retry.
Running Tests
git clone https://github.com/h19overflow/polycode
cd polycode
pip install -e ".[dev]"
# Unit tests — no CLIs required
pytest tests/ --ignore=tests/test_integration.py -v
# Integration tests — requires opencode + ollama
pytest tests/test_integration.py -m integration -v
Contributing
- Fork the repo
pip install -e ".[dev]"- Write tests first (TDD)
pytest tests/ --ignore=tests/test_integration.pymust passpyright .must show 0 errors- Open a PR
License
MIT — see LICENSE
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.