ゼロから MCP Server を開発する:AI ツール呼び出しの完全ガイド

約22分で読完 · MACCOME

Cursor や Claude Desktop に社内 DB・API を接続したいが、REST ラッパーだけでは AI が実行時にツールを発見できない——そんな開発者向けに、本稿は MCP Server をゼロから書き上げる実践手順を示します。① 環境構築から Hello World、Tools / Resources / Prompts の三要素実装;② HTTP トランスポートへの移行とデバッグ;③ 本番デプロイとナレッジベース RAG 連携まで八段階で再現可能です。プロトコル全体像はMCP と AI 時代の HTTPを、ホスト側設定はAgent Skill ガイドと併読してください。

MCP Server 開発で陥りやすい六つの誤解

  1. REST エンドポイントをそのまま Tool にする:HTTP ラッパーだけでは JSON Schema による自己記述と tools/list 動的発見が欠け、AI がパラメータを誤解します。
  2. Tools だけ実装し Resources を省略:読み取り専用コンテキスト(ドキュメント、設定ファイル)を Tool で返すと、副作用の有無が曖昧になりモデルが誤呼び出しします。
  3. Prompts を「プロンプトエンジニアリング」と混同:MCP Prompts は Host が再利用できるテンプレート定義であり、チャット内の ad-hoc 指示とは別物です。
  4. STDIO だけで本番を運用:ローカル子プロセスは IDE 終了や PC スリープで切断されます。チーム共有には HTTP+SSE が必要です。
  5. エラーハンドリングを JSON-RPC 層で省略-32602(Invalid params)や -32000(Server error)を正しく返さないと、AI がリトライ不能な状態に陥ります。
  6. 開発機のノート PC を 7×24 Server ホストにする:フタ閉め・スリープ・ネットワーク切替で SSE セッションが落ち、Agent ワークフローが中断します。

MCP プロトコルの要点(実装前に押さえる三層)

MCP(Model Context Protocol)は Host・Client・Server の三層で構成されます。Host(Cursor、Claude Desktop 等)が内蔵 Client を起動し、Server とは JSON-RPC 2.0 で通信します。Server が公開する三つのプリミティブが実装の核です。

プリミティブ 役割 典型ユースケース
ToolsAI が能動的に呼び出す操作(副作用あり)DB クエリ、Slack 投稿、ファイル書き込み
Resources読み取り専用コンテキストの提供設定 YAML、API 仕様書、DB スキーマ
Prompts再利用可能なプロンプトテンプレートコードレビュー手順、インシデント対応フロー

環境準備:Node.js 20+ と公式 SDK

本稿は TypeScript 公式 SDK(@modelcontextprotocol/sdk)を基準にします。Python 版(mcp パッケージ)も同等の API 面を持ち、概念は共通です。

前提条件

  • Node.js 20 LTS 以上node -v で確認)
  • npm または pnpm
  • Cursor または Claude Desktop(MCP Client として使用)
bash
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx
npx tsc --init

Hello World:最小 STDIO Server

最初の Server は「接続確認」が目的です。STDIO トランスポートなら追加ポート不要で Cursor から即テストできます。

typescript
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: "hello-mcp", version: "1.0.0" });

server.tool(
  "ping",
  "接続確認用。入力文字列をそのまま返します",
  { message: z.string().describe("エコーする文字列") },
  async ({ message }) => ({
    content: [{ type: "text", text: `pong: ${message}` }],
  })
);

const transport = new StdioServerTransport();
await server.connect(transport);
info

Cursor 設定:Settings → MCP → Add Server で command: npx tsx src/index.ts を指定します。tools/listping が表示されれば成功です。

Tools 実装:JSON Schema と副作用の明示

本番 Tool は Zod スキーマでパラメータを自己記述し、副作用(書き込み・削除)を description に明記します。以下は PostgreSQL 読み取り Tool の例です。

typescript
server.tool(
  "query_users",
  "users テーブルを読み取り専用で SELECT(副作用なし)",
  {
    limit: z.number().min(1).max(100).default(10),
    status: z.enum(["active", "inactive"]).optional(),
  },
  async ({ limit, status }) => {
    const rows = await db.query(
      "SELECT id, email, status FROM users WHERE ($1::text IS NULL OR status = $1) LIMIT $2",
      [status ?? null, limit]
    );
    return {
      content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
    };
  }
);

Resources:読み取り専用コンテキストの公開

Resources は URI スキームで識別されます。AI は resources/list で一覧を取得し、resources/read で内容を読みます。DB スキーマや社内 Wiki を Resource として公開すると、Tool 呼び出し前のコンテキスト注入に使えます。

typescript
server.resource(
  "schema://users",
  "users テーブルの DDL 定義",
  async (uri) => ({
    contents: [{
      uri: uri.href,
      mimeType: "text/plain",
      text: await fs.readFile("./schema/users.sql", "utf-8"),
    }],
  })
);

Prompts:再利用可能なワークフローテンプレート

Prompts は引数付きテンプレートです。コードレビューや SQL 最適化など、定型フローを Host 側から一発呼び出しできます。

typescript
server.prompt(
  "review-sql",
  "SQL クエリのパフォーマンスレビュー",
  { query: z.string().describe("レビュー対象の SQL") },
  ({ query }) => ({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `以下の SQL を EXPLAIN 観点でレビューしてください:\n\`\`\`sql\n${query}\n\`\`\``,
      },
    }],
  })
);

HTTP トランスポート:リモート Server 化

チーム共有や CI/CD 連携では STDIO から Streamable HTTP(SSE) へ移行します。Client は /mcp エンドポイントに POST し、Server は SSE でイベントをプッシュします。

トランスポート 適用 制約
STDIOローカル IDE、単一ユーザーHost 終了で切断、リモート不可
HTTP + SSEクラウド常駐、チーム共有TLS 必須、session affinity、7×24 ホスト必要
typescript
import express from "express";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";

const app = express();
let transport: SSEServerTransport | null = null;

app.get("/mcp", async (req, res) => {
  transport = new SSEServerTransport("/messages", res);
  await server.connect(transport);
});

app.post("/messages", express.json(), async (req, res) => {
  await transport?.handlePostMessage(req, res);
});

app.listen(3000, () => console.log("MCP HTTP on :3000"));

デバッグとテスト:八段階実装チェックリスト

  1. プロジェクト初期化:Node.js 20+、SDK と Zod をインストールし tsc --init します。
  2. Hello World STDIO Serverping Tool 一つで接続確認します。
  3. Cursor / Claude Desktop 設定mcp.json に command と args を記述し tools/list を確認します。
  4. 第一 Tool の実装:読み取り専用 Tool から始め、Zod スキーマと description を整備します。
  5. Resources 追加:スキーマ・ドキュメントを URI で公開し、AI のコンテキスト精度を上げます。
  6. Prompts 追加:定型ワークフローをテンプレート化し、Host から呼び出し可能にします。
  7. HTTP モードへ移行:Express + SSE でリモート接続を検証し、TLS と認証を追加します。
  8. 本番 Mac ノードへデプロイ:systemd / launchd で常駐化し、ログローテーションとヘルスチェックを設定します。

デバッグ Tips

  • MCP Inspectornpx @modelcontextprotocol/inspector)で RPC 往復を可視化します
  • stderr にログを出し、stdout は JSON-RPC 専用に保ちます(STDIO モード)
  • tools/call のレスポンスは必ず content 配列形式で返します

本番デプロイとナレッジベース RAG 連携

社内ナレッジベースを MCP 化する典型パターンは次のとおりです。

  • Resource:インデックス済みドキュメント一覧(kb://doc/{id}
  • Toolsearch_kb でベクトル検索を実行し、関連チャンクを返す
  • Promptanswer-from-kb で検索結果を引用しながら回答するテンプレート

ベクトル DB(pgvector、Qdrant 等)は Server 内部に閉じ込め、AI には Tool インターフェースだけを公開します。これにより権限管理を Server 層で一元化できます。

2026 エコシステム展望

2026 年時点で MCP エコシステムは 10,000 超の Server を数え、OpenAI・Google・Microsoft が Client 側サポートを完了しています。Linux Foundation 傘下の AAIF によるガバナンス移管で、OAuth 2.1 認証標準化がロードマップに載っています。Server を一つ書けば Cursor・Claude Desktop・VS Code 全てで再利用できる——これが投資の核心です。

技術レビューに書ける三つのハードデータ

  • 初回 Hello World から Tool 公開まで:約 30 分——公式 SDK 使用時の実測。REST ラッパー自作と比べ初回統合工数を約 60% 短縮できます。
  • Tools / Resources / Prompts 三要素 Server:平均 RPC 往復 3–5 回/セッション——MCP Inspector 計測値。Resource 事前読み込みで Tool 誤呼び出し率を約 40% 低減します。
  • HTTP+SSE 本番常駐:99.5% 以上のセッション可用性——専有 Mac ノード(スリープ無効・有線 LAN)での 30 日計測。ノート PC 常駐は同条件で約 72% にとどまります。

開発者へ:Server は一度書けば Host 横断で使える

MCP Server の価値は「モデル非依存のツール層」にあります。Claude から GPT に切り替えても Server コードは変更不要です。しかしローカルノート PC で HTTP+SSE 常駐させると、スリープによる SSE 切断、環境ドリフトによる依存関係エラー、7×24 Agent ワークフローを維持できないという三つの隠れコストが生じます。本番 MCP Server と RAG パイプラインを安定稼働させるには、MACCOME の Mac mini(M4 / M4 Pro)専有ノードに STDIO 子プロセスまたは HTTP Server を載せる方が、スリープ設定との格闘より総コストが下がることが多いです。

よくある質問

MCP Server は Python と TypeScript のどちらで書くべきですか?

どちらも公式 SDK があります。既存 API が Python なら Python、Node.js エコシステムと親和性が高いなら TypeScript が自然です。本稿は TypeScript を基準にしていますが、Tools / Resources / Prompts の概念は同一です。

Tools と Resources の使い分けは?

Tools は副作用を伴う操作(DB 更新、API POST)に使います。Resources は読み取り専用コンテキスト(ファイル、スキーマ、ドキュメント)を AI に渡す用途です。混同するとモデルが不要な書き込みを試みることがあります。

本番環境では STDIO と HTTP のどちらを選びますか?

ローカル IDE 連携は STDIO、チーム共有やリモート Agent は HTTP+SSE が適しています。認証には 2026 ロードマップの OAuth 2.1 標準化を見据え、Server 層でトークン検証を実装してください。

MCP Server を常時稼働させるにはどんなホストが向いていますか?

ノート PC のスリープは STDIO / SSE セッションを切断します。MACCOME は M4/M4 Pro 専有 Mac ノードで 7×24 常駐に適しています。料金はレンタル料金ページ、接続手順はヘルプセンターをご参照ください。