# PostHog

Product analytics, HogQL/SQL queries, insights, dashboards, feature flags, experiments, surveys, session replay, error tracking, logs, LLM analytics, data warehouse, notebooks, workflows and the rest of what PostHog's own MCP server exposes (about 1,100 tools in PostHog's published definitions; the server filters them by the account's scopes and feature flags). Read-only tools (`readOnlyHint`) run without approval; every other tool asks.

## Interface

PostHog's official remote MCP server, `https://mcp.posthog.com/mcp` ([docs](https://posthog.com/docs/model-context-protocol), [source](https://github.com/PostHog/posthog/tree/master/services/mcp)). One endpoint serves US and EU Cloud: PostHog routes each account to its own region.

PostHog's full REST OpenAPI document (2,337 tools) was used before, but its tool listing is 35.7 MB and fails on hosted Executor (see `catalog/cloudflare-large-specs.md`). No subset of it is mounted alongside the MCP server: the MCP server already covers the same products, and a hand-picked OpenAPI subset would duplicate it.

## Projects

PostHog's MCP server keeps a connection's *active project* in MCP session state. Executor opens a new session for every tool call, so `switch-project` reports success and then does not survive the call that made it: the next call is back on the account's default project. Do not rely on it.

The only project context that holds is the one in the request, which PostHog pins with `?project_id=` (or `x-posthog-project-id`). This app sends both: the query parameter keeps one catalog cache scope per project, and the header is a second pin channel. This app therefore mounts **one tool namespace per project**:

```
mattrax.exec          mattrax.dashboards-get-all
spotistats.exec       spotistats.query-trends
projects.list         the keys, names and project IDs above
```

`exec` stays the server's own CLI-mode tool. The `x-posthog-mcp-mode: cli` header is sent explicitly so a client that would otherwise get tools mode still gets one `exec` tool per project instead of roughly 1,100.

## Switch commands

PostHog still lists `switch-project` and `switch-organization` inside `exec`. The exclusion its docs describe applies to the MCP tool list; in CLI mode the only MCP tool is `exec`, and its `search` command reads the unfiltered registry, so the names remain visible. Inside a pinned session they report success — including the new project's name — while changing nothing that survives the call, because Executor opens a new session per call. They are therefore a trap, not a feature.

The app refuses those two commands in its approval policy, so a call is denied instead of confirmed, and its router instructions tell agents to pick a namespace rather than switch. Filtering the names out of `exec`'s output is not possible from the app: there is no per-project tool to remove, only one `exec` per project.

Projects are declared in `index.ts` in the `projects` array. Adding one is an edit there plus a redeploy; a new PostHog project is not mounted until then. It is still visible: `call projects-get` in any namespace lists every project in the organization, so an unmounted one can be spotted and added.

## Auth

- **Personal API key** (`apiKey`): create a key with the **MCP Server** preset at https://app.posthog.com/settings/user-api-keys?preset=mcp_server (EU: https://eu.posthog.com/settings/user-api-keys?preset=mcp_server). The preset scopes the key to one project. Sent as `Authorization: Bearer`, as PostHog's MCP docs describe.
- **OAuth** (`oauth`): discovered from the MCP server's protected-resource metadata (authorization server `https://oauth.posthog.com`, which routes to US or EU). Executor registers a public PKCE client through dynamic client registration (`tokenEndpointAuthMethod: none`; the server advertises none, client_secret_post and private_key_jwt). If you register an OAuth app by hand (Settings > User > OAuth applications), add the redirect URI Executor shows on the connect page. 153 scopes are requested: the server's `scopes_supported` minus `introspection`, `organization:write`, `user:write` and `access_control:write`.

One OAuth sign-in is enough for every project namespace: the connection is per PostHog *account*, and the project lives in the request. Link more than one account only for a different PostHog account or region; each account gets the same project namespaces.

Health check: `mcpHealth` (initialize and list tools with the account's credentials).

## Known gaps

- About 26 tools need the admin scopes left out above (reverse proxies, 2FA enforcement, identity provider configs, access-control rule sets, reminders and user settings). They fail over OAuth; a personal API key works only for the scopes it was created with.
- Some tools are LLM-backed and count as PostHog AI usage; they need AI data processing enabled for the organization.
- The event capture API (`/capture`, `/batch`) is not part of the MCP server, so events cannot be sent.
- A personal API key scoped to one project will fail in a namespace pinned to a different project. Use OAuth for multi-project work, or one key per project.
- Self-hosted PostHog is not supported.
- The tool count was not listed live (listing needs a real account); the number above comes from PostHog's `services/mcp/schema/tool-definitions-all.json` at commit `62ef2af`.
