posthog-context-mcp

posthog-context-mcp

MCP server that serves PostHog JavaScript SDK documentation to coding agents via efficient retrieval, offering tools to query and assemble cited context for tasks like capturing custom events.

Category
访问服务器

README

posthog-context-mcp

An MCP server that hands PostHog's JavaScript SDK docs to a coding agent, plus an eval that scores what comes back.

Ask it how to capture a custom event in React. It returns around 680 tokens of deduplicated, cited passages. Wire a retriever straight to a tool and dump the top five chunks, and the same question costs about 2,650 tokens. Both find a correct doc every time.

the CLI comparing a naive dump against the assembled block

naive vs engineered

metric naive top-5 naive (budgeted) engineered better
Hit rate 100% 79% 100% higher
Precision 61% 69% 88% higher
Restraint (1-source cases) 21% 57% 86% higher
Wrong sources per question 1.14 0.25 0.29 lower
Mean context tokens 2,655 461 678 lower

28 hand-labelled questions. naive top-5 concatenates the top five chunks. naive (budgeted) takes that same ranking and stops at the 800-token ceiling, so the engineered config gets no credit for being allowed to stop. Truncating early costs it 21% of the answers.

Restraint is the metric worth explaining. Fourteen of the questions have exactly one doc that answers them. On those, I score whether the system returned that doc alone. Bringing a second source counts as a failure even when hit rate and precision look fine. Any eval that only rewards finding the answer will tell you a system returning the whole index is perfect.

How it works

Ingest (posthog_context/ingest.py) sparse-clones PostHog/posthog.com and reads the MDX. PostHog composes their docs from shared _snippets/ fragments, so a section like "Capturing events" is two lines on disk: an import and <WebSendEvents />. The loader resolves that import graph recursively before it chunks anything. Skip that step and you index empty sections where the useful content should be.

Retrieval (retrieval.py) runs BM25 over two fields, heading and body, scored separately and summed. Tokens get stemmed and camelCase gets split, so usePostHog matches a query about posthog and "Capturing custom events" matches someone asking how to capture a custom event. No vector database.

Assembly (assemble.py) is where the work is. Seven steps, and only the first one adds anything: retrieve 24 candidates, re-score them for task fit, cut everything below 42% of the top passage, fold near-duplicates together, cap the answer at four distinct docs, fill the token budget in value order, then sort into reading order with a citation on every passage.

Server (server.py) exposes three tools over stdio.

Eval (eval/) runs 28 cases through three configs and writes the chart.

Setup

python3 -m venv .venv && source .venv/bin/activate && pip install -e .
python -m posthog_context.ingest

The ingest pulls about 11MB of markdown into data/, which is gitignored, and builds the index. PostHog's /contents/ directory is MIT licensed and the rest of that repo is not, so this reads only /contents/ and vendors nothing.

The tools

tool what it's for
how_do_i(task, token_budget=800) The one that matters. Give it a task in plain language and it returns assembled, cited, budgeted context.
search_posthog_docs(query, k=5) Ranked snippets with no assembly, for when an agent wants to see what documentation exists.
get_posthog_doc(path_or_slug) A whole page, for when it genuinely needs all of it.

Connect it to an agent

Claude Desktop reads ~/Library/Application Support/Claude/claude_desktop_config.json on macOS. Cursor reads .cursor/mcp.json in your project. Same shape either way:

{
  "mcpServers": {
    "posthog-context": {
      "command": "/absolute/path/to/posthog-mcp-mini/.venv/bin/python",
      "args": ["-m", "posthog_context.server"],
      "cwd": "/absolute/path/to/posthog-mcp-mini"
    }
  }
}

Use absolute paths, and run the ingest first. The server refuses to start without an index rather than quietly serving an empty one.

Run the eval

python -m eval.run

It prints the table, names every case that failed, and writes eval/out/comparison.png.

The loader asserts that the number of parsed cases matches the number the file declares, and that every gold label points at a doc the index actually contains. A loader that silently skips a malformed case still gives you a confident number, just for a smaller experiment you didn't run, and nothing downstream can tell you that happened.

Each module also checks itself:

python -m posthog_context.ingest      # 6 assertions on chunk quality
python -m posthog_context.retrieval   # ranking sanity
python -m posthog_context.assemble    # budgets are never exceeded

Scope

The JavaScript and Web SDK, plus client-side capture. Installation, custom events, identify, person properties, autocapture, configuration. 185 chunks from 15 docs.

Feature flags, session replay, experiments and the server-side SDKs are all left out on purpose. Widening the index makes it vaguer, and the argument here is about depth.

What's still wrong with it

28 cases is a small eval, and I tuned constants while watching it. I kept myself honest by only making changes I could argue from principle, so the length penalty became concave because a 28-token stub can't answer anything, rather than because 0.38 happened to score better. A held-out set is the next step.

The token counter estimates four characters per token. Every number here inherits that approximation. It routes through one function, so swapping in a real tokenizer is a one-line change.

Two restraint failures survive. Ask how to remove a stored super property and you get the right passage plus a passage about removing person properties. Those two read almost identically and they're different APIs. BM25 can't separate them, and neither can my re-scoring. That specific problem is the honest case for adding embeddings.

NOTES.md has the full decision log, including four bugs that produced output looking completely fine.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选