mcpField
T
testing-mcp-tools-locally

testing-mcp-tools-locally

github.com/posthogテスト・品質9561 回インストール今日更新
リポジトリ

testing-mcp-tools-locally について

PostHogのmanaged migrations MCPツールをローカルでテストする手順です。環境構築、スコープ付きスタッフ認証情報、ツールの検出、ローカルAPIへの一連の呼び出しを扱います。

主な機能

  1. 01ローカル開発環境の起動
  2. 02Postgresとバッチインポート用テストデータの準備
  3. 03スタッフユーザーとスコープ付きAPIキーの作成
  4. 04MCP経由のツール検出と呼び出しのテスト
  5. 05認証と接続の問題の診断

活用シーン

  1. 01スタッフキーでのサポートツール表示確認
  2. 02スコープ不足や非スタッフの場合の権限確認
  3. 03ローカルAPIに対するバッチインポートツールの実行

MCP 対応

Hono MCPサーバーをローカルAPIに接続し、MCP Inspectorでテストする手順です。execモードでのツール検出と呼び出し、スタッフ判定とAPIスコープのゲートを説明します。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/posthog/ai-plugin --skill testing-mcp-tools-locally

インストール後、Claude Code で/testing-mcp-tools-locallyを入力して読み込まれたか確認します。

同梱ファイルreferences/seed-data.md

詳細

管理対象のマイグレーション MCP ツールをローカルでテストする

前提条件

Docker サービスが正常に稼働している状態で、開発環境を起動しておく必要があります。 バッチインポートサポート API と MCP ツールには、以下が必要です。

  • スタッフユーザー(is_staff = True)
  • batch_import_support:read と user:read の両方を明示的に付与した Personal API Key
  • 適用済みの Postgres マイグレーション(ClickHouse は不要)

両方のスコープが必要な理由:バックエンドは batch_import_support:read のみでも受け付けますが、MCP ツールの検出では /api/users/@me/ を通じてスタッフ権限を確認します。その呼び出しをキーで実行できない場合、ツールはフェイルクローズで非表示になります。 * ワイルドカードは、どちらのスコープの代わりにもなりません。ツールの検出ゲートでは非公開スコープを明示的に要求し、バックエンドの INTERNAL スコープ処理ではワイルドカードキーが明示的に拒否されます。 本番環境でのセットアップ手順については、docs/support-mcp-tools.md を参照してください。

1. 開発環境を起動する

hogli start -d
hogli wait

hogli wait が migrate-persons-db または migrate-behavioral-cohorts で失敗した場合、これらはオプションの個別データベースなので無視してください。 migrate-postgres で失敗した場合は、Docker のポートフォワーディングを確認してください(下記のトラブルシューティングを参照)。

2. Postgres マイグレーションを実行する

hogli migrations:run

ClickHouse のマイグレーションが失敗しても問題ありません。バッチインポートで必要なのは Postgres のみです。

3. Django シェルから DB 接続を確認する

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

ポート 5432 で connection refused が発生して失敗する場合は、下記のトラブルシューティングを参照してください。

4. バッチインポートのテストデータをシードする

hogli dev:shell-plus を使って、さまざまな状態の BatchImport レコードを作成します。 secrets フィールドは EncryptedJSONStringField です。空の {} はシリアライズ時に null となり、NOT NULL 制約に違反します。必ず空でない dict を渡してください。

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'},
    ]},
)

すべてのステータスを網羅したシードスクリプトについては、references/seed-data.md を参照してください。

重要: ローカルの batch-import-worker プロセスは RUNNING レコードを処理し、そのステータスを変更する場合があります(例:設定の検証エラーによる一時停止)。 テスト中にレコードの状態を維持するには、ワーカーを停止するか、COMPLETED/FAILED/PAUSED のステータスを使用してください。

5. ユーザーをスタッフにしてテストキーを発行する

既存のキーのスコープを編集するのではなく、新しいキーを発行してください。 MCP サーバーはトークンごとにキーのスコープをキャッシュするため、スコープを編集すると古い結果が返される場合があります。

from posthog.models import User
from posthog.models.personal_api_key import PersonalAPIKey
from 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"]))

検出ゲートの否定ケースをテストするには、次のキーも発行してください。 ["*"] のキー(ツールは表示されてはなりません)、 user:read を含まない ["batch_import_support:read"] のキー(スタッフの照会はフェイルクローズとなるため、ツールは表示されてはなりません)、 およびスタッフではないユーザーに両方のスコープを付与したキー(ツールは表示されてはなりません)。

6. API を直接テストする

# List all batch imports
curl -H "Authorization: Bearer <token>" \
     http://localhost:8010/api/managed_migrations_support/ | jq

# Get detail for a specific import
curl -H "Authorization: Bearer <token>" \
     http://localhost:8010/api/managed_migrations_support/<uuid>/ | jq

7. MCP 経由でテストする

pnpm run dev ではなく、Hono サーバーを実行してください。 wrangler worker(pnpm run dev、ポート 8787)は、MCP_HONO_URL が設定されていない場合、/mcp を本番環境の mcp.us.posthog.com にプロキシします。 そのため、ローカルのキーでは 401 Invalid API key が返されます。 Hono サーバーは、ローカル API に直接接続して MCP を提供します。

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

PAT は Bearer ヘッダーで認証し、OAuth フローは使わないでください。 非公開のスコープは OAuth には構造上存在しないため、Inspector の OAuth ログインでサインインしても、これらのツールが表示されることはありません。

Hono サーバーは exec モードで実行されます。tools/list は単一の exec ツールを返し、 実際のツールはそれを通じて検出・呼び出しされます。 MCP Inspector CLI でテストしてください。

# Discovery — should list both support tools for the staff key, none for the others
npx @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 Django
npx @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 {}"

想定される検出結果:

key表示されるツール
スタッフユーザー、batch_import_support:read + user:read両方
スタッフユーザー、* のみなし
スタッフユーザー、user:read なしの batch_import_support:readなし(スタッフ確認はフェイルクローズ)
非スタッフユーザー、両方のスコープなし(直接 API 呼び出しも 403)

インタラクティブな Inspector UI(http://localhost:6274)も動作します — OAuth ログインを使わず、接続設定で PAT を Bearer トークンとして貼り付けてください。

トラブルシューティング

localhost:8787 から 401「Invalid API key」が返される

wrangler worker に接続しています。この worker は /mcp を本番環境にプロキシするため、ローカルのキーはそこで無効です。 ポート 3001 の Hono サーバーを使う(手順 7 を参照)か、.dev.vars に MCP_HONO_URL=http://localhost:3001 を設定してください。

表示されるはずのキーでツールが表示されない

次の順に確認してください。

  1. キーに batch_import_support:read が明示的に含まれていること — * は非公開スコープに一致しません。
  2. キーに user:read(または *)も含まれていること — 検出時のスタッフ確認では /api/users/@me/ を読み取り、フェイルクローズします。
  3. キーのユーザーが is_staff = True であること。
  4. キーが最初からこれらのスコープを指定して発行されていること — MCP サーバーはトークンごとにスコープをキャッシュするため、既存のキーを編集するのではなく、新しいキーを発行してください。
ホストからポート 5432 に接続できない

posthog-db-1 Docker コンテナのポートマッピングが古くなっている可能性があります (現在のポートバインディング設定なしで数日前に作成されたコンテナなど)。 次のコマンドで強制的に再作成してください。

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

確認方法:nc -z 127.0.0.1 5432 が成功するはずです。

secrets={} によって NOT NULL 制約違反が発生する

EncryptedJSONStringField は値を暗号化します — 空の辞書はシリアライズ時に null になります。 必ず空でない辞書を渡してください:secrets={'placeholder': 'true'}。

バッチインポートワーカーがシード済みレコードを変更する

ローカルの batch-import-worker プロセスは RUNNING レコードを自動的に取得します。 設定の検証エラー(例:skip_blanks がない場合)が発生すると、status_message に詳細な Rust バックトレースを記録してインポートを一時停止します。 これを防ぐには、ワーカーを停止するか、RUNNING 以外のステータスでシードしてください。

ゲートの全体像

リクエストは、独立した 2 つのレイヤーを通過します。

  1. MCP 検出(表示制御):OAuth で非表示のスコープを必要とするツールが表示されるのは、キーにそのスコープが明示的に含まれ、かつ /api/users/@me/ で is_staff が確認された場合のみです — それ以外の場合は非表示となり、フェイルクローズします(services/mcp/src/lib/staff-only-tools.ts)。
  2. Django による強制(セキュリティ境界):scope_object = "INTERNAL" および batch_import_support:read を条件とする IsAuthenticated + IsStaffUser + APIScopePermission。セッションではスタッフであることだけが必要です。PAT ではスタッフであることに加えて明示的なスコープが必要です。* のみのキーは常に 403 になります。

コメント