MCPサーバーを自作する入門ガイド
MCPサーバーを自作する理由
Section titled “MCPサーバーを自作する理由”既存のMCPサーバー(GitHub、Supabase、Slack等)では対応していない社内ツール・独自APIをClaude Codeから使いたい場合、MCPサーバーを自作します。
- 社内API(在庫管理・顧客管理など)をClaude Codeから呼び出す
- 社内ドキュメント検索をClaude Codeに組み込む
- 独自のデプロイツールをClaude Codeと連携させる
- よく使う社内コマンドをMCPツールとして公開する
※デプロイ: 開発したコードを本番サーバーに公開・適用すること。※API: Application Programming Interface の略。外部サービスとプログラム間でデータをやりとりするための接続口。※MCP: Model Context Protocol の略。Claude Codeと外部ツール(GitHub・Supabaseなど)を接続するための標準プロトコル。
MCPの基本概念
Section titled “MCPの基本概念”MCPサーバーは以下を提供できます:
| 種類 | 説明 | 用途 |
|---|---|---|
tools | 実行可能な機能 | APIコール・コマンド実行 |
resources | 読み取れるデータ | ドキュメント・設定値 |
prompts | プロンプトテンプレート | よく使う指示のセット |
※プロンプト: AIへの指示文。Claude Codeへの指示の質がそのまま出力の質に影響する。
セットアップ手順
Section titled “セットアップ手順”1. プロジェクトを作成する
Section titled “1. プロジェクトを作成する”mkdir my-mcp-servercd my-mcp-servernpm init -ynpm install @modelcontextprotocol/sdknpm install -D typescript @types/node tsx2. TypeScript設定
Section titled “2. TypeScript設定”{ "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "outDir": "./dist", "strict": true, "esModuleInterop": true }, "include": ["src/**/*.ts"]}3. 最小限のMCPサーバーを実装する
Section titled “3. 最小限のMCPサーバーを実装する”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-company-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, });
// ツールの一覧を返すserver.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'get_inventory', description: '在庫情報を取得します', inputSchema: { type: 'object', properties: { product_id: { type: 'string', description: '商品ID', }, }, required: ['product_id'], }, }, { name: 'search_customers', description: '顧客を検索します', inputSchema: { type: 'object', properties: { query: { type: 'string', description: '検索キーワード(名前・メールアドレス)', }, limit: { type: 'number', description: '取得件数(デフォルト: 10)', default: 10, }, }, required: ['query'], }, }, ], };});
// ツールの実行server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params;
switch (name) { case 'get_inventory': { const { product_id } = args as { product_id: string };
// 実際の社内APIを呼び出す const response = await fetch( `${process.env.INTERNAL_API_URL}/inventory/${product_id}`, { headers: { 'Authorization': `Bearer ${process.env.INTERNAL_API_KEY}`, }, } );
if (!response.ok) { return { content: [ { type: 'text', text: `エラー: 商品ID ${product_id} の在庫情報を取得できませんでした`, }, ], }; }
const data = await response.json(); return { content: [ { type: 'text', text: JSON.stringify(data, null, 2), }, ], }; }
case 'search_customers': { const { query, limit = 10 } = args as { query: string; limit?: number };
const response = await fetch( `${process.env.INTERNAL_API_URL}/customers?q=${encodeURIComponent(query)}&limit=${limit}`, { headers: { 'Authorization': `Bearer ${process.env.INTERNAL_API_KEY}`, }, } );
const customers = await response.json(); return { content: [ { type: 'text', text: JSON.stringify(customers, null, 2), }, ], }; }
default: throw new Error(`未知のツール: ${name}`); }});
// サーバーを起動async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('MCPサーバーが起動しました');}
main().catch(console.error);4. package.json にビルドコマンドを追加
Section titled “4. package.json にビルドコマンドを追加”{ "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "tsx src/index.ts" }}5. ビルドして動作確認
Section titled “5. ビルドして動作確認”npm run build6. Claude Code の settings.json に登録
Section titled “6. Claude Code の settings.json に登録”{ "mcpServers": { "my-company": { "command": "node", "args": ["/absolute/path/to/my-mcp-server/dist/index.js"], "env": { "INTERNAL_API_URL": "https://api.internal.company.com", "INTERNAL_API_KEY": "${INTERNAL_API_KEY}" } } }}※TypeScript: JavaScriptに型定義を追加した言語。型チェックによりバグを事前に発見しやすくなる。※npm: Node.jsのパッケージ管理ツール。`npm install` でライブラリをインストールする。
# Claude Codeを起動claude
# MCPサーバーの接続確認/mcp
# ツールが使えるか確認「社内APIを使って、商品ID 'PROD-001' の在庫を確認してください」Resourcesを実装する(ドキュメント読み取り)
Section titled “Resourcesを実装する(ドキュメント読み取り)”ツールだけでなく、Claudeが「読み込める情報」を提供するResourcesも実装できます。
import { ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
// Resourceの登録server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: 'company://runbook/deployment', name: 'デプロイ手順書', description: '本番デプロイの手順', mimeType: 'text/markdown', }, ], };});
// Resourceの内容を返すserver.setRequestHandler(ReadResourceRequestSchema, async (request) => { if (request.params.uri === 'company://runbook/deployment') { // 社内Confluenceやドキュメントから取得することも可能 const content = await fetchRunbook('deployment'); return { contents: [ { uri: request.params.uri, mimeType: 'text/markdown', text: content, }, ], }; } throw new Error('リソースが見つかりません');});注意点・制限事項
Section titled “注意点・制限事項”セキュリティ
Section titled “セキュリティ”MCPサーバーはClaude Codeのプロセスと同じ権限で動作します。社内APIのキーは環境変数で管理し、絶対にソースコードに埋め込まないでください。
エラーハンドリング
Section titled “エラーハンドリング”ツールがエラーを返す場合は、エラーメッセージをClaude Codeに返すようにします。クラッシュするとセッションが中断されます。
try { const result = await callAPI(); return { content: [{ type: 'text', text: JSON.stringify(result) }] };} catch (error) { return { content: [{ type: 'text', text: `エラー: ${error.message}` }], isError: true, };}※環境変数: APIキーやデータベース接続情報など、コードに直接書きたくない値をOSやサーバーに設定する仕組み。