「MCPサーバーって、Pythonじゃないと作れないの?」
TypeScriptのプロジェクトでMCPを使いたいのに、チュートリアルがPython前提のものばかりで困った経験はないでしょうか。実は、TypeScriptでも同等以上のDXでMCPサーバーを構築できます。しかも、長期的な保守性ではTypeScriptに軍配が上がります。
この記事では、TypeScript でMCPサーバーを実装する2つのアプローチ(公式SDK vs FastMCP)を比較し、Claude Codeへの統合まで30分で完成させる手順をコピペ可能なコード付きで解説します。
MCP(Model Context Protocol)を30秒でおさらい
MCP は Anthropic が策定したオープン標準プロトコルです。AIアシスタント(Claude、Cursor、VS Code Copilot等)が外部ツールと会話するための「USBソケット」のような役割を果たします。
MCPサーバーが提供できるのは主に3種類:
- Tools — LLMが呼び出せる関数(例:天気取得、DB検索)
- Resources — ファイルやAPIレスポンスなどの読み取り専用データ
- Prompts — 再利用可能なプロンプトテンプレート
既存のPython版MCPサーバー構築ガイドと合わせて読むと、言語選択の判断材料が揃います。
TypeScript vs Python:どちらで作るべきか
| 観点 | TypeScript | Python |
|---|---|---|
| 型安全性 | 静的型チェック(コンパイル時エラー検出) | 型ヒントは任意 |
| セットアップ速度 | やや遅い(ビルドステップ必要) | 速い(FastMCPなら即起動) |
| 長期保守性 | IDEサポートが厚く、リファクタ安全 | 大規模になると型管理が煩雑 |
| エコシステム | Webサービスとの統合が容易 | データサイエンス系ライブラリが豊富 |
| Claude Code連携 | 公式SDK + FastMCP両対応 | FastMCPが特に簡単 |
| 推奨ユースケース | プロダクション長期運用、Webサービス統合 | プロトタイプ、データ処理パイプライン |
結論として、「今すぐ試したい」ならPython FastMCPが最速ですが、「チームで長期運用する」なら TypeScript が安全です。本記事ではTypeScript実装を中心に解説します。
【コード集1】公式SDK(@modelcontextprotocol/sdk)での実装
まずは公式 TypeScript SDK を使った基本的なMCPサーバーを作ります。
セットアップ
# プロジェクト初期化
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node ts-node
# tsconfig.json を作成
npx tsc --init --module nodenext --moduleResolution nodenext --target es2022 --outDir dist
動作環境: Node.js 20+, TypeScript 5.4+
基本的なMCPサーバー(天気情報ツール)
以下は、天気情報を取得するツールを持つMCPサーバーの完全な実装例です。
// src/index.ts
// 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// MCPサーバーを初期化
const server = new McpServer({
name: "weather-server",
version: "1.0.0",
});
// ツールを定義: 天気情報の取得
server.tool(
"get_weather",
"指定した都市の現在の天気情報を取得します",
{
city: z.string().describe("都市名(例: Tokyo, Osaka)"),
units: z.enum(["metric", "imperial"]).default("metric"),
},
async ({ city, units }) => {
// 実際の実装では外部APIを呼び出す
// const apiKey = process.env.WEATHER_API_KEY;
// const response = await fetch(`https://api.openweathermap.org/...`);
// デモ用のモックレスポンス
const temp = units === "metric" ? "22°C" : "72°F";
return {
content: [
{
type: "text",
text: JSON.stringify({
city,
temperature: temp,
condition: "晴れ",
humidity: "60%",
timestamp: new Date().toISOString(),
}),
},
],
};
}
);
// リソースを定義: 対応都市一覧
server.resource(
"supported-cities",
"mcp://weather-server/cities",
async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
cities: ["Tokyo", "Osaka", "Kyoto", "Sapporo", "Fukuoka"],
}),
},
],
})
);
// STDIOトランスポートで起動(Claude Code/Desktop用)
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Weather MCP Server running on stdio");
}
main().catch(console.error);
ポイント: `console.error` でログを出力しているのは意図的です。STDIOトランスポートは stdin/stdout をMCPプロトコルの通信に使うため、デバッグログは stderr に向ける必要があります。
// package.json に追加
{
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}
【コード集2】FastMCP(TypeScript版)での実装
FastMCP にはPython版だけでなく、TypeScript版も存在します。よりシンプルなAPIで、ボイラープレートを大幅に削減できます。
# FastMCP TypeScript版のインストール
npm install fastmcp zod
// src/server.ts
// FastMCP TypeScript版 — 公式SDKより簡潔に書ける
// 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
import { FastMCP } from "fastmcp";
import { z } from "zod";
const server = new FastMCP({
name: "My MCP Server",
version: "1.0.0",
});
// ツール追加がシンプル
server.addTool({
name: "calculate",
description: "2つの数値の四則演算を実行します",
parameters: z.object({
a: z.number().describe("1つ目の数値"),
b: z.number().describe("2つ目の数値"),
operation: z.enum(["add", "subtract", "multiply", "divide"]),
}),
execute: async ({ a, b, operation }) => {
const results: Record = {
add: a + b,
subtract: a - b,
multiply: a * b,
divide: b !== 0 ? a / b : NaN,
};
return String(results[operation]);
},
});
// HTTP Streaming(本番リモート展開用)にも対応
server.start({
transportType: "stdio", // ローカル開発用
// transportType: "httpStream", // 本番用
// port: 8080,
});
公式SDK vs FastMCP の選択基準:
- ツールを5個以上追加する予定 → FastMCPのシンプルAPIが管理しやすい
- 低レベルのプロトコル制御が必要 → 公式SDK
- Cloudflare Workers等エッジ環境 → FastMCP(エッジランタイム対応済み)
【コード集3】Claude Codeへの統合
作成したMCPサーバーをClaude Codeに接続します。2つの方法があります。
方法A: CLIコマンドで登録(推奨)
# ビルドしてからClaude Codeに追加
npm run build
# グローバル登録(全プロジェクトで使用可能)
claude mcp add weather-server -- node /path/to/dist/index.js
# 環境変数ありの場合
claude mcp add weather-server
--env WEATHER_API_KEY=your-key
-- node /path/to/dist/index.js
# 登録確認
claude mcp list
方法B: プロジェクト設定ファイルで管理
// .mcp.json(プロジェクトルート)
{
"mcpServers": {
"weather-server": {
"command": "node",
"args": ["/path/to/dist/index.js"],
"env": {
"WEATHER_API_KEY": "${WEATHER_API_KEY}"
}
}
}
}
`.mcp.json` をプロジェクトルートに置くと、そのディレクトリで `claude` コマンドを実行したときに自動的にMCPサーバーが起動します。チームでの共有に最適です。
FastMCP版を Claude Code に登録する場合
# FastMCP CLIで自動セットアップ(v2.10.3+)
fastmcp install claude-code server.py
# 手動で追加する場合
claude mcp add my-server -- node dist/server.js
Streamable HTTP:本番リモート展開のセットアップ
ローカルSTDIOではなく、チーム全員が使えるリモートMCPサーバーを立てる場合のコード例です。
// src/remote-server.ts — HTTPSで公開するMCPサーバー
// 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";
import { z } from "zod";
const app = express();
app.use(express.json());
const server = new McpServer({ name: "remote-mcp", version: "1.0.0" });
// ツール定義(省略)
server.tool("ping", "疎通確認", {}, async () => ({
content: [{ type: "text", text: "pong" }],
}));
// MCP エンドポイント
app.all("/mcp", async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // ステートレスモード
});
res.on("close", () => transport.close());
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
const PORT = process.env.PORT ?? 8080;
app.listen(PORT, () => {
console.log(`MCP Server listening on port ${PORT}`);
});
OAuth 2.1 認証を加えることで、本番環境でのセキュアな公開も可能です。詳細は MCP公式ドキュメント を参照してください。
セキュリティと運用のチェックリスト
| チェック項目 | 対策 |
|---|---|
| APIキーのハードコード禁止 | process.env.API_KEYを使い、.envをgitignoreに追加 |
| SSRF(サーバーサイドリクエストフォージェリ) | URLを受け取るツールでは許可リストでドメインを制限 |
| 最小権限の原則 | ツールは必要最小限の権限のみ要求。ファイルシステムへのアクセスは必要なパスのみ |
| 入力検証 | Zodスキーマで全入力を型安全に検証(サイズ制限も設定) |
| 認証(リモートサーバー) | OAuth 2.1 + スコープ付きトークン。APIキーのみは非推奨 |
| ログ記録 | ツール呼び出しと結果をsterrで記録(PII含む引数はマスク) |
【要注意】よくある失敗パターンと回避策
失敗1: console.log でデバッグしてMCPが動かない
❌ STDIOサーバーで console.log を使う
⭕ 必ず console.error に変える
なぜ重要か: STDIOトランスポートは stdout をMCPプロトコルのデータ通信に使います。console.log で余分なデータが流れると、プロトコルパーサーが壊れてサーバーが無反応になります。
失敗2: ツールの戻り値の形式が間違っている
❌ 単純な文字列を返す: return "結果"
⭕ content配列を返す:
return {
content: [{ type: "text", text: "結果" }],
};
なぜ重要か: MCPプロトコルはコンテンツブロック形式を要求します。型エラーにならないためにもTypeScriptの型チェックを活用しましょう。
失敗3: ビルドせずにTypeScriptファイルを直接実行しようとする
❌ claude mcp add server -- node src/index.ts
⭕ npm run build && claude mcp add server -- node dist/index.js
回避策: 開発中は ts-node で直接実行できますが、本番(Claude Code登録)では必ずコンパイル済みのJSファイルを使いましょう。
失敗4: 環境変数のロードタイミング問題
❌ .env ファイルを自動読み込みと仮定してAPIキーを参照
⭕ dotenv を使って明示的にロード、またはclaude mcpの--envフラグで渡す
// 正しい環境変数のロード
import "dotenv/config"; // または: import dotenv from "dotenv"; dotenv.config();
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
参考・出典
- Build an MCP server — Model Context Protocol 公式ドキュメント(参照日: 2026-04-11)
- punkpeye/fastmcp — GitHub — FastMCP TypeScript フレームワーク(参照日: 2026-04-11)
- Claude Code Integration — FastMCP 公式ドキュメント(参照日: 2026-04-11)
- The Complete MCP Server Handbook for Developers (April 2026 Edition) — Apify Blog(参照日: 2026-04-11)
まとめ:今日から始める3つのアクション
- 今日やること: 公式SDK版の「天気サーバー」コードをローカルで動かし、
claude mcp addで Claude Code に接続する(所要30分) - 今週中: 業務で使いたいAPIを1つ選び、MCPツールとして実装する。スプレッドシート読み取り・社内DB検索・Slack投稿など
- 今月中: Streamable HTTP でリモートサーバー化し、チーム全員のClaude Codeで共有。開発生産性の変化を測定する
あわせて読みたい:
- AIエージェント構築完全ガイド — フレームワーク選定からデプロイまでの全体像
- Python FastMCP でMCPサーバーを作る — Python版の実装ガイド
この記事はAIgent Lab編集部がお届けしました。AIエージェントの設計・開発でお困りのことがあれば、Uravationのお問い合わせフォームからご相談ください。