Testing Mcp Tools Locally

作者 PostHog469d1773e9cb無授權條款收錄於 2026年10月8日更新於 2026年10月8日

Set up the local dev environment, seed data, and API keys to test the staff-only managed migrations MCP tools (managed-migrations-support-list, managed-migrations-support-get) end to end. Use when testing batch import support tooling, debugging MCP tool responses or discovery (tools not appearing), or verifying the support API before deploying. Covers the discovery gate: hidden scope, is_staff, user:read, and why wildcard keys and OAuth never work.

AI 產生的概覽

架設本機開發環境、種子資料與 API 金鑰,端對端測試僅限員工使用的受管理遷移 MCP 工具。

功能
說明如何啟動本機開發環境、執行 Postgres 遷移、驗證資料庫連線,並建立處於不同狀態的 BatchImport 紀錄。介紹如何將使用者設為員工並簽發帶有所需權限範圍的個人 API 金鑰,接著用 curl 呼叫支援 API,並透過 Hono 伺服器與 MCP Inspector 呼叫 MCP 工具。也說明了工具探索閘門以及常見錯誤的疑難排解清單。
適用情境
適用於在本機測試批次匯入支援工具、排查 MCP 工具回應或工具未出現等探索問題,或在部署前驗證支援 API。
執行需求
需要執行中的本機開發環境與 Docker 服務、已套用的 Postgres 遷移、hogli 命令列工具、services/mcp 中以 pnpm 執行的 Hono MCP 伺服器,以及用於 MCP Inspector 的 npx。還需要員工使用者與帶有 batch_import_support:read 和 user:read 權限範圍的個人 API 金鑰。僅為說明文件,未隨附指令碼。

Testing managed migrations MCP tools locally

Prerequisites

The dev environment must be running with Docker services healthy. The batch import support API and MCP tools require:

  • A staff user (is_staff = True)
  • A Personal API Key carrying both batch_import_support:read and user:read, explicitly
  • Postgres migrations applied (ClickHouse not required)

Why both scopes: the backend accepts batch_import_support:read alone, but MCP tool discovery verifies staffness via /api/users/@me/ and hides the tools (fail-closed) when the key cannot make that call. A * wildcard does not substitute for either — the discovery gate requires the hidden scope explicitly, and the backend's INTERNAL scope handling rejects wildcard keys outright. For the production setup flow, see docs/support-mcp-tools.md.

1. Start the dev environment

bash
hogli start -dhogli wait

If hogli wait fails on migrate-persons-db or migrate-behavioral-cohorts, those are optional separate databases — ignore them. If it fails on migrate-postgres, check Docker port forwarding (see troubleshooting below).

2. Run Postgres migrations

bash
hogli migrations:run

ClickHouse migration failures are fine — batch imports only need Postgres.

3. Verify DB connectivity from the Django shell

bash
hogli dev:shell-plus -y -- -c "from posthog.models import Team, Userprint(Team.objects.first(), User.objects.first())"

If this fails with connection refused on port 5432, see troubleshooting below.

4. Seed batch import test data

Use hogli dev:shell-plus to create BatchImport records in various states. The secrets field is an EncryptedJSONStringField — empty {} serializes to null and violates the NOT NULL constraint; always pass a non-empty dict.

python
from products.managed_migrations.backend.models.batch_imports import BatchImport
BatchImport.objects.create(    team=team,    created_by_id=user.id,    status=BatchImport.Status.PAUSED,    import_config={        'source': {'type': 's3', 'bucket': 'test', 'region': 'us-east-1', 'prefix': 'data/'},        'data_format': {'type': 'json_lines', 'skip_blanks': True, 'content': {'type': 'mixpanel'}},        'sink': {'type': 'capture'},    },    secrets={'access_key': 'test', 'secret_key': 'test'},    state={'parts': [        {'key': 'part-1', 'current_offset': 50000, 'total_size': 50000},        {'key': 'part-2', 'current_offset': 10000, 'total_size': 50000},        {'key': 'part-3'},    ]},)

See references/seed-data.md for a full seeding script covering all statuses.

Important: the local batch-import-worker process will pick up RUNNING records and may modify their status (e.g. pausing them due to config validation errors). To keep records stable for testing, either stop the worker or use COMPLETED/FAILED/PAUSED statuses.

5. Make your user staff and mint test keys

Mint fresh keys rather than editing scopes on an existing one — the MCP server caches a key's scopes per token, so edited scopes can serve stale results.

python
from posthog.models import Userfrom posthog.models.personal_api_key import PersonalAPIKeyfrom posthog.models.utils import generate_random_token_personal, hash_key_value
me = User.objects.first()me.is_staff = True; me.save()
def mint(user, scopes):    token = generate_random_token_personal()    PersonalAPIKey.objects.create(user=user, label=str(scopes)[:40], secure_value=hash_key_value(token), scopes=scopes)    return token
print(mint(me, ["batch_import_support:read", "user:read"]))

To test the negative cases of the discovery gate, also mint: a ["*"] key (tools must NOT appear), a ["batch_import_support:read"] key without user:read (tools must NOT appear — staff lookup fails closed), and the full pair on a non-staff user (tools must NOT appear).

6. Test the API directly

bash
# List all batch importscurl -H "Authorization: Bearer <token>" \     http://localhost:8010/api/managed_migrations_support/ | jq
# Get detail for a specific importcurl -H "Authorization: Bearer <token>" \     http://localhost:8010/api/managed_migrations_support/<uuid>/ | jq

7. Test via MCP

Run the Hono server, not pnpm run dev. The wrangler worker (pnpm run dev, port 8787) proxies /mcp to production mcp.us.posthog.com unless MCP_HONO_URL is set, so local keys get 401 Invalid API key. The Hono server serves MCP directly against the local API:

bash
cd services/mcpcp .dev.vars.example .dev.vars   # POSTHOG_API_BASE_URL=http://localhost:8010pnpm run dev:hono                # serves http://localhost:3001/mcp

Authenticate with the PAT as a Bearer header, never the OAuth flow. The hidden scope is structurally absent from OAuth — signing in through the inspector's OAuth login can never surface these tools.

The Hono server runs exec mode: tools/list returns a single exec tool, and real tools are discovered and invoked through it. Test with the MCP Inspector CLI:

bash
# Discovery — should list both support tools for the staff key, none for the othersnpx @modelcontextprotocol/inspector --cli http://localhost:3001/mcp \  --header "Authorization: Bearer <token>" \  --method tools/call --tool-name exec --tool-arg "command=search managed-migrations-support"
# Invocation — end-to-end through Djangonpx @modelcontextprotocol/inspector --cli http://localhost:3001/mcp \  --header "Authorization: Bearer <token>" \  --method tools/call --tool-name exec --tool-arg "command=call managed-migrations-support-list {}"

Expected discovery matrix:

keytools visible
staff user, batch_import_support:read + user:readboth
staff user, * onlynone
staff user, batch_import_support:read without user:readnone (staff lookup fails closed)
non-staff user, both scopesnone (and direct API calls 403)

The interactive Inspector UI (http://localhost:6274) also works — paste the PAT as the Bearer token in connection settings instead of using its OAuth login.

Troubleshooting

401 "Invalid API key" from localhost:8787

You're talking to the wrangler worker, which proxies /mcp to production — your local key is invalid there. Use the Hono server on port 3001 (see step 7), or set MCP_HONO_URL=http://localhost:3001 in .dev.vars.

Tools don't appear for a key that should see them

Check, in order:

  1. The key carries batch_import_support:read explicitly — * does not match hidden scopes.
  2. The key also carries user:read (or *) — the discovery staff check reads /api/users/@me/ and fails closed.
  3. The key's user has is_staff = True.
  4. The key was minted with those scopes from the start — the MCP server caches scopes per token, so mint a fresh key instead of editing an existing one.

Port 5432 not reachable from host

The posthog-db-1 Docker container may have stale port mappings (container created days ago without the current port binding config). Fix by force-recreating:

bash
docker compose -f docker-compose.dev.yml -f docker-compose.profiles.yml \  up -d --force-recreate db

Verify: nc -z 127.0.0.1 5432 should succeed.

secrets={} causes NOT NULL violation

EncryptedJSONStringField encrypts the value — an empty dict serializes to null. Always pass a non-empty dict: secrets={'placeholder': 'true'}.

Batch import worker modifies seeded records

The local batch-import-worker process automatically claims RUNNING records. If it encounters a config validation error (e.g. missing skip_blanks), it will pause the import with a detailed Rust backtrace in status_message. Stop the worker or seed with non-RUNNING statuses to prevent this.

The gates, end to end

A request passes through two independent layers:

  1. MCP discovery (presentation): a tool requiring an OAuth-hidden scope surfaces only when the key explicitly carries the scope AND /api/users/@me/ confirms is_staff — otherwise it is hidden, fail-closed (services/mcp/src/lib/staff-only-tools.ts).
  2. Django enforcement (the security boundary): IsAuthenticated + IsStaffUser + APIScopePermission with scope_object = "INTERNAL" and batch_import_support:read. Sessions need staffness only; PATs need staffness plus the explicit scope; *-only keys always 403.

來源與署名

來源:PostHog/ai-plugin位於skills/testing-mcp-tools-locally提交469d177

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架

更多來自 PostHog/ai-plugin 的技能

Writing Simplified Technical English

PostHog

套用 ASD-STE100 簡化技術英語規則,讓代理撰寫的文字語意明確、方便執行。

Writing & Content2026年10月8日

Working With Task Comments

PostHog

透過 PostHog MCP exec 調度器讀取並解讀 PostHog 任務、成品和畫布上的留言。

Productivity & Workflow2026年10月8日

Working With Skills

PostHog

指導代理使用 PostHog 的 skill-* MCP 工具來探索、讀取、建立、更新與重構技能。

AI & Agents2026年10月8日

Working With Scouts

PostHog

說明如何把監看工作委派給 PostHog Signals 偵察代理、處理其回報,並長期調校整個代理團隊的操作手冊。

AI & Agents2026年10月8日

Validating And Publishing Canvases

PostHog

Validate and publish a canvas source project safely: the source-project shape, declared capabilities, reading the current version pointer, iterating on validation diagnostics, guarded publishing with expected_current_version_id, staging a draft build and promoting it, waiting out the queued build, and recovering from a 409 version_conflict or a 429 capacity limit without overwriting concurrent work. Use whenever a canvas edit is ready to save, a draft build is wanted, a canvas publish or build returns diagnostics or a conflict, or a task needs to understand canvas version history.

待分類2026年10月8日

Understanding Billing Usage

PostHog

Explains PostHog billing usage and spend from the customer's visible Billing MCP tools. Use when the user asks why usage or spend is high, which product or project is driving usage, what a usage type means, how to reduce usage, what changed over time, why they got a usage change alert, or whether a spike/drop alert was real or noisy. Also use before product-specific analytics skills when the user names a billable PostHog product metric such as events, recordings, feature flag requests, exceptions, survey responses, synced rows, logs, AI events, AI credits, or Inbox credits. Starts from Billing usage/spend tools, then routes to customer-visible product MCP surfaces for deeper investigation.

待分類2026年10月8日