Claude Code MCP 自作完全ガイド|TypeScript でカスタムサーバーを作る手順【2026】
Claude Code 向けカスタム MCP サーバーの作り方を完全解説。TypeScript + @modelcontextprotocol/sdk で社内 API・独自ツールを Claude に接続する実装手順をステップバイステップで紹介。
MCP(Model Context Protocol)の既存サーバーには載っていない 社内 API・独自データベース・レガシーシステム を Claude Code と繋ぎたい場合、自作 MCP サーバーが解決策になります。本記事では MCP 公式 TypeScript SDK を使い、TypeScript で動く自作 MCP サーバーを 30 分で作り切る手順 をステップバイステップで解説します。
本手順は 2026 年 6 月時点の
@modelcontextprotocol/sdkv1 系をベースにしています。SDK のバージョンアップで API が変わる場合があるため、実装前に 公式リポジトリの最新情報 を必ず確認してください。
1. MCP を自作するべき 3 つのケース
Claude Code MCP 完全ガイド で解説した Slack / Notion / GitHub 等の公式 MCP サーバーで対応できない場合、自作が必要になります。具体的には以下の 3 ケースです。
1-1. 社内専用システムへの接続
レガシー ERP・自社開発の基幹システム・社内 API など、外部には MCP サーバーが存在しないシステムとの連携。
1-2. 認証ロジックのカスタマイズ
OAuth 2.0 フローや社内 SSO、HMAC 署名など、既存サーバーでは扱えない独自認証が必要なケース。
1-3. 複数 API の集約
在庫 API・受注 API・会計 API の3つを1つの MCP Tool にまとめ、Claude が1回の呼び出しで横断データを取得できるようにする場合。
2. MCP サーバーの 3 要素
自作 MCP サーバーが提供できる機能は以下の 3 種類です。
| 要素 | 役割 | 典型的な用途 |
|---|---|---|
| Tools | Claude が呼び出せる「操作」 | API への書き込み、検索、計算 |
| Resources | Claude が読み取れる「データ」 | ファイル、DB レコード、ドキュメント |
| Prompts | 再利用可能なプロンプトテンプレート | 定型業務のチェックリスト等 |
多くのユースケースでは Tools だけ 実装すれば十分です。Resources は Claude が自律的にデータを参照する場面(コンテキスト補充)で使います。
3. TypeScript で MCP サーバーを作る(ステップバイステップ)
ステップ 1: プロジェクト初期化
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 --target es2022 --module nodenext --moduleResolution nodenext
package.json に以下を追記します。
{
"type": "module",
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsc"
}
}
ステップ 2: サーバーの骨格を作る
src/index.ts を作成します。
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: "my-company-mcp",
version: "1.0.0",
});
// --- Tools をここに追加 ---
// サーバーを起動(stdio 通信)
const transport = new StdioServerTransport();
await server.connect(transport);
ステップ 3: Tools を実装する
社内の在庫 API を叩く Tool を追加してみます。// --- Tools をここに追加 --- の下に記述します。
// 在庫照会 Tool
server.tool(
"get_inventory",
"SKU コードで在庫数を照会する",
{
sku: z.string().describe("商品 SKU コード(例: ITEM-001)"),
},
async ({ sku }) => {
const res = await fetch(`https://api.internal.example.com/inventory/${sku}`, {
headers: { "Authorization": `Bearer ${process.env.INTERNAL_API_TOKEN}` },
});
if (!res.ok) {
return {
content: [{ type: "text", text: `エラー: ${res.status} ${res.statusText}` }],
isError: true,
};
}
const data = await res.json() as { sku: string; stock: number; updated_at: string };
return {
content: [
{
type: "text",
text: `SKU: ${data.sku} の在庫数は ${data.stock} 個です(最終更新: ${data.updated_at})`,
},
],
};
}
);
ポイント:
zodでパラメータのバリデーションを定義する(型安全 + SDK が JSON Schema に自動変換)- API 認証情報は
process.envで管理し、コードに直書きしない - エラー時は
isError: trueを返すと Claude が「失敗した」と認識して対処する
ステップ 4: Resources を実装する(オプション)
Claude に「コンテキストとして読んでほしいデータ」がある場合に使います。
// 社内 FAQ ドキュメントを Resource として公開
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
server.resource(
"faq",
new ResourceTemplate("faq://{category}", { list: undefined }),
async (uri) => {
const category = uri.pathname.replace("/", "");
const faqs = await loadFaqByCategory(category); // 社内 DB からロード
return {
contents: [
{
uri: uri.toString(),
text: faqs.map((f) => `Q: ${f.question}\nA: ${f.answer}`).join("\n\n"),
mimeType: "text/plain",
},
],
};
}
);
ステップ 5: ローカルで動作確認する
# stdio でインタラクティブに確認
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | node --loader tsx src/index.ts
Tools の一覧が JSON で返ってくれば動作しています。
4. Claude Code に自作 MCP を接続する
プロジェクトルートの .mcp.json に追記します。
{
"mcpServers": {
"my-company-mcp": {
"command": "node",
"args": ["--loader", "tsx", "/path/to/my-mcp-server/src/index.ts"],
"env": {
"INTERNAL_API_TOKEN": "${env:INTERNAL_API_TOKEN}"
}
}
}
}
本番環境(ビルド後)の場合:
{
"mcpServers": {
"my-company-mcp": {
"command": "node",
"args": ["/path/to/my-mcp-server/dist/index.js"],
"env": {
"INTERNAL_API_TOKEN": "${env:INTERNAL_API_TOKEN}"
}
}
}
}
.mcp.json を保存して claude を起動すると、自作サーバーが自動検出されます。claude "get_inventory を使って ITEM-001 の在庫を確認して" と入力すれば Tool が呼ばれます。
5. 実践例:複数 API を集約した受注確認 Tool
5-1. 要件
「注文番号を渡すと、受注システム・在庫システム・配送システムの3つから情報をまとめて返す」Tool。
5-2. 実装コード
server.tool(
"get_order_summary",
"注文番号から受注・在庫・配送の統合サマリーを取得する",
{
order_id: z.string().describe("注文番号(例: ORD-20260612-001)"),
},
async ({ order_id }) => {
// 3 API を並列で叩く(モデルケース)
const [order, stock, shipping] = await Promise.all([
fetch(`https://api.example.com/orders/${order_id}`).then((r) => r.json()),
fetch(`https://api.example.com/stock/${order_id}`).then((r) => r.json()),
fetch(`https://api.example.com/shipping/${order_id}`).then((r) => r.json()),
]);
const summary = [
`## 注文 ${order_id} サマリー`,
`- 受注日時: ${order.created_at}`,
`- 商品: ${order.items.map((i: { name: string; qty: number }) => `${i.name} × ${i.qty}`).join(", ")}`,
`- 在庫ステータス: ${stock.status}(残 ${stock.available} 個)`,
`- 配送ステータス: ${shipping.status}(追跡番号: ${shipping.tracking_id ?? "未割当"})`,
].join("\n");
return { content: [{ type: "text", text: summary }] };
}
);
Claude Code にこの Tool が追加されると、「ORD-20260612-001 の状況を教えて」の一言で3システムの情報を横断取得できます。
※上記コードは実装パターンを示すモデルケースです。実際の API レスポンス形状は各社の仕様に合わせて修正してください。効果や処理速度は環境・API の応答時間によって異なります。
6. Python で作る場合
TypeScript の代わりに Python でも実装できます。公式の mcp ライブラリを使います。
pip install mcp
import mcp.server.stdio
from mcp.server import Server
from mcp.types import Tool, TextContent
app = Server("my-company-mcp")
@app.list_tools()
async def list_tools():
return [
Tool(
name="get_inventory",
description="SKU コードで在庫数を照会する",
inputSchema={
"type": "object",
"properties": {"sku": {"type": "string"}},
"required": ["sku"],
},
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "get_inventory":
sku = arguments["sku"]
# ここに API 呼び出しを実装
return [TextContent(type="text", text=f"SKU: {sku} の在庫数は 42 個です")]
if __name__ == "__main__":
import asyncio
asyncio.run(mcp.server.stdio.run(app))
Python の場合は .mcp.json の command を python3、args に .py ファイルのパスを指定します。
7. よくある失敗と対処法
7-1. Claude が Tool を認識しない
.mcp.json のパスが間違っているか、起動時エラーで MCP サーバーが落ちているケースがほとんどです。
# MCP サーバーが正常起動するか単体確認
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0"}}}' \
| node --loader tsx src/index.ts
7-2. ERR_UNKNOWN_FILE_EXTENSION エラー
package.json に "type": "module" が抜けているか、tsconfig.json の moduleResolution が nodenext になっていない場合に発生します。「ステップ 1」の設定を再確認してください。
7-3. 環境変数が渡らない
.mcp.json の env フィールドで ${env:VAR} 記法を使っているのに、シェルに変数がセットされていないパターン。export INTERNAL_API_TOKEN=xxx を .zshrc や .env.local に追加し、シェルを再起動してから claude を起動してください。
7-4. Tool の応答が空になる
return する content 配列が空、または text フィールドが空文字になっているケースです。デバッグ時は console.error(JSON.stringify(result)) で stderr に出力し、Claude Code の MCP ログ(~/.claude/logs/)で確認できます。
8. まとめ
MCP 自作は「5 ステップ + 50 行のコード」で始められます。
@modelcontextprotocol/sdkをインストールMcpServerに Tools / Resources を定義.mcp.jsonで Claude Code に接続process.envで認証情報を安全に管理- 必要に応じて Tools を追加・組み合わせ
既存の公式 MCP では対応できない社内システムや独自 API を Claude Code と繋ぐことで、「自然言語で社内全システムを横断操作する」ワークフローが実現できます。
次のステップ:
- Claude Code MCP 完全ガイド — 公式 MCP サーバーの設定方法
- Claude Code Hooks 完全ガイド — MCP Tools の呼び出し前後にバリデーションを挟む方法
- Claude Code 完全ガイド — Skills / Hooks / Subagent を含む全機能の全体像
自作 MCP の実装でお困りのことがあれば、週1ニュースレター(無料)でも実装サンプル・Q&A を毎週木曜に配信しています。
関連記事
- Claude Code × MCP 完全ガイド — MCP の基本から業務統合まで
- Claude Code MCP おすすめ 12 選 — 即戦力 MCP サーバー厳選リスト
- MCPとは? — Model Context Protocol の仕組みと活用法を完全解説
- Claude Code × Supabase 完全ガイド — MCP 連携・スキーマ設計・RLS自動生成
- Claude Code Skills 完全ガイド — MCP と組み合わせる Skills の設計
- Claude Code 完全ガイド — MCP 以外の全機能を網羅したピラー記事
この記事の著者
claude-code-media 編集部/ Claude Code 専門編集チーム
Claude Code の非エンジニア向け業務効率化メディア『claude-code-lab.jp』を運営。フリーランス・中小企業・個人開発者向けに、実装テンプレ・業務自動化テクニック・Vibe Coding 入門を配信。
