zeph-mcp
MCP client with multi-server lifecycle and Qdrant tool registry for Zeph.
Overview
Implements the Model Context Protocol client for Zeph, managing connections to multiple MCP servers, discovering their tools at startup, and routing tool calls through a unified executor. Built on rmcp 0.17.
Key Modules
- client — low-level MCP transport and session handling;
ToolListChangedHandlerreceivestools/list_changednotifications, appliessanitize_tools()(rate-limited to once per 5 s per server, capped at 100 tools), and forwards the sanitized list toMcpManagervia a refresh channel - manager —
McpManager,McpTransport,ServerEntryfor multi-server lifecycle; command allowlist validation (npx, uvx, node, python3, docker, mcpls, etc.), env var blocklist (LD_PRELOAD, DYLD_*, NODE_OPTIONS, etc.), and path separator rejection; statically configured servers (from[[mcp.servers]]) bypass SSRF validation to allow connections tolocalhostand private IPs — dynamically added servers retain full SSRF protection - sanitize —
sanitize_tools()applied to all tool definitions at registration time and again on everytools/list_changedrefresh; strips 17 injection-detection patterns, Unicode Cf-category characters, and caps descriptions at 1024 bytes; fields triggering a pattern are replaced with"[sanitized]"— tool registration is never blocked - executor —
McpToolExecutorbridging MCP tools into theToolExecutortrait - registry —
McpToolRegistryfor tool lookup and optional Qdrant-backed search - tool —
McpToolwrapper with schema and metadata - prompt — MCP prompt template support
- error —
McpErrorerror types
Semantic tool discovery
SemanticToolIndex indexes all registered MCP tool definitions as embedding vectors in Qdrant (or the SQLite vector backend). On each LLM turn, only the top-K most relevant tools — ranked by cosine similarity to the current query — are included in the tools array sent to the model. This keeps the tools payload small for models with narrow context windows and reduces prompt injection surface area.
[]
= true
= 20 # max tools sent per request (0 = all tools, disables discovery)
= 0.55 # minimum similarity threshold
= "zeph_mcp_tools"
[!NOTE] Tool discovery requires an embedding model. Configure
[llm.orchestrator] embedding_modelor set a dedicatedembedding_providerfor the mcp subsystem. When Qdrant is unavailable the index falls back to BM25 keyword matching.
Per-message pruning cache
PruningCache tracks which tool set was sent in the previous LLM request. If the ranked tool list for the current turn is identical, the cache returns the pre-serialized JSON blob directly, skipping re-serialization and re-ranking.
Cache invalidation triggers on: new tool registered, tool removed, tools/list_changed notification, or config reload. No manual configuration is required; the cache is always active when [mcp.tool_discovery] enabled = true.
Tool attestation
expected_tools in a server config entry declares the tool names that server is authorised to expose. If a tool name appears in tools/list that is not in expected_tools, it is logged as a security warning and excluded from the registry.
[[]]
= "filesystem"
= "npx"
= ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
= ["read_file", "write_file", "list_directory"]
[!IMPORTANT] Leave
expected_toolsempty (or omit it) to allow all tools from a server. Setting it to an empty list[]blocks all tools from that server.
MCPShield trust calibration
MCPShield assigns a per-server trust score that starts at 1.0 and degrades on anomalous events: tool definition mutations between tools/list_changed cycles, sanitization hits, unexpected tool names, and tool execution errors. When the trust score drops below shield.quarantine_threshold, the server is quarantined and its tools are excluded from the registry until the score recovers (exponential half-life decay).
[]
= true
= 0.4 # score below which a server is quarantined
= 3600 # half-life for trust score recovery
[!TIP] View per-server trust scores in the TUI with
mcp:listfrom the command palette — the trust column shows the current score and a coloured indicator (green ≥ 0.7, yellow ≥ 0.4, red < 0.4).
Configuration
[[]]
= "filesystem"
= "npx"
= ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
= {}
[[]]
= "fetch"
= "uvx"
= ["mcp-server-fetch"]
[!NOTE] Statically configured servers (from
[[mcp.servers]]) bypass SSRF validation to allow connections tolocalhostand private IPs. Dynamically added servers retain full SSRF protection.
Features
| Feature | Description |
|---|---|
mock |
Enables MockMcpClient for downstream tests |
Installation
Documentation
Full documentation: https://bug-ops.github.io/zeph/
License
MIT