FastMCP class and the 2.x MCPServer. Python 3.10+.
Install
fastmcp>=2,<5 or mcp>=1.27,<3).
Set your API key
ANALYTICS_INGEST_URL (the SDK defaults to the US endpoint); EU workspaces must set it to https://eu.armature.tech/api/mcp-analytics/ingest. If the key is missing, the SDK quietly no-ops — your server keeps working, and no data is sent.
Instrument your server
Callinstrument_fastmcp once, before registering tools:
server.py
instrument_fastmcp twice does not double-count. Sync and async tool handlers are both supported.
Custom dispatcher
If you handletools/list and tools/call yourself, use the recorder directly:
Using
stateless_http=True, or deploying to serverless? Wrap your ASGI app in StatelessHttpSessionMiddleware and set "delivery": "await". Without the middleware, a stateless FastMCP app issues no Mcp-Session-Id at all, so calls arrive with no session identity: Armature falls back to heuristic grouping, separate conversations from the same caller can run together, and Client shows as Unknown. See Stateless & serverless servers.Configuration
Pass options as a dict under thearmature key. Both snake_case and camelCase keys are accepted:
Flush on shutdown
With background delivery, drain pending events before the process exits:Verify locally
Capture batches in memory instead of sending them:Troubleshooting
Telemetry fields missing from tool schemas
Telemetry fields missing from tool schemas
FastMCP builds the advertised schema from your function’s type hints, so the SDK adds a keyword-only
telemetry parameter to instrumented functions. If you post-process signatures or schemas yourself, make sure that parameter survives.HTTP sessions all look anonymous
HTTP sessions all look anonymous
The SDK reads the
Mcp-Session-Id header, including on FastMCP versions that exclude it from get_http_headers() by default. If you run a custom ASGI stack, make sure the header reaches the app.No data arrives
No data arrives
Check that
ANALYTICS_INGEST_API_KEY is set in the server’s environment (missing keys no-op silently by design), that ANALYTICS_INGEST_URL matches your workspace region — EU keys are rejected by the US endpoint — and pass an on_error hook to surface delivery failures.
