implementing-mcp-tools

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Implementing MCP tools

实现MCP工具

Read the full guide at docs/published/handbook/engineering/ai/implementing-mcp-tools.md.
完整指南请查阅 docs/published/handbook/engineering/ai/implementing-mcp-tools.md

Quick workflow

快速工作流

sh
undefined
sh
undefined

1. Scaffold a starter YAML with all operations disabled.

1. Scaffold a starter YAML with all operations disabled.

--product is a substring match on URL paths: it selects every endpoint

--product is a substring match on URL paths: it selects every endpoint

whose path contains /<name>/ (hyphens normalized to underscores).

whose path contains /<name>/ (hyphens normalized to underscores).

The value can be a product name or any path-segment needle

The value can be a product name or any path-segment needle

(e.g. --product actions matches /api/projects/{project_id}/actions/).

(e.g. --product actions matches /api/projects/{project_id}/actions/).

pnpm --filter=@posthog/mcp run scaffold-yaml -- --product your_product
--output ../../products/your_product/mcp/tools.yaml
pnpm --filter=@posthog/mcp run scaffold-yaml -- --product your_product
--output ../../products/your_product/mcp/tools.yaml

2. Configure the YAML — enable tools, add scopes, annotations, descriptions

2. Configure the YAML — enable tools, add scopes, annotations, descriptions

Place in products/<product>/mcp/.yaml (preferred) or services/mcp/definitions/.yaml

Place in products/<product>/mcp/.yaml (preferred) or services/mcp/definitions/.yaml

3. Add a HogQL system table in posthog/hogql/database/schema/system.py

3. Add a HogQL system table in posthog/hogql/database/schema/system.py

and a model reference in products/posthog_ai/skills/query-examples/references/

and a model reference in products/posthog_ai/skills/query-examples/references/

4. Generate handlers and schemas

4. Generate handlers and schemas

hogli build:openapi
undefined
hogli build:openapi
undefined

Before you scaffold: fix the backend first

生成脚手架前:先修复后端问题

The codegen pipeline can only generate correct tools if the Django backend exposes correct types. Read the type system guide for the full picture.
Before scaffolding YAML, verify:
  1. Serializers have explicit field types and
    help_text
    — these flow all the way to Zod
    .describe()
    in the generated tool. Missing descriptions = agents guessing at parameters. Use
    ListField(child=serializers.CharField())
    instead of bare
    ListField()
    , and
    @extend_schema_field(PydanticModel)
    on
    JSONField
    subclasses to get typed Zod output (see
    posthog/api/alert.py
    for the pattern).
  2. Plain
    ViewSet
    methods have
    @extend_schema(request=...)
    — without it, drf-spectacular can't discover the request body and the generated tool gets
    z.object({})
    (zero parameters).
    ModelViewSet
    with a
    serializer_class
    is fine; plain
    ViewSet
    with manual validation is not.
  3. Query parameters use
    @validated_request
    or
    @extend_schema
    with a query serializer — otherwise boolean and array query params may produce type mismatches in the generated code.
If a generated tool has an empty or wrong schema, the fix is almost always on the Django side, not in the YAML config.
只有当Django后端暴露了正确的类型时,代码生成流水线才能生成正确的工具。完整说明请查阅类型系统指南
生成YAML脚手架前,请确认:
  1. 序列化器有明确的字段类型和
    help_text
    ——这些信息会一路传递到生成工具中的Zod
    .describe()
    方法。缺少描述会导致Agent猜测参数含义。请使用
    ListField(child=serializers.CharField())
    而非裸写的
    ListField()
    ,在
    JSONField
    子类上使用
    @extend_schema_field(PydanticModel)
    来获得带类型的Zod输出(可参考
    posthog/api/alert.py
    中的写法)。
  2. 普通
    ViewSet
    方法带有
    @extend_schema(request=...)
    注解
    ——没有该注解的话,drf-spectacular无法识别请求体,生成的工具会得到
    z.object({})
    (零参数)。配置了
    serializer_class
    ModelViewSet
    无需额外处理,但采用手动校验的普通
    ViewSet
    需要补充该注解。
  3. 查询参数使用
    @validated_request
    或搭配查询序列化器的
    @extend_schema
    ——否则布尔类型和数组类型的查询参数可能会在生成代码中出现类型不匹配问题。
如果生成的工具 schema 为空或错误,问题几乎都出在Django侧,而非YAML配置。

When to add MCP tools

何时需要添加MCP工具

When a product exposes API endpoints that agents should be able to call. MCP tools are atomic capabilities (list, get, create, update, delete) — not workflows.
If you're adding a new endpoint, check whether it should be agent-accessible. If yes, add a YAML definition and generate the tool.
当产品暴露的API端点需要允许Agent调用时,就需要添加MCP工具。MCP工具是原子能力(列表查询、详情查询、创建、更新、删除),而非工作流。
如果你正在添加新的端点,请确认是否需要允许Agent访问。如果是,则添加对应的YAML定义并生成工具。

Tool design

工具设计

Tools should be basic capabilities — atomic CRUD operations and simple actions. Agents compose these primitives into higher-level workflows.
Good: "List feature flags", "Get experiment by ID", "Create a survey". Bad: "Search for session recordings of an experiment" — bundles multiple concerns.
工具应当是基础能力——原子化的CRUD操作和简单动作。Agent会将这些基础原语组合成更高层级的工作流。
正面示例:"列出功能开关"、"根据ID获取实验信息"、"创建调研"。 负面示例:"搜索某个实验的会话录像"——捆绑了多个职责。

YAML definitions

YAML定义

YAML files configure which operations are exposed as MCP tools. See existing definitions for patterns:
  • products/<product>/mcp/*.yaml
    — preferred, keeps config close to the code
  • services/mcp/definitions/*.yaml
    — shared location
The build pipeline discovers YAML files from both paths.
YAML文件用于配置哪些操作会被暴露为MCP工具。可以参考现有定义的写法:
  • products/<product>/mcp/*.yaml
    ——推荐路径,让配置贴近业务代码
  • services/mcp/definitions/*.yaml
    ——共享配置路径
构建流水线会从以上两个路径自动发现YAML文件。

Key fields

关键字段

yaml
category: Human readable name
feature: snake_case_name
url_prefix: /path
tools:
  your-tool-name: # kebab-case
    operation: operationId_from_openapi
    enabled: true
    scopes:
      - your_product:read
    annotations:
      readOnly: true
      destructive: false
      idempotent: true
    # Optional:
    mcp_version: 1 # 2 for create/update/delete ops, 1 for read/list if available via HogQL
    title: List things
    description: >
      Human-friendly description for the LLM.
    list: true
    enrich_url: '{id}'
    param_overrides:
      name:
        description: Custom description for the LLM
Unknown keys are rejected at build time (Zod
.strict()
).
yaml
category: Human readable name
feature: snake_case_name
url_prefix: /path
tools:
  your-tool-name: # kebab-case
    operation: operationId_from_openapi
    enabled: true
    scopes:
      - your_product:read
    annotations:
      readOnly: true
      destructive: false
      idempotent: true
    # Optional:
    mcp_version: 1 # 2 for create/update/delete ops, 1 for read/list if available via HogQL
    title: List things
    description: >
      Human-friendly description for the LLM.
    list: true
    enrich_url: '{id}'
    param_overrides:
      name:
        description: Custom description for the LLM
未知字段会在构建阶段被拒绝(采用Zod
.strict()
校验)。

Syncing after endpoint changes

端点变更后同步

sh
pnpm --filter=@posthog/mcp run scaffold-yaml -- --sync-all
Idempotent and non-destructive — adds new operations as
enabled: false
, removes stale ones.
sh
pnpm --filter=@posthog/mcp run scaffold-yaml -- --sync-all
该操作是幂等且无破坏性的——会将新操作添加为
enabled: false
状态,移除已失效的操作。

Serializer descriptions

序列化器描述

Descriptions flow through the entire pipeline:
text
Django serializer field → OpenAPI spec → Zod schema → MCP tool description
These descriptions are what agents read to understand tool parameters.
  • Use
    help_text
    on serializer fields — it becomes the OpenAPI description.
  • Use
    param_overrides
    in YAML to override generated descriptions with imperative instructions.
  • Be specific about formats, constraints, and valid values.
  • Avoid jargon that an LLM wouldn't understand without context.
描述信息会贯穿整个流水线:
text
Django serializer field → OpenAPI spec → Zod schema → MCP tool description
这些描述是Agent理解工具参数的依据。
  • 在序列化器字段上使用
    help_text
    ——它会成为OpenAPI的描述信息。
  • 在YAML中使用
    param_overrides
    可以覆盖生成的描述,添加更明确的指令。
  • 要明确说明参数的格式、约束和合法值。
  • 避免使用LLM没有上下文就无法理解的行业黑话。

HogQL system tables

HogQL系统表

Every list/get endpoint should have a corresponding HogQL system table in
posthog/hogql/database/schema/system.py
. This lets agents query data via SQL in v2 of the MCP.
Each system table must include a
team_id
column
for data isolation.
Use
mcp_version: 1
on read/list YAML tools when a system table covers the same data — v2 agents use SQL instead.
When adding a system table, also add a model reference file (
models-<domain>.md
) in
products/posthog_ai/skills/query-examples/references/
and register it in
products/posthog_ai/skills/query-examples/SKILL.md
under Data Schema.
每个列表/详情查询端点都应该在
posthog/hogql/database/schema/system.py
中有对应的HogQL系统表,这样Agent就可以在MCP v2版本中通过SQL查询数据。
每个系统表必须包含
team_id
字段
以实现数据隔离。
如果读/列表类YAML工具对应的能力已经被系统表覆盖,请设置
mcp_version: 1
——v2版本的Agent会优先使用SQL查询。
添加系统表时,还需要在
products/posthog_ai/skills/query-examples/references/
中添加模型引用文件(
models-<domain>.md
),并在
products/posthog_ai/skills/query-examples/SKILL.md
Data Schema部分注册该文件。

Two MCP versions

MCP的两个版本

  • v1 (legacy): all CRUD tools exposed, for clients without skill support.
  • v2 (SQL-first): read/list tools replaced by HogQL, create/update/delete tools kept. For coding agents.
Control per-tool availability with
mcp_version: 1/2
in the YAML definition.
  • v1( legacy 版本):暴露所有CRUD工具,适用于不支持skill的客户端。
  • v2(SQL优先版本):读/列表类工具被HogQL替代,保留创建/更新/删除类工具,适用于编码Agent。
可以通过YAML定义中的
mcp_version: 1/2
参数控制每个工具的可用版本。