Cursor や Claude Desktop に社内 DB・API を接続したいが、REST ラッパーだけでは AI が実行時にツールを発見できない——そんな開発者向けに、本稿は MCP Server をゼロから書き上げる実践手順を示します。① 環境構築から Hello World、Tools / Resources / Prompts の三要素実装;② HTTP トランスポートへの移行とデバッグ;③ 本番デプロイとナレッジベース RAG 連携まで八段階で再現可能です。プロトコル全体像はMCP と AI 時代の HTTPを、ホスト側設定はAgent Skill ガイドと併読してください。
tools/list 動的発見が欠け、AI がパラメータを誤解します。-32602(Invalid params)や -32000(Server error)を正しく返さないと、AI がリトライ不能な状態に陥ります。MCP(Model Context Protocol)は Host・Client・Server の三層で構成されます。Host(Cursor、Claude Desktop 等)が内蔵 Client を起動し、Server とは JSON-RPC 2.0 で通信します。Server が公開する三つのプリミティブが実装の核です。
| プリミティブ | 役割 | 典型ユースケース |
|---|---|---|
| Tools | AI が能動的に呼び出す操作(副作用あり) | DB クエリ、Slack 投稿、ファイル書き込み |
| Resources | 読み取り専用コンテキストの提供 | 設定 YAML、API 仕様書、DB スキーマ |
| Prompts | 再利用可能なプロンプトテンプレート | コードレビュー手順、インシデント対応フロー |
本稿は TypeScript 公式 SDK(@modelcontextprotocol/sdk)を基準にします。Python 版(mcp パッケージ)も同等の API 面を持ち、概念は共通です。
node -v で確認)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
最初の Server は「接続確認」が目的です。STDIO トランスポートなら追加ポート不要で Cursor から即テストできます。
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);
Cursor 設定:Settings → MCP → Add Server で command: npx tsx src/index.ts を指定します。tools/list に ping が表示されれば成功です。
本番 Tool は Zod スキーマでパラメータを自己記述し、副作用(書き込み・削除)を description に明記します。以下は PostgreSQL 読み取り Tool の例です。
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 は URI スキームで識別されます。AI は resources/list で一覧を取得し、resources/read で内容を読みます。DB スキーマや社内 Wiki を Resource として公開すると、Tool 呼び出し前のコンテキスト注入に使えます。
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 は引数付きテンプレートです。コードレビューや SQL 最適化など、定型フローを Host 側から一発呼び出しできます。
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\`\`\``,
},
}],
})
);
チーム共有や CI/CD 連携では STDIO から Streamable HTTP(SSE) へ移行します。Client は /mcp エンドポイントに POST し、Server は SSE でイベントをプッシュします。
| トランスポート | 適用 | 制約 |
|---|---|---|
| STDIO | ローカル IDE、単一ユーザー | Host 終了で切断、リモート不可 |
| HTTP + SSE | クラウド常駐、チーム共有 | TLS 必須、session affinity、7×24 ホスト必要 |
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"));
tsc --init します。ping Tool 一つで接続確認します。mcp.json に command と args を記述し tools/list を確認します。npx @modelcontextprotocol/inspector)で RPC 往復を可視化しますtools/call のレスポンスは必ず content 配列形式で返します社内ナレッジベースを MCP 化する典型パターンは次のとおりです。
kb://doc/{id})search_kb でベクトル検索を実行し、関連チャンクを返すanswer-from-kb で検索結果を引用しながら回答するテンプレートベクトル DB(pgvector、Qdrant 等)は Server 内部に閉じ込め、AI には Tool インターフェースだけを公開します。これにより権限管理を Server 層で一元化できます。
2026 年時点で MCP エコシステムは 10,000 超の Server を数え、OpenAI・Google・Microsoft が Client 側サポートを完了しています。Linux Foundation 傘下の AAIF によるガバナンス移管で、OAuth 2.1 認証標準化がロードマップに載っています。Server を一つ書けば Cursor・Claude Desktop・VS Code 全てで再利用できる——これが投資の核心です。
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 層でトークン検証を実装してください。