はじめてのMCPサーバーの作り方:実践ガイド
MCP(Model Context Protocol)は、コンテキストウィンドウを圧迫せずに、データベース、ファイル、APIなどの外部ツールやデータへの構造化されたアクセスをClaudeに与える方法です。サーバーは見た目より簡単です:SDKをインストールし、ツールをJSONスキーマとして定義し、ハンドラーを実装し、stdioで接続します。30分以内にClaudeがカスタムツールを呼び出せるようになります。
毎週水曜。28,400人以上の読者。無駄なし。
✓ メールをご確認ください — 確認リンクをクリックして登録を完了してください。
✓ 登録が完了しました!
✓ すでに登録済みです。
目次
2026年6月更新。
TL;DR: MCP(Model Context Protocol)は、コンテキストウィンドウを圧迫せずに、データベース、ファイル、APIなどの外部ツールやデータへの構造化されたアクセスをClaudeに与える方法です。サーバーは見た目より簡単です:SDKをインストールし、ツールをJSONスキーマとして定義し、ハンドラーを実装し、stdioで接続します。30分以内にClaudeがカスタムツールを呼び出せるようになります。
[オペレーターの視点] 私は定期的にエージェントに新しいツールを組み込んでいます。MCPは今やそれをクリーンに行うための標準的な方法です。サーバーを一度構築すれば、MCP対応のすべてのクライアント——Claude Desktop、Claude Code、Anthropic SDKを使用するすべてのアプリ——が呼び出し側のコードを変更せずに使用できます。これが価値です:一度作って、どこでも再利用する。
MCPとは何か
Model Context Protocolは、AIモデルが外部コンテキストとツールに接続する方法を標準化するオープンプロトコルです。AIインテグレーションのUSB-C規格と考えてください:以前は、Claudeにデータベースを読ませたりAPIを呼び出させたりしたいアプリは、それぞれ独自の仕組みを考える必要がありました。MCPを使えば、MCPサーバーを一つ作るだけで、準拠するすべてのホストが使用できます。
MCPはサーバーが提供できる3つのものを定義します:
- ツール——Claudeが呼び出せる関数(ファイルを読む、DBをクエリする、Slackメッセージを送る)
- リソース——Claudeが読めるデータ(ドキュメント、データベースの行、ファイルツリー)
- プロンプト——ホストが挿入できる再利用可能なプロンプトテンプレート
ほとんどのオペレーターユースケースでは、ツールサーバーを構築します。リソースとプロンプトは基本が動いてから後で対応します。
アーキテクチャはクライアント-サーバーで、クライアント(Claude Desktop、Claude Code、カスタムアプリ)がすべてを制御します。サーバーは受動的——ツール呼び出しリクエストを待ち受けて結果を返すだけです。
すべてのMCPサーバーの3つの部品
構築するMCPサーバーはすべて同じ構造を持ちます:
- サーバーオブジェクト——サーバーの名前、バージョン、提供する機能(ツール、リソース、プロンプト)を宣言
- ツール定義——名前、説明、入力のJSONスキーマを持つツールのリスト
- リクエストハンドラー——Claudeがツールを呼び出したときに実行される関数
それだけです。開始時にデータベース、HTTPスタック、auth層は不要です。最小サーバーは30行未満のTypeScriptです。
前提条件(2分)
- Node.js 18+——
node --versionで確認 - TypeScript 5+(下記でdev dependencyとして含む)
- テスト用のMCPクライアント——Claude Desktopは無料で、サーバーの動作を確認する最も簡単な方法
MCPサーバーを実行するためにAnthropic APIキーは不要です。APIキーはクライアント(Claude Desktop)にあり、サーバーにはありません。
ステップ1:プロジェクトのセットアップ(3分)
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript tsx @types/nodepackage.jsonに追加:
{
"type": "module",
"scripts": {
"build": "tsc",
"dev": "tsx src/index.ts"
}
}tsconfig.jsonを作成:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"strict": true
},
"include": ["src/**/*"]
}ステップ2:最小サーバーを書く(5分)
src/index.tsを作成:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "my-mcp-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// このサーバーが提供するツールを宣言
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "get_word_count",
description: "テキストブロックの単語数を数えます。",
inputSchema: {
type: "object",
properties: {
text: {
type: "string",
description: "単語を数えるテキスト",
},
},
required: ["text"],
},
},
],
}));
// クライアントからのツール呼び出しを処理
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "get_word_count") {
const { text } = args as { text: string };
const count = text.trim().split(/\s+/).filter(Boolean).length;
return {
content: [{ type: "text", text: `単語数:${count}` }],
};
}
throw new Error(`不明なツール:${name}`);
});
// stdioで接続——Claude DesktopがサーバーとやりとりするEways
const transport = new StdioServerTransport();
await server.connect(transport);これが完全なサーバーです。1つのツール(get_word_count)を登録して実装します。構造が重要です。
ステップ3:ビルドしてClaude Desktopに登録(5分)
TypeScriptをビルド:
npm run buildClaude Desktopの設定ファイルに登録します。
macOS:~/Library/Application Support/Claude/claude_desktop_config.json
Windows:%APPDATA%\Claude\claude_desktop_config.json
ファイルが存在しない場合は作成:
{
"mcpServers": {
"my-mcp-server": {
"command": "node",
"args": ["/absolute/path/to/my-mcp-server/build/index.js"]
}
}
}絶対パスを使用します。保存後にClaude Desktopを再起動します。メッセージ入力にハンマーアイコン(🔨)が表示されれば、Claudeがあなたのツールをdiscoverしたサインです。
ステップ4:便利なツールを作る
単語カウントは説明用です。より便利なツールを紹介します:プロジェクトディレクトリからファイルを読む——コードベース、changelog、設定ファイルを要約するコンテキスト注入エージェントに使用しているものです。
import { readFileSync, readdirSync } from "fs";
import { join, extname } from "path";
const ALLOWED_EXTENSIONS = [".md", ".txt", ".ts", ".json", ".yaml"];
const PROJECT_DIR = process.env.PROJECT_DIR ?? process.cwd();ロジックは同じです:正確なJSONスキーマでツールを定義し、ハンドラーを実装し、パストラバーサルを防ぐために入力を検証し、クライアントにテキストを返します。
私がはまった落とし穴(あなたがはまらないように)
パスは絶対パスでなければなりません。 Claude Desktop設定の相対パスは期待通りに解決されません。常に完全パス /home/user/... を使用してください。
stdioはサーバーでconsole.logを使えないことを意味します。 Claude Desktopはstdin/stdoutを通じてサーバーと通信します。デバッグのconsole.logはJSON-RPCストリームを破壊します。代わりにstderrにログ:
process.stderr.write(`デバッグ:${message}\n`);設定変更のたびにClaude Desktopを再起動してください。 MCPサーバーは起動時に読み込まれます。編集した設定ファイルはアプリを閉じて再度開くまで何もしません。
ツールの説明がプロダクトです。 Claudeはdescriptionフィールドに基づいてツールを呼び出すかどうか決めます。曖昧な説明はClaudeがいつ使うべきか分からないことを意味します。正確な説明はClaudeが適切なタイミングでそれを使うことを意味します。実装よりも説明に時間をかけてください。
本番環境でのMCPサーバーの使い方
stdioパターンはClaude DesktopとClaude Code(ローカル)に最適です。本番エージェント——Cloudflare Workersで実行している30以上——では、ステップごとにHaikuとSonnetにルーティングする柔軟性が必要なため、Anthropic SDKのtool-use APIを直接使用します。
実際に使っているパターン:
- ローカル開発ツール——プロジェクト固有のツールを公開するClaude Code用MCPサーバー
- コンテキスト注入——手動コピーなしに関連ドキュメントをプリロードするMCPサーバー
- プロトタイプからAPIへのブリッジ——MCP を先に構築し(イテレーションが速い)、その後ロジックをSDK tool-useに移植
次に作るもの
サーバー構造が理解できたら、有用なツールはClaudeの外部コンテキストにアクセスするものです:
- データベースリーダー——読み取り専用SQLクエリを実行してJSONとして結果を返す
- Slackリーダー——チャンネルから最新N件のメッセージを取得
- GitHubリーダー——オープンPRをリスト、特定のコミットのファイルを読む
- 内部APIラッパー——認証ヘッダーを組み込んで自分のREST APIを呼び出す
よくある質問
MCPサーバーを構築するにはAnthropic APIキーが必要ですか?
いいえ。MCPサーバーはAnthropic APIを呼び出しません。クライアントからのツール呼び出しリクエストに応答するだけです。APIキーはクライアントにあり、サーバーにはありません。
MCPサーバーは外部APIを呼び出せますか?
はい——ハンドラーは単なる非同期TypeScriptコードです。天気APIをフェッチし、データベースをクエリし、ファイルに書き込む。サーバーはハンドラーが内部で何をするかを気にしません。
stdioとHTTPトランスポートの違いは何ですか?
stdioはローカルサーバー用——Claude DesktopやClaude Codeと同じマシン。HTTP with SSEはWebサービスとしてデプロイできるリモートサーバー用。stdioから始めてください;デバッグが簡単です。
Claudeはいつ自分のツールを呼び出すべきか知っていますか?
Claudeはツールのdescriptionフィールドと会話のコンテキストに基づいて決定します。Claudeがツールを無視し続ける場合は、説明を絞り込んでください。
毎週水曜。28,400人以上の読者。無駄なし。
✓ メールをご確認ください — 確認リンクをクリックして登録を完了してください。
✓ 登録が完了しました!
✓ すでに登録済みです。
AIプレイブックをメールでお届け
毎週水曜。28,400人以上の読者。無駄なし。
メールをご確認ください。
確認メールをお送りしました — リンクをクリックして登録を完了してください。1分以内に届かない場合は迷惑メールをご確認ください。
登録が完了しました。
ようこそ — 次号がまもなくお手元に届きます。
すでに登録済みです — 毎週水曜日にお届けします。