「MCPサーバー開発」に関する有料の技術書や講座がいくつか出ているのを見かけました。内容を確認したところ、公式ドキュメントと実際に手を動かせば十分キャッチアップできるレベルだったので、独自にリサーチして無料の入門ガイドとしてまとめてみました。
MCPとは何か|よくある誤解を解く
MCP(Model Context Protocol)は、AIモデルと外部ツール・データソースを接続するためのオープンプロトコルです。Anthropicが2024年末に公開し、2025年に入って急速に普及が進んでいます。
「MCPサーバー」と聞くと、物理的なサーバーマシンやクラウドインスタンスを想像する方が多いと思います。しかし実際には、MCPサーバーはローカルで動作する軽量なプロセスにすぎません。Webサーバーのような常駐プロセスというより、AIアシスタントが呼び出すプラグインのようなイメージが近いです。
MCPが解決する問題
従来のAIアプリケーション開発では、AIモデルに外部機能を追加するたびに個別の実装が必要でした。データベースに接続するコード、APIを叩くコード、ファイルを操作するコード…それぞれがバラバラの実装になりがちだったんですよね。
MCPはこの問題を解決するために、統一的なインターフェースを提供します。USB-Cのような標準規格だと考えるとわかりやすいかもしれません。どんなデバイスでも同じ端子で接続できるように、どんなツールでも同じプロトコルでAIモデルに接続できる仕組みです。
MCPのアーキテクチャ
MCPは3つの主要コンポーネントで構成されています。それぞれの役割を整理しておきます。
ホスト(Host)
ユーザーが直接操作するアプリケーションのことです。Claude Desktop、VS Code拡張のCline、Cursorなどが該当します。ホストはMCPクライアントを内蔵しており、ユーザーのリクエストに応じてMCPサーバーと通信する仕組みです。
クライアント(Client)
ホスト内部に存在するコンポーネントで、MCPサーバーとの通信を管理します。各MCPサーバーとの間に1対1のセッションを維持し、JSON-RPCでメッセージをやり取りするのが主な役割です。開発者が直接触ることは少ないものの、仕組みを理解しておくとデバッグ時に役立つでしょう。
サーバー(Server)
実際に機能を提供する側です。データベースへのアクセス、外部APIの呼び出し、ファイル操作など、具体的なツールやリソースを公開します。開発者が主に実装するのはこの部分になります。
通信フロー
実際の通信は以下のような流れで進んでいきます。
- ユーザーがホスト(例:Claude Desktop)に質問する
- ホスト内のクライアントが、利用可能なMCPサーバーの機能一覧をAIモデルに渡す
- AIモデルが「この機能を使いたい」と判断する
- クライアントがMCPサーバーにリクエストを送信
- MCPサーバーが処理を実行して結果を返す
- AIモデルが結果を元にユーザーへの回答を生成
ポイントは、AIモデル自身が「どのツールを使うか」を判断するという点です。開発者がツールの呼び出しタイミングをハードコードする必要はありません。
開発環境のセットアップ
MCPサーバーはTypeScriptまたはPythonで開発できます。今回はTypeScriptでの実装を紹介しますが、Pythonでもほぼ同じ概念で開発可能です。
必要なもの
- Node.js 18以上
- TypeScript 5.0以上
- MCPホスト(Claude Desktop、Cline等)
プロジェクト初期化
mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
npx tsc --init
@modelcontextprotocol/sdkがMCPの公式SDKで、zodはツールのパラメータバリデーションに使用します。
基本的なMCPサーバーの実装
まずはシンプルなMCPサーバーを作ってみます。天気情報を返すサーバーを例に、基本的な構造を理解しましょう。
最小構成のサーバー
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-server",
version: "1.0.0",
});
// ツールの定義
server.tool(
"get-weather",
"指定した都市の天気情報を取得します",
{
city: z.string().describe("都市名(例:Tokyo, Osaka)"),
},
async ({ city }) => {
// 実際にはAPIを呼び出す
const response = await fetch(
\`https://wttr.in/\${encodeURIComponent(city)}?format=j1\`
);
const data = await response.json();
const current = data.current_condition[0];
return {
content: [
{
type: "text",
text: \`\${city}の天気: \${current.weatherDesc[0].value}, 気温: \${current.temp_C}°C, 湿度: \${current.humidity}%\`,
},
],
};
}
);
// サーバー起動
const transport = new StdioServerTransport();
await server.connect(transport);
たったこれだけで、AIモデルが天気情報を取得できるMCPサーバーが完成します。コード量は30行程度で、思ったより簡単だと感じた方も多いのではないでしょうか。
MCPの3つの主要機能
MCPサーバーが公開できる機能は大きく3種類あります。それぞれの使い分けを理解することが、良いMCPサーバー設計の鍵になります。
1. ツール(Tools)
AIモデルが能動的に呼び出す機能です。データの取得、計算の実行、外部サービスへの操作など、「何かを実行する」ものはツールとして定義します。先ほどの天気取得がこれに該当するものです。
2. リソース(Resources)
データの読み取り専用アクセスを提供します。ファイルの内容、データベースのレコード、設定情報など、AIモデルのコンテキストに追加したいデータはリソースとして定義するのが一般的です。
server.resource(
"config",
"config://app",
async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
version: "2.0",
features: ["dark-mode", "notifications"],
}),
},
],
})
);
3. プロンプト(Prompts)
再利用可能なプロンプトテンプレートです。特定のタスクに最適化されたプロンプトをサーバー側で管理し、AIモデルに提供できます。
server.prompt(
"code-review",
{ code: z.string() },
({ code }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: \`以下のコードをレビューしてください。
セキュリティ、パフォーマンス、可読性の観点から問題点を指摘し、
改善案を提示してください。
\${code}\`,
},
},
],
})
);
実用的なMCPサーバーの例
基礎がわかったところで、もう少し実用的な例を見てみましょう。
データベース接続サーバー
SQLiteデータベースに接続して、AIモデルがデータを検索・分析できるようにするMCPサーバーは非常に実用的です。
server.tool(
"query-database",
"SQLクエリを実行してデータを取得します(SELECT文のみ)",
{
query: z.string().describe("実行するSELECTクエリ"),
},
async ({ query }) => {
// SELECT文のみ許可(安全対策)
if (!query.trim().toUpperCase().startsWith("SELECT")) {
return {
content: [{ type: "text", text: "エラー: SELECT文のみ実行可能です" }],
isError: true,
};
}
const results = db.prepare(query).all();
return {
content: [
{
type: "text",
text: JSON.stringify(results, null, 2),
},
],
};
}
);
安全対策として、SELECT文のみに制限している点に注目してください。MCPサーバーはAIモデルが自律的に呼び出すため、意図しない破壊的操作を防ぐガードレールが重要になります。
API連携サーバー
GitHub APIやSlack API、社内システムのAPIなど、既存のREST APIをMCPサーバーでラップすることで、AIモデルからシームレスにアクセスできるようになります。これはMCPの最も一般的なユースケースの1つです。
デバッグとテスト
MCPサーバーの開発で最も困るのがデバッグかもしれません。通常のWebサーバーと違って、直接ブラウザで確認できないためです。
MCP Inspector
Anthropicが提供するMCP Inspectorは、MCPサーバーの動作確認に便利なツールです。
npx @modelcontextprotocol/inspector node build/index.js
ブラウザ上でツールの一覧確認、手動実行、レスポンスの確認が可能です。開発中は常にこれを使うことをおすすめします。
ログ出力
MCPサーバーはstdio通信を使うため、console.logは使えません(stdoutがプロトコル通信に使われるため)。代わりにstderrに出力するか、ファイルにログを書き出す必要があります。
// console.log は使わない(stdoutを汚染する)
// 代わりにstderrを使う
console.error("[DEBUG]", someValue);
// またはファイルに書き出す
import { appendFileSync } from "fs";
appendFileSync("/tmp/mcp-debug.log", \`\${new Date().toISOString()} \${message}\n\`);
これは初心者がよくハマるポイントなので、覚えておくと良いと思います。
Claude Desktopでの設定方法
作成したMCPサーバーをClaude Desktopで使うには、設定ファイルに追加します。
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["/path/to/weather-server/build/index.js"]
},
"database": {
"command": "node",
"args": ["/path/to/db-server/build/index.js"],
"env": {
"DB_PATH": "/path/to/database.sqlite"
}
}
}
}
設定ファイルの場所はOSによって異なります。
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
MCPサーバー開発のベストプラクティス
実際に開発する際に意識しておくと良いポイントをまとめます。
ツール設計のコツ
- 説明文は丁寧に書く:AIモデルはツールの説明文を読んで使用判断するため、曖昧な説明だと適切に呼び出されません
- パラメータ名は直感的に:
qよりquery、nよりcountのように、意味が明確な名前を使います - エラーメッセージも丁寧に:AIモデルがエラー内容を理解してリトライできるよう、具体的なエラーメッセージを返しましょう
セキュリティ対策
- 入力バリデーション:zodスキーマで型と範囲を制限する
- 破壊的操作の制限:DELETE、UPDATE等は確認フローを挟むか、そもそも提供しない
- レート制限:外部API呼び出しにはレート制限を設ける
- 機密情報の保護:APIキーなどは環境変数で管理し、レスポンスに含めない
まとめ
MCPサーバーの開発は、想像以上にシンプルです。公式SDKを使えば、30行程度のコードで実用的なサーバーが作れてしまいます。
今回の記事で押さえておきたいポイントは以下の通りです。
- MCPは物理サーバーではなく、AIモデルとツールを繋ぐプロトコル
- アーキテクチャはホスト・クライアント・サーバーの3層構造
- 提供できる機能はツール・リソース・プロンプトの3種類
- stdioの制約があるため、デバッグにはMCP InspectorやstderrLが必要
- セキュリティ(入力バリデーション、破壊的操作の制限)は特に重要
MCPのエコシステムは急速に拡大しており、すでに公式リポジトリには多数のリファレンス実装が公開されています。まずは既存のサーバーのコードを読んで、構造を理解するところから始めてみるのも良いアプローチだと思います。