Design Notes¶
This page documents the tradeoffs and error semantics behind metatools-mcp.
Design tradeoffs¶
- MCP-native surface. All metatools (search, describe, run, chain, execute_code) are exposed via the official MCP Go SDK types to keep wire compatibility.
- Adapters, not re-implementation. The server delegates to tooldiscovery/index, tooldiscovery/tooldoc, toolexec/run, and toolexec/code via thin adapters so the libraries remain the source of truth.
- Structured error objects. Tool-level errors are returned in a consistent
ErrorObjectshape rather than raw Go errors, preserving the MCP tool contract. - Explicit limits. Inputs such as
limitandmaxare capped for safe defaults (e.g., search limit cap 100, examples cap 5). - Opaque pagination. Cursor tokens are opaque and validated against index mutations to prevent stale paging.
- Pluggable search. BM25 is optional via build tags (
toolsearch) and runtime config via env vars. - Change notifications. Tool list updates emit
notifications/tools/list_changedwith a debounce window; notifications can be disabled and are emitted as a single list change per debounce window. - Transport abstraction. The
Transportinterface decouples protocol handling from server logic, enabling stdio, SSE, and Streamable HTTP without code changes. - Runtime isolation.
execute_codeis optional; thetoolruntimebuild tag enables sandboxed execution via toolexec/runtime with runtime profile selection.
Transport layer¶
The transport layer abstracts how clients connect to the MCP server. All transports implement the same Transport interface, ensuring identical behavior regardless of protocol:
type Transport interface {
Name() string
Info() Info
Serve(ctx context.Context, server Server) error
Close() error
}
Transport selection rationale¶
| Transport | Status | Use Case | Rationale |
|---|---|---|---|
stdio | Recommended (local) | Claude Desktop, local CLIs | Zero config, implicit session, lowest latency |
streamable | Recommended (HTTP) | Web apps, remote clients | MCP spec 2025-11-25 compliant, session management, bidirectional |
sse | Deprecated | Legacy web clients | Superseded by streamable per MCP spec |
Streamable HTTP design decisions¶
-
Single endpoint (
/mcp): Follows MCP spec 2025-11-25 with POST/GET/DELETE methods on one path, simplifying routing and CORS configuration. -
Session management via header: Uses
Mcp-Session-Idheader (not cookies) for stateless load balancing compatibility and explicit session lifecycle. -
Stateless mode option: Enables serverless/FaaS deployments where session persistence is impractical. Trade-off: no server-initiated requests.
-
JSON vs SSE response modes: Default SSE streaming supports long-running tools;
JSONResponse=trueoption for simpler request/response patterns. -
TLS built-in: Direct TLS support avoids reverse proxy requirements for simple deployments while allowing proxy termination for complex setups.
-
Graceful shutdown: 5-second timeout allows in-flight requests to complete, balancing responsiveness with reliability.
Error semantics¶
metatools-mcp distinguishes protocol errors from tool errors:
- Protocol errors (invalid input) return a non-nil error from handlers.
- Tool errors are wrapped into
ErrorObjectand returned withisError = trueso MCP clients treat them as tool failures.
Key error behaviors:
run_toolrejectsstream=trueandbackend_overridein the default handler (not supported yet).run_chainstops on first error and returns partial results with anErrorObject.describe_tool/list_tool_examplesreturn validation errors when required fields are missing.- Invalid cursors return JSON-RPC invalid params.
- Cancellation and timeouts map to
cancelledandtimeouterror codes.
Extension points¶
- Transport: implement
Transportinterface to add new protocols (e.g., WebSocket, gRPC). - Search strategy: enable BM25 via the
toolsearchbuild tag and env vars. - Tool execution: swap
toolexec/runrunner implementation or configure different backends. - Code execution: plug in a different
toolexec/codeengine (e.g., toolexec/runtime-backed). - Progress: when a progress token is provided,
run_tool,run_chain, andexecute_codeemit progress notifications. If the runner supports progress callbacks, step-level updates are forwarded; otherwise a coarse start/end signal is sent.
Runtime profile selection¶
When built with -tags toolruntime, metatools-mcp wires toolexec/runtime into toolexec/code:
- Dev profile (
dev) uses the unsafe subprocess backend for fast iteration. - Standard profile (
standard) uses Docker by default or WASM when selected. - Set
METATOOLS_RUNTIME_PROFILE=standardto opt into standard isolation. - Use
METATOOLS_RUNTIME_BACKEND=wasmwithMETATOOLS_WASM_ENABLED=trueto prefer the WASM backend (wazero) for standard profile. - If Docker is unavailable and WASM is enabled, the server falls back to WASM for the standard profile.
Operational guidance¶
Transport configuration¶
- Use
--transport=stdio(default) for Claude Desktop and local CLI integration. - Use
--transport=streamable --port=8080for HTTP-based clients and web applications. - Configure TLS for production HTTP deployments:
--tls --tls-cert=cert.pem --tls-key=key.pem - Use
--statelessfor serverless/FaaS where session persistence is unavailable. - Set
METATOOLS_TRANSPORT_STREAMABLE_SESSION_TIMEOUTto control idle session cleanup.
Search configuration¶
- Use environment variables to configure search strategy:
METATOOLS_SEARCH_STRATEGY=lexical|bm25METATOOLS_SEARCH_BM25_*for weighting and caps
General guidance¶
- Keep tool schemas in
toolfoundation/modelto preserve MCP compatibility end-to-end. - Treat metatools as the stable surface; update libraries behind it as needed.