fix(mcp): restore analytics capture for standalone FastMCP 4 - #936
fix(mcp): restore analytics capture for standalone FastMCP 4#936lucasheriques wants to merge 2 commits into
Conversation
Select the adapter by handler registry so standalone FastMCP 4 captures tool calls without changing the customer's instrument() invocation. Preserve application-owned arguments, including mounted tools, and use native v2 model fields when FastMCP exposes deprecated v1 aliases. Add HTTP regression coverage for both protocol eras, late tool registration, repeat instrumentation, failed tools, unavailable capture, and mounted argument ownership. Include FastMCP 4 in the existing MCP v2 CI matrix, document adapter behavior, and add a Sampo patch changeset. Validation on current main: MCP v1 suite 307 passed; MCP v2 suite 291 passed, 13 skipped. Ruff lint/format, filtered mypy (231 source files), import warning check, and actionlint passed. Real stdio tools emitted HTTP capture batches to a local receiver under both protocol eras, two events per run. Authenticated wizard installation and hosted ingestion remain untested.
Prompt To Fix All With AI### Issue 1
posthog/mcp/__init__.py:294-296
**Instrumentation remains order-dependent**
If a standalone FastMCP 4 server's low-level server is instrumented before its wrapper, both calls use the same tracking key, so the wrapper call returns without installing FastMCP-specific argument stripping. The low-level adapter then advertises analytics parameters but forwards them unchanged, causing tools that do not declare those parameters to reject otherwise valid calls. The existing installation needs to support adding the wrapper-specific context, with coverage for this reverse order.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "fix(mcp): capture standalone FastMCP on ..." | Re-trigger Greptile |
| instrument_lowlevel_v2( | ||
| server._mcp_server, data, strip_injected_for=server | ||
| ) |
There was a problem hiding this comment.
Instrumentation remains order-dependent
If a standalone FastMCP 4 server's low-level server is instrumented before its wrapper, both calls use the same tracking key, so the wrapper call returns without installing FastMCP-specific argument stripping. The low-level adapter then advertises analytics parameters but forwards them unchanged, causing tools that do not declare those parameters to reject otherwise valid calls. The existing installation needs to support adding the wrapper-specific context, with coverage for this reverse order.
Prompt To Fix With AI
This is a comment left during a code review.
Path: posthog/mcp/__init__.py
Line: 294-296
Comment:
**Instrumentation remains order-dependent**
If a standalone FastMCP 4 server's low-level server is instrumented before its wrapper, both calls use the same tracking key, so the wrapper call returns without installing FastMCP-specific argument stripping. The low-level adapter then advertises analytics parameters but forwards them unchanged, causing tools that do not declare those parameters to reject otherwise valid calls. The existing installation needs to support adding the wrapper-specific context, with coverage for this reverse order.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
posthog-python Compliance ReportDate: 2026-09-09 21:25:04 UTC ✅ All Tests Passed!111/111 tests passed Capture_V1 Tests✅ 94/94 tests passed View Details
Feature_Flags Tests✅ 17/17 tests passed View Details
|
Keep a weak reference to the standalone FastMCP wrapper in shared tracking state so a later wrapper install enriches existing low-level instrumentation without wrapping handlers twice. Resolve schemas for the client-requested tool version before stripping analytics-owned arguments. Consolidate adapter imports at the optional MCP dependency boundary instead of repeating imports in dispatch branches. Keep FastMCP-specific imports lazy so the official SDK remains usable without that optional package. Validation: reproduced both setup-order failures and the versioned-tool argument failure before fixing them. MCP v1 suite 307 passed; MCP v2 suite 296 passed, 13 skipped. Ruff format/lint and filtered mypy passed. Fresh-process checks passed with MCP absent and with FastMCP absent.
💡 Motivation and Context
Standalone FastMCP 4 servers execute tools successfully but emit no MCP analytics after instrumentation.
FastMCP 4 uses the MCP SDK v2 registry; the standalone adapter still assumes the v1 handler dictionary.
Adapter routing before and after
Before:
After:
💚 How did you test it?
The MCP suite passed locally on the PR branch:
Reproduce the regression suite with
pytest posthog/test/mcp --timeout=30in each dependency environment.📝 Checklist
If releasing new changes
sampo addto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Codex used the MCP Analytics debugging, CI workflow, dataclass, and PR description skills with shell tools and the GitHub CLI.
The fix reuses registry-based SDK detection. Adapter imports share one lazy dependency boundary; FastMCP-specific helpers load only when needed. Existing mutable tracking state holds a weak wrapper reference. Tests exercise public FastMCP HTTP entry points with synthetic tools. Human review is required.