local-model
Provides integration with Ollama, enabling coding tasks to be delegated to local models running on an Ollama server through a configurable coding-agent CLI.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@local-modelAdd unit tests for the add() function in src/utils.py and run them locally."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
local-model-mcp
MCP (Model Context Protocol) server that lets Claude Code (or any MCP client)
delegate coding tasks to a local model through a coding-agent CLI — pointed at
whatever OpenAI-compatible endpoint you run: a local vLLM instance, Ollama, or
any other self-hosted server. Ships configured for the
Qwen Code CLI by default (WORKER_CLI_BIN=qwen),
but the model behind MODEL_BASE_URL — and the CLI binary itself — are both
configurable.
Architecture
┌─────────────────────────┐
│ Claude Code / Claude │
│ (MCP Client) │
└────────────┬────────────┘
│ MCP Protocol
▼
┌─────────────────────────┐
│ local-model-mcp │
│ (this project) │
│ │
│ - execute_task │
│ - health_check │
│ - execute_task_with_ │
│ context │
└────────────┬────────────┘
│ subprocess + env vars
▼
┌─────────────────────────┐
│ Worker CLI │
│ (WORKER_CLI_BIN, │
│ default: qwen) │
│ │
│ http://YOUR_SERVER: │
│ 8002/v1 │
└─────────────────────────┘Related MCP server: Clanker
Installation
Prerequisites
Python 3.9+
The worker CLI in
PATH(Qwen Code CLI by default)An OpenAI-compatible model server reachable at some
http://HOST:PORT/v1
Install
./install.shThis will:
Copy
.env.exampleto.env(edit it to point at your server)Create a Python virtual environment and install dependencies
Register the server as
local-modelin~/.claude/claude.jsonif presentIf
WORKER_CLI_BINis the default (qwen), patch~/.qwen/settings.jsonwith the write/exec permissions the CLI needs to run unattended
Configure
Edit .env:
MODEL_BASE_URL=http://your-server:8002/v1
MODEL_NAME=/models/your-model # optional — auto-detected from the server if unset
WORKER_CLI_BIN=qwen # optional — set to a different coding-agent CLI if not using Qwen CodeVerify:
.venv/bin/python check_env.pyRegister manually (if install.sh didn't find claude.json)
Add to .mcp.json or ~/.claude/claude.json:
{
"mcpServers": {
"local-model": {
"command": "local-model-mcp/.venv/bin/python",
"args": ["local-model-mcp/server.py"]
}
}
}.env in this directory supplies MODEL_BASE_URL/MODEL_NAME/WORKER_CLI_BIN if you don't set them in the client's own env block — either works, the client's env block wins.
Tools
health_check()
Check if the model server is online.
Returns:
{
"status": "online" | "offline",
"models": ["model1", "model2"],
"url": "http://YOUR_SERVER:8002/v1",
"error": "optional error message"
}Usage:
result = await client.call_tool("health_check", {})
if result["status"] == "online":
# Safe to proceed with execute_taskexecute_task(task, working_dir, files?)
Execute a task on the model.
Args:
task(str): Detailed task descriptionworking_dir(str): Absolute path to working directory (must exist)files(list[str], optional): File paths to mention in context
Returns:
{
"status": "success" | "error" | "timeout",
"return_code": 0,
"stdout": "task output",
"stderr": "error output if any",
"files_changed": ["src/file.py", "tests/test.py"],
"warning": "null or warning string if the model responded but made no file changes"
}Return fields:
files_changed— Git-based diff of files modified or created (snapshots dirty state before and after execution). Requires a git repo; returns empty list for non-git projects.warning— Non-null when the run succeeded butfiles_changedis empty on a previously clean repo. Usually the task description was too vague. Rephrase with explicit file paths and concrete instructions.
Example:
result = await client.call_tool("execute_task", {
"task": "Write unit tests for add() function in src/utils.py",
"working_dir": "/Users/you/workspace/myproject",
"files": ["src/utils.py"]
})execute_task_with_context(task, working_dir, context_files)
Execute task with file contents prepended to the prompt.
Args:
task(str): Task descriptionworking_dir(str): Absolute path to project rootcontext_files(list[str]): File paths to read and include as context
Returns:
Same as execute_task
Example:
result = await client.call_tool("execute_task_with_context", {
"task": "Add error handling to the request function",
"working_dir": "/Users/you/workspace/api",
"context_files": [
"/Users/you/workspace/api/src/client.py",
"/Users/you/workspace/api/src/errors.py"
]
})Usage from Claude Code
In your prompt when invoking tools:
AVAILABLE MCP:
- local-model (health_check, execute_task, execute_task_with_context)
WORKFLOW:
1. Call health_check() → verify the model server is online
2. IF online → call execute_task() with full task details
3. IF offline → implement yourself, notify userBest Practices
Always health check first:
status = health_check() if status["status"] != "online": # Fall back to manual implementationUse absolute paths:
working_dir: Must be absolute, must existcontext_files: Should be absolute for clarity
Include full context in task string:
The model has no memory of previous calls
Explicitly list file names, requirements, constraints
Parse output carefully:
files_changeduses git snapshots (before vs after)Read full
stdoutfor detailed results
For multi-file tasks:
Use
execute_task_with_contextto pass existing codeOr include file paths in task string with full instructions
Prompt Engineering
Sandwich Instruction Pattern
Every execute_task call automatically wraps the task with a PREFIX and SUFFIX to combat "description instead of code" failures. The model is instructed to write complete, runnable code — not explanations, partial snippets, or markdown-wrapped output.
The prompt structure is:
PREFIX + [project conventions] + task + SUFFIXPREFIX:
[INSTRUCTION: You are a code-writing worker.
Write complete, runnable code only.
No descriptions or explanations instead of code.
No markdown fences around output.
Write the full file content, not partial snippets.]SUFFIX:
[REMINDER: Output only complete, runnable code.
No descriptions. No markdown fences. Full file content.]This "sandwich" approach combats the "lost in the middle" problem on long prompts — the model sees code-writing instructions both before and after the task.
Automatic Project Conventions Injection
If the working directory contains qwen-memory/project-conventions.md, its contents are automatically injected between the PREFIX and the task. This directory name is kept as-is (not generalized along with the rest of this project) so existing per-project convention files keep working without a silent migration. This lets projects define conventions once and have them applied to every call without repeating them each time.
mkdir -p /path/to/project/qwen-memory
cat > /path/to/project/qwen-memory/project-conventions.md << 'EOF'
- Use Python type hints on all function signatures
- Write docstrings using Google style
- Run black and ruff before committing
- Tests live in tests/ and use pytest
EOFThe final prompt structure when conventions exist:
PREFIX
PROJECT CONVENTIONS:
(contents of qwen-memory/project-conventions.md)
task
SUFFIXTroubleshooting
" CLI not found"
which qwen # or whatever WORKER_CLI_BIN points to
# If not in PATH, add to ~/.zshrc or ~/.bashrc:
export PATH="/path/to/qwen/bin:$PATH""Cannot connect to http://YOUR_SERVER:8002/v1"
curl http://YOUR_SERVER:8002/v1/models
# If not running, start your model server first.Task timeout (>300s)
Reduce task scope
Break into smaller tasks
Increase
EXECUTION_TIMEOUTinserver.pyif needed
Files not detected in output
files_changeduses git snapshots (before vs after the run)If the repo was already dirty beforehand, pre-existing changes are not counted
Non-git projects return an empty list
Always read full
stdoutfor file paths
Development
.venv/bin/python -m pytest tests/ # run tests
.venv/bin/python server.py # start server manually
DEBUG=true .venv/bin/python server.py # enable MCP debug loggingEnvironment Variables
Set via .env (see .env.example) or exported directly:
MODEL_BASE_URL=http://custom-host:8002/v1
MODEL_NAME=/models/custom-model
WORKER_CLI_BIN=qwenThis server cannot be deployed
Maintenance
Related MCP Connectors
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.
One MCP endpoint for Claude, GPT & Gemini: 100+ tools + no-code connectors + agent workers.
A paid remote MCP for OpenAI Codex agent coordination MCP, built to return verdicts, receipts, usage
Related MCP Servers
- AlicenseAqualityCmaintenanceLocal MCP server that wraps the headless Claude Code CLI as MCP tools, providing stateless access to Claude's coding capabilities through prompt-based interactions. It enables users to execute Claude Code commands with various prompt formats and structured outputs directly from MCP clients.3MIT
- AlicenseAqualityFmaintenanceMCP server for running external coding agents as background tasks inside Claude Code. Supports multiple backends including Codex, Grok, GLM, DeepSeek, and more.7MIT
- FlicenseNot gradedqualityCmaintenanceEnables ISLI agents and MCP clients to dispatch natural-language coding and terminal tasks to a locally-installed Claude Code CLI, supporting both one-shot execution and persistent sessions with workspace and security controls.-
- AlicenseAqualityAmaintenanceEnables MCP clients like Claude Code and Codex to delegate coding tasks to Cursor's CLI agent, which implements changes in the workspace and returns clean, structured results for review.3181 npm4MIT