- Published on
MCPサーバーを自作する:プロトコル原理から構築まで — Grafana、Mermaid、PPT、DB連携MCPエコシステム完全解剖
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 1. MCPプロトコル内部構造の解剖
- 2. Python FastMCPでMCPサーバーを作る(実習1)
- 3. TypeScript SDKでMCPサーバーを作る(実習2)
- 4. MCPサーバーのテストとデバッグ
- 5. MCPサーバーのデプロイ
- 6. MCPエコシステム100+サーバー総整理
- 7. オリジナルMCPサーバーアイデア10選
- 8. MCPセキュリティベストプラクティス
- 9. MCPの未来:2026年展望
- 10. クイズ
- 11. 参考資料
1. MCPプロトコル内部構造の解剖
MCP(Model Context Protocol)はAIモデルと外部システムを接続する標準プロトコルです。この記事では、前回のMCP概要ガイドでは扱えなかったプロトコル内部の動作原理を深く掘り下げ、MCPサーバーを自分で構築する方法を解説します。
1-1. JSON-RPC 2.0ベースのメッセージ構造
MCPの全通信はJSON-RPC 2.0仕様に従います。メッセージは大きく3種類に分かれます。
Request(リクエスト): クライアントまたはサーバーが相手に作業を要求する際に送信します。
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"city": "Tokyo"
}
}
}
Response(レスポンス): リクエストに対する結果を返します。
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "Tokyo: 18 degrees, sunny"
}
]
}
}
Notification(通知): レスポンスを期待しない一方向メッセージです。idフィールドがありません。
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
Error Response(エラーレスポンス): リクエスト処理に失敗した場合に返します。
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "Invalid params: city is required"
}
}
主なエラーコード:
| コード | 意味 | 説明 |
|---|---|---|
| -32700 | Parse Error | JSONパース失敗 |
| -32600 | Invalid Request | 不正なリクエスト形式 |
| -32601 | Method Not Found | 存在しないメソッド |
| -32602 | Invalid Params | 不正なパラメータ |
| -32603 | Internal Error | サーバー内部エラー |
1-2. 接続ライフサイクル
MCP接続は明確な段階を経ます:
Phase 1 - Initialize(初期化)
クライアントがサーバーにinitializeリクエストを送信し、双方のプロトコルバージョンと機能(Capabilities)を交換します。
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {
"roots": {
"listChanged": true
},
"sampling": {}
},
"clientInfo": {
"name": "Claude Desktop",
"version": "1.5.0"
}
}
}
サーバーが応答します:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-03-26",
"capabilities": {
"tools": {
"listChanged": true
},
"resources": {
"subscribe": true,
"listChanged": true
},
"prompts": {
"listChanged": true
}
},
"serverInfo": {
"name": "Weather MCP Server",
"version": "1.0.0"
}
}
}
Phase 2 - Initialized(初期化完了)
クライアントがnotifications/initialized通知を送信し、初期化完了を宣言します。
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
Phase 3 - Normal Operation(通常運用)
クライアントとサーバーが自由にメッセージをやり取りします:
- クライアントが
tools/listで利用可能なツール一覧を要求 - クライアントが
tools/callで特定のツールを呼び出し - クライアントが
resources/readでリソースを読み取り - サーバーが
notifications/tools/list_changedでツール一覧変更を通知
Phase 4 - Shutdown(シャットダウン)
クライアントが接続を終了すると、Transport層がクリーンアップされます。
1-3. Transport方式
MCPは2つのTransport方式をサポートしています。
stdio(Standard Input/Output)
最も一般的な方式で、ローカルでMCPサーバープロセスを直接実行します。
- クライアントがサーバープロセスをspawn
- stdinでリクエストを送信し、stdoutでレスポンスを受信
- 各メッセージは改行で区切り
- デバッグログはstderrに出力
- Claude Desktop、Claude Codeで主に使用
Client Server Process
| |
|--- spawn process -------->|
| |
|--- stdin: JSON-RPC ------>|
|<-- stdout: JSON-RPC -----|
| |
|--- stdin: JSON-RPC ------>|
|<-- stdout: JSON-RPC -----|
| |
|--- kill process --------->|
Streamable HTTP
リモートサーバーに接続する際に使用します。2025年3月のスペック更新で従来のHTTP+SSE方式を置き換えました。
- HTTP POSTでリクエストを送信
- サーバーがSSE(Server-Sent Events)でストリーミングレスポンス
- リモートデプロイ、マルチテナントシナリオに適合
- セッション管理のため
Mcp-Session-Idヘッダーを使用
Client HTTP Server
| |
|--- POST /mcp ----------->|
| (initialize) |
|<-- SSE: result -----------|
|<-- Mcp-Session-Id --------|
| |
|--- POST /mcp ----------->|
| (tools/call) |
|<-- SSE: result -----------|
| |
|--- DELETE /mcp ---------->|
| (session close) |
どちらのTransportを選ぶべきか?
| 基準 | stdio | Streamable HTTP |
|---|---|---|
| ユースケース | ローカル開発、デスクトップアプリ | リモートサーバー、クラウドデプロイ |
| セットアップ難易度 | とても簡単 | 中程度 |
| セキュリティ | ローカルプロセス分離 | OAuth 2.0、TLSが必要 |
| マルチユーザー | 不可 | 可能 |
| ネットワーク | 不要 | 必要 |
| 代表例 | Claude Desktop | チーム共有MCPサーバー |
1-4. 3つのコアプリミティブ
MCPサーバーは3種類の機能をAIに提供します。
Resources(リソース) - 読み取り専用データ
URIで識別されるデータを提供します。ファイル内容、DBスキーマ、APIレスポンスなどが該当します。
{
"jsonrpc": "2.0",
"id": 2,
"method": "resources/read",
"params": {
"uri": "file:///project/schema.sql"
}
}
レスポンス:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"contents": [
{
"uri": "file:///project/schema.sql",
"mimeType": "text/plain",
"text": "CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100));"
}
]
}
}
特徴:
- アプリケーションがコンテキストとして活用(ユーザー制御)
- URIベースの識別(file://、db://、api://など)
- サブスクリプションで変更通知を受信可能
- テキストまたはバイナリ(base64)コンテンツに対応
Tools(ツール) - 実行可能な関数
AIモデルが呼び出せる関数を定義します。JSON Schemaで入力パラメータを明示します。
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/list"
}
レスポンス:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"tools": [
{
"name": "get_weather",
"description": "Get current weather for a city",
"inputSchema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name"
}
},
"required": ["city"]
}
}
]
}
}
特徴:
- モデルが自律的に呼び出し(モデル制御)
- JSON Schemaでパラメータ型を検証
- 副作用(side effect)を持つ場合がある
- ユーザー確認が必要な場合がある(セキュリティ)
Prompts(プロンプトテンプレート) - 再利用可能なプロンプト
よく使うプロンプトパターンを事前に定義します。
{
"jsonrpc": "2.0",
"id": 4,
"method": "prompts/get",
"params": {
"name": "code_review",
"arguments": {
"language": "python"
}
}
}
レスポンス:
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"description": "Code review prompt for Python",
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Please review the following Python code for best practices, bugs, and performance issues."
}
}
]
}
}
1-5. Capability Negotiationの詳細
初期化時にクライアントとサーバーはそれぞれサポートする機能を交換します。サーバーは提供するプリミティブのみ宣言すればよいです。
サーバーCapabilities例(Toolsのみ):
{
"capabilities": {
"tools": {
"listChanged": true
}
}
}
このサーバーはToolsのみを提供し、ツール一覧が変更されると通知を送信できます。ResourcesやPromptsは提供しません。
全機能を提供するサーバー:
{
"capabilities": {
"tools": { "listChanged": true },
"resources": { "subscribe": true, "listChanged": true },
"prompts": { "listChanged": true },
"logging": {}
}
}
クライアントCapabilities例:
{
"capabilities": {
"roots": { "listChanged": true },
"sampling": {}
}
}
roots: クライアントが作業中のディレクトリ/プロジェクト情報を提供sampling: サーバーがクライアントのLLMを逆に呼び出し可能
1-6. Sampling:サーバーからLLMへの逆呼び出し
SamplingはMCPの独特な機能で、サーバーがクライアントのAIモデルに逆にリクエストを送信できます。
{
"jsonrpc": "2.0",
"id": 10,
"method": "sampling/createMessage",
"params": {
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Summarize this error log: Connection timeout after 30s..."
}
}
],
"maxTokens": 500,
"modelPreferences": {
"hints": [{ "name": "claude-sonnet-4-20250514" }]
}
}
}
活用シナリオ:
- サーバーが収集したログデータをAIに分析要求
- 複雑なデータをAIに要約要求
- エージェントワークフローでの中間判断要求
ただし、Samplingはセキュリティ上ユーザー確認が必須です。クライアントがHuman-in-the-loopで制御します。
2. Python FastMCPでMCPサーバーを作る(実習1)
FastMCPはPythonでMCPサーバーを最も簡単に構築できるフレームワークです。MCP Python SDKの公式高レベルAPIでもあります。
2-1. 天気MCPサーバー(最もシンプルな例)
インストール:
pip install fastmcp
weather_server.py:
from fastmcp import FastMCP
# MCPサーバーインスタンス作成
mcp = FastMCP("Weather Server")
@mcp.tool()
def get_weather(city: str) -> str:
"""Get current weather for a city.
Args:
city: Name of the city (e.g., Tokyo, Seoul, New York)
"""
# 実際には天気APIを呼び出しますが、ここではシミュレーション
weather_data = {
"Tokyo": "18 degrees, sunny",
"Seoul": "15 degrees, partly cloudy",
"New York": "10 degrees, rainy",
"London": "8 degrees, foggy",
}
result = weather_data.get(city, f"Weather data not available for {city}")
return f"Weather in {city}: {result}"
@mcp.tool()
def get_forecast(city: str, days: int = 3) -> str:
"""Get weather forecast for a city.
Args:
city: Name of the city
days: Number of days to forecast (1-7)
"""
if days < 1 or days > 7:
return "Days must be between 1 and 7"
return f"{days}-day forecast for {city}: Mostly sunny with temperatures 10-20C"
@mcp.resource("weather://cities")
def list_cities() -> str:
"""List all supported cities."""
cities = ["Tokyo", "Seoul", "New York", "London", "Paris"]
return "\n".join(cities)
if __name__ == "__main__":
mcp.run()
実行方法:
# 直接実行(stdioモード)
python weather_server.py
# またはFastMCP CLIで実行
fastmcp run weather_server.py
# MCP Inspectorでテスト
fastmcp dev weather_server.py
Claude Desktop連携(claude_desktop_config.json):
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/path/to/weather_server.py"]
}
}
}
Claude Code連携(.mcp.json):
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["weather_server.py"]
}
}
}
2-2. データベースMCPサーバー(実践)
実務で最も多く使われるDB連携MCPサーバーを構築しましょう。
import sqlite3
import json
from fastmcp import FastMCP
mcp = FastMCP("Database Server")
DB_PATH = "myapp.db"
def get_connection():
"""Get database connection."""
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
@mcp.resource("db://schema")
def get_schema() -> str:
"""Get the database schema information."""
conn = get_connection()
cursor = conn.cursor()
cursor.execute(
"SELECT sql FROM sqlite_master WHERE type='table' ORDER BY name"
)
tables = cursor.fetchall()
conn.close()
schema_lines = []
for table in tables:
if table[0]:
schema_lines.append(table[0])
return "\n\n".join(schema_lines)
@mcp.resource("db://tables")
def list_tables() -> str:
"""List all tables in the database."""
conn = get_connection()
cursor = conn.cursor()
cursor.execute(
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
)
tables = [row[0] for row in cursor.fetchall()]
conn.close()
return json.dumps(tables, indent=2)
@mcp.tool()
def query_database(sql: str) -> str:
"""Execute a read-only SQL query and return results.
Args:
sql: SQL SELECT query to execute
"""
# セキュリティ:SELECTのみ許可
if not sql.strip().upper().startswith("SELECT"):
return "Error: Only SELECT queries are allowed for safety"
conn = get_connection()
try:
cursor = conn.cursor()
cursor.execute(sql)
rows = cursor.fetchall()
if not rows:
return "No results found"
columns = [description[0] for description in cursor.description]
results = []
for row in rows:
results.append(dict(zip(columns, row)))
return json.dumps(results, indent=2, ensure_ascii=False)
except Exception as e:
return f"Query error: {str(e)}"
finally:
conn.close()
@mcp.tool()
def insert_record(table: str, data: str) -> str:
"""Insert a record into a table.
Args:
table: Table name
data: JSON string of column-value pairs
"""
try:
record = json.loads(data)
except json.JSONDecodeError:
return "Error: Invalid JSON data"
columns = ", ".join(record.keys())
placeholders = ", ".join(["?" for _ in record])
values = list(record.values())
conn = get_connection()
try:
cursor = conn.cursor()
cursor.execute(
f"INSERT INTO {table} ({columns}) VALUES ({placeholders})",
values,
)
conn.commit()
return f"Successfully inserted record with id {cursor.lastrowid}"
except Exception as e:
return f"Insert error: {str(e)}"
finally:
conn.close()
@mcp.tool()
def update_record(table: str, record_id: int, data: str) -> str:
"""Update a record in a table.
Args:
table: Table name
record_id: ID of the record to update
data: JSON string of column-value pairs to update
"""
try:
updates = json.loads(data)
except json.JSONDecodeError:
return "Error: Invalid JSON data"
set_clause = ", ".join([f"{k} = ?" for k in updates.keys()])
values = list(updates.values()) + [record_id]
conn = get_connection()
try:
cursor = conn.cursor()
cursor.execute(
f"UPDATE {table} SET {set_clause} WHERE id = ?", values
)
conn.commit()
return f"Successfully updated {cursor.rowcount} record(s)"
except Exception as e:
return f"Update error: {str(e)}"
finally:
conn.close()
@mcp.tool()
def delete_record(table: str, record_id: int) -> str:
"""Delete a record from a table.
Args:
table: Table name
record_id: ID of the record to delete
"""
conn = get_connection()
try:
cursor = conn.cursor()
cursor.execute(f"DELETE FROM {table} WHERE id = ?", [record_id])
conn.commit()
return f"Successfully deleted {cursor.rowcount} record(s)"
except Exception as e:
return f"Delete error: {str(e)}"
finally:
conn.close()
if __name__ == "__main__":
mcp.run()
このサーバーは以下を提供します:
- Resources: DBスキーマ情報とテーブル一覧
- Tools: SELECTクエリ、INSERT、UPDATE、DELETE操作
Claudeに「usersテーブルから最近登録した10人を表示して」と言えば、自動的にquery_databaseツールを呼び出します。
2-3. ファイルシステムMCPサーバー
import os
from pathlib import Path
from fastmcp import FastMCP
mcp = FastMCP("Filesystem Server")
# セキュリティ:許可されたディレクトリのみアクセス可能
ALLOWED_DIRS = [
Path("/Users/dev/projects"),
Path("/Users/dev/documents"),
]
def is_path_allowed(path: str) -> bool:
"""Check if the path is within allowed directories."""
resolved = Path(path).resolve()
return any(
resolved == allowed or allowed in resolved.parents
for allowed in ALLOWED_DIRS
)
@mcp.tool()
def read_file(path: str) -> str:
"""Read contents of a file.
Args:
path: Absolute file path
"""
if not is_path_allowed(path):
return "Error: Access denied. Path is outside allowed directories."
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
return f"Error: File not found: {path}"
except UnicodeDecodeError:
return "Error: File is not a text file"
@mcp.tool()
def write_file(path: str, content: str) -> str:
"""Write content to a file.
Args:
path: Absolute file path
content: Content to write
"""
if not is_path_allowed(path):
return "Error: Access denied. Path is outside allowed directories."
try:
with open(path, "w", encoding="utf-8") as f:
f.write(content)
return f"Successfully wrote to {path}"
except Exception as e:
return f"Error writing file: {str(e)}"
@mcp.tool()
def list_directory(path: str) -> str:
"""List contents of a directory.
Args:
path: Absolute directory path
"""
if not is_path_allowed(path):
return "Error: Access denied. Path is outside allowed directories."
try:
entries = []
for entry in sorted(Path(path).iterdir()):
entry_type = "dir" if entry.is_dir() else "file"
size = entry.stat().st_size if entry.is_file() else 0
entries.append(f"[{entry_type}] {entry.name} ({size} bytes)")
return "\n".join(entries)
except FileNotFoundError:
return f"Error: Directory not found: {path}"
@mcp.tool()
def search_files(directory: str, pattern: str) -> str:
"""Search for files matching a glob pattern.
Args:
directory: Directory to search in
pattern: Glob pattern (e.g., '*.py', '**/*.md')
"""
if not is_path_allowed(directory):
return "Error: Access denied."
matches = list(Path(directory).glob(pattern))
if not matches:
return "No files found matching the pattern"
return "\n".join(str(m) for m in matches[:50])
@mcp.resource("fs://allowed-dirs")
def get_allowed_dirs() -> str:
"""List allowed directories."""
return "\n".join(str(d) for d in ALLOWED_DIRS)
if __name__ == "__main__":
mcp.run()
重要なセキュリティポイント:
ALLOWED_DIRSでアクセス可能なディレクトリを制限Path.resolve()でシンボリックリンクのバイパスを防止- 親ディレクトリ探索(
../)をブロック
3. TypeScript SDKでMCPサーバーを作る(実習2)
TypeScriptはMCPサーバー開発でPythonの次に多く使われています。公式の@modelcontextprotocol/sdkパッケージが提供されています。
3-1. 基本構造
インストール:
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"declaration": true
},
"include": ["src/**/*"]
}
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 MCP Server',
version: '1.0.0',
})
// Tool登録
server.tool(
'hello',
'Say hello to someone',
{
name: z.string().describe('Name of the person'),
},
async ({ name }) => {
return {
content: [
{
type: 'text',
text: `Hello, ${name}! Welcome to MCP.`,
},
],
}
}
)
// Resource登録
server.resource('info', 'server://info', async (uri) => {
return {
contents: [
{
uri: uri.href,
mimeType: 'text/plain',
text: 'This is My MCP Server v1.0.0',
},
],
}
})
// Transport接続と実行
async function main() {
const transport = new StdioServerTransport()
await server.connect(transport)
console.error('MCP Server running on stdio')
}
main().catch(console.error)
実行:
npx tsc
node dist/index.js
Zodを使ったパラメータ検証がTypeScript SDKの核心です。型安全性と自動JSON Schema生成の両方を実現します。
3-2. GitHub Issue MCPサーバー(実践)
実践で役立つGitHub Issue管理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: 'GitHub Issue Server',
version: '1.0.0',
})
const GITHUB_TOKEN = process.env.GITHUB_TOKEN
const BASE_URL = 'https://api.github.com'
// 共通ヘッダー
function getHeaders() {
return {
Authorization: `Bearer ${GITHUB_TOKEN}`,
Accept: 'application/vnd.github.v3+json',
'Content-Type': 'application/json',
'User-Agent': 'MCP-GitHub-Server',
}
}
// Tool: Issue一覧取得
server.tool(
'list_issues',
'List issues in a GitHub repository',
{
owner: z.string().describe('Repository owner'),
repo: z.string().describe('Repository name'),
state: z.enum(['open', 'closed', 'all']).default('open').describe('Issue state filter'),
labels: z.string().optional().describe('Comma-separated label names'),
per_page: z.number().min(1).max(100).default(10).describe('Results per page'),
},
async ({ owner, repo, state, labels, per_page }) => {
const params = new URLSearchParams({
state,
per_page: String(per_page),
})
if (labels) params.set('labels', labels)
const response = await fetch(`${BASE_URL}/repos/${owner}/${repo}/issues?${params}`, {
headers: getHeaders(),
})
if (!response.ok) {
return {
content: [
{
type: 'text' as const,
text: `Error: ${response.status} ${response.statusText}`,
},
],
}
}
const issues = await response.json()
const formatted = issues.map(
(issue: Record<string, unknown>) =>
`#${issue.number} [${issue.state}] ${issue.title}\n Labels: ${
(issue.labels as Array<Record<string, string>>).map((l) => l.name).join(', ') || 'none'
}\n Created: ${issue.created_at}`
)
return {
content: [
{
type: 'text' as const,
text: formatted.join('\n\n') || 'No issues found',
},
],
}
}
)
// Tool: Issue作成
server.tool(
'create_issue',
'Create a new issue in a GitHub repository',
{
owner: z.string().describe('Repository owner'),
repo: z.string().describe('Repository name'),
title: z.string().describe('Issue title'),
body: z.string().optional().describe('Issue body (Markdown)'),
labels: z.array(z.string()).optional().describe('Labels to add'),
assignees: z.array(z.string()).optional().describe('Usernames to assign'),
},
async ({ owner, repo, title, body, labels, assignees }) => {
const response = await fetch(`${BASE_URL}/repos/${owner}/${repo}/issues`, {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify({ title, body, labels, assignees }),
})
if (!response.ok) {
const error = await response.text()
return {
content: [
{
type: 'text' as const,
text: `Error creating issue: ${response.status} - ${error}`,
},
],
}
}
const issue = await response.json()
return {
content: [
{
type: 'text' as const,
text: `Created issue #${issue.number}: ${issue.title}\nURL: ${issue.html_url}`,
},
],
}
}
)
// Tool: Issueクローズ
server.tool(
'close_issue',
'Close an issue in a GitHub repository',
{
owner: z.string().describe('Repository owner'),
repo: z.string().describe('Repository name'),
issue_number: z.number().describe('Issue number'),
comment: z.string().optional().describe('Optional closing comment'),
},
async ({ owner, repo, issue_number, comment }) => {
if (comment) {
await fetch(`${BASE_URL}/repos/${owner}/${repo}/issues/${issue_number}/comments`, {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify({ body: comment }),
})
}
const response = await fetch(`${BASE_URL}/repos/${owner}/${repo}/issues/${issue_number}`, {
method: 'PATCH',
headers: getHeaders(),
body: JSON.stringify({ state: 'closed' }),
})
if (!response.ok) {
return {
content: [
{
type: 'text' as const,
text: `Error closing issue: ${response.status}`,
},
],
}
}
return {
content: [
{
type: 'text' as const,
text: `Closed issue #${issue_number} successfully`,
},
],
}
}
)
// Resource: リポジトリ情報
server.resource('repo-info', 'github://repo-info', async (uri) => {
return {
contents: [
{
uri: uri.href,
mimeType: 'text/plain',
text: 'GitHub Issue MCP Server - Manage issues via AI',
},
],
}
})
// Prompt: Issueトリアージ
server.prompt(
'triage_issues',
'Generate a prompt for triaging open issues',
{ owner: z.string(), repo: z.string() },
({ owner, repo }) => ({
messages: [
{
role: 'user' as const,
content: {
type: 'text' as const,
text: `Please triage the open issues in ${owner}/${repo}. For each issue:
1. Assess priority (critical/high/medium/low)
2. Suggest appropriate labels
3. Recommend if it should be assigned to someone
4. Provide a brief action plan`,
},
},
],
})
)
async function main() {
const transport = new StdioServerTransport()
await server.connect(transport)
console.error('GitHub Issue MCP Server running')
}
main().catch(console.error)
package.json:
{
"name": "github-issue-mcp",
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.0",
"zod": "^3.24.0"
},
"devDependencies": {
"typescript": "^5.7.0",
"@types/node": "^22.0.0"
}
}
Claude Desktop連携:
{
"mcpServers": {
"github-issues": {
"command": "node",
"args": ["/path/to/github-issue-mcp/dist/index.js"],
"env": {
"GITHUB_TOKEN": "ghp_your_token_here"
}
}
}
}
4. MCPサーバーのテストとデバッグ
4-1. MCP Inspector
MCP InspectorはMCPサーバーをローカルでテストできる公式ツールです。
# npxで直接実行
npx @modelcontextprotocol/inspector
# 特定のサーバーを指定して実行
npx @modelcontextprotocol/inspector python weather_server.py
# FastMCPのdevモード(Inspector自動接続)
fastmcp dev weather_server.py
Inspectorが提供する機能:
- サーバーのすべてのツール、リソース、プロンプトを確認
- ツールを直接呼び出して結果を確認
- JSON-RPCメッセージログをリアルタイムで確認
- 接続状態をモニタリング
4-2. Claude Desktopでの連携
macOSでの設定ファイルの場所:
# 設定ファイルを開く
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
Windows:
%APPDATA%\Claude\claude_desktop_config.json
設定例:
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/absolute/path/to/weather_server.py"]
},
"database": {
"command": "python",
"args": ["/absolute/path/to/db_server.py"],
"env": {
"DB_PATH": "/data/myapp.db"
}
},
"github": {
"command": "node",
"args": ["/absolute/path/to/github-mcp/dist/index.js"],
"env": {
"GITHUB_TOKEN": "ghp_xxx"
}
}
}
}
4-3. Claude Codeでの連携
プロジェクトルートに.mcp.jsonファイルを作成します:
{
"mcpServers": {
"my-server": {
"command": "python",
"args": ["./tools/my_mcp_server.py"]
}
}
}
またはグローバル設定:
claude mcp add my-server python ./tools/my_mcp_server.py
4-4. ロギング戦略
MCPサーバーでデバッグログはstderrに出力します。stdoutはJSON-RPCメッセージ専用だからです。
Python:
import sys
import logging
# stderrへのログ出力設定
logging.basicConfig(
level=logging.DEBUG,
stream=sys.stderr,
format="%(asctime)s [%(levelname)s] %(message)s",
)
logger = logging.getLogger("my-mcp-server")
@mcp.tool()
def my_tool(param: str) -> str:
logger.debug(f"Tool called with param: {param}")
try:
result = do_something(param)
logger.info(f"Tool succeeded: {result}")
return result
except Exception as e:
logger.error(f"Tool failed: {e}")
return f"Error: {str(e)}"
TypeScript:
// stderrへのログ出力
function log(level: string, message: string) {
console.error(`[${new Date().toISOString()}] [${level}] ${message}`)
}
server.tool('my_tool', 'Description', { param: z.string() }, async ({ param }) => {
log('DEBUG', `Tool called with: ${param}`)
try {
const result = await doSomething(param)
log('INFO', `Success: ${result}`)
return { content: [{ type: 'text', text: result }] }
} catch (error) {
log('ERROR', `Failed: ${error}`)
return { content: [{ type: 'text', text: `Error: ${error}` }] }
}
})
4-5. エラーハンドリングパターン
from fastmcp import FastMCP
from fastmcp.exceptions import ToolError
mcp = FastMCP("Robust Server")
@mcp.tool()
def safe_operation(param: str) -> str:
"""A tool with proper error handling."""
# 入力検証
if not param or len(param) > 1000:
raise ToolError("Parameter must be 1-1000 characters")
try:
result = external_api_call(param)
return result
except ConnectionError:
raise ToolError("Failed to connect to external service. Please try again.")
except TimeoutError:
raise ToolError("Request timed out. The service may be temporarily unavailable.")
except Exception as e:
# 予想外のエラーは詳細情報を隠して一般メッセージのみ返却
logging.error(f"Unexpected error: {e}", exc_info=True)
raise ToolError("An unexpected error occurred. Check server logs for details.")
5. MCPサーバーのデプロイ
5-1. Cloudflare Workersへのデプロイ
Cloudflare Workersを使えば、サーバーレスでMCPサーバーをグローバルにデプロイできます。
# プロジェクト作成
npm create cloudflare@latest -- my-mcp-server
cd my-mcp-server
npm install @modelcontextprotocol/sdk
src/index.ts(Workers用):
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
export default {
async fetch(request: Request): Promise<Response> {
const server = new McpServer({
name: 'Cloudflare MCP Server',
version: '1.0.0',
})
// Tools登録
server.tool('hello', 'Say hello', { name: z.string() }, async ({ name }) => ({
content: [{ type: 'text', text: `Hello ${name} from the edge!` }],
}))
// Streamable HTTP transportの処理
// 実際にはMCP SDKのHTTP transport handlerを使用
return new Response('MCP Server Running', { status: 200 })
},
}
# デプロイ
npx wrangler deploy
5-2. Dockerコンテナ化
FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY dist/ ./dist/
# MCPサーバーはstdioで通信
CMD ["node", "dist/index.js"]
docker build -t my-mcp-server .
docker run -i my-mcp-server
Claude DesktopからDockerサーバーに接続:
{
"mcpServers": {
"my-server": {
"command": "docker",
"args": ["run", "-i", "--rm", "my-mcp-server"]
}
}
}
5-3. npm/PyPIパッケージとして配布
npmパッケージ:
{
"name": "@myorg/mcp-weather",
"version": "1.0.0",
"bin": {
"mcp-weather": "./dist/index.js"
}
}
npm publish --access public
ユーザーはすぐに使えます:
{
"mcpServers": {
"weather": {
"command": "npx",
"args": ["-y", "@myorg/mcp-weather"]
}
}
}
PyPIパッケージ:
pip install build twine
python -m build
twine upload dist/*
{
"mcpServers": {
"weather": {
"command": "uvx",
"args": ["mcp-weather"]
}
}
}
5-4. Smitheryマーケットプレイス
Smithery(smithery.ai)はMCPサーバー専用のレジストリです。登録プロセス:
- GitHubリポジトリにMCPサーバーコードをプッシュ
smithery.yaml設定ファイルを追加- Smitheryウェブサイトでリポジトリを接続
- 自動ビルド・デプロイ
# smithery.yaml
name: my-weather-mcp
description: Weather MCP server for AI assistants
icon: cloud-sun
startCommand:
type: stdio
configSchema:
type: object
properties:
API_KEY:
type: string
description: Weather API key
required:
- API_KEY
commandFunction:
- node
- dist/index.js
5-5. OAuth 2.0認証の追加
リモートMCPサーバーには認証が必須です。2025年6月のスペックでOAuth 2.0サポートが公式化されました。
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
const server = new McpServer({
name: 'Authenticated Server',
version: '1.0.0',
})
// OAuth 2.0ミドルウェア(Streamable HTTP transport)
async function validateToken(token: string): Promise<boolean> {
const response = await fetch('https://auth.example.com/introspect', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `token=${token}`,
})
const data = await response.json()
return data.active === true
}
// HTTPハンドラーで認証確認
async function handleRequest(req: Request): Promise<Response> {
const authHeader = req.headers.get('Authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return new Response('Unauthorized', { status: 401 })
}
const token = authHeader.slice(7)
if (!(await validateToken(token))) {
return new Response('Invalid token', { status: 403 })
}
// MCPリクエスト処理を続行...
return new Response('OK')
}
6. MCPエコシステム100+サーバー総整理
2026年3月現在、MCPエコシステムは爆発的に成長しています。カテゴリ別に主要サーバーを整理します。
6-1. データと分析
| サーバー | 機能 | 活用 |
|---|---|---|
| Grafana MCP | ダッシュボード照会、メトリクスクエリ、アラート管理 | AIでモニタリング自動化 |
| PostgreSQL MCP | SQLクエリ、スキーマ探索、データ分析 | 自然言語からSQLへ変換 |
| MongoDB MCP | コレクションCRUD、集約パイプライン | NoSQLデータ探索 |
| ClickHouse MCP | 分析クエリ、ログ照会 | ログベースの問題解決 |
| BigQuery MCP | 大規模データ分析 | データチームの生産性向上 |
| MySQL MCP | SQLクエリ、テーブル管理 | RDBMS直接操作 |
| Redis MCP | キャッシュ照会、キーバリュー管理 | キャッシュデバッグ |
| Elasticsearch MCP | 全文検索、インデックス管理 | ログ分析自動化 |
| Snowflake MCP | データウェアハウスクエリ | ビジネスインテリジェンス |
| DuckDB MCP | ローカル分析クエリ | 高速データ探索 |
| Prometheus MCP | メトリクスクエリ(PromQL) | システムモニタリング |
| Datadog MCP | メトリクス、ダッシュボード、アラート管理 | 統合オブザーバビリティ |
Grafana MCP活用例:
ユーザー:「過去24時間でCPU使用率が80%を超えたサーバーを見つけて」
Claude:[Grafana MCPのquery_metricsツールを呼び出し]
-> PromQL: avg by (instance)(cpu_usage_percent > 80)
-> 結果: server-03(平均87%)、server-07(平均92%)
6-2. 開発ツール
| サーバー | 機能 | 活用 |
|---|---|---|
| GitHub MCP | Issue、PR、Actions、コード検索 | コードレビュー自動化 |
| GitLab MCP | パイプライン、マージリクエスト | CI/CD管理 |
| Jira MCP | Issue追跡、スプリント管理 | PM業務自動化 |
| Linear MCP | Issue/プロジェクト管理 | モダンIssueトラッカー連携 |
| Sentry MCP | エラー追跡、パフォーマンスモニタリング | デバッグ自動化 |
| CircleCI MCP | CI/CDパイプライン管理 | ビルドモニタリング |
| npm MCP | パッケージ検索、バージョン確認 | 依存関係管理 |
| Snyk MCP | セキュリティ脆弱性スキャン | セキュリティ監査自動化 |
| SonarQube MCP | コード品質分析 | コードレビュー補助 |
| Postman MCP | APIテスト、コレクション管理 | API開発効率化 |
6-3. ドキュメントと可視化
| サーバー | 機能 | 活用 |
|---|---|---|
| Mermaid MCP | ダイアグラム生成(flowchart、sequence、ER) | ドキュメント自動化 |
| PPT/Slides MCP | プレゼンテーション作成/編集 | レポート自動化 |
| Notion MCP | ページ、データベースCRUD | ナレッジ管理 |
| Google Docs MCP | ドキュメント読み取り/編集 | 協働ドキュメント自動化 |
| Confluence MCP | Wikiページ管理 | 技術文書化 |
| Excalidraw MCP | ホワイトボードダイアグラム | アーキテクチャダイアグラム |
| Google Sheets MCP | スプレッドシート読み取り/編集 | データレポート |
| Obsidian MCP | マークダウンノート管理 | 個人ナレッジベース |
6-4. コミュニケーション
| サーバー | 機能 | 活用 |
|---|---|---|
| Slack MCP | チャネルメッセージ、検索、DM | チームコミュニケーション自動化 |
| Discord MCP | サーバー/チャネル管理 | コミュニティ管理 |
| Email MCP | メール読み取り/送信/検索 | メール自動化 |
| Microsoft Teams MCP | チャット、ミーティング管理 | エンタープライズ協業 |
| Telegram MCP | ボットメッセージ、チャネル管理 | 通知自動化 |
| Twilio MCP | SMS、音声通話 | 顧客コミュニケーション |
6-5. クラウドとインフラ
| サーバー | 機能 | 活用 |
|---|---|---|
| AWS MCP | S3、Lambda、EC2管理 | インフラ自動化 |
| Kubernetes MCP | クラスター管理、Pod照会 | K8s運用自動化 |
| Docker MCP | コンテナ/イメージ管理 | 開発環境管理 |
| Terraform MCP | IaC実行、状態照会 | インフラコード生成 |
| Vercel MCP | デプロイ、環境変数、ログ | フロントエンドデプロイ |
| GCP MCP | Google Cloudサービス管理 | GCPインフラ自動化 |
| Azure MCP | Azureサービス管理 | エンタープライズクラウド |
| Cloudflare MCP | Workers、DNS、CDN | エッジコンピューティング管理 |
| Pulumi MCP | IaC(プログラミング言語) | コードベースのインフラ |
| Ansible MCP | 構成管理、自動化 | サーバープロビジョニング |
6-6. AIとML
| サーバー | 機能 | 活用 |
|---|---|---|
| HuggingFace MCP | モデル検索、推論 | MLワークフロー |
| Weights and Biases MCP | 実験追跡、メトリクス | ML実験管理 |
| LangChain MCP | チェーン/エージェント実行 | AIパイプライン |
| Ollama MCP | ローカルLLM管理 | プライベートAI実行 |
| Replicate MCP | クラウドモデル実行 | MLモデルデプロイ |
| Pinecone MCP | ベクトルDB管理 | RAG構築 |
| Weaviate MCP | ベクトル検索 | セマンティック検索 |
| ChromaDB MCP | ローカルベクトルDB | エンベディング管理 |
6-7. ブラウザとスクレイピング
| サーバー | 機能 | 活用 |
|---|---|---|
| Playwright MCP | ブラウザ自動化、スクリーンショット | Webテスト |
| Puppeteer MCP | Chrome自動化 | クローリング |
| Brave Search MCP | Web検索API | AIにリアルタイム情報提供 |
| Firecrawl MCP | Webサイトクローリング、マークダウン変換 | RAGデータ収集 |
| Browserbase MCP | クラウドブラウザ | 大規模Web自動化 |
| Exa MCP | セマンティックWeb検索 | 高品質検索結果 |
| Tavily MCP | AI最適化検索 | リサーチ自動化 |
6-8. ファイルとストレージ
| サーバー | 機能 | 活用 |
|---|---|---|
| Filesystem MCP | ローカルファイル読み取り/書き込み | ファイル管理 |
| Google Drive MCP | ファイル検索/ダウンロード | クラウドファイル管理 |
| S3 MCP | バケット/オブジェクト管理 | クラウドストレージ |
| Dropbox MCP | ファイル同期、共有 | ファイル協業 |
| OneDrive MCP | Microsoftファイル管理 | エンタープライズファイル |
| Box MCP | エンタープライズファイル管理 | エンタープライズコンテンツ管理 |
6-9. デザインとメディア
| サーバー | 機能 | 活用 |
|---|---|---|
| Figma MCP | デザインファイル照会、コンポーネント抽出 | デザインからコードへ変換 |
| Canva MCP | デザイン作成/編集 | マーケティング資料 |
| FFmpeg MCP | オーディオ/ビデオ変換 | メディア処理 |
| Sharp MCP | 画像処理 | 画像最適化 |
6-10. その他の特化サーバー
| サーバー | 機能 | 活用 |
|---|---|---|
| Stripe MCP | 決済、サブスクリプション管理 | 決済システム |
| Shopify MCP | ストア、商品管理 | Eコマース |
| SendGrid MCP | メール配信 | マーケティングメール |
| Airtable MCP | スプレッドシートDB | ノーコードデータ管理 |
| Supabase MCP | BaaS(DB、Auth、Storage) | フルスタック開発 |
| Firebase MCP | Google BaaS | モバイル/Webバックエンド |
| Neon MCP | サーバーレスPostgreSQL | データベース管理 |
| PlanetScale MCP | サーバーレスMySQL | スケーラブルDB |
| Turso MCP | エッジSQLite | 分散データベース |
7. オリジナルMCPサーバーアイデア10選
MCPサーバーを自作すれば、AIの活用範囲が大きく広がります。以下は実用的なMCPサーバーアイデアです。
7-1. 個人ブログMCP
MDXファイルベースのブログを管理するMCPサーバーです。
from fastmcp import FastMCP
import glob
import yaml
mcp = FastMCP("Blog Manager")
BLOG_DIR = "/path/to/blog/data"
@mcp.tool()
def list_posts(tag: str = "") -> str:
"""List all blog posts, optionally filtered by tag."""
posts = glob.glob(f"{BLOG_DIR}/**/*.mdx", recursive=True)
results = []
for post_path in sorted(posts, reverse=True):
with open(post_path) as f:
content = f.read()
if content.startswith("---"):
fm_end = content.index("---", 3)
frontmatter = yaml.safe_load(content[3:fm_end])
if tag and tag not in frontmatter.get("tags", []):
continue
results.append(
f"- {frontmatter['title']} ({frontmatter['date']})"
)
return "\n".join(results[:20])
@mcp.tool()
def create_post(title: str, tags: str, content: str) -> str:
"""Create a new blog post."""
# 実装...
return "Post created successfully"
7-2. 株式/暗号通貨レートMCP
リアルタイムレートを照会し、ポートフォリオを分析します。
7-3. カレンダー/スケジュールMCP
GoogleカレンダーやAppleカレンダーと連携してスケジュールを管理します。
7-4. CI/CDパイプラインモニタリングMCP
GitHub Actions、Jenkins、CircleCIパイプラインの状態をモニタリングします。
7-5. Kubernetesログ分析MCP
kubectlをラップしてPodログを分析し、異常を検知します。
7-6. Figmaデザイン抽出MCP
Figma APIでデザインコンポーネントを抽出しコードに変換します。
7-7. 翻訳MCP
DeepLやGoogle Translate APIで多言語翻訳を提供します。
7-8. 個人メモ/WikiMCP
ObsidianやLogseqのボールトをAIが探索・編集できるようにします。
7-9. ヘルスケアデータMCP
Apple HealthやFitbitのデータを分析します。
7-10. スマートホームIoT制御MCP
Home Assistant APIで照明、温度、セキュリティシステムを制御します。
from fastmcp import FastMCP
import httpx
mcp = FastMCP("Smart Home")
HA_URL = "http://homeassistant.local:8123"
HA_TOKEN = "your_long_lived_token"
@mcp.tool()
def control_light(entity_id: str, action: str) -> str:
"""Control a smart light.
Args:
entity_id: Home Assistant entity ID (e.g., light.living_room)
action: 'on' or 'off'
"""
service = "turn_on" if action == "on" else "turn_off"
response = httpx.post(
f"{HA_URL}/api/services/light/{service}",
headers={"Authorization": f"Bearer {HA_TOKEN}"},
json={"entity_id": entity_id},
)
return f"Light {entity_id}: {action} (status: {response.status_code})"
@mcp.tool()
def get_temperature(entity_id: str) -> str:
"""Get temperature from a sensor."""
response = httpx.get(
f"{HA_URL}/api/states/{entity_id}",
headers={"Authorization": f"Bearer {HA_TOKEN}"},
)
data = response.json()
return f"Temperature: {data['state']}{data['attributes'].get('unit_of_measurement', '')}"
8. MCPセキュリティベストプラクティス
MCPサーバーはAIにシステムアクセス権限を付与するため、セキュリティが非常に重要です。
8-1. 入力検証
Python(Pydantic):
from pydantic import BaseModel, Field, validator
class QueryInput(BaseModel):
sql: str = Field(..., max_length=5000)
timeout: int = Field(default=30, ge=1, le=300)
@validator("sql")
def validate_sql(cls, v):
forbidden = ["DROP", "DELETE", "TRUNCATE", "ALTER"]
upper = v.upper()
for keyword in forbidden:
if keyword in upper:
raise ValueError(f"Forbidden SQL keyword: {keyword}")
return v
TypeScript(Zod):
const querySchema = z.object({
sql: z
.string()
.max(5000)
.refine(
(sql) => {
const forbidden = ['DROP', 'DELETE', 'TRUNCATE', 'ALTER']
const upper = sql.toUpperCase()
return !forbidden.some((kw) => upper.includes(kw))
},
{ message: 'Forbidden SQL keyword detected' }
),
timeout: z.number().min(1).max(300).default(30),
})
8-2. 最小権限の原則
- DBサーバー:読み取り専用アカウントを使用(SELECTのみ許可)
- ファイルサーバー:許可ディレクトリの制限、シンボリックリンクの解決
- APIサーバー:必要なスコープのみのトークンを使用
- ネットワーク:内部ネットワークアクセスをブロック
8-3. レートリミット
import time
from collections import defaultdict
class RateLimiter:
def __init__(self, max_calls: int = 10, window: int = 60):
self.max_calls = max_calls
self.window = window
self.calls = defaultdict(list)
def check(self, key: str) -> bool:
now = time.time()
self.calls[key] = [
t for t in self.calls[key] if now - t < self.window
]
if len(self.calls[key]) >= self.max_calls:
return False
self.calls[key].append(now)
return True
limiter = RateLimiter(max_calls=20, window=60)
@mcp.tool()
def rate_limited_tool(param: str) -> str:
if not limiter.check("default"):
return "Rate limit exceeded. Please wait before trying again."
return do_work(param)
8-4. シークレット管理
import os
# 環境変数からシークレットをロード(ハードコーディング禁止)
DB_PASSWORD = os.environ.get("DB_PASSWORD")
API_KEY = os.environ.get("API_KEY")
if not DB_PASSWORD or not API_KEY:
raise RuntimeError("Required environment variables not set")
Claude Desktop設定で環境変数を渡す:
{
"mcpServers": {
"my-server": {
"command": "python",
"args": ["server.py"],
"env": {
"DB_PASSWORD": "secret_from_keychain",
"API_KEY": "sk-xxx"
}
}
}
}
8-5. 監査ログ
import json
import sys
from datetime import datetime
def audit_log(tool_name: str, params: dict, result: str, success: bool):
log_entry = {
"timestamp": datetime.utcnow().isoformat(),
"tool": tool_name,
"params": params,
"success": success,
"result_length": len(result),
}
print(json.dumps(log_entry), file=sys.stderr)
8-6. サンドボックス化
DockerやVMを使ってMCPサーバーを分離します:
# docker-compose.yml
services:
mcp-server:
build: .
read_only: true
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:size=100M
mem_limit: 512m
cpus: '0.5'
networks:
- mcp-net
volumes:
- ./allowed-data:/data:ro
networks:
mcp-net:
driver: bridge
internal: true # 外部インターネットアクセスをブロック
9. MCPの未来:2026年展望
9-1. Agent-to-Agent MCP通信
現在のMCPは主に人間(ユーザー)-AI間のツール接続に焦点を当てています。2026年にはAIエージェント同士がMCPを通じて直接通信するシナリオが拡大するでしょう。
例えば:
- コードレビューエージェントがセキュリティ分析エージェントにMCPで脆弱性スキャンを要求
- データパイプラインエージェントがモニタリングエージェントに異常検知を確認
- カスタマーサポートエージェントが注文管理エージェントに注文状況を照会
9-2. マルチモーダルMCP
現在のMCPはテキスト中心ですが、画像、オーディオ、ビデオをやり取りするマルチモーダル拡張が進行中です。
- 画像リソース:スクリーンショット、チャート、デザインファイルをAIに直接配信
- オーディオ処理:会議の録音をAIが分析
- ビデオ分析:セキュリティカメラの映像をAIがモニタリング
9-3. MCP Registry(中央サーバーレジストリ)
npmやPyPIのように、MCPサーバーを中央で検索・インストールできる公式レジストリが構築されています。現在はSmitheryがこの役割を果たしていますが、より標準化されたレジストリが登場する予定です。
9-4. Enterprise MCP Gateway
企業環境ではMCPサーバーを中央で管理するゲートウェイが必要です:
- アクセス制御:誰がどのMCPサーバーを使用できるか
- 監査ログ:すべてのツール呼び出しを記録
- コスト管理:API呼び出し回数の制限
- セキュリティポリシー:機密データへのアクセス制御
9-5. Linux Foundation AAIFガバナンス
2025年末にMCPがLinux FoundationのAI and AI Foundation(AAIF)に寄贈されました。これはMCPのガバナンスが特定の企業(Anthropic)からオープンコミュニティに移行することを意味します。
期待される効果:
- より透明なスペック発展プロセス
- 多様な企業の参加促進
- 相互運用性標準の強化
- 認定プログラムの導入可能性
10. クイズ
これまで学んだ内容を確認しましょう。
Q1. MCPの基盤プロトコルは何で、メッセージの3種類は?
正解: MCPはJSON-RPC 2.0ベースです。メッセージの種類は以下の3つです:
- Request: idを持つリクエスト(レスポンスを期待)
- Response: リクエストに対する結果を返却
- Notification: idを持たない一方向の通知
Q2. MCPの2つのTransport方式とそれぞれの主な使用場面は?
正解:
- stdio: ローカルプロセスのstdin/stdoutで通信。Claude DesktopやClaude Codeでローカルサーバーを実行する際に使用。
- Streamable HTTP: HTTP POSTとSSEで通信。リモートサーバーデプロイ、マルチテナント環境で使用。
Q3. MCPの3つのコアプリミティブ(Resources、Tools、Prompts)の違いは?
正解:
- Resources: URIベースの読み取り専用データ。アプリケーションがコンテキストとして活用。(ユーザー制御)
- Tools: AIモデルが呼び出す実行可能な関数。JSON Schemaでパラメータ定義。副作用あり得る。(モデル制御)
- Prompts: 事前定義された再利用可能なプロンプトテンプレート。(ユーザー制御)
Q4. FastMCPでMCPツール(Tool)を定義する最もシンプルな方法は?
正解: @mcp.tool()デコレーターを使用します。関数のdocstringがツールの説明になり、型ヒントがJSON Schemaに自動変換されます。
from fastmcp import FastMCP
mcp = FastMCP("My Server")
@mcp.tool()
def greet(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"
Q5. MCPサーバーセキュリティで最も重要な5つの原則は?
正解:
- 入力検証: Zod/Pydanticですべてのパラメータを検証
- 最小権限: 必要な権限のみ付与(読み取り専用DBアカウントなど)
- レートリミット: API呼び出し回数を制限
- シークレット管理: 環境変数を使用、ハードコーディング禁止
- 監査ログ: すべてのツール呼び出しを記録
加えて、サンドボックス化(Docker分離)とOAuth 2.0認証も重要です。
11. 参考資料
- MCP公式仕様 - Model Context Protocolの全スペック
- MCP公式ドキュメント - 入門ガイド、チュートリアル
- MCP GitHubリポジトリ - SDK、リファレンスサーバー
- FastMCPドキュメント - Python FastMCPフレームワーク
- MCP TypeScript SDK - TypeScript SDK
- MCP Python SDK - Python SDK
- Awesome MCP Servers - コミュニティMCPサーバーリスト
- Smithery - MCPサーバーレジストリ
- MCP Inspector - MCPサーバーテストツール
- Claude Desktop MCP設定ガイド - Claude Desktop連携
- Claude Code MCP設定 - Claude Code連携
- Grafana MCP Server - Grafana公式MCPサーバー
- GitHub MCP Server - GitHub公式MCPサーバー
- PostgreSQL MCP Server - PostgreSQL MCP
- Playwright MCP Server - Microsoft Playwright MCP
- Slack MCP Server - Slack MCP
- Filesystem MCP Server - ファイルシステムMCP
- Cloudflare MCP Workers - Cloudflareデプロイガイド
- MCP OAuth 2.0仕様 - 認証スペック
- Linux Foundation AAIF - MCPガバナンス
- Anthropic MCPブログ - MCP発表ブログ
- OpenAI MCPサポート発表 - OpenAIのMCP採用
- Google MCPサポート - GoogleのMCPサポート