Skip to content

posthog-v7.40.0

Choose a tag to compare

@github-actions github-actions released this 21 Aug 12:06
Immutable release. Only release title and notes can be modified.
627c4a6

Minor changes

  • b0ab12c feat(mcp): support MCP Python SDK v2 and bring posthog.mcp to parity with the TypeScript SDK (@posthog/mcp). Most of this reaches SDK 1.x servers too — the parity work is not v2-only.

    MCP SDK v2 / spec 2026-07-28. instrument() now wraps mcp.server.mcpserver.MCPServer (the renamed FastMCP) and the v2 low-level Server (constructor-injected handlers, string-keyed registry, late add_request_handler registrations included), capturing tool calls, tools/list, errors, intent, client identity, and $mcp_protocol_version on both protocol eras — the legacy handshake and the stateless 2026-07-28 envelope, decided per request. Previously instrument() raised ImportError on mcp>=2 and took the host application down with it; it now degrades to a logged no-op on any unsupported or unrecognized SDK.

    Cross-SDK parity (SDK 1.x and 2.x alike). Conversation-anchored sessions land as the cross-pod correlation the stateless era needs: with enable_conversation_id, $session_id derives deterministically from the agent-echoed conversation_id (new export derive_session_id_from_conversation, byte-compatible with @posthog/mcp). Only a handle the SDK could have minted (a uuidv7) anchors a session, so two callers inventing the same id can no longer be merged. The handle is delivered over both channels a tool result has — a content text block carrying it as plain JSON data on the minting response (an imperative server sentence inside a tool result is prompt-injection-shaped, and a client that strips it silently breaks the feature), and an _mcp_instructions key declared on the tool's output schema and mirrored into structuredContent on every response. That second channel is what makes the feature work at all for tools with structured output: clients that read structuredContent never render content, so the agent had no handle to echo (0% echo rate measured against Claude Code before the mirror). The prompt-back now rides errored results too, so a failure on a conversation's first call doesn't split the retry into a new session. The session is resolved only once the handle's fate is known, so the call that mints a handle joins the same session as the calls that echo it — while a handle that could not be delivered anchors nothing, rather than stranding events in a conversation nobody holds. Host callbacks (identify, intent_fallback, event_properties) receive the SDK's own per-request context as extra["ctx"] identically on both majors, with a new exported get_request_headers(extra) to read HTTP headers off it — the underlying shape differs per major, and a hand-rolled read that works on one silently returns nothing on the other, sending every event out anonymous.

    Fixes affecting existing SDK 1.x users. Analytics could break a tool call in three ways, each now fixed and regression-tested: the SDK's tool cache is rebuilt from an internal listing pass we skipped injecting on, so after any call to an unlisted tool name a strict schema rejected either the analytics parameters we advertise (Input validation error) or the conversation key we write (Output validation error); the conversation handle was written into the caller's result object in place, so a tool returning a shared or cached result served one conversation's handle to every later caller; and on jlowin's FastMCP the advertised schema marked context required while the adapter strips it before validation, failing every call under strict_input_validation=True. Two behavioural changes come with the parity work: an invented (non-uuidv7) conversation_id echo is replaced with a fresh handle rather than trusted, and minted prompt-backs are now appended to errored results. — Thanks @gesh!