Skip to main content
Glama

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.sh

This will:

  1. Copy .env.example to .env (edit it to point at your server)

  2. Create a Python virtual environment and install dependencies

  3. Register the server as local-model in ~/.claude/claude.json if present

  4. If WORKER_CLI_BIN is the default (qwen), patch ~/.qwen/settings.json with 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 Code

Verify:

.venv/bin/python check_env.py

Register 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_task

execute_task(task, working_dir, files?)

Execute a task on the model.

Args:

  • task (str): Detailed task description

  • working_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 but files_changed is 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 description

  • working_dir (str): Absolute path to project root

  • context_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 user

Best Practices

  1. Always health check first:

    status = health_check()
    if status["status"] != "online":
        # Fall back to manual implementation
  2. Use absolute paths:

    • working_dir: Must be absolute, must exist

    • context_files: Should be absolute for clarity

  3. Include full context in task string:

    • The model has no memory of previous calls

    • Explicitly list file names, requirements, constraints

  4. Parse output carefully:

    • files_changed uses git snapshots (before vs after)

    • Read full stdout for detailed results

  5. For multi-file tasks:

    • Use execute_task_with_context to pass existing code

    • Or 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 + SUFFIX

PREFIX:

[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
EOF

The final prompt structure when conventions exist:

PREFIX
PROJECT CONVENTIONS:
(contents of qwen-memory/project-conventions.md)

task

SUFFIX

Troubleshooting

" 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_TIMEOUT in server.py if needed

Files not detected in output

  • files_changed uses 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 stdout for 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 logging

Environment 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=qwen

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Local 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.
    3
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables 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.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables 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.
    3
    157
    4
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/alkrcaaa/local-llm-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server