要点
TypeScriptでMCPサーバーを構築し、run_test、validate_schema、list_environmentsの3つのツールを公開します。Claude Codeの場合は~/.claude/settings.json、Cursorの場合は.cursor/mcp.jsonで設定します。これにより、AIエージェントはチャットインターフェースを離れることなく、Apidogテストの実行、OpenAPIスキーマの検証、環境の取得が可能になります。全ソースコードは約150行で、@modelcontextprotocol/sdkパッケージを使用しています。
Claude Code、Cursor、およびその他のAIエージェントが、チャットインターフェースを離れることなく、Apidog APIテストの実行、スキーマの検証、応答の比較を行えるMCPサーバーを構築します。
💡 ユースケース例:
AIエージェントがAPIエンドポイントの構築を終えた後、コマンド一発でApidogテストの実行・検証が可能。Apidogを手動で開く必要はありません。
これこそが、モデルコンテキストプロトコル(MCP)が実現することです。MCPは、AIエージェントが標準化されたインターフェースを介して外部ツールにアクセスすることを可能にします。Apidog用のMCPサーバーを構築すれば、AIエージェントはコンテキストを切り替えることなく、テストの実行、スキーマの検証、環境の取得を行うことができます。
MCPとは何か?
MCP(モデルコンテキストプロトコル)は、AIエージェントが外部ツールやデータソースにアクセスするためのプロトコルです。Claude Code、Cursor、およびその他のMCP互換クライアント間で動作するプラグインシステムと考えることができます。
MCPサーバーは、ツール(エージェントが呼び出せる関数)とリソース(エージェントが読み取れるデータ)を公開します。Apidog MCPサーバーは、APIテスト用のツールを公開します。
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ AI Agent │ │ MCP Server │ │ Apidog │
│ (Claude Code) │◄───────►│ (Your Code) │◄───────►│ API │
└─────────────────┘ JSON └──────────────────┘ HTTP └─────────────┘
ステップ1: プロジェクトのセットアップ
新しいTypeScriptプロジェクトを作成し、必要な依存パッケージをインストールします。
mkdir apidog-mcp-server
cd apidog-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
tsconfig.jsonを作成します:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
package.jsonにビルド&起動スクリプトを追加:
{
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}
ステップ2: MCPサーバーのスケルトンを作成する
src/index.tsを作成します:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "apidog",
version: "1.0.0",
description: "Apidog API testing tools for AI agents"
});
// 後でツールを定義
const transport = new StdioServerTransport();
await server.connect(transport);
これでMCPサーバーが標準入出力(stdio)トランスポートにバインドされ、AIエージェントと通信できるようになります。
ステップ3: run_testツールの定義
APIテストコレクションを実行するツールを追加します。
// Tool: run_test
server.tool(
"run_test",
{
projectId: z.string().describe("Apidog project ID (found in project URL)"),
environmentId: z.string().optional().describe("Optional environment ID for test execution"),
testSuiteId: z.string().optional().describe("Optional test suite ID to run specific suite")
},
async ({ projectId, environmentId, testSuiteId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY environment variable not set"
}]
};
}
let url = `https://api.apidog.com/v1/projects/${projectId}/tests/run?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation`;
const params = new URLSearchParams();
if (environmentId) params.append("environmentId", environmentId);
if (testSuiteId) params.append("testSuiteId", testSuiteId);
if (params.toString()) url += `&${params.toString()}`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
}
});
if (!response.ok) {
const error = await response.text();
return {
content: [{
type: "text",
text: `API Error: ${response.status} ${error}`
}]
};
}
const results = await response.json();
return {
content: [{
type: "text",
text: JSON.stringify(results, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Request failed: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
ステップ4: validate_schemaツールの追加
OpenAPIスキーマの検証ツールを追加します。
// Tool: validate_schema
server.tool(
"validate_schema",
{
schema: z.object({}).describe("OpenAPI 3.x schema object to validate"),
strict: z.boolean().optional().default(false).describe("Enable strict mode for additional checks")
},
async ({ schema, strict }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY environment variable not set"
}]
};
}
try {
const response = await fetch("https://api.apidog.com/v1/schemas/validate?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ schema, strict })
});
const result = await response.json();
if (!response.ok) {
return {
content: [{
type: "text",
text: `Validation failed: ${JSON.stringify(result.errors, null, 2)}`
}]
};
}
return {
content: [{
type: "text",
text: result.valid
? "Schema is valid OpenAPI 3.x"
: `Warnings: ${JSON.stringify(result.warnings, null, 2)}`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Validation failed: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
ステップ5: list_environmentsツールの追加
利用可能なテスト環境を取得するツールを追加します。
// Tool: list_environments
server.tool(
"list_environments",
{
projectId: z.string().describe("Apidog project ID")
},
async ({ projectId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY environment variable not set"
}]
};
}
try {
const response = await fetch(
`https://api.apidog.com/v1/projects/${projectId}/environments?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation`,
{
headers: {
"Authorization": `Bearer ${apiKey}`
}
}
);
if (!response.ok) {
const error = await response.text();
return {
content: [{
type: "text",
text: `API Error: ${response.status} ${error}`
}]
};
}
const environments = await response.json();
return {
content: [{
type: "text",
text: environments.length === 0
? "No environments found for this project"
: environments.map((e: any) =>
`- ${e.name} (ID: ${e.id})${e.isDefault ? " [default]" : ""}`
).join("\n")
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Request failed: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
ステップ6: ビルドとテスト
ビルドします。
npm run build
MCPクライアントでローカルテストを行うには、test-client.jsを作成:
import { spawn } from "child_process";
const server = spawn("node", ["dist/index.js"], {
env: { ...process.env, APIDOG_API_KEY: "your-api-key" }
});
server.stdout.on("data", (data) => {
console.log(`Server output: ${data}`);
});
server.stderr.on("data", (data) => {
console.error(`Server error: ${data}`);
});
// Send a test message
const message = {
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: {
protocolVersion: "2024-11-05",
capabilities: {},
clientInfo: { name: "test-client", version: "1.0.0" }
}
};
server.stdin.write(JSON.stringify(message) + "\n");
ステップ7: Claude Codeの構成
~/.claude/settings.jsonにMCPサーバーを追加します:
{
"mcpServers": {
"apidog": {
"command": "node",
"args": ["/absolute/path/to/apidog-mcp-server/dist/index.js"],
"env": {
"APIDOG_API_KEY": "your-api-key-here"
}
}
}
}
Claude Codeを再起動し、APIテストのヘルプを求めるとApidogツールが利用可能になります。
Claude Codeでの使用例:
Use the run_test tool to run tests on my Apidog project.
Project ID: proj_12345
Environment: staging
Validate this OpenAPI schema against Apidog rules:
[paste schema]
List all environments for project proj_12345
ステップ8: Cursorの構成
Cursorでは、プロジェクト直下に.cursor/mcp.jsonを作成します:
{
"mcpServers": {
"apidog": {
"command": "node",
"args": ["/absolute/path/to/apidog-mcp-server/dist/index.js"],
"env": {
"APIDOG_API_KEY": "your-api-key-here"
}
}
}
}
Cursorでの使用例:
@apidog run_test projectId="proj_12345" environmentId="staging"
完全なソースコード
src/index.tsの全体例です。
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "apidog",
version: "1.0.0",
description: "Apidog API testing tools for AI agents"
});
// Tool: run_test
server.tool(
"run_test",
{
projectId: z.string().describe("Apidog project ID"),
environmentId: z.string().optional().describe("Environment ID"),
testSuiteId: z.string().optional().describe("Test suite ID")
},
async ({ projectId, environmentId, testSuiteId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY not set"
}]
};
}
let url = `https://api.apidog.com/v1/projects/${projectId}/tests/run?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation`;
const params = new URLSearchParams();
if (environmentId) params.append("environmentId", environmentId);
if (testSuiteId) params.append("testSuiteId", testSuiteId);
if (params.toString()) url += `&${params.toString()}`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
}
});
const results = await response.json();
return {
content: [{
type: "text",
text: JSON.stringify(results, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Request failed: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
// Tool: validate_schema
server.tool(
"validate_schema",
{
schema: z.object({}).describe("OpenAPI schema"),
strict: z.boolean().optional().default(false)
},
async ({ schema, strict }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY not set"
}]
};
}
const response = await fetch("https://api.apidog.com/v1/schemas/validate?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ schema, strict })
});
const result = await response.json();
return {
content: [{
type: "text",
text: result.valid
? "Schema is valid"
: `Issues: ${JSON.stringify(result.errors || result.warnings, null, 2)}`
}]
};
}
);
// Tool: list_environments
server.tool(
"list_environments",
{
projectId: z.string().describe("Apidog project ID")
},
async ({ projectId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY not set"
}]
};
}
const response = await fetch(
`https://api.apidog.com/v1/projects/${projectId}/environments?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation`,
{
headers: { "Authorization": `Bearer ${apiKey}` }
}
);
const environments = await response.json();
return {
content: [{
type: "text",
text: environments.map((e: any) =>
`- ${e.name} (${e.id})${e.isDefault ? " [default]" : ""}`
).join("\n")
}]
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
構築したもの
| コンポーネント | 目的 |
|---|---|
| MCPサーバー | AIエージェントとApidog APIを連携させる |
run_test |
テストコレクションをプログラムで実行する |
validate_schema |
デプロイ前にOpenAPIエラーを捕捉する |
list_environments |
利用可能なテスト環境を検出する |
| Zod検証 | 型安全なパラメータ処理 |
| 標準入出力トランスポート | Claude Code、Cursor、任意のMCPクライアントで動作する |
次のステップ
サーバーの拡張:
- 環境間のテスト結果を比較する
compare_responsesツールを追加 - 過去のテスト実行履歴を取得する
get_test_historyを追加 - モックエンドポイントを開始/停止する
trigger_mock_serverを追加
本番環境での考慮事項:
- 不安定なネットワークリクエストのためのリトライロジックを追加
- APIスロットリングを避けるためにレート制限を実装
- 失敗したツール呼び出しのデバッグのためにログを追加
- APIキーを環境変数の代わりにセキュアなボールトに保存
チームとの共有:
-
@your-org/apidog-mcp-serverとしてnpmに公開 - 必要な環境変数を文書化
- 一般的なクライアント向けのMCP設定例を含める
一般的な問題のトラブルシューティング
Claude CodeでMCPサーバーがロードされない場合:
-
~/.claude/settings.json内のパスが絶対パスであることを確認 -
nodeコマンドがPATHに存在するか:which node - ビルド済み
dist/index.jsが存在するか:ls -la dist/ - Claude CodeのMCPログでエラーを確認
設定後にツールが表示されない場合:
- Claude Codeを完全に再起動
-
npm run buildでTypeScriptがコンパイルされているか確認 -
server.connect()の前に3つすべてのツールが定義されていることを確認 - サーバーが正常に起動するか:
node dist/index.js
APIリクエストが401で失敗する場合:
-
APIDOG_API_KEYが正しく設定されているか確認 - キー値の余分なスペースや引用符を除去
- ApidogアカウントでAPIアクセスが有効か確認
- キーを手動でテスト:
curl -H "Authorization: Bearer $APIDOG_API_KEY" https://api.apidog.com/v1/user?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation
Zod検証エラーの場合:
- パラメータ名がスキーマと一致しているか
- 必須フィールドが入力されているか
- オプションフィールドは
.optional()を使っているか - エラーメッセージの内容を確認
TypeScriptコンパイルエラーの場合:
- すべての依存を
npm installでインストール - TypeScriptのバージョン確認:
npx tsc --version(5.x推奨) -
rm -rf dist && npm run buildで再ビルド - 型不一致発生時は
as型アサーションを活用
MCPサーバーをローカルでテストする
本番展開前に、ローカルでサーバーを手動テストできます。
標準入出力での動作確認:
# サーバー起動
node dist/index.js
# 別ターミナルからツール一覧リクエスト
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | node dist/index.js
期待される出力例:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{ "name": "run_test", "description": "...", "inputSchema": {...} },
{ "name": "validate_schema", "description": "...", "inputSchema": {...} },
{ "name": "list_environments", "description": "...", "inputSchema": {...} }
]
}
}
ツール呼び出しテスト:
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_environments","arguments":{"projectId":"your-project-id"}}}' | node dist/index.js
→ これでAIエージェントから直接Apidogテスト機能を利用できます。チャットとブラウザ間のコピー&ペーストや手動テストは不要です。
MCPの力でAIエージェントをドメイン固有ツールで強化し、より高速な開発・フィードバックを実現しましょう。
主要なポイント
MCPサーバーはAIエージェントと外部APIを連携
一度構築すれば、Claude Code、Cursor、その他のMCP互換クライアントで利用可能3つのツールでAPIテストニーズをカバー
実行にはrun_test、OpenAPI検証にはvalidate_schema、環境検出にはlist_environmentsZod検証で不正パラメータを防止
型安全なツール定義によりAPI呼び出し前のエラーを捕捉設定ファイルはクライアントごとに異なる
Claude Codeは~/.claude/settings.json、Cursorは.cursor/mcp.jsonを利用本番運用ではエラーハンドリング必須
リトライロジック、レート制限、セキュアなキー保存を追加してから運用
FAQ
AIにおけるMCPとは?
MCP(モデルコンテキストプロトコル)は、AIエージェントが外部ツールやデータソースにアクセスできるようにする標準化されたプロトコルです。AIエージェント用のプラグインシステムと考えることができます。
Apidog用のMCPサーバーを作成するには?
@modelcontextprotocol/sdkをインストールし、Zod検証でツールを定義し、Apidog APIを呼び出すハンドラーを実装し、StdioServerTransport経由で接続します。
Cursorで利用できますか?
はい。プロジェクトルートの.cursor/mcp.jsonにMCPサーバー設定を追加してください。同じサーバーがClaude Code、Cursor、その他MCPクライアントで利用可能です。
どのようなツールを公開すべき?
まずはテストコレクション実行用run_test、OpenAPI検証用validate_schema、利用可能な環境取得用list_environmentsが基本です。
Apidog MCPサーバーは本番対応?
チュートリアルコードはスタートポイントです。本番利用時はリトライ、レート制限、エラー処理、セキュアなAPIキー保存などを実装してください。
Apidog APIキーは必要?
はい。APIDOG_API_KEYを環境変数として設定してください。サーバーはこれを使ってAPIリクエストを認証します。
このMCPサーバーをチームで共有できますか?
はい。プライベートnpmパッケージ化、必須環境変数やMCP設定例のドキュメント化を行いましょう。
Top comments (0)