MCP Server 開発入門:ゼロから始める初めての MCP サービス構築
导语: Cursor でコードを書いているとき、AI にプロジェクトの依存関係の最新バージョンを直接確認させたいと思ったことはありませんか?または Claude でデータを分析しているとき、データベース内の情報を読み取らせたいと思ったことはありませんか?それぞれ個別に実装しようとすると、各 AI ツールごとに适配コードを書く必要があります。MCP Server を使えば、一度書くだけで MCP をサポートするすべてのクライアントが利用できるようになります。この記事では、TypeScript を使ってゼロから完全な MCP Server を構築する方法を解説します。
MCP とは何か?3 分で理解する核心概念
USB インターフェースの物語
数年前にデジタル製品を使ったことのある人なら、あの尴尬な時期を覚えているでしょう:マウスは丸型、キーボードは角型、プリンターは並列ポート、それぞれのデバイスに対応する挿し口を探す必要がありました。その後 USB が登場し、一つのインターフェースで全てが解決するようになりました。
MCP(Model Context Protocol)は、AI ツール界の「USB 規格」になりつつあります。
MCP がない時代、AI に特定のデータソースへアクセスさせようとすると、各 AI ツールごとに适配層を個別に書く必要がありました。Claude にはプラグインを、Cursor には拡張を、Windsurf にはまた別のものを…复杂度は N x M(N 個のデータソース x M 個の AI ツール)になります。
MCP があると、MCP Server を一つ書くだけで、MCP をサポートするすべてのクライアントが直接呼び出せるようになります。复杂度は N+M に下がります。
3 層アーキテクチャは非常にシンプルです:
+-------------+ +-------------+ +-------------+
| Host | -> | Client | -> | Server |
| (Claude) | | (MCP クライアント)| |(あなたのサービス)|
+-------------+ +-------------+ +-------------+
- Host:AI アプリケーションそのもの(例:Claude Desktop、Cursor)
- Client:MCP クライアント、Host との通信を担当
- Server:あなたが書くサービス、具体的な機能を提供
MCP Server が提供する 3 つの機能
MCP Server は、3 つの異なるタイプの機能を提供できます:
| 機能 | 用途 | 例 |
|---|---|---|
| Tools(ツール) | 操作の実行 | 天気照会、メッセージ送信、データベース読み取り |
| Resources(リソース) | データの提供 | ファイル内容、API 応答、設定情報 |
| Prompts(プロンプト) | 事前定義テンプレート | コードレビューテンプレート、日報生成テンプレート |
Tools は「関数」として理解できます。AI がそれを呼び出して特定のアクションを実行します。Resources は「データソース」で、AI がその内容を読み取れます。Prompts は「テンプレート」で、AI がタスクをより早く理解するのに役立ちます。
既存の記事との違い:他の MCP チュートリアルでは Python と FastMCP を使ったバージョンを見かけるかもしれません。この記事では TypeScript 原生 SDK を使用しており、フロントエンドおよびフルスタック開発者に向いています。両者の機能は同等なので、慣れた言語を選べばよいでしょう。
""
開発環境の準備
事前要件
この記事では、以下を前提としています:
- Node.js 18+ または Bun 1.0+ がインストールされている
- TypeScript を書いたことがあり、
interfaceやasync/awaitが何かを知っている - Claude Desktop または MCP をサポートするクライアント(Cursor、Windsurf など)を持っている
Bun を使ったことがない場合は、試してみることをおすすめします。npm よりもはるかに高速で、TypeScript サポートが内置されているため、ts-node を追加で設定する必要がありません。
プロジェクトの初期化
# プロジェクトディレクトリを作成
mkdir mcp-weather-server && cd mcp-weather-server
# 初期化(Bun または npm を使用)
bun init -y
# または npm init -y
# MCP TypeScript SDK をインストール
bun add @modelcontextprotocol/sdk zod
# または npm install @modelcontextprotocol/sdk zod
ここで 2 つの依存関係を使用しています:
@modelcontextprotocol/sdk:MCP 公式 TypeScript SDKzod:TypeScript ランタイム型検証用。ツールのパラメータ schema 定義に使用
TypeScript 設定の要点
bun init を使用すれば、tsconfig.json はすでに設定済みです。手動で設定する場合は、以下のオプションに注意してください:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true
}
}
moduleResolution: "bundler" は ESM モジュールにとって重要です。これを設定しないと、「xxx is not defined」というエラーに遭遇する可能性があります。
実践:手書き天気照会 MCP Server
このチュートリアルでは、完全な MCP Server を完成させます。以下の機能を持ちます:
- AI の呼び出しリクエストを受信
- OpenWeatherMap API に照会してリアルタイム天気を取得
- フォーマットされた結果を返す
プロジェクト構造設計
mcp-weather-server/
+-- src/
| +-- index.ts # エントリーファイル
| +-- weather.ts # 天気ツール実装
| +-- resources.ts # リソース定義
+-- package.json
+-- tsconfig.json
実際のコードはすべて index.ts に配置することもできます(この記事ではそのようにしますが)、モジュールに分割した方がメンテナンス性は高まります。
ステップ 1:MCP Server の骨組みを作成
最もシンプルな部分から始めます。起動可能な MCP Server を作成します:
// 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: "weather-service",
version: "1.0.0",
});
// ツール(Tools)を登録
server.tool(
"get_weather",
"指定した都市の現在の天気情報を取得",
{
city: z.string().describe("都市名、例:北京、上海"),
},
async ({ city }) => {
// ツール実装は次節で展開
return { content: [{ type: "text", text: `${city} の天気を照会中...` }] };
}
);
// サーバーを起動
const transport = new StdioServerTransport();
await server.connect(transport);
McpServer は SDK が提供する核心クラスで、name と version を渡す必要があります。tool() メソッドはツールの登録に使用し、第 1 引数がツール名、第 2 引数が説明、第 3 引数がパラメータ schema、最後が実行関数です。
ステップ 2:天気照会ツールを実装(核心コード)
ツールを実際に機能するようにします。OpenWeatherMap の無料 API を使用します:
// src/weather.ts
import { z } from "zod";
// OpenWeatherMap API レスポンス型を定義
interface WeatherResponse {
name: string;
main: { temp: number; feels_like: number; humidity: number };
weather: [{ description: string }];
wind: { speed: number };
}
// 天気照会ツール実装
server.tool(
"get_weather",
"指定した都市の現在の天気情報を取得",
{
city: z.string().describe("都市名、例:北京、上海"),
},
async ({ city }) => {
const API_KEY = process.env.OPENWEATHER_API_KEY;
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric&lang=zh_cn`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`API リクエスト失敗:${response.status}`);
}
const data: WeatherResponse = await response.json();
// フォーマットされた結果を返す
return {
content: [
{
type: "text",
text: JSON.stringify({
city: data.name,
temperature: `${data.main.temp}°C`,
feels_like: `${data.main.feels_like}°C`,
description: data.weather[0].description,
humidity: `${data.main.humidity}%`,
wind_speed: `${data.wind.speed} m/s`,
}, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `照会失敗:${error instanceof Error ? error.message : '不明なエラー'}`,
},
],
isError: true,
};
}
}
);
注意点:
- API Key は環境変数から読み込む:コードにハードコードしないでください
- エラーハンドリング:
isError: trueを返して、クライアントに呼び出し失敗を知らせます - 型定義:
WeatherResponseインターフェースで TypeScript にデータ構造を検証させます
OpenWeatherMap で無料アカウントを登録し、API Key を取得したら環境変数を設定します:
export OPENWEATHER_API_KEY=your_api_key_here
ステップ 3:Resources を追加(オプションだが推奨)
Resources を使うと、Server が「読み取り専用」データを提供できるようになります。たとえば、AI がサーバーステータスを確認できるようにするリソースを提供できます:
// src/resources.ts
// サーバー状態情報を提供
server.resource(
"server-status",
"status://server",
async (uri) => ({
contents: [
{
uri: uri.href,
text: JSON.stringify({
name: "Weather Service",
version: "1.0.0",
status: "running",
timestamp: new Date().toISOString(),
}, null, 2),
},
],
})
);
// API ドキュメントを提供
server.resource(
"api-docs",
"docs://api",
async (uri) => ({
contents: [
{
uri: uri.href,
text: `
# Weather MCP Server API
## Tools
- get_weather(city: string): 指定した都市の天気を取得
## Resources
- status://server - サーバー状態
- docs://api - API ドキュメント
`.trim(),
},
],
})
);
resource() の最初の 2 つのパラメータはリソース名と URI で、3 番目は読み取り関数です。URI は任意の scheme でかまいません(status://、docs:// など)。区別できれば問題ありません。
ステップ 4:Prompts を追加(高度な機能)
Prompts は事前定義された対話テンプレートです。たとえば、「天気レポート」テンプレートを定義しておくと、AI が使用する際に都市名を自動的に埋め込みます:
// 事前定義された天気レポートテンプレート
server.prompt(
"weather_report",
"フォーマットされた天気レポートを生成",
{
city: z.string().describe("都市名"),
include_tips: z.boolean().optional().describe("服装アドバイスを含むかどうか"),
},
({ city, include_tips }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `${city} の天気レポートを生成してください。${include_tips ? "服装アドバイスも含めてください。" : ""}`,
},
},
],
})
);
prompt() の戻り値はメッセージの配列で、各メッセージには role と content があります。これにより、AI 使用時に事前設定されたコンテキストが直接得られます。
ステップ 5:エントリーファイルを完成
上記のすべてのコードを src/index.ts に統合し、エラーハンドリングを追加します:
// 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: "weather-service",
version: "1.0.0",
});
// すべてのツール、リソース、プロンプトを登録
// ...(上記のコード)
// エラーハンドリング
process.stdin.on("error", (err) => {
console.error("標準入力エラー:", err);
process.exit(1);
});
process.stdout.on("error", (err) => {
console.error("標準出力エラー:", err);
process.exit(1);
});
// 優雅な終了
process.on("SIGINT", async () => {
await server.close();
process.exit(0);
});
// サーバーを起動
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP Weather Server が起動しました。接続を待機中...");
StdioServerTransport は標準入出力を使用して通信するため、stdin/stdout のエラーハンドリングは重要です。SIGINT 処理により、Ctrl+C でサービスを優雅に停止できます。
bun run src/index.ts で起動し、「起動しました」のメッセージが表示されれば正常です。
クライアント設定:Claude で Server を活用
Server が完成したので、次は Claude Desktop や Cursor から呼び出せるようにします。
Claude Desktop 設定
設定ファイルを見つけます:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Server 設定を追加します:
{
"mcpServers": {
"weather": {
"command": "bun",
"args": ["run", "/absolute/path/to/mcp-weather-server/src/index.ts"],
"env": {
"OPENWEATHER_API_KEY": "あなたの API Key"
}
}
}
}
注意:args 内のパスは絶対パスである必要があります。相対パスだと Server の起動に失敗します。
Cursor / Windsurf 設定
Cursor と Windsurf の設定方法も同様で、IDE 設定で MCP 設定を見つけ、サーバー条目を追加します(形式は上記と同じ)。
Cursor の設定ファイルは通常以下にあります:
- macOS:
~/Library/Application Support/Cursor/User/globalStorage/state.vscdb - または IDE 内:設定 -> AI -> MCP -> サーバーを追加
Server のテスト
- Claude Desktop / Cursor を再起動
- 入力ボックスに「北京の天気を調べて」と入力
- Claude が自動的に MCP Server を呼び出すはずです
以下のような出力が表示されれば成功です:
{
"city": "北京",
"temperature": "18°C",
"feels_like": "16°C",
"description": "曇り",
"humidity": "65%",
"wind_speed": "3.2 m/s"
}
よくある問題のトラブルシューティング
| 問題 | 考えられる原因 | 解決策 |
|---|---|---|
| Server が未接続 | パスエラー | args 内の絶対パスを確認 |
| API Key が無効 | 環境変数が未渡り | env 設定が正しいか確認 |
| 応答なし | TypeScript コンパイルエラー | bun build または tsc でコンパイル |
| 権限エラー | 設定ファイルの権限 | 設定ファイルが読み取り可能か確認 |
""
拡張とデプロイの提案
さらに多くのツールを追加
天気照会は始まりにすぎません。以下を追加できます:
- 過去の天気照会:過去データ API を呼び出して、過去の特定の日付の天気を返す
- 複数都市比較:一度に複数都市を照会し、比較テーブルを返す
- 天気警報購読:悪天候の警報があるか確認
これらのツールの登録方法は get_weather と全く同じで、実装ロジックのみが異なります。
デプロイオプションの比較
Server をチームで共有したい場合、ローカルの stdio 伝送では不十分です。以下にいくつかのデプロイ方法を挙げます:
| デプロイ方法 | 適用シーン | メリット | デメリット |
|---|---|---|---|
| ローカル stdio | 個人使用、開発テスト | シンプル、安全 | 共有可能ではない |
| HTTP/SSE | チーム共有、複数ユーザー | リモートアクセス可能 | 認証処理が必要 |
| Serverless | 本番環境 | 自動スケーリング | コールドスタート遅延 |
本番環境の注意点
認証:HTTP デプロイでは認証の実装が必須です。MCP は OAuth 2.1 をサポートしていますが、シンプルな API Key を使用することもできます:
// リクエストヘッダー内の API Key を確認
const apiKey = request.headers.get("Authorization");
if (apiKey !== `Bearer ${process.env.API_KEY}`) {
return new Response("Unauthorized", { status: 401 });
}
レート制限:悪意のある呼び出しで API クォータを使い果たさないようにします。express-rate-limit や Cloudflare Workers の内置レート制限を使用できます。
ログ:pino または winston を使用してツール呼び出しを記録し、問題のトラブルシューティングを容易にします:
import pino from "pino";
const logger = pino();
server.tool("get_weather", /* ... */, async ({ city }) => {
logger.info({ city }, "天気を照会");
// ...
});
監視:ツールの呼び出し成功率や応答時間を追跡します。Prometheus + Grafana が一般的な組み合わせです。
まとめ
この記事では、TypeScript を使用してゼロから MCP Server を構築する方法を紹介しました。内容は以下の通りです:
- MCP の核心概念と 3 層アーキテクチャの理解
- MCP TypeScript SDK を使用したサーバー作成
- 天気照会ツール(Tools)の実装
- サーバー状態リソース(Resources)の追加
- 天気レポートテンプレート(Prompts)の定義
- Claude Desktop / Cursor が Server を呼び出す設定
これで以下のことが可能になります:
- よく使用する API の MCP ラッパーを構築(GitHub、Slack、Notion など)
- 内部業務システムの MCP インターフェースを作成(CRM、データベース)
- MCP コミュニティの既存の成果を探索
学習リソース:
MCP プロトコルの原理を深く理解したい場合は、MCP プロトコル原理の深い理解 という記事もご覧ください。
FAQ
MCP Server 開発にはどのような基礎が必要ですか?
MCP Server と FastMCP の違いは何ですか?
MCP Server が正常に動作しているかをテストするにはどうすればよいですか?
MCP Server はリモートサーバーにデプロイできますか?
6 min read · 公開日: 2026年3月19日 · 更新日: 2026年3月19日
関連記事
OpenClaw 2026.3 実践アドバンス:新バージョンのコア機能とベストプラクティス
OpenClaw 2026.3 実践アドバンス:新バージョンのコア機能とベストプラクティス
OpenClaw 実践完全マニュアル:入門から精通まで
OpenClaw 実践完全マニュアル:入門から精通まで
単一モデルの囚人にならない:Antigravity で Gemini 3、Claude 4.5、GPT-OSS を柔軟に使い分ける方法

コメント
GitHubアカウントでログインしてコメントできます