services/mcp/README.md
Documentation: https://posthog.com/docs/model-context-protocol
You can install the MCP server automatically into Cursor, Claude, Claude Code, VS Code and Zed by running the following command:
npx @posthog/wizard@latest mcp add
Obtain a personal API key using the MCP Server preset.
Add the MCP configuration to your desktop client (e.g. Cursor, Windsurf, Claude Desktop) and add your personal API key
{
"mcpServers": {
"posthog": {
"command": "npx",
"args": [
"-y",
"mcp-remote@latest",
"https://mcp.posthog.com/mcp", // You can replace this with https://mcp.posthog.com/sse if your client does not support Streamable HTTP
"--header",
"Authorization:${POSTHOG_AUTH_HEADER}"
],
"env": {
"POSTHOG_AUTH_HEADER": "Bearer {INSERT_YOUR_PERSONAL_API_KEY_HERE}"
}
}
}
}
If you want to call MCP from Node (outside an IDE), use the Model Context Protocol SDK’s Streamable HTTP transport.
Authorization.Accept: application/json, text/event-stream.initialize then a client notifications/initialized; the SDK performs this during connect().// tools-list.mjs
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
import { ListToolsResultSchema } from '@modelcontextprotocol/sdk/types.js'
import { mkdirSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { URL } from 'node:url'
const AUTH = process.env.POSTHOG_AUTH_HEADER // "Bearer phx_…"
const MCP_URL = process.env.MCP_URL || 'https://mcp.posthog.com/mcp'
if (!AUTH?.startsWith('Bearer ')) {
console.error('Set POSTHOG_AUTH_HEADER="Bearer phx_..."')
process.exit(1)
}
const transport = new StreamableHTTPClientTransport(new URL(MCP_URL), {
requestInit: {
headers: {
Authorization: AUTH,
// Required for Streamable HTTP (JSON + SSE)
Accept: 'application/json, text/event-stream',
},
},
serverInfo: { name: 'example-node-client', version: '0.0.1' },
})
const client = new Client({ name: 'example-node-client', version: '0.0.1' })
// Handles initialize + notifications/initialized
await client.connect(transport)
const toolsResp = await client.request({ method: 'tools/list' }, ListToolsResultSchema) // { tools: [...] }
const tools = toolsResp?.tools ?? []
console.log('Tools:', tools.length)
// (Optional) Save the full JSON-RPC envelope to a file (run from repo root)
const envelope = { jsonrpc: '2.0', id: 'list-1', result: toolsResp }
mkdirSync('reports', { recursive: true })
writeFileSync(join('reports', 'tools-list-http.json'), JSON.stringify(envelope, null, 2))
console.log('Saved: reports/tools-list-http.json')
await client.close()
Why these headers & steps?
Accept header to include both JSON and SSE.initialize, the client must send notifications/initialized; the SDK does this for you in connect().See also the main PostHog MCP docs for available tools and setup flows: https://posthog.com/docs/model-context-protocol
If you prefer to use Docker instead of running npx directly:
pnpm docker:build
# or
docker build -t posthog-mcp .
{
"mcpServers": {
"posthog": {
"type": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"--env",
"POSTHOG_AUTH_HEADER=${POSTHOG_AUTH_HEADER}",
"--env",
"POSTHOG_REMOTE_MCP_URL=${POSTHOG_REMOTE_MCP_URL:-https://mcp.posthog.com/mcp}",
"posthog-mcp"
],
"env": {
"POSTHOG_AUTH_HEADER": "Bearer {INSERT_YOUR_PERSONAL_API_KEY_HERE}",
"POSTHOG_REMOTE_MCP_URL": "https://mcp.posthog.com/mcp"
}
}
}
}
pnpm docker:inspector
# or
npx @modelcontextprotocol/inspector docker run -i --rm --env POSTHOG_AUTH_HEADER=${POSTHOG_AUTH_HEADER} posthog-mcp
Environment Variables:
POSTHOG_AUTH_HEADER: Your PostHog API token (required)POSTHOG_REMOTE_MCP_URL: The MCP server URL (optional, defaults to https://mcp.posthog.com/mcp)This approach allows you to use the PostHog MCP server without needing Node.js or npm installed locally.
Below are detailed examples showing realistic prompts and expected outputs:
Prompt: "Create a feature flag called 'new-checkout-flow' that's enabled for 20% of users, and show me the configuration"
What happens:
create-feature-flag tool creates the flag with a 20% rolloutExpected output:
Created feature flag 'new-checkout-flow':
- Key: new-checkout-flow
- Active: true
- Rollout: 20% of all users
- URL: https://app.posthog.com/feature_flags/12345
Prompt: "How many unique users signed up in the last 7 days, broken down by day?"
What happens:
query-run tool executes a trends query filtering for $signup eventsExpected output:
Signups over the last 7 days:
| Date | Unique users |
|------------|--------------|
| 2025-01-17 | 142 |
| 2025-01-18 | 156 |
| 2025-01-19 | 98 |
| ... | ... |
Total: 847 unique signups
Prompt: "Create an A/B test for our pricing page that measures conversion to the checkout page"
What happens:
experiment-create tool creates an experiment with control/test variantsExpected output:
Created experiment 'Pricing page test':
- Feature flag: pricing-page-test
- Variants: control (50%), test (50%)
- Primary metric: Funnel conversion (pricing_page → checkout)
- Status: Draft (ready to launch)
- URL: https://app.posthog.com/experiments/789
Prompt: "What are the top 5 errors in my project this week and how many users are affected?"
What happens:
list-errors tool fetches error groups sorted by occurrence countExpected output:
Top 5 errors this week:
1. TypeError: Cannot read property 'id' of undefined
- Occurrences: 1,247
- Users affected: 89
- First seen: 2 days ago
2. NetworkError: Failed to fetch
- Occurrences: 856
- Users affected: 234
- First seen: 5 days ago
...
For simpler queries, you can use shorter prompts:
You can limit which tools are available by adding query parameters to the MCP URL:
https://mcp.posthog.com/mcp?features=flags,workspace
Available features:
workspace - Organization and project managementerror-tracking - Error monitoring and debuggingdashboards - Dashboard creation and managementinsights - Analytics insights and SQL queriesexperiments - A/B testing experimentsflags - Feature flag managementllm-analytics - LLM usage and cost trackingdocs - PostHog documentation searchTo view which tools are available per feature, see our documentation or alternatively check out schema/tool-definitions.json,
The MCP server is hosted on a Cloudflare worker which can be located outside of the EU / US, for this reason the MCP server does not store any sensitive data outside of your cloud region.
If you're using a self-hosted instance of PostHog, you can specify a custom base URL by adding the POSTHOG_BASE_URL environment variable when running the MCP server locally or on your own infrastructure, e.g. POSTHOG_BASE_URL=https://posthog.example.com
To run the MCP server locally, run the following command:
pnpm run dev
And replace https://mcp.posthog.com/mcp with http://localhost:8787/mcp in the MCP configuration.
To develop with warm loading for MCP resources (workflows, prompts, examples):
cd ../context-mill && npm run devpnpm run dev:local-resourcesChanges in the examples repo will be reflected on the next request.
This repository is organized to support multiple language implementations:
typescript/ - TypeScript implementation of the MCP server & toolsschema/ - Shared schema files generated from TypeScriptpnpm run dev - Start development serverpnpm run schema:build:json - Generate JSON schema for other language implementationspnpm run lint && pnpm run format - Format and lint codeSee the tools documentation for a guide on adding new tools to the MCP server.
.dev.vars in the rootdocs-search tool (see Inkeep API key - mcp)INKEEP_API_KEY="..."
During development you can directly inspect the MCP tool call results using the MCP Inspector.
You can run it using the following command:
npx @modelcontextprotocol/inspector npx -y mcp-remote@latest http://localhost:8787/mcp --header "\"Authorization: Bearer {INSERT_YOUR_PERSONAL_API_KEY_HERE}\""
Alternatively, you can use the following configuration in the MCP Inspector:
Use transport type STDIO.
Command:
npx
Arguments:
-y mcp-remote@latest http://localhost:8787/mcp --header "Authorization: Bearer {INSERT_YOUR_PERSONAL_API_KEY_HERE}"
Claude Desktop is one of the easiest ways to test MCP Apps - while PostHog Code doesn't support it. You can configure access Settings > Developer and then edit claude_desktop_config.json with the following:
{
"mcpServers": {
"posthog-local": {
"command": "npx",
"args": ["-y", "mcp-remote@latest", "http://localhost:8787/mcp"]
}
}
}
The MCP server acts as a proxy to your PostHog instance. It does not store your analytics data - all queries are executed against your PostHog project and results are returned directly to your AI client. Session state (active project/organization) is cached temporarily using Cloudflare Durable Objects tied to your API key hash.
For EU users, use the mcp-eu.posthog.com endpoint to ensure OAuth flows route to the EU PostHog instance.