A production-grade Python host for the Model Context Protocol β multi-LLM, multi-transport, spec-compliant.
The Python brain behind the Dive desktop app β also runnable standalone as a service or library.
The MCP ecosystem is growing fast, but most hosts are tightly coupled to a single LLM, a single transport, or a single front-end. Dive MCP Host is the opposite: a transport-agnostic, model-agnostic runtime that turns any collection of MCP servers into a single agent backend.
- π§ Any LLM β OpenAI, Anthropic, Google, AWS Bedrock, Mistral, DeepSeek, Ollama, Azure OpenAI β anything LangChain speaks.
- π Any MCP server β
stdio,sse,streamable-http,websocket, plus a "spawn-and-connect" local-HTTP mode. - π° Three ways to use it β embed the SDK, talk to the HTTP API, or drive it from the CLI.
- π§© Spec-compliant capability negotiation β calls
tools/list/prompts/listonly when the server advertises them; gracefully toleratesMethod not found. - π¬ MCP prompts as first-class β list, fetch, render, and refresh on
notifications/prompts/list_changed. - π OAuth + elicitation built in β interactive flows surface to the host without bespoke glue per server.
- π Persistent threads β SQLite or Postgres checkpointing via LangGraph; messages, OAuth tokens, and conversation state survive restarts.
flowchart LR
subgraph Clients
UI[Dive Desktop<br/>Electron / Tauri]
CLI[dive_cli]
SDK[Python SDK<br/>DiveMcpHost]
end
subgraph Host[dive-mcp-host]
HTTP[FastAPI<br/>dive_httpd]
Core[DiveMcpHost<br/>+ ToolManager<br/>+ Chat / LangGraph]
DB[(SQLite / Postgres<br/>messages β’ OAuth β’ checkpoints)]
end
subgraph LLMs
OAI[OpenAI]
Anthropic[Anthropic]
Ollama[Ollama]
Etc[β¦and more]
end
subgraph MCPServers[MCP Servers]
Stdio[stdio<br/>local subprocess]
SSE[SSE]
StreamHTTP[streamable-http]
WS[websocket]
end
UI --> HTTP
CLI --> Core
SDK --> Core
HTTP --> Core
Core --> LLMs
Core --> MCPServers
Core --> DB
The same DiveMcpHost core is reused by all three entry points. The HTTP
layer is a thin FastAPI wrapper; the CLI and SDK are even thinner.
Everything is an async context manager (ContextProtocol). Resources nest
strictly so cleanup is deterministic:
sequenceDiagram
participant App
participant Host as DiveMcpHost
participant TM as ToolManager
participant Srv as McpServer Γ N
participant Chat
App->>Host: async with DiveMcpHost(config)
Host->>TM: enter
TM->>Srv: spawn / connect each server (parallel)
Srv->>Srv: initialize() β read capabilities
Srv-->>Srv: tools/list (if advertised)
Srv-->>Srv: prompts/list (if advertised)
App->>Host: host.chat(chat_id=...)
Host->>Chat: build LangGraph agent over tools
App->>Chat: query("hello")
Chat-->>App: stream events
App->>Host: __aexit__
Host->>TM: shutdown all servers
# Recommended: uv (respects uv.lock)
uv sync --frozen
# Or with pip
pip install -e .
# With dev tools (pytest, ruff, β¦)
uv sync --extra dev --frozenRequires Python 3.12+.
dive_httpdServes on 0.0.0.0:61990 by default. Configure via dive_httpd.json (DB,
checkpointer, CORS).
dive_cli "Summarize today's news"
dive_cli -c CHAT_ID "and what about tech?" # resume a threadfrom dive_mcp_host.host.host import DiveMcpHost
from dive_mcp_host.host.conf import HostConfig
config = HostConfig(...) # see model_config.json / mcp_config.json samples
async with DiveMcpHost(config) as host:
async with host.chat(chat_id="demo") as chat:
async for event in chat.query("hello"):
print(event)Three JSON files drive the service. Samples are at the repo root.
π mcp_config.json β which MCP servers to mount
{
"mcpServers": {
"fetch": {
"transport": "stdio",
"command": "uvx",
"args": ["mcp-server-fetch@latest"]
},
"weather": {
"transport": "streamable",
"url": "https://example.com/mcp"
}
}
}Supported transport values: stdio, sse, streamable, websocket.
Add "command" together with "url" to spawn a local HTTP MCP server
and connect to it.
π model_config.json β which LLM to use
{
"activeProvider": "ollama",
"configs": {
"openai": { "modelProvider": "openai", "model": "gpt-4o-mini", "apiKey": "..." },
"anthropic": { "modelProvider": "anthropic", "model": "claude-sonnet-4-5" },
"ollama": { "modelProvider": "ollama", "model": "qwen2.5:14b",
"configuration": { "baseURL": "http://localhost:11434" } }
}
}π dive_httpd.json β HTTP service settings
{
"db": { "uri": "sqlite:///db.sqlite", "async_uri": "sqlite+aiosqlite:///db.sqlite", "migrate": true },
"checkpointer": { "uri": "sqlite:///db.sqlite" },
"cors_origin": "http://localhost:5173"
}Dive negotiates capabilities per the MCP spec: each server's
initialize response is inspected, and only the advertised endpoints are
queried. This protects you from the entire class of "host crashes because
server doesn't implement X" bugs.
| Capability | Behavior |
|---|---|
tools π§ |
Listed at startup, exposed to the LLM, dispatched via call_tool. |
prompts π¬ |
Listed at startup, cached, refreshed on notifications/prompts/list_changed. Surfaced via McpServerInfo.prompts and the /api/tools response. |
resources π¦ |
Discovery wired up; expose as needed by your front-end. |
| OAuth π | Full authorization-code + dynamic-client-registration flow with persistent token store. |
| Elicitation π¨ | Server-initiated prompts surface to the front-end via the elicitation manager. |
Method not found |
Treated as "server doesn't support that"; never breaks initialization. |
GET /api/tools/{server}/prompts # list (?refresh=true bypasses cache)
POST /api/tools/{server}/prompts/get # body: {"name": "...", "arguments": {...}}Both endpoints return the repo-standard DataResult[T] envelope
({success, message, data}); data is the list of prompts or the
rendered GetPromptResult (description + ordered PromptMessages)
ready to append to a chat thread.
| Prefix | Purpose |
|---|---|
/api/chat |
Streaming chat, message history |
/api/v1/mcp |
Remote MCP-style endpoints (re-mount of /api/chat) |
/api/tools |
Server inventory, prompts, OAuth, elicitation, log stream |
/api/config |
Hot-reload MCP and model config |
/api/skills |
Markdown-defined skills (with frontmatter) |
/v1/openai |
OpenAI-compatible chat completion endpoint |
/model_verify |
One-shot credential check for an LLM provider |
# Install dev deps
uv sync --extra dev --frozen
# (Optional) local Postgres for tests/dev
./scripts/run_pg.sh
# Run the full suite
pytest
# Single test
pytest tests/test_tools.py::test_mcp_server_info
# Integration tests only
pytest -m integration
# Lint & format (ruff is the only tool you need)
ruff check .
ruff format .
# DB migrations
alembic upgrade head
alembic revision --autogenerate -m "describe change"Without activating the venv:
uv run --extra dev --frozen pytestThis repo is the Python backend of the Dive desktop app. PRs that change wire formats must stay backwards-compatible with shipped Dive builds β favor additive fields and new endpoints over breaking existing ones.
MIT β see LICENSE.