A Docker-powered MCP server framework — extend with drop-in kit files and connect to Claude Desktop, LM Studio, OpenWebUI, and more.
Project description
SimpleMCP V2
A Docker-powered MCP-compatible tool server you extend with drop-in kit files — no server code to touch, no venvs to manage. Drop a _kit.py file in, install it with one command, and it's live.
How it works
- CLI (
simplemcp): Manages the full lifecycle — install kits, start/stop/restart/rebuild the server, configure client integrations, manage the CDP browser broker. - Docker container: The server runs in an isolated container built from a bundled Dockerfile. On first
simplemcp start, the image is built automatically. Usesimplemcp rebuild --pullto pull the latest precompiled image fromghcr.io/dwhite-sys/simplemcp. - Kits (
~/.simplemcp/kits/): Each kit is a plain Python file with@tool-decorated functions and four metadata fields. The container mounts this directory as a volume — kits are live without rebuilding. bootstrap.py: Runs inside the container on every start. Clears__pycache__so edits to kit files are always picked up fresh. AST-parses each kit'srequirementslist and installs anything missing viauv pip. Starts the socat CDP proxy (localhost:9222 → host.docker.internal:9222) for the Playwright kit. Execs the real server when done.server.py: FastAPI server inside the container. Auto-discovers all kits at startup, and again on hot-reload. Speaks both SimpleMCP protocol and MCP JSON-RPC on the same port — no mode flag needed. Each kit also gets its own dedicated MCP port (base port + 1, +2, … per kit) for per-kit stdio shim connections.utils/registry.py: The@tooldecorator registers functions into a global dict. Type hints are scraped to build JSON Schema for each tool's input automatically.stdio_shim.py: Thin bridge that translates MCP JSON-RPC from stdin to the already-running container's HTTP API. One shim process per kit entry in the client config, all sharing the same container.- Config (
~/.simplemcp/config.json): Tracks installed kits, running port, container ID, and per-kit port assignments. Written and read exclusively by the CLI. - Kit configs (
~/.simplemcp/kit_configs/<kit_stem>/config.json): Per-kit config values. Populated from the kit'sconfig = {}dict on install (preserving any values the user has already set), volume-mounted into the container at runtime.
Kit anatomy
# my_kit.py
from utils import tool
kit_name = "My Tools"
kit_description = "What this kit does."
requirements = ["requests", "some-package"]
config = {"MY_VAR": "default_value"}
@tool
def do_something(query: str, limit: int = 10):
"""
Does something cool with a query.
query: The search query to use.
limit: Maximum number of results (default 10).
Returns {"result": str, "count": int}.
"""
my_var = os.getenv("MY_VAR", "default_value")
return {"result": "stuff", "count": limit}
kit_nameandkit_descriptionare displayed by the protocol,simplemcp list, and client UIs.requirementsis AST-parsed bybootstrap.py— no import needed. Missing packages are installed on container start, or immediately into a live container onsimplemcp install.configis a dict of{"VAR_NAME": "default"}pairs. Values are stored in~/.simplemcp/kit_configs/<kit_stem>/config.json, volume-mounted into the container, and available as environment variables. User-set values are preserved across reinstalls.@toolis the only import required. Type hints are mandatory — they build the JSON Schema. Docstrings become the tool description shown to the model; write them for the model, not for humans.
Server protocol
One container, one port, both protocols always running. Each kit also gets its own dedicated MCP port for per-kit stdio connections.
SimpleMCP protocol
| Endpoint | Method | Body | Description |
|---|---|---|---|
/list_kits |
GET | — | All installed kit names and descriptions |
/inspect_kit |
POST | {"kit": "Name"} |
Kit metadata (name, description, tool count) |
/list_tools_in_kit |
POST | {"kit": "Name"} |
Full tool schemas for a kit |
/inspect_tool |
POST | {"tool": "fn_name"} |
Schema for a single tool |
/run_tool |
POST | {"tool": "fn_name", "arguments": {...}} |
Execute a tool, returns result directly |
/reload_kit |
POST | {"kit_stem": "my_kit"} |
Hot-reload a kit without restart |
MCP protocol (JSON-RPC 2.0)
| Endpoint | Method | Description |
|---|---|---|
/mcp |
POST | Handles initialize, tools/list, tools/call |
/mcp |
GET | SSE keepalive stream |
The /mcp endpoint accepts an optional X-SimpleMCP-Kit header to scope tool listings and calls to a single kit — used automatically by the stdio shim.
Per-kit MCP ports
Each installed kit gets its own MCP port (e.g. kit 1 → 8468, kit 2 → 8469, …). The same /mcp endpoint is served on each port, pre-scoped to that kit. This is what simplemcp compat uses when writing client configs — each kit gets its own stdio shim pointed at its dedicated port.
stdio mode
simplemcp start stdio [kit_name] runs a shim process that bridges MCP JSON-RPC from stdin to the already-running container's per-kit MCP port. The container must already be running.
- No per-connection container spawning — zero overhead per client connect
- Multiple shim processes share the same container
- Each shim targets a specific kit's dedicated port, so tool listings are automatically scoped
- Omit
kit_nameto connect to the main port (all kits)
CLI reference
simplemcp install <path/to/kit.py> Install or update a kit
simplemcp remove <kit_name> Remove a kit
simplemcp list List installed kits and status
simplemcp start Start the server (progress bar)
simplemcp start --verbose Start with full log output
simplemcp start stdio Start stdio shim (all kits)
simplemcp start stdio <kit_name> Start stdio shim (single kit)
simplemcp stop Stop the server
simplemcp restart Restart the server
simplemcp rebuild Build image locally and restart
simplemcp rebuild --pull Pull precompiled image from ghcr.io and restart
simplemcp rebuild --verbose Show full Docker build output
simplemcp compat claude Write kit entries to Claude Desktop config
simplemcp compat lmstudio Write kit entries to LM Studio config
simplemcp compat openwebui <url> <api_key> Register kits with OpenWebUI
simplemcp config list <kit_name> List config variables for a kit
simplemcp config get <kit_name> <var> Get a config value
simplemcp config set <kit_name> <var> <val> Set a config value
simplemcp config reset <kit_name> <var> Reset a config value to its default
simplemcp browser start Start Chrome with CDP + the broker process
simplemcp browser stop Stop the Chrome CDP broker
simplemcp browser status Show broker and Chrome CDP status
Environment variables and kit config
Kits receive configuration through environment variables. There are two sources:
Host env forwarding — The host environment is filtered through an allowlist before being passed into the container. Only variables matching these suffixes are forwarded: _API_KEY, _TOKEN, _SECRET, _URL, _INSTANCE, _HOST, _PORT, _USER, _PASSWORD, _DB. Exact matches TZ, LANG, LC_ALL are also forwarded. Everything else (Windows paths, GPU vars, session vars, etc.) is blocked. Any localhost or 127.0.0.1 references in forwarded values are rewritten to host.docker.internal so kit code can reach host services from inside the container.
Kit config — The config = {"VAR": "default"} dict in a kit file is stored in ~/.simplemcp/kit_configs/<kit_stem>/config.json on install and volume-mounted into the container. Manage values with simplemcp config set/get/list/reset. These are available as environment variables inside the container and take precedence over host env forwarding.
Setup
pip install simplemcp-framework
Docker is the only other dependency. On Linux, simplemcp will attempt to install Docker automatically if missing and will fix docker group permissions with a single sudo call if needed.
simplemcp start
The Docker image builds automatically on first start (~2 minutes). Kit dependencies are installed into a persistent volume so subsequent starts are fast. To use the precompiled image instead:
simplemcp rebuild --pull
Adding a kit
simplemcp install my_kit.py
If the server is already running, the kit is hot-reloaded immediately — no restart needed. Running the same command again on an already-installed kit prints "Updated" and clears the stale bytecode for that kit.
Client integrations
Claude Desktop
simplemcp compat claude
Writes a mcpServers entry for each installed kit to claude_desktop_config.json (path is OS-aware). Each kit gets its own stdio entry pointed at its dedicated port. Removes stale entries for uninstalled kits. Restart Claude Desktop after.
Manual config example:
{
"mcpServers": {
"Web Kit V2": {
"command": "simplemcp",
"args": ["start", "stdio", "web_kit"]
},
"Gotify": {
"command": "simplemcp",
"args": ["start", "stdio", "gotify_kit"]
}
}
}
LM Studio
simplemcp compat lmstudio
Same format as Claude Desktop, written to ~/.lmstudio/mcp.json. Restart LM Studio after.
OpenWebUI
simplemcp compat openwebui https://your-instance:port sk-your-api-key
Hits POST /api/v1/configs/tool_servers directly — no manual UI steps. Existing non-SimpleMCP tool server entries are preserved. Generate an API key under your OpenWebUI profile → Settings → Account → API Keys (enable API keys in Admin Panel → Settings → General first).
Browser automation (Playwright kit)
The Playwright V2 kit connects to Chrome on your host via the Chrome DevTools Protocol (CDP). It doesn't launch its own browser — it drives the one you're already using.
simplemcp browser start
Launches Chrome with --remote-debugging-port=9222 and starts the broker process that the kit talks to. A socat proxy inside the container forwards localhost:9222 → host.docker.internal:9222 to work around Chrome's host header validation.
Available tools: browser_start, browser_stop, browser_list_tabs, browser_open_tab, browser_close_tab, browser_navigate, browser_get_text, browser_get_page, browser_click, browser_fill_and_submit, browser_screenshot, browser_evaluate.
Tabs are identified by their CDP target ID (a stable UUID assigned by Chrome). No manual tab registry — _context.pages is always the live source of truth.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file simplemcp_framework-2.1.10.tar.gz.
File metadata
- Download URL: simplemcp_framework-2.1.10.tar.gz
- Upload date:
- Size: 59.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09f4851dc24c823fc97108de4f3c3a1b9c142750efe223077c8e9beb1473c396
|
|
| MD5 |
d1bc7150691dde069d3e33502584ffcf
|
|
| BLAKE2b-256 |
d1464b532e7c3fbbdbd6e52edd6cc3bf01b810c3f48ca5a082c803120927f8ee
|
File details
Details for the file simplemcp_framework-2.1.10-py3-none-any.whl.
File metadata
- Download URL: simplemcp_framework-2.1.10-py3-none-any.whl
- Upload date:
- Size: 62.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fad8982c012b474b3ff819f3bfbc4b6867943fdbe1c1f6341fab64577fb0cf2
|
|
| MD5 |
12df6596f1f8142adfe08dfc247f7f76
|
|
| BLAKE2b-256 |
7dc1b33d8c5d2b8b7af0db571985c4b32fa05dd41639e1214fd585d2570cc78f
|