Skip to content

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サーバーは以下を提供できます:

種類説明用途
tools実行可能な機能APIコール・コマンド実行
resources読み取れるデータドキュメント・設定値
promptsプロンプトテンプレートよく使う指示のセット
プロンプト: AIへの指示文。Claude Codeへの指示の質がそのまま出力の質に影響する。
Terminal window
mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node tsx
tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*.ts"]
}

3. 最小限のMCPサーバーを実装する

Section titled “3. 最小限のMCPサーバーを実装する”
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-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"
}
}
Terminal window
npm run build

6. Claude Code の settings.json に登録

Section titled “6. Claude Code の settings.json に登録”
~/.claude/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` でライブラリをインストールする。
Terminal window
# 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('リソースが見つかりません');
});

MCPサーバーはClaude Codeのプロセスと同じ権限で動作します。社内APIのキーは環境変数で管理し、絶対にソースコードに埋め込まないでください。

ツールがエラーを返す場合は、エラーメッセージを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やサーバーに設定する仕組み。