From 78c278968833550aecd62ca4545e85ecf453701b Mon Sep 17 00:00:00 2001 From: Lucas Faria Date: Mon, 22 Jun 2026 15:12:38 -0300 Subject: [PATCH 1/3] docs(mcp-analytics): add Python SDK install section Document posthog[mcp] / posthog.mcp.instrument() alongside the TypeScript SDK on the MCP analytics installation page. Generated-By: PostHog Code Task-Id: b21bc954-5de3-4512-a0d5-6bec2371f782 --- contents/docs/mcp-analytics/installation.mdx | 52 +++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/contents/docs/mcp-analytics/installation.mdx b/contents/docs/mcp-analytics/installation.mdx index 13b1799c856f..e2feea68a81a 100644 --- a/contents/docs/mcp-analytics/installation.mdx +++ b/contents/docs/mcp-analytics/installation.mdx @@ -12,8 +12,8 @@ import CalloutBox from 'components/Docs/CalloutBox' ## Requirements -- Node.js 18 or later -- A TypeScript or JavaScript MCP server built on `@modelcontextprotocol/sdk`. (Running a custom dispatcher with no server object to wrap? See [Custom servers](/docs/mcp-analytics/custom-servers).) +- Node.js 18 or later (TypeScript/JavaScript), or Python 3.10+ — see [Python](#python) below +- An MCP server built on `@modelcontextprotocol/sdk` (TS) or the `mcp` package (Python). (Running a custom dispatcher with no server object to wrap? See [Custom servers](/docs/mcp-analytics/custom-servers).) - A PostHog [project API key](/docs/getting-started/project-token) (`phc_…`) ## Install @@ -72,6 +72,54 @@ server.tool("search_events", { /* ... */ }, async (args) => { }) ``` +## Python + +A Python SDK ships inside the [`posthog`](/docs/libraries/python) package (the same way [`posthog.ai`](/docs/ai-engineering) does). Install it with the `mcp` extra: + +```bash +pip install posthog[mcp] +``` + +`instrument(server, posthog_client, options?)` wraps a [`FastMCP`](https://github.com/modelcontextprotocol/python-sdk) server or the low-level `mcp.server.Server`: + +```python +from posthog import Posthog +from posthog.mcp import instrument +from mcp.server.fastmcp import FastMCP + +posthog = Posthog( + "phc_your_project_api_key", + host="https://us.i.posthog.com", # or https://eu.i.posthog.com +) +server = FastMCP("my-server") + +# register your tools as usual... + +analytics = instrument(server, posthog) +``` + +Options mirror the TypeScript ones in snake_case, passed as `MCPAnalyticsOptions`: + +```python +from posthog.mcp import instrument +from posthog.mcp.types import MCPAnalyticsOptions, UserIdentity + +instrument(server, posthog, MCPAnalyticsOptions( + context=True, # inject the `context` intent argument (default) + report_missing=True, # register the get_more_tools virtual tool + enable_conversation_id=True, # stitch calls across reconnects + identify=lambda request, extra: UserIdentity(distinct_id="user_123"), +)) +``` + +No server object to wrap (a custom HTTP/edge dispatcher)? Use `PostHogMCP`, a `posthog` client subclass with `capture_tool_call()`, `capture_initialize()`, `prepare_tool_list()`, and `prepare_tool_call()` — the Python equivalent of [Custom servers](/docs/mcp-analytics/custom-servers). + + + +The Python SDK is alpha and TypeScript-only features may land first. It emits the identical `$mcp_*` events documented on the [events](/docs/mcp-analytics/events) page. + + + ## Configuration The `posthog` client is passed as the required second positional argument — not in this options object. `instrument()` accepts these options as an optional third argument: From 0f0971bcf9372a54018ecc18d282ffb03eea9a35 Mon Sep 17 00:00:00 2001 From: Lucas Faria Date: Mon, 22 Jun 2026 15:20:43 -0300 Subject: [PATCH 2/3] docs(mcp-analytics): note supported Python server types Clarify that the Python SDK supports the official mcp package (FastMCP + Server), jlowin's standalone fastmcp 2.0, and PostHogMCP for custom dispatchers. Generated-By: PostHog Code Task-Id: b21bc954-5de3-4512-a0d5-6bec2371f782 --- contents/docs/mcp-analytics/installation.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contents/docs/mcp-analytics/installation.mdx b/contents/docs/mcp-analytics/installation.mdx index e2feea68a81a..df0ec11f112e 100644 --- a/contents/docs/mcp-analytics/installation.mdx +++ b/contents/docs/mcp-analytics/installation.mdx @@ -80,7 +80,11 @@ A Python SDK ships inside the [`posthog`](/docs/libraries/python) package (the s pip install posthog[mcp] ``` -`instrument(server, posthog_client, options?)` wraps a [`FastMCP`](https://github.com/modelcontextprotocol/python-sdk) server or the low-level `mcp.server.Server`: +`instrument(server, posthog_client, options?)` works with every common Python MCP server: + +- `FastMCP` and the low-level `Server` from the official [`modelcontextprotocol/python-sdk`](https://github.com/modelcontextprotocol/python-sdk) (the `mcp` package) +- [jlowin's standalone **FastMCP 2.0**](https://github.com/jlowin/fastmcp) (the separate `fastmcp` package) +- `PostHogMCP` for custom dispatchers with no server object (see below) ```python from posthog import Posthog From ec3c4634a5b2b49cfa89d6f34567c070d1a8630c Mon Sep 17 00:00:00 2001 From: Lucas Faria Date: Mon, 22 Jun 2026 17:05:26 -0300 Subject: [PATCH 3/3] docs(mcp-analytics): Python options table, flush/shutdown, PostHogMCP section - Add a snake_case MCPAnalyticsOptions table (the shared Configuration table is camelCase TS names that aren't valid Python kwargs), incl. missing_capability_tool_name. - Add Python flush-on-exit guidance (analytics.flush() then posthog.shutdown()). - Add a Python PostHogMCP section to custom-servers.mdx with real signatures. Generated-By: PostHog Code Task-Id: b21bc954-5de3-4512-a0d5-6bec2371f782 --- .../docs/mcp-analytics/custom-servers.mdx | 43 +++++++++++++++++++ contents/docs/mcp-analytics/installation.mdx | 28 +++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/contents/docs/mcp-analytics/custom-servers.mdx b/contents/docs/mcp-analytics/custom-servers.mdx index 6824fda94b2a..8b8b52c0674c 100644 --- a/contents/docs/mcp-analytics/custom-servers.mdx +++ b/contents/docs/mcp-analytics/custom-servers.mdx @@ -104,3 +104,46 @@ await posthog.flush() // or keep the runtime alive until the flush completes ctx.waitUntil(posthog.flush()) ``` + +## Python + +The Python SDK ships the same custom-dispatcher path as `PostHogMCP`, a subclass of the [`posthog`](/docs/libraries/python) client. Method names are snake_case and arguments are keyword args rather than an options object: + +```python +from posthog.mcp import PostHogMCP, get_more_tools_result + +posthog = PostHogMCP("phc_your_project_api_key", host="https://us.i.posthog.com") + +# Decorate your tools/list response so agents state their intent (and, optionally, +# advertise the get_more_tools virtual tool): +tools = posthog.prepare_tool_list(my_tools, report_missing=True) + +# On an inbound tools/call, pull the intent and strip the injected `context`: +prepared = posthog.prepare_tool_call(name, arguments) +if prepared.is_missing_capability: + posthog.capture_missing_capability(context=prepared.intent, distinct_id=user_id) + return get_more_tools_result() + +result = run_tool(name, prepared.args) + +# Capture the call (fire-and-forget, like posthog.capture): +posthog.capture_tool_call( + name, + intent=prepared.intent, + intent_source=prepared.intent_source, + parameters=arguments, + response=result, + duration_ms=elapsed_ms, + is_error=False, + distinct_id=user_id, + session_id=mcp_session_id, + groups={"organization": org_id}, +) + +# On the handshake: +posthog.capture_initialize(client_name="claude-code", client_version="1.2.3", distinct_id=user_id) + +posthog.flush() # PostHogMCP is a posthog client — flush/shutdown it yourself +``` + +`PostHogMCP(api_key, missing_capability_tool_name="get_more_tools", **posthog_kwargs)` accepts the standard `posthog` client kwargs (e.g. `host`). As in TypeScript, the wrapping-path hooks (`identify`, `context`, `intent_fallback`, `event_properties`) don't apply here — pass identity and properties on each `capture_*` call. diff --git a/contents/docs/mcp-analytics/installation.mdx b/contents/docs/mcp-analytics/installation.mdx index df0ec11f112e..a067a1d7b2c1 100644 --- a/contents/docs/mcp-analytics/installation.mdx +++ b/contents/docs/mcp-analytics/installation.mdx @@ -102,7 +102,7 @@ server = FastMCP("my-server") analytics = instrument(server, posthog) ``` -Options mirror the TypeScript ones in snake_case, passed as `MCPAnalyticsOptions`: +Options are passed as `MCPAnalyticsOptions`, the snake_case equivalent of the TypeScript options: ```python from posthog.mcp import instrument @@ -116,6 +116,32 @@ instrument(server, posthog, MCPAnalyticsOptions( )) ``` +`MCPAnalyticsOptions` fields (the TypeScript [Configuration](#configuration) table below uses camelCase — these are the Python names): + +| Option | Type | Default | What it does | +|---|---|---|---| +| `context` | `bool \| MCPAnalyticsContextOptions` | `True` | Inject the `context` intent argument into every tool. | +| `report_missing` | `bool` | `False` | Register the `get_more_tools` virtual tool. | +| `missing_capability_tool_name` | `str` | `"get_more_tools"` | Rename the virtual tool registered by `report_missing`. | +| `enable_conversation_id` | `bool` | `False` | Inject an optional `conversation_id` argument to stitch calls. | +| `enable_exception_autocapture` | `bool` | `True` | Emit a `$exception` sibling on failed tool calls. | +| `identify` | `(request, extra) -> UserIdentity \| None` (sync or async) | — | Map a request to one of your users. | +| `intent_fallback` | `(request, extra) -> str \| None` | — | Provide intent when the agent didn't pass `context`. | +| `before_send` | `(event) -> event \| None` | — | Inspect/modify/drop each event before send. | +| `event_properties` | `(request, extra) -> dict` | — | Properties merged onto every event. | +| `logger` | `(message: str) -> None` | no-op | STDIO-safe log sink. | + +### Flushing on exit + +The `posthog` client batches events asynchronously and you own its lifecycle. On the `instrument()` path, auto-captured events are scheduled in the background — `await analytics.flush()` waits for in-flight events, then `posthog.flush()` / `posthog.shutdown()` sends them. Call this from your shutdown/`SIGTERM` handler so trailing events aren't dropped (see [`examples/mcp_analytics_demo.py`](https://github.com/PostHog/posthog-python/blob/main/examples/mcp_analytics_demo.py) for a runnable end-to-end example): + +```python +analytics = instrument(server, posthog) +# ... serve ... +await analytics.flush() # drain in-flight auto-capture events +posthog.shutdown() # flush + stop the posthog client +``` + No server object to wrap (a custom HTTP/edge dispatcher)? Use `PostHogMCP`, a `posthog` client subclass with `capture_tool_call()`, `capture_initialize()`, `prepare_tool_list()`, and `prepare_tool_call()` — the Python equivalent of [Custom servers](/docs/mcp-analytics/custom-servers).