LocalLLM-MCP
MCP server that exposes local llama.cpp LLM models to IBM Bob in VS Code via STDIO, enabling natural language interaction with models like Granite, Nemotron, Gemma, Qwen, and Llama. Configuration is managed through a single models.json file, making it easy to add or disable models without code changes.
README
LocalLLM-MCP
A production-ready Python MCP (Model Context Protocol) server that exposes local LLM models running under llama.cpp to IBM Bob in VS Code via the STDIO transport.
All model configuration lives in models.json. Adding a new model requires
editing that file only — no Python changes needed.
Table of Contents
- Architecture
- Prerequisites
- Installation
- Configuration
- Adding a New Model
- IBM Bob Integration
- MCP Tools Reference
- Supported Runtimes
- Troubleshooting
- Testing
Architecture
<project-root>\
│
├── server.py MCP entry point — STDIO transport, tool registration
├── router.py Orchestration — validates requests, calls HTTP client
├── config.py Pydantic loader — reads models.json + .env
├── models.json All model configuration (single source of truth)
├── requirements.txt Pinned Python dependencies
├── .env.example Environment variable template
├── install.bat One-click setup (creates .venv, installs deps)
└── start_server.bat Manual launch for testing
│
└── src/
├── clients/
│ └── openai_client.py Async HTTP client — POST /v1/chat/completions
├── logging_setup/
│ └── logger.py Structured JSON logging to stderr
└── utils/
└── helpers.py retry_async, with_timeout, sanitize_model_name
Data Flow
IBM Bob (VS Code)
│ JSON-RPC over stdin/stdout
▼
server.py ── @server.tool() handlers
│
▼
router.py ── validate model → get config → call client
│
▼
src/clients/openai_client.py
│ POST /v1/chat/completions
▼
llama-server instance (one per model, unique port)
│
▼
*.gguf model file on D:\Local-LLM\
Transport: STDIO — Bob spawns server.py as a child process. MCP
JSON-RPC messages travel over stdin/stdout. All application logs travel
over stderr and never corrupt the MCP stream.
Prerequisites
| Requirement | Version | Notes |
|---|---|---|
| Python | 3.14+ | Must be on PATH |
| llama.cpp | latest | llama-server binary must be on PATH or full path used |
| IBM Bob | latest | VS Code extension installed |
| VS Code | latest | |
| Internet access | — | Required during install.bat only (pip download) |
Installation
Step 1 — Clone / copy the project
Place the project folder anywhere on your machine (e.g. C:\LocalLLM-MCP\) and
update the paths in mcp.json accordingly.
Step 2 — Run the installer
Open a terminal in the project root and run:
cd C:\path\to\LocalLLM-MCP
install.bat
This will:
- Create
.venv\(Python virtual environment) - Upgrade pip
- Install all dependencies from
requirements.txt - Copy
.env.example→.env(if.envdoes not already exist)
Step 3 — Start llama-server instances
Each model requires its own llama-server process running on a unique port.
Open five separate terminals and run one command per terminal:
REM Granite (port 8080)
llama-server --model "<YOUR_MODELS_DIR>\IBM-Models\granite-4.1-3b\<model>.gguf" --port 8080
REM Nemotron (port 8081)
llama-server --model "<YOUR_MODELS_DIR>\Nvidia-Models\NVIDIA-Nemotron-3-Nano-4B-GGUF\<model>.gguf" --port 8081
REM Gemma (port 8082)
llama-server --model "<YOUR_MODELS_DIR>\Google-Models\googlegemma-4-E4B-it-qat-q4_0-gguf\<model>.gguf" --port 8082
REM Qwen (port 8083)
llama-server --model "<YOUR_MODELS_DIR>\Alibaba-Models\Qwen2.5-Coder-3B-Instruct-GGUF\<model>.gguf" --port 8083
REM Llama (port 8084)
llama-server --model "<YOUR_MODELS_DIR>\Meta-Models\Llama-3.2-3B-Instruct-Q4_K_M-GGUF\<model>.gguf" --port 8084
Replace
<model>.ggufwith the actual filename inside each folder.
Step 4 — Register with IBM Bob
Add the snippet from the IBM Bob Integration section
to your mcp.json file, then restart Bob.
Configuration
models.json field reference
models.json is the single source of truth for all model configuration.
It lives in the project root and is loaded at server startup.
{
"models": {
"<key>": {
"display_name": "Human-readable name shown in list_models()",
"vendor": "Model vendor / creator",
"runtime": "llama.cpp",
"model_path": "Absolute path to the model FOLDER on disk",
"endpoint": "Base URL of the running llama-server, e.g. http://localhost:8080",
"context_length": 8192,
"temperature": 0.7,
"enabled": true
}
}
}
| Field | Type | Required | Description |
|---|---|---|---|
display_name |
string | yes | Shown in list_models() responses |
vendor |
string | yes | Model creator (IBM, NVIDIA, Google, …) |
runtime |
string | yes | Always "llama.cpp" in this deployment |
model_path |
string | yes | Absolute path to the model folder (not the .gguf file) |
endpoint |
string | yes | Full base URL of the llama-server for this model |
context_length |
integer | yes | Max context window in tokens |
temperature |
float | yes | Sampling temperature (0.0 – 2.0) |
enabled |
boolean | yes | false hides the model from Bob and blocks calls to it |
.env variable reference
Copy .env.example to .env and edit as needed. Variables set in .env
override models.json endpoint defaults. OS-level environment variables
take precedence over .env.
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL |
INFO |
Logging verbosity: DEBUG, INFO, WARNING, ERROR |
REQUEST_TIMEOUT_SECONDS |
120 |
Seconds before a request is abandoned |
MAX_RETRIES |
3 |
Retry attempts on connection errors and 5xx responses |
GRANITE_ENDPOINT |
(from models.json) | Override endpoint for the granite model |
NEMOTRON_ENDPOINT |
(from models.json) | Override endpoint for the nemotron model |
GEMMA_ENDPOINT |
(from models.json) | Override endpoint for the gemma model |
QWEN_ENDPOINT |
(from models.json) | Override endpoint for the qwen model |
LLAMA_ENDPOINT |
(from models.json) | Override endpoint for the llama model |
The endpoint override pattern works for any model key:
<MODEL_KEY_UPPER>_ENDPOINT=http://...
Adding a New Model
No Python code changes are needed. Edit models.json only.
Example — adding a new Mistral model
1. Add an entry to models.json:
"mistral": {
"display_name": "Mistral 7B Instruct",
"vendor": "Mistral AI",
"runtime": "llama.cpp",
"model_path": "D:\\Local-LLM\\Mistral-Models\\Mistral-7B-Instruct-GGUF",
"endpoint": "http://localhost:8085",
"context_length": 32768,
"temperature": 0.7,
"enabled": true
}
2. Start a new llama-server instance on port 8085:
llama-server --model "D:\Local-LLM\Mistral-Models\Mistral-7B-Instruct-GGUF\mistral-7b-instruct.gguf" --port 8085
3. Restart the MCP server (Bob will restart it automatically on next use, or restart VS Code).
4. Verify: ask Bob to call list_models() — the new model should appear.
To disable a model temporarily
Set "enabled": false in models.json and restart the server.
The model will no longer appear in list_models() and calls to it
will return a clear error message.
IBM Bob Integration
The server communicates with Bob via STDIO transport — Bob spawns
server.py as a child process; no port or HTTP server is needed.
Step 1 — Locate your mcp.json
Bob supports two configuration levels:
| Level | File location | Scope |
|---|---|---|
| Global | C:\Users\<YOUR_USERNAME>\.bob\mcp.json |
All workspaces |
| Project | C:\path\to\LocalLLM-MCP\.bob\mcp.json |
This project only |
If the file does not exist, create it.
Step 2 — Add the server entry
Add the following JSON to your chosen mcp.json:
{
"mcpServers": {
"localllm-mcp": {
"command": "C:\\path\\to\\LocalLLM-MCP\\.venv\\Scripts\\python.exe",
"args": ["C:\\path\\to\\LocalLLM-MCP\\server.py"],
"cwd": "C:\\path\\to\\LocalLLM-MCP",
"env": {
"LOG_LEVEL": "INFO"
},
"alwaysAllow": ["list_models", "health_check"],
"disabled": false
}
}
}
If you placed the project at a different path, update
command,args, andcwdaccordingly.
Step 3 — Reload Bob
In VS Code, open the Bob panel → Settings → MCP tab, or restart VS Code. Bob will spawn the server process automatically when a tool is first called.
Step 4 — Verify
Ask Bob:
Call the health_check tool on the localllm-mcp server.
Expected response:
{
"status": "ok",
"models_total": 5,
"models_enabled": 5,
"model_keys": ["gemma", "granite", "llama", "nemotron", "qwen"],
"runtime": "llama.cpp"
}
MCP Tools Reference
ask_model
Send a prompt to a model and receive the complete response.
| Parameter | Type | Description |
|---|---|---|
model_name |
string | Model key (e.g. granite, llama, gemma, nemotron, qwen) |
prompt |
string | The text prompt to send |
Example:
Ask granite: "Explain what the MCP protocol is in two sentences."
ask_model_stream
Send a prompt and receive the response assembled from streaming token
chunks. Functionally identical to ask_model from Bob's perspective;
uses less peak memory on the server for long responses.
| Parameter | Type | Description |
|---|---|---|
model_name |
string | Model key |
prompt |
string | The text prompt to send |
list_models
List all enabled models and their metadata. No parameters.
Example response:
{
"models": [
{
"key": "granite",
"display_name": "IBM Granite 4.1 3B",
"vendor": "IBM",
"runtime": "llama.cpp",
"endpoint": "http://localhost:8080",
"context_length": 8192,
"temperature": 0.7
},
...
]
}
model_pathis intentionally omitted from responses.
health_check
Report server and configuration status. No parameters. Does not contact inference servers — fast in-process check only.
Example response:
{
"status": "ok",
"models_total": 5,
"models_enabled": 5,
"model_keys": ["gemma", "granite", "llama", "nemotron", "qwen"],
"runtime": "llama.cpp"
}
Supported Runtimes
This deployment uses llama.cpp for all models. The server is designed to
work with any OpenAI-compatible inference API. To switch a model to a
different runtime, update its endpoint in models.json — no Python
changes needed.
| Runtime | Default Port | OpenAI-compatible endpoint | Notes |
|---|---|---|---|
| llama.cpp | 8080–8084 | /v1/chat/completions |
Used in this deployment |
| Ollama | 11434 | /v1/chat/completions |
Requires OLLAMA_ORIGINS=* |
| LM Studio | 1234 | /v1/chat/completions |
Enable local server in UI |
| vLLM | 8000 | /v1/chat/completions |
python -m vllm.entrypoints.openai.api_server |
| NVIDIA NIM | 8000 | /v1/chat/completions |
Docker container |
Troubleshooting
[Error] Could not reach 'IBM Granite 4.1 3B' at http://localhost:8080
The llama-server for that model is not running. Start it:
llama-server --model "D:\Local-LLM\IBM-Models\granite-4.1-3b\<model>.gguf" --port 8080
[Error] Unknown model 'xyz'. Available model keys: ...
The model key you used does not match any key in models.json.
Use one of the listed keys. Keys are case-insensitive.
[Error] Model 'xyz' is disabled.
The model has "enabled": false in models.json. Set it to true and
restart the MCP server.
models.json failed validation: ...
models.json contains a syntax error or an invalid field value. Run the
config smoke test to see the exact error:
cd C:\LocalLLM-MCP
.venv\Scripts\python config.py
Bob does not see the server / tools are not listed
- Check that
mcp.jsonexists and has valid JSON (no trailing commas). - Check that
disabledisfalsein the server entry. - Check that the path in
commandpoints to the correct.venv\Scripts\python.exe. - Restart VS Code.
- Look at the Bob output panel for MCP connection errors.
Server starts but requests time out
- Increase
REQUEST_TIMEOUT_SECONDSin.env(default: 120 seconds). - Check that the llama-server for that model is fully loaded (watch its
terminal — it prints
llama server listeningwhen ready). - Reduce
context_lengthinmodels.jsonfor the slow model.
Port conflict — address already in use
Another process is using that port. Either:
- Stop the conflicting process:
netstat -ano | findstr :8080 - Change the port in
models.jsonand.env.examplefor that model, and restart llama-server on the new port.
Testing
1. Config smoke test
Verifies models.json loads and validates correctly:
cd C:\LocalLLM-MCP
.venv\Scripts\python config.py
Expected: prints all 5 models with their endpoints and exits with code 0.
2. Logger smoke test
Verifies structured JSON logging to stderr:
.venv\Scripts\python -m src.logging_setup.logger
3. Utilities smoke test
Verifies retry decorator, timeout, and name sanitisation:
.venv\Scripts\python -m src.utils.helpers
4. HTTP client smoke test (offline)
Verifies the client raises ConnectionError correctly when no server is running:
.venv\Scripts\python -m src.clients.openai_client
5. Router smoke test
Verifies validation, error handling, and config integration:
.venv\Scripts\python router.py
6. Full server tools test (manual)
Start a llama-server on port 8080, then run:
.venv\Scripts\python -c "
import asyncio
import server
async def t():
print(await server.health_check())
print(await server.list_models())
print(await server.ask_model('granite', 'Say hello in one sentence.'))
asyncio.run(t())
"
7. End-to-end via Bob
With at least one llama-server running, open Bob in VS Code and ask:
Use the localllm-mcp server to ask granite: "What is 2 + 2?"
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。