コンテンツへスキップ
Guide

MCP サーバーを保護する: 8 項目の開発者チェックリスト

| 8 min read

2026 年に、43 のエージェント フレームワークに脆弱性が埋め込まれて出荷されました。AI エージェントがギャップを見つける前に、MCP サーバーをロックダウンするための 8 つのチェック。

Server room with colorful network cables representing MCP server infrastructure
Photo by Taylor Vick on Unsplash

2026 年 11 月、バラクーダ社は、43 個のエージェント フレームワーク コンポーネントに脆弱性が埋め込まれた状態で出荷されたと報告しました。 サプライチェーンの妥協によって。 攻撃者はサーバーに侵入しませんでした。 彼らはAIエージェントが使用するツールを汚染した 電話する。 OWASP Agentic Apps トップ 10 では、サプライ チェーンの脆弱性とツールの誤用が専用としてリストされるようになりました カテゴリー。

MCP (Model Context Protocol) サーバーは、AI アシスタントの呼び出し可能なツールとして API を公開します: Claude Desktop、 カーソル、VS Code 副操縦士、ウィンドサーフィン。 登録したすべてのツールが攻撃対象領域となります。 そしてほとんどの MCP サーバー 認証、入力検証、ログ記録なしで出荷されます。

この投稿は、MCP サーバーをセキュリティで保護するための 8 項目のチェックリストです。 各項目には、実行可能なコードが含まれています。 適応する。 例では、 Botoi API スキーマ検証、ハッシュ、 および JWT デコードですが、パターンはどの MCP サーバーにも当てはまります。

1 つのテーブルにおける MCP セキュリティ問題

リスク 何が起こるのですか チェックリスト項目
MCP エンドポイントに認証がありません ネットワーク上のすべてのクライアントが任意のツールを呼び出します #1 認証を追加する
すべてのキーに公開されるすべてのツール 読み取り専用の監視エージェントが破壊的な操作をトリガーする #2 キーごとのスコープ ツール
入力検証なし エージェントは不正な形式の JSON を送信します。 ツールがクラッシュしたり、意図しない操作が実行されたりする #3 Zod で検証する
ツールの注釈がありません エージェントは読み取り専用ツールと破壊的なツールを区別できない #4 アノテーションを設定する
レート制限なし 1 つのループ エージェントが数分で API クォータを使い果たす #5 エージェントごとのレート制限
監査証跡なし どのエージェントが本番環境の問題を引き起こしたかを追跡できない #6 ログ呼び出し
ツールマニフェストの改ざん 攻撃者がツール定義を変更して通話をリダイレクトする #7 ピンとハッシュのマニフェスト
書き込みの確認なし エージェントは人間によるレビューなしでデータを作成、削除、または変更します #8 サンドボックスの破壊的な作戦

1. MCP エンドポイントに認証を追加します。

「ローカルホスト上にある」というのはセキュリティモデルではありません。 ブラウザ拡張機能、ローカル プロセス、および上で実行されているコード 同じマシンが到達できる localhost:3000。 リモート MCP は急速に成長しています。 クロード デスクトップ、カーソル、 および Windsurf はすべて、HTTPS 経由のリモート MCP サーバーへの接続をサポートしています。

すべてのリクエストで Bearer トークンを要求します。 Botoi の MCP サーバー: api.botoi.com/mcp APIを受け入れる キーを介して Authorization ヘッダ。 有効なキーがないと、1 分あたり 5 件のリクエストがバーストされます そして1日あたり100。 1 つあれば、制限は計画に合わせて拡張されます。

エージェントが静的キーの代わりに JWT を渡す場合は、それをデコードしてスコープと ID を抽出します。 その方法は次のとおりです MCP リクエスト ヘッダーから JWT を検査するには:

curl -s -X POST https://api.botoi.com/v1/jwt/decode \\
  -H "Content-Type: application/json" \\
  -d '{
    "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZ2VudC1jdXJzb3ItNDIiLCJzY29wZSI6InJlYWQ6ZG5zIHJlYWQ6c3NsIiwiaWF0IjoxNzE3MDAwMDAwfQ.signature"
  }'
{
  "success": true,
  "data": {
    "header": { "alg": "RS256", "typ": "JWT" },
    "payload": {
      "sub": "agent-cursor-42",
      "scope": "read:dns read:ssl",
      "iat": 1717000000
    },
    "signature": "signature"
  }
}

sub クレームによりエージェントが特定されます。 の scope クレームには、どのツールが使用できるかがリストされています アクセスします。 これらの値を信頼する前に、サーバー側の署名を確認してください。

2. API キーごとのツールのスコープ

すべてのエージェントがすべてのツールを必要とするわけではありません。 DNS レコードをチェックする監視エージェントは、決してアクセスしてはなりません。 ペースト作成エンドポイント。 各 API キーを特定のツール セットにマップする許可リストを作成します。

// Scope tools per API key using an allowlist
interface AgentPermissions {
  allowedTools: Set<string>;
  maxBurst: number;
  dailyLimit: number;
}

const AGENT_PERMISSIONS: Record<string, AgentPermissions> = {
  "key_readonly_monitor": {
    allowedTools: new Set(["lookup_dns", "lookup_ssl", "lookup_ip"]),
    maxBurst: 10,
    dailyLimit: 500,
  },
  "key_full_access": {
    allowedTools: new Set(["*"]),  // wildcard for all tools
    maxBurst: 30,
    dailyLimit: 5000,
  },
};

function canUseTool(apiKey: string, toolName: string): boolean {
  const perms = AGENT_PERMISSIONS[apiKey];
  if (!perms) return false;
  if (perms.allowedTools.has("*")) return true;
  return perms.allowedTools.has(toolName);
}

Botoi の MCP サーバーは、150 以上の API エンドポイントから 49 の厳選されたツールを公開します。 キュレーション自体が形式である スコープの設定: AI アシスタントにとって意味のあるツールのみが登録されます。 サーバーも同じことを行う必要があります。

3. Zod スキーマを使用してツール入力を検証する

MCP ツールは、エージェントからの任意の JSON を受け入れます。 エージェントが送信できるのは あなたはどこで 文字列を期待するか、ツールが認識しないフィールドを含めます。 実行前にすべての入力を検証します。

Botoi の MCP サーバーは、起動時に次を使用して OpenAPI パス定義を Zod スキーマに変換します。 schema-builder.ts。 の buildZodSchema 関数は OpenAPI 仕様を読み取り、マップします 各プロパティ タイプを Zod タイプに設定し、必須フィールドにマークを付けます。

import { z } from "zod";
import { buildZodSchema } from "./schema-builder";

// Build a Zod schema from your OpenAPI spec for each tool
const dnsSchema = z.object(buildZodSchema("/v1/dns/lookup", "post"));

function validateToolInput(toolName: string, input: unknown) {
  const schemas: Record<string, z.ZodTypeAny> = {
    lookup_dns: dnsSchema,
    // ... one schema per tool
  };

  const schema = schemas[toolName];
  if (!schema) {
    throw new Error(\`Unknown tool: \${toolName}\`);
  }

  const result = schema.safeParse(input);
  if (!result.success) {
    return {
      error: "Invalid input",
      issues: result.error.issues.map((i) => ({
        path: i.path.join("."),
        message: i.message,
      })),
    };
  }

  return { data: result.data };
}

サンプル入力から Zod スキーマを生成する

OpenAPI 仕様がない場合は、サンプル ツール入力から Zod スキーマを生成します。

curl -s -X POST https://api.botoi.com/v1/schema/json-to-zod \\
  -H "Content-Type: application/json" \\
  -d '{
    "json": {
      "domain": "github.com",
      "check_mx": true,
      "timeout_ms": 5000
    }
  }'
{
  "success": true,
  "data": {
    "schema": "z.object({ domain: z.string(), check_mx: z.boolean(), timeout_ms: z.number() })"
  }
}

実行時に JSON スキーマに対して入力を検証する

Zod を使用しない実行時検証には、JSON スキーマを使用します。 スキーマとエージェントの入力をに送信します。 /v1/schema/validate:

curl -s -X POST https://api.botoi.com/v1/schema/validate \\
  -H "Content-Type: application/json" \\
  -d '{
    "schema": {
      "type": "object",
      "required": ["domain"],
      "properties": {
        "domain": { "type": "string", "minLength": 1 },
        "record_type": { "type": "string", "enum": ["A", "AAAA", "MX", "TXT", "CNAME", "NS"] }
      },
      "additionalProperties": false
    },
    "data": { "domain": "stripe.com", "record_type": "MX" }
  }'

有効な入力は次を返します。

{
  "success": true,
  "data": {
    "valid": true,
    "errors": []
  }
}

エージェントが合格した場合 "record_type": "INVALID"、ツールを実行する前に対処可能なエラーが発生します。

{
  "success": true,
  "data": {
    "valid": false,
    "errors": [
      {
        "path": "/record_type",
        "message": "must be equal to one of the allowed values"
      }
    ]
  }
}

4. ツールの注釈を設定する

MCP プロトコルでは、次の 4 つの注釈ヒントが定義されています。 readOnlyHintdestructiveHintidempotentHint、 そして openWorldHint。 これらは、ツールがデータを読み取るかどうかをエージェントに伝えます。 状態を変更するか、再試行しても安全か、または外部サービスに連絡します。 ほとんどの MCP サーバーはそれらを無視します。

Botoi の MCP サーバーは、49 個のツールすべてに注釈を付けます。 実際には次のようになります。

// curated-tools.ts (from Botoi's MCP server)
export const CURATED_TOOLS: Record<string, CuratedTool> = {
  lookup_dns: {
    path: "/v1/dns/lookup",
    method: "post",
    title: "DNS Lookup",
    description: "Query DNS records for a domain.",
    annotations: {
      readOnlyHint: true,   // reads data, changes nothing
      openWorldHint: true,  // contacts external DNS servers
    },
  },
  storage_paste_create: {
    path: "/v1/paste/create",
    method: "post",
    title: "Create Paste",
    description: "Store text content and return a short URL.",
    annotations: {
      destructiveHint: true,  // creates new data
      idempotentHint: false,  // each call creates a new paste
    },
  },
};

注釈を尊重するエージェントは、確認なしに破壊的なツールを呼び出すことを避け、優先します。 失敗したリクエストを再試行するときの冪等ツール。 すべてのツールに設定します。

注釈 意味
readOnlyHint: true ツールはデータを読み取り、何も変更しません DNS ルックアップ、SSL チェック、IP 地理位置情報
destructiveHint: true ツールはデータを作成、更新、または削除します ペーストの作成、短縮 URL の生成、Webhook の送信
idempotentHint: true 同じ入力で複数回呼び出しても安全 ハッシュ生成、JSONフォーマット、単位変換
openWorldHint: true ツールが外部サービスに接続する DNS over HTTPS、WHOIS、URL メタデータ抽出

5. エージェント ID ごとのレート制限

1 つの不正エージェントが再試行ループに陥って API クォータ全体を使い果たさないようにする必要があります。 標準レート制限 IP アドレスによるだけでは十分ではありません。 複数のエージェントが同じ IP を共有でき、単一のエージェントが IP をローテーションできます。

API キーまたは JWT からエージェント ID を抽出し、スライディング ウィンドウを使用して ID ごとの制限を適用します。 または KV に保存されたトークン バケット:

// Hono middleware for per-agent rate limiting
import type { Context, Next } from "hono";

const BURST_LIMIT = 5;   // requests per minute
const DAILY_LIMIT = 100;  // requests per day

async function rateLimitAgent(c: Context, next: Next) {
  // Extract agent identity from API key or JWT
  const apiKey = c.req.header("Authorization")?.replace("Bearer ", "");
  const agentId = apiKey || c.req.header("X-Forwarded-For") || "anonymous";

  // Check burst limit (sliding window in KV)
  const burstKey = \`rate:\${agentId}:burst\

Botoi の認証ミドルウェアは、バースト制限 (1 分あたり 5 リクエスト) と 1 日あたりの上限の 2 つの層を強制します。 (1 日あたり 100 リクエスト) 匿名アクセス。 認証されたキーには、そのキーに基づいてより高い制限が適用されます。 Stripe サブスクリプション層。 同じ 2 層パターンを MCP サーバーに適用します。

6. エージェントの属性を使用してすべてのツール呼び出しをログに記録する

運用環境で何か問題が発生した場合、どのエージェントがどのエージェントに電話をかけたかという 3 つの質問に答える必要があります。 ツール、いつ、どのような入力を使用したか。 呼び出しログがなければ、盲目的にデバッグすることになります。

生の引数を保存する代わりに入力をハッシュします。 これにより、関連付けと重複排除を行うのに十分な情報が得られます。 機密データをログに記録せずに:

// Log every tool invocation with agent identity
interface ToolInvocationLog {
  timestamp: string;
  agent_id: string;
  tool_name: string;
  input_hash: string;   // SHA-256 of the input (not the raw input)
  duration_ms: number;
  status: "success" | "error";
}

async function logToolInvocation(
  agentId: string,
  toolName: string,
  input: unknown,
  startTime: number,
  status: "success" | "error"
) {
  const inputStr = JSON.stringify(input);
  const inputHash = await crypto.subtle.digest(
    "SHA-256",
    new TextEncoder().encode(inputStr)
  );
  const hashHex = Array.from(new Uint8Array(inputHash))
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");

  const log: ToolInvocationLog = {
    timestamp: new Date().toISOString(),
    agent_id: agentId,
    tool_name: toolName,
    input_hash: hashHex,
    duration_ms: Date.now() - startTime,
    status,
  };

  // Send to your logging pipeline (Datadog, Loki, Cloudflare Logpush)
  console.log(JSON.stringify(log));
}

これらの構造化ログを既存のパイプライン (Datadog、Grafana Loki、Cloudflare Logpush) に送信します。 異常なパターンに対するアラートを作成します。1 人のエージェントが通常の 10 倍の通話量を発生する場合や、読み取り専用のエージェントが発生する場合などです。 エージェントが破壊的なツールを呼び出そうとしています。

7. ツール定義を固定し、整合性をチェックする

Barracuda のレポートでは、攻撃者が悪意のあるツール定義をエージェント フレームワークに挿入していることが判明しました。 侵害された依存関係。 誰かが MCP ツール マニフェスト (名前、説明、パラメータ) を変更した場合 スキーマ)を使用すると、エージェントは意図したものとは異なる動作でツールを呼び出します。

ビルド時にツール マニフェストをハッシュします。 起動時にハッシュを再計算して比較します。

import crypto from "node:crypto";
import Botoi from "@botoi/sdk";

const botoi = new Botoi();

// Your tool manifest (the source of truth)
const tools = [
  { name: "dns_lookup", path: "/v1/dns/lookup" },
  { name: "ip_lookup", path: "/v1/ip/lookup" },
  { name: "email_validate", path: "/v1/email/validate" },
  // ... all your tools
];

const manifest = JSON.stringify(tools);

// Hash it at build time and store the expected hash
const EXPECTED_HASH = "a3f2b8c1d4e5f6..."; // from your CI build

// At startup, verify integrity
function verifyManifestIntegrity() {
  const currentHash = crypto
    .createHash("sha256")
    .update(manifest)
    .digest("hex");

  if (currentHash !== EXPECTED_HASH) {
    throw new Error(
      \`Tool manifest tampered. Expected \${EXPECTED_HASH}, got \${currentHash}\`
    );
  }

  console.log("Tool manifest integrity verified.");
}

// Or use the Botoi API for environments without Node crypto
async function verifyWithApi() {
  const result = await botoi.hash.sha256({ input: manifest });
  if (result.data.hash !== EXPECTED_HASH) {
    throw new Error("Tool manifest tampered.");
  }
}

Botoi API を使用してハッシュを生成する

CI 環境またはエッジ ランタイムの場合 node:crypto:

curl -s -X POST https://api.botoi.com/v1/hash \\
  -H "Content-Type: application/json" \\
  -d '{
    "text": "[{\\"name\\":\\"dns_lookup\\",\\"path\\":\\"/v1/dns/lookup\\"},{\\"name\\":\\"ip_lookup\\",\\"path\\":\\"/v1/ip/lookup\\"}]",
    "algorithm": "sha256"
  }'
{
  "success": true,
  "data": {
    "hash": "a3f2b8c1d4e5f67890abcdef1234567890abcdef1234567890abcdef12345678",
    "algorithm": "sha256"
  }
}

期待されるハッシュを環境変数として保存します。 デプロイ時に比較してください。 ハッシュが異なる場合、 展開をブロックします。

8. サンドボックスの破壊的操作

データの書き込み、副作用のトリガー、または外部サービスへの接続を行うツールには、確認フローが必要です。 人間によるレビューなしにエージェントに 1,000 個のペーストを作成させたり、Webhook を起動させたりしないでください。

// Confirmation flow for destructive MCP tools
interface ToolResult {
  content: Array<{ type: string; text: string }>;
  isError?: boolean;
}

function handleDestructiveTool(
  toolName: string,
  input: Record<string, unknown>,
  confirmed: boolean
): ToolResult {
  const destructiveTools = new Set([
    "paste_create",
    "short_url_create",
    "webhook_inbox_create",
  ]);

  if (!destructiveTools.has(toolName)) {
    // Non-destructive: execute immediately
    return executeTool(toolName, input);
  }

  if (!confirmed) {
    // Return a preview instead of executing
    return {
      content: [{
        type: "text",
        text: JSON.stringify({
          action: "confirmation_required",
          tool: toolName,
          input,
          message: \`This tool will create new data. Pass "confirmed": true to proceed.\`,
        }),
      }],
    };
  }

  // Confirmed: execute the destructive operation
  return executeTool(toolName, input);
}

パターンは単純です。破壊ツールは最初の呼び出し時にプレビューを返します。 エージェントが示します ユーザーにプレビューを表示します。 ユーザーが確認した後でのみ、エージェントは次のコールを送信します。 "confirmed": true

外部サービスに接続するツールの場合 (openWorldHint: true)、追加を検討してください タイムアウトとサーキットブレーカー。 外部 API が遅いか停止している場合、MCP サーバーは 応答を待って永遠にハングアップします。

Botoi の MCP サーバーがこれらのチェックを適用する方法

Botoi の MCP サーバー: api.botoi.com/mcp これは作業上の参照として機能します チェックリスト。 各項目がどのようにマッピングされるかは次のとおりです。

チェックリスト項目 ボトイのやり方
認証 API キー経由 Authorization ヘッダ; 厳格なレート制限のある匿名アクセス
ツールの範囲設定 150 以上のエンドポイントから厳選された 49 のツール。 エージェントに適したツールのみが登録されます
入力の検証 schema-builder.ts OpenAPI スキーマを Zod に変換します。 実行前に検証します
ツールの注釈 あらゆるツールが curated-tools.ts もっている readOnlyHintdestructiveHint、など。
レート制限 5 リクエスト/分バースト + 100 リクエスト/日匿名キャップ (KV 経由)。 API キー層ごとの制限の上限
ロギング エージェントの属性を含むすべてのリクエストの Cloudflare Workers 可観測性ログ
明示的な整合性 ツール定義はバージョン管理された TypeScript で行われます。 実行時の変更なしで CI 経由でデプロイされる
破壊的なサンドボックス化 ストレージ ツール (貼り付け、短縮 URL、Webhook) は、読み取り専用の検索ツールとは別のものです。 注釈はリスクを知らせます

Botoi の MCP サーバーには、Claude Desktop、Claude Code、Cursor、VS Code、または Windsurf から接続できます。 の MCP設定ページ 各クライアントの設定があります。

MCP サーバーのセキュリティ チェックリスト

完全なチェックリストをここにまとめました。 印刷するか、固定するか、チームの Runbook に貼り付けます。

  • すべての MCP エンドポイント (ベアラー トークン、API キー、または JWT) で認証を要求する
  • 各 API キーを許可されたツールの許可リストにマッピングする
  • 実行前に Zod スキーマまたは JSON スキーマを使用してすべてのツール入力を検証します
  • すべてのツールに注釈を付ける readOnlyHintdestructiveHintidempotentHint、 そして openWorldHint
  • IP ごとではなく、エージェント ID ごとのレート制限。 バーストキャップとデイリーキャップの両方を使用する
  • すべてのツール呼び出しをエージェント ID、ツール名、入力ハッシュ、期間、ステータスとともに記録します。
  • ビルド時にツールマニフェストをハッシュします。 起動時に検証し、改ざんされた展開をブロックする
  • 破壊的なツールからプレビューを返します。 実行前に明示的な確認が必要

2026 年に、43 のエージェント フレームワークに脆弱性が埋め込まれて出荷されました。MCP サーバーが次のターゲットです。 上記の 8 つのチェックはサーバーを無敵にするものではありませんが、攻撃者が侵入する隙を埋めることにはなります。 最初に悪用します: オープンエンドポイント、未検証の入力、および目に見えない呼び出し。

FAQ

MCP サーバーには認証が必要ですか?
はい。 ほとんどの MCP サーバーは認証なしで出荷されるため、エンドポイントに到達するクライアントはすべてのツールを呼び出すことができます。 リモート MCP の導入が進むにつれて、すべてのエンドポイントで API キーまたは JWT ベースの認証が必要になります。 Localhost-only はセキュリティを保証するものではありません。 ローカル プロセスとブラウザ拡張機能も localhost に到達できます。
MCP ツールのアノテーションとは何ですか?なぜ重要ですか?
ツールの注釈は、MCP プロトコルで定義されているメタデータ ヒントです (readOnlyHint、destructiveHint、idempotentHint、openWorldHint)。 これらは、ツールがデータを読み取るか、状態を変更するか、再試行しても安全か、または外部サービスに接続するかどうかを AI エージェントに伝えます。 エージェントはこれらのヒントを使用して、どのツールをどの順序で呼び出すかについてより安全な決定を下します。
MCP サーバーを呼び出す AI エージェントをレート制限するにはどうすればよいですか?
各エージェント (または API キー) に ID を割り当て、ID ごとのレート制限を適用します。 トークン バケットまたはスライディング ウィンドウ アルゴリズムを使用します。 KV ストアまたは Redis でリクエストを追跡します。 Botoi 独自の MCP サーバーは、匿名アクセスに対して 1 分あたり 5 件のバースト要求と 1 日あたり 100 件の要求を強制し、認証されたキーの制限を高めます。
Zod を使用して MCP ツールの入力を検証できますか?
はい。 MCP ツールはエージェントから任意の JSON を受け取ります。 各ツールの予期される入力形状の Zod スキーマを定義し、実行前に検証します。 Botoi /v1/schema/json-to-zod エンドポイントを使用してサンプル JSON から Zod スキーマを生成したり、Botoi の schema-builder.ts のように OpenAPI パス定義を Zod オブジェクトに変換したりできます。
MCP ツール マニフェストの改ざんを検出するにはどうすればよいですか?
ツール マニフェストを SHA-256 でハッシュし、そのハッシュを保存します。 各サーバーの起動または展開前に、ハッシュを再計算して比較します。 ハッシュが異なる場合は、誰か (または何か) がツール定義を変更したことになります。 SHA-256 ハッシュは、Botoi /v1/hash エンドポイント経由、またはネイティブ暗号ライブラリを使用して計算できます。

botoiで開発を始めよう

150以上のAPIエンドポイント。検索、テキスト処理、画像生成、開発者ユーティリティに対応。無料プラン、クレジットカード不要。