MCP Server Internals

How the Leif FastMCP server is put together — tool registration, service lifecycle, annotations, output guards, and auth hardening (as of the June 2026 overhaul).

How the FastMCP server (leif-mcp.service on the Leif host) is wired internally. This reflects the June 2026 overhaul (leif#49); if behavior here disagrees with the code, the code wins — start at src/mcp/server.py.

Tool registration

Registration is declarative: src/mcp/tools/__init__.py holds a table mapping each tool module to a toolset group and the services its register() needs. Modules import lazily, and the registered tool list is derived from the running server rather than maintained by hand.

  • REGISTERED_TOOL_NAMES is an exact-equality snapshot enforced by tests — if a module adds or drops a tool without updating the snapshot, the suite fails in both directions.
  • Some tool groups only register when their feature flag is on (ENABLE_SONICWALL_LOGS for sonicwall_*, ENABLE_CLOUDFLARE for cf_*). expected_tool_names() accounts for these.
  • MCP_TOOLSETS (comma-separated: business, core, google, infrastructure, msp, personal) limits which groups register. Unset means everything; MCP_TOOLSETS=core boots roughly 87 tools instead of ~300.

Shared helpers live in src/mcp/tools/_common.py — one implementation of JSON serialization, service resolution, enabled checks, and method invocation. Tool modules do not construct integration clients themselves; services come from the runtime container, and an unconfigured integration returns a clean “not enabled” error.

Service lifecycle

initialize_runtime_services builds every service from an ordered factory table with per-service failure isolation: if one integration’s constructor throws (bad credentials, unreachable API), that slot is left unconfigured and the server still boots. Outcomes land in container.service_status.

Shutdown is symmetric — anything in the container exposing close() or stop() gets shut down, so new services can’t be forgotten.

One config note: the RunPod SSH tools require RUNPOD_HOST in the environment. There is deliberately no default host in source.

Annotations

Every tool carries MCP ToolAnnotations (readOnlyHint / destructiveHint), assigned by explicit overrides plus name rules in src/mcp/tools/annotations.py. Read-only tools (searches, reports, status) are marked as such; destructive ones (delete_*, *_destroy, arbitrary command execution, service restarts) are flagged so MCP clients can make safer permission decisions. A handful of ambiguous names stay unannotated on purpose rather than risk a wrong hint.

Output size guard

Serialized tool output is capped by MCP_TOOL_MAX_OUTPUT_CHARS (default 100,000 characters). Oversized payloads come back as a valid-JSON truncation envelope with a preview and a hint to narrow the query, so a single unbounded result can’t flood a client context window.

Auth

OAuth (for Claude.ai / Claude Code connections) is hardened:

  • Access tokens, refresh tokens, and authorization codes are stored sha256-hashed in data/mcp/oauth_store.json; legacy plaintext entries migrate transparently on first use.
  • Expired credentials are pruned on every store write, and refresh-token / auth-code expiry is enforced on load.
  • The consent page rate-limits failed access-code attempts: 5 per authorization request, 10 per IP per 15 minutes (HTTP 429).

Sessions and restarts

Streamable-HTTP sessions are stateful by default: after a leif-mcp restart, old mcp-session-id values are invalid and clients get a 404 with X-MCP-Reinitialize: true until they re-initialize (Claude clients do this automatically). Setting MCP_STATELESS_HTTP=true removes the session table entirely, which eliminates the post-restart reconnect dance at the cost of per-session state.

Already-connected clients cache the tool list at connect time — a newly deployed tool won’t appear in an existing session until it reconnects.

Environment variables

VariableDefaultEffect
MCP_TOOLSETSall groupsComma-separated toolset groups to register
MCP_TOOL_MAX_OUTPUT_CHARS100000Output cap before the truncation envelope kicks in
MCP_STATELESS_HTTPfalseStateless streamable-http (no session table)
ENABLE_SONICWALL_LOGSoffRegisters the sonicwall_* tools
ENABLE_CLOUDFLAREoffRegisters the cf_* tools
RUNPOD_HOSTunsetRequired for the runpod_* SSH tools
MCP_DEBUG_HTTPfalseRequest/response logging (auth headers redacted)