MCPサーバの基本アーキテクチャとライフサイクル
大規模言語モデル(LLM)を用いたAIエージェントが、外部のデータソースやシステムと連携するための標準規格として「Model Context Protocol(MCP)」の採用が進んでいます。MCPサーバの実装に着手する前に、まずはクライアント(LLMアプリケーション)とサーバ間の根幹となるアーキテクチャと通信のライフサイクルを正確に把握することが重要です。
クライアント・サーバ間の通信フロー
MCPの通信プロトコルは、軽量かつ言語非依存な「JSON-RPC 2.0」をベースに設計されています。通信は常にクライアントからのリクエストで始まり、サーバがレスポンスを返すリクエスト・リプライ型のモデルを採用しています。さらに、サーバからクライアントへ状態変化を通知する「Notification」の仕組みも備わっています。
接続が確立されると、最初に「Initialize」フェーズが実行されます。このフェーズでは、クライアントとサーバが互いのメタデータ(名称やバージョン)と、サポートしている機能(Capabilities)を交換します。サーバ側は、「リソースの提供(Resources)」「ツールの実行(Tools)」「プロンプトテンプレートの提供(Prompts)」といった機能要件のうち、自らが提供可能なものを明示的に宣言する必要があります。この初期化が完了して初めて、実際のデータアクセスやツール呼び出しが許可される堅牢なステートマシンが形成されます。
トランスポート層の選択:Stdio vs HTTP
MCPでは、実行環境やセキュリティ要件に応じて2つの主要なトランスポート層を選択できます。
1つ目は「Stdio(標準入出力)」方式です。これはローカル環境での実行に最適化されており、クライアント(例:デスクトップアプリ)がMCPサーバをサブプロセスとして直接起動し、標準入出力を介してJSON-RPCメッセージをやり取りします。ネットワークポートを開放する必要がないため、ローカルマシン上のファイルシステムやセキュアな認証情報にアクセスする用途に適しています。
2つ目は「HTTP(SSE: Server-Sent Events)」方式です。サーバからクライアントへの非同期なメッセージ送信にSSEを使用し、クライアントからのリクエストには通常のHTTP POSTを使用します。この方式は、MCPサーバを社内ネットワーク上の独立したコンテナやクラウド環境にデプロイし、複数のクライアントからアクセスさせるリモート連携のケースで採用されます。本番環境の要件に合わせて、適切なトランスポート層を選択することがアーキテクチャ設計の第一歩となります。
開発環境のセットアップとSDKの初期化仕様
アーキテクチャの全体像を把握した後は、実際のコード実装に入ります。Anthropicの公式ドキュメントでは、PythonおよびTypeScript向けの公式SDKを用いた開発が推奨されています。
Python/TypeScript SDKのインストール
各言語のパッケージマネージャを使用して、最新のSDKをインストールします。具体的なバージョン番号については、依存関係の競合を避けるため、常に公式ドキュメントで最新のリリース情報を確認してください。
Python環境の場合は、以下のコマンドでパッケージをインストールします。
# Python環境でのインストール例
pip install mcp
TypeScript環境の場合は、Node.jsのパッケージマネージャを使用します。
# TypeScript環境でのインストール例
npm install @modelcontextprotocol/sdk
サーバインスタンスの定義とメタデータ設定
SDKを用いたサーバオブジェクトの初期化プロセスでは、クライアントに識別されるためのメタデータ(サーバ名とバージョン)を正確に定義することが求められます。
以下は、TypeScript SDKを用いた最小構成のサーバインスタンス定義の例です。
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// サーバインスタンスの初期化
const server = new Server(
{
name: "enterprise-internal-api",
version: "1.0.0",
},
{
capabilities: {
resources: {},
tools: {},
prompts: {},
},
}
);
// Stdioトランスポートを用いた接続確立
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.log("MCP Server is running on Stdio");
}
main().catch(console.error);
このように、capabilities ブロックで実装する機能を宣言することで、クライアント側はサーバがどのような役割を担っているかを初期化フェーズで正確に認識できるようになります。
リソースAPI:静的データへのアクセス定義
AIエージェントに社内のドキュメントやデータベースの読み取り権限を与えるための機能が「リソースAPI」です。リソースは、LLMがコンテキストとして読み込むための静的なデータソースとして機能します。
リソースURLのスキーマ設計
MCPにおけるリソースは、URI(Uniform Resource Identifier)形式で一意に識別されます。標準的な file:// スキーマだけでなく、自社システムに合わせたカスタムスキーマを設計することが可能です。
例えば、社内のナレッジベースにアクセスするためのスキーマとして knowledge:// を定義し、特定の記事を knowledge://articles/12345 のように表現します。URIテンプレートを使用することで、動的なパラメータを含むリソース群を効率的にクライアントへ提示することができます。
リソース読み取りハンドラの実装
リソースへのリクエストを受け取ったサーバは、指定されたURIを解析し、適切なデータを返却するハンドラを実装します。この際、データ形式を明示するためのMIMEタイプの指定が不可欠です。
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("KnowledgeBaseServer")
# リソースの定義
@mcp.resource("knowledge://articles/{article_id}")
def get_article(article_id: str) -> str:
"""指定されたIDの社内記事を取得します"""
# 実際の環境ではデータベースから取得するロジックを実装
content = fetch_from_database(article_id)
return content
プレーンテキストだけでなく、application/pdf や image/png などのバイナリデータをBase64エンコードして返却することで、マルチモーダルモデル(画像解析に対応したClaude 3.5 Sonnetなど)に視覚情報を提供することも可能です。
ツールAPI:AIに実行権限を与えるアクションの実装
リソースが「読み取り専用」のデータソースであるのに対し、「ツールAPI」はAIエージェントに外部システムへの副作用(APIへのPOSTリクエスト、データベースの更新、外部スクリプトの実行など)を伴うアクションの実行権限を与えます。
ツールの名称と説明文の最適化(Prompt Engineering)
ツール定義において最も重要なのは、関数名と「説明文(Description)」の書き方です。LLMは提供されたツールの説明文を読み込み、ユーザーの要求を満たすためにどのツールを、いつ呼び出すべきかを自律的に判断します。
単なる機能の説明ではなく、「どのような入力に対して、どのような結果を返すか」「どのような状況でこのツールを使用すべきか」をプロンプトエンジニアリングの観点で詳細に記述することが、AIエージェントの精度を飛躍的に向上させます。
入力パラメータのJSON Schema定義
ツールが受け取る引数は、JSON Schemaを用いて厳密に定義する必要があります。これにより、LLMが生成する引数の型や必須項目がクライアント側でバリデーションされ、サーバ側のエラーを防ぐことができます。
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
// ツールのリスト定義
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "create_jira_ticket",
description: "ソフトウェアのバグや新機能の要望を受け、Jiraに新しいチケットを作成します。ユーザーから具体的な要件が提示された場合のみ使用してください。",
inputSchema: {
type: "object",
properties: {
title: {
type: "string",
description: "チケットの簡潔なタイトル",
},
issue_type: {
type: "string",
enum: ["Bug", "Task", "Story"],
description: "課題のタイプ",
}
},
required: ["title", "issue_type"],
},
},
],
};
});
型安全な引数定義と明確な説明文の組み合わせが、商用環境に耐えうる安定したツール呼び出し(Function calling)の基盤となります。
プロンプトAPI:再利用可能なテンプレートの提供
プロンプトAPIは、特定の業務タスクに最適化されたプロンプトテンプレートをサーバ側からクライアントに提供する機能です。これにより、ユーザーが毎回複雑な指示を入力する手間を省き、定型業務の自動化を促進します。
引数付きプロンプトテンプレートの定義
プロンプトは、ユーザーからの入力を引数として受け取る動的なテンプレートとして定義できます。例えば、「コードレビュー」というプロンプトに対して、対象となる「ファイルパス」を引数として設定することが可能です。
コンテキストを注入する動的生成ロジック
高度な実装パターンとして、プロンプトAPIの中でリソースAPIやツールAPIと連携し、必要なコンテキストを事前に収集してテンプレートに注入する方法があります。
例えば、指定されたファイルパスのソースコードを読み込み、さらに直近のエラーログを取得した上で、「以下のコードとエラーログを分析し、修正案を提示してください」という完全なメッセージ構造を構築してLLMに渡すことができます。これにより、AIエージェントは常に最新かつ正確な社内データを前提とした推論を開始できるようになります。
エラーハンドリングとレート制限の実装リファレンス
本番環境で稼働するMCPサーバにおいて、システムの安定性を担保するためのエラー処理と運用設計は不可欠です。予期せぬ入力やバックエンドの障害に対して、適切にフェイルセーフを機能させる必要があります。
標準エラーコードの活用
JSON-RPC 2.0の仕様に従い、エラー発生時は標準化されたエラーコードを返却します。例えば、クライアントから送信された引数がJSON Schemaの定義と一致しない場合は -32602 (Invalid params) を、存在しないツールが呼び出された場合は -32601 (Method not found) を返します。
さらに、サーバ内部の処理で例外が発生した場合は、機密情報(スタックトレースなど)をマスクした上で、一般的なサーバーエラーとして処理し、クライアントには安全なエラーメッセージのみを伝達する実装が推奨されます。
タイムアウトとリトライの設計方針
外部APIと連携するツールを実装する場合、ネットワーク遅延によるサーバのハングアップを防ぐため、厳格なタイムアウト処理を設ける必要があります。また、LLMクライアント側からの過剰なリクエスト(無限ループによるツール呼び出しなど)を防ぐため、サーバ側でトークンバケットアルゴリズム等を用いたレート制限(クォータ管理)を導入し、サーバ負荷を適切に抑止する設計を組み込むことが、エンタープライズ品質の要件となります。
デバッグとデプロイメント:Claude Desktopへの統合手順
開発したMCPサーバを実際のLLM環境で動作検証するためには、クライアントアプリケーションへの統合が必要です。ここでは、Anthropicが公式に案内しているClaude Desktopアプリケーションを用いたテスト手順を解説します。
MCP Inspectorを用いたローカル検証
本番環境へのデプロイ前に、公式のデバッグツールである「MCP Inspector」を利用してサーバの挙動を検証します。以下のコマンドを実行することで、ブラウザベースのUIからサーバのリソースやツールを直接テストできます。
# MCP Inspectorの起動(TypeScriptサーバの場合)
npx @modelcontextprotocol/inspector node build/index.js
Inspectorを使用することで、LLMを介さずにJSON-RPCメッセージの生データを監視でき、引数のバリデーションエラーやレスポンスの遅延などを効率的にトラブルシューティングできます。
コンフィグファイルによる接続設定
ローカルでの検証が完了したら、Claude Desktopにサーバを認識させます。OSの所定のディレクトリにある claude_desktop_config.json ファイルを編集し、サーバの起動コマンドとパスを指定します。
{
"mcpServers": {
"enterprise-api": {
"command": "node",
"args": [
"/absolute/path/to/your/server/build/index.js"
],
"env": {
"API_KEY": "your_secret_key_here"
}
}
}
}
設定時のポイントとして、実行ファイルのパスは必ず「絶対パス」で指定すること、またAPIキーなどの機密情報は env オブジェクトを通じて環境変数として安全に渡すことが挙げられます。設定ファイルを保存してClaude Desktopを再起動すると、チャットインターフェース上から自作のツールやリソースが利用可能になります。
実稼働を見据えたAIエージェントの展望
Model Context Protocolの仕様に準拠したサーバを構築することで、自社の独自データと強力なLLMをシームレスかつ安全に統合する基盤が完成します。今回解説したSDKの初期化、リソース・ツールの設計、そして堅牢なエラーハンドリングの実装は、PoC(概念実証)から本番運用へと移行するための必須要件です。
自社データを活用したAIエージェントのアーキテクチャ設計が完了した後は、実際のビジネス現場でどのように運用され、どのような成果を上げているかを確認することが次の重要なステップとなります。他社がどのようなセキュリティ要件をクリアし、既存の社内システムとAIを連携させて業務プロセスを変革しているのか。
自社と同規模・同業種の企業が実践している成功パターンや、導入フェーズごとの具体的な課題解決の手法を知ることで、本番導入に向けたプロジェクトの確実性はさらに高まります。システムの仕様策定から一歩進み、実際の運用規模に応じた最適な導入アプローチを決定するためにも、ぜひ業界別の導入事例や実践的なユースケースをチェックし、自社への適用イメージをより強固なものにしてください。
コメント