MCPサーバーとは何か:AIツール拡張の新標準プロトコル
2024年11月にAnthropicが公開したModel Context Protocol(MCP)は、AIアシスタントと外部ツールを接続するオープンな標準プロトコルだ。2026年4月時点で、Claude Code、Cursor、Cline、Windsurf、VS Code Copilotなど主要なAI開発ツールがMCPクライアントとして対応し、公式リポジトリ modelcontextprotocol/servers にはGitHub Star 83,000超が集まっている。
MCPが解決する問題はシンプルだ。従来、AIツールに「ファイルを読む」「データベースを検索する」「APIを叩く」といった機能を追加するには、ツールごとに独自のプラグイン仕様を学ぶ必要があった。MCPは1つのプロトコルで複数のAIクライアントに対応できるため、一度サーバーを作れば Claude Code でも Cursor でも使い回せる。
MCPの通信構造: MCPクライアント(Claude Code, Cursor等)がJSON-RPC over stdio/SSEでMCPサーバー(自作ツール)と通信し、MCPサーバーが任意のプロトコルで外部リソース(DB, API, ファイル等)にアクセスする。
MCPサーバーが提供できる機能は3種類ある。
| 機能 | 説明 | 具体例 |
|---|---|---|
| Tools(ツール) | AIが呼び出せる関数 | ファイル書き込み、DB検索、API呼び出し |
| Resources(リソース) | AIが読み取れるデータ | 設定ファイル、ログ、ドキュメント |
| Prompts(プロンプト) | 再利用可能なプロンプトテンプレート | コードレビュー用プロンプト、SQL生成テンプレート |
この記事では、TypeScriptとPythonの両方の公式SDKを使って、実用的なMCPサーバーをゼロからステップバイステップで構築する方法を解説する。
Claude Code / Cursor"] -->|"JSON-RPC"| B["MCPサーバー
自作ツール"] B -->|"読み取り"| C["ファイルシステム"] B -->|"クエリ"| D["データベース"] B -->|"HTTP"| E["外部API"] B -->|"ツール定義"| A style A fill:#4A90D9,color:#fff style B fill:#7B68EE,color:#fff style C fill:#50C878,color:#fff style D fill:#50C878,color:#fff style E fill:#50C878,color:#fff

MCPサーバー開発環境のセットアップ手順
TypeScript SDK(推奨)
TypeScript SDKは最も成熟しており、公式サーバーの大半がTypeScriptで書かれている。
# プロジェクト作成
mkdir my-mcp-server && cd my-mcp-server
npm init -y
# MCP SDK と 型定義をインストール
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
# TypeScript設定
npx tsc --init --target ES2022 --module NodeNext --moduleResolution NodeNext --outDir build --rootDir src --strict true
mkdir src
package.json に以下を追加する。
{
"name": "my-mcp-server",
"version": "1.0.0",
"type": "module",
"bin": {
"my-mcp-server": "./build/index.js"
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
}
}
Python SDK
Python SDKは mcp パッケージとして提供されている。FastMCPという高レベルAPIが用意されており、デコレータベースで直感的にサーバーを定義できる。
# uvを使う場合(推奨)
uv init my-mcp-server && cd my-mcp-server
uv add "mcp[cli]"
# pipを使う場合
pip install "mcp[cli]"
Python SDKの FastMCP は、Flaskのようなデコレータスタイルでツールを定義できるのが特徴だ。
from mcp.server.fastmcp import FastMCP
# サーバーインスタンス作成
mcp = FastMCP("my-server")
# ツール定義(デコレータで宣言)
@mcp.tool()
def hello(name: str) -> str:
"""挨拶を返すツール"""
return f"こんにちは、{name}さん!"
# サーバー起動
if __name__ == "__main__":
mcp.run()
公式SDKの全体像(9言語対応)
MCPは2026年4月時点で9言語の公式SDKが提供されている。
| SDK | Tier | GitHub Stars | 推奨用途 |
|---|---|---|---|
| TypeScript | Tier 1 | 12,100+ | 公式サーバーの主要言語、本番運用 |
| Python | Tier 1 | 22,500+ | FastMCPで高速開発、データ分析連携 |
| C# | Tier 1 | — | .NETエンタープライズ環境 |
| Go | Tier 1 | — | 高速・軽量サーバー |
| Java | Tier 2 | — | Spring Boot連携 |
| Rust | Tier 2 | — | 高パフォーマンス要件 |
| Swift | Tier 3 | — | macOS/iOSネイティブ |
| Ruby | Tier 3 | — | Railsアプリ連携 |
| PHP | Tier 3 | — | WordPress/Laravel連携 |
Tier 1は公式が完全サポート、Tier 2はコミュニティ主導+公式レビュー、Tier 3はコミュニティ主導。
TypeScript vs Python SDKの選択基準
| 基準 | TypeScript SDK | Python SDK (FastMCP) |
|---|---|---|
| 成熟度 | ★★★★★ 公式サーバーの主要言語 | ★★★★★ Star 22,500+で最も人気 |
| 学習コスト | 中(Zodスキーマ定義が必要) | 低(デコレータのみ) |
| 型安全性 | 完全な型推論 | Pydantic連携で型検証 |
| デプロイ | npx で即実行可能 |
uv run または python -m |
| 向いている用途 | 大規模・本番運用 | プロトタイプ・データ分析連携 |
実践1:ファイル操作MCPサーバーをTypeScriptで構築する
最初のサーバーとして、指定ディレクトリ内のファイルを検索・読み取り・要約できるツールを作る。AIアシスタントが「プロジェクト内のREADMEを読んで」と言われたときに、このサーバーを通じてファイルにアクセスする。
サーバー本体の実装
src/index.ts を作成する。
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as fs from "fs/promises";
import * as path from "path";
// サーバーインスタンス作成
const server = new McpServer({
name: "file-explorer",
version: "1.0.0",
});
// ルートディレクトリ(環境変数 or デフォルト)
const ROOT_DIR = process.env.MCP_ROOT_DIR || process.cwd();
// ツール1: ファイル一覧取得
server.tool(
"list_files",
"指定パターンにマッチするファイルの一覧を返す",
{
directory: z.string().describe("検索するディレクトリの相対パス"),
extension: z.string().optional().describe("フィルタする拡張子(例: .md, .ts)"),
},
async ({ directory, extension }) => {
const targetDir = path.resolve(ROOT_DIR, directory);
// ディレクトリトラバーサル防止
if (!targetDir.startsWith(ROOT_DIR)) {
return { content: [{ type: "text", text: "エラー: ルートディレクトリ外へのアクセスは禁止" }] };
}
const entries = await fs.readdir(targetDir, { withFileTypes: true, recursive: true });
let files = entries
.filter(e => e.isFile())
.map(e => path.relative(ROOT_DIR, path.join(e.parentPath || targetDir, e.name)));
if (extension) {
files = files.filter(f => f.endsWith(extension));
}
return {
content: [{ type: "text", text: files.join("\n") || "ファイルが見つかりません" }],
};
}
);
// ツール2: ファイル読み取り
server.tool(
"read_file",
"指定パスのファイル内容を返す",
{
file_path: z.string().describe("読み取るファイルの相対パス"),
max_lines: z.number().optional().default(200).describe("最大行数(デフォルト200)"),
},
async ({ file_path, max_lines }) => {
const fullPath = path.resolve(ROOT_DIR, file_path);
if (!fullPath.startsWith(ROOT_DIR)) {
return { content: [{ type: "text", text: "エラー: ルートディレクトリ外へのアクセスは禁止" }] };
}
const content = await fs.readFile(fullPath, "utf-8");
const lines = content.split("\n").slice(0, max_lines);
const truncated = content.split("\n").length > max_lines
? `\n\n--- (${content.split("\n").length - max_lines}行省略) ---`
: "";
return {
content: [{ type: "text", text: lines.join("\n") + truncated }],
};
}
);
// ツール3: ファイル内テキスト検索
server.tool(
"search_files",
"ファイル内容をキーワード検索し、マッチした行を返す",
{
query: z.string().describe("検索キーワード"),
directory: z.string().optional().default(".").describe("検索ディレクトリ"),
extension: z.string().optional().describe("対象拡張子"),
},
async ({ query, directory, extension }) => {
const targetDir = path.resolve(ROOT_DIR, directory);
if (!targetDir.startsWith(ROOT_DIR)) {
return { content: [{ type: "text", text: "エラー: アクセス禁止" }] };
}
const entries = await fs.readdir(targetDir, { withFileTypes: true, recursive: true });
const results: string[] = [];
for (const entry of entries) {
if (!entry.isFile()) continue;
const filePath = path.join(entry.parentPath || targetDir, entry.name);
if (extension && !filePath.endsWith(extension)) continue;
try {
const content = await fs.readFile(filePath, "utf-8");
const lines = content.split("\n");
lines.forEach((line, i) => {
if (line.toLowerCase().includes(query.toLowerCase())) {
const relPath = path.relative(ROOT_DIR, filePath);
results.push(`${relPath}:${i + 1}: ${line.trim()}`);
}
});
} catch { /* バイナリファイル等はスキップ */ }
}
return {
content: [{ type: "text", text: results.slice(0, 50).join("\n") || "マッチなし" }],
};
}
);
// リソース: プロジェクト情報
server.resource(
"project-info",
"project://info",
async (uri) => ({
contents: [{
uri: uri.href,
mimeType: "text/plain",
text: `ルートディレクトリ: ${ROOT_DIR}\nサーバー: file-explorer v1.0.0`,
}],
})
);
// サーバー起動
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("file-explorer MCPサーバーが起動しました");
}
main().catch(console.error);
ビルドと動作確認
# ビルド
npm run build
# MCP Inspectorで動作確認(公式デバッグツール)
npx @modelcontextprotocol/inspector node ./build/index.js
MCP Inspectorはブラウザベースのデバッグツールで、ツールの一覧確認・テスト実行・レスポンス検証ができる。http://localhost:6274 でUIが開く。
実践2:データベース連携MCPサーバーをPythonで構築する
次に、SQLiteデータベースに自然言語でクエリできるMCPサーバーをPythonで作る。「売上トップ10の商品を教えて」とAIに聞くと、このサーバーがSQLクエリを実行して結果を返す。
サーバー実装
import sqlite3
from pathlib import Path
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("db-explorer")
# データベースパス(環境変数 or デフォルト)
DB_PATH = Path(__file__).parent / "data.db"
def get_connection() -> sqlite3.Connection:
conn = sqlite3.connect(str(DB_PATH))
conn.row_factory = sqlite3.Row
return conn
@mcp.tool()
def list_tables() -> str:
"""データベース内の全テーブルとカラム情報を返す"""
conn = get_connection()
cursor = conn.execute(
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
)
tables = [row["name"] for row in cursor]
result = []
for table in tables:
cols = conn.execute(f"PRAGMA table_info({table})").fetchall()
col_info = ", ".join(f"{c['name']} ({c['type']})" for c in cols)
count = conn.execute(f"SELECT COUNT(*) as cnt FROM {table}").fetchone()["cnt"]
result.append(f"📊 {table} ({count}行): {col_info}")
conn.close()
return "\n".join(result) if result else "テーブルが見つかりません"
@mcp.tool()
def run_query(sql: str) -> str:
"""SQLクエリを実行して結果を返す(SELECT文のみ)
Args:
sql: 実行するSELECTクエリ
"""
# SELECT文のみ許可(安全対策)
normalized = sql.strip().upper()
if not normalized.startswith("SELECT"):
return "エラー: SELECT文のみ実行可能です(安全対策)"
conn = get_connection()
try:
cursor = conn.execute(sql)
rows = cursor.fetchmany(100) # 最大100行
if not rows:
return "結果: 0行"
# ヘッダー
headers = [desc[0] for desc in cursor.description]
lines = [" | ".join(headers)]
lines.append("-" * len(lines[0]))
# データ行
for row in rows:
lines.append(" | ".join(str(v) for v in row))
total = conn.execute(f"SELECT COUNT(*) FROM ({sql})").fetchone()[0]
if total > 100:
lines.append(f"\n(100/{total}行を表示)")
return "\n".join(lines)
except sqlite3.Error as e:
return f"SQLエラー: {e}"
finally:
conn.close()
@mcp.tool()
def get_sample_data(table_name: str, limit: int = 5) -> str:
"""テーブルのサンプルデータを返す
Args:
table_name: テーブル名
limit: 取得行数(デフォルト5)
"""
conn = get_connection()
try:
# テーブル名のバリデーション(SQLインジェクション防止)
tables = [r["name"] for r in conn.execute(
"SELECT name FROM sqlite_master WHERE type='table'"
)]
if table_name not in tables:
return f"エラー: テーブル '{table_name}' は存在しません"
cursor = conn.execute(f"SELECT * FROM [{table_name}] LIMIT ?", (limit,))
rows = cursor.fetchall()
if not rows:
return "データなし"
headers = [desc[0] for desc in cursor.description]
lines = [" | ".join(headers)]
for row in rows:
lines.append(" | ".join(str(v) for v in row))
return "\n".join(lines)
finally:
conn.close()
# リソース: スキーマ情報
@mcp.resource("schema://all")
def get_schema() -> str:
"""全テーブルのCREATE TABLE文を返す"""
conn = get_connection()
schemas = conn.execute(
"SELECT sql FROM sqlite_master WHERE type='table' AND sql IS NOT NULL"
).fetchall()
conn.close()
return "\n\n".join(row["sql"] for row in schemas)
if __name__ == "__main__":
mcp.run()
テスト用データベース作成
# テスト用DBを作成
python3 -c "
import sqlite3
conn = sqlite3.connect('data.db')
conn.execute('''CREATE TABLE products (
id INTEGER PRIMARY KEY, name TEXT, price INTEGER, category TEXT, stock INTEGER
)''')
conn.executemany('INSERT INTO products VALUES (?,?,?,?,?)', [
(1, 'AIカメラ', 29800, 'ガジェット', 150),
(2, 'スマートスピーカー', 12800, 'ガジェット', 300),
(3, 'コーディングキーボード', 19800, 'デバイス', 80),
(4, 'LLMサーバーGPU', 498000, 'サーバー', 12),
(5, 'USBマイク', 5980, 'デバイス', 500),
])
conn.commit()
conn.close()
print('テストDB作成完了')
"
# MCP Inspectorで動作確認
mcp dev server.py
実践3:外部API連携MCPサーバーの構築パターン
3つ目のパターンとして、外部REST APIをラップするMCPサーバーを作る。ここでは GitHub APIを叩いて、リポジトリ情報を取得するサーバーを TypeScript で実装する。
#!/usr/bin/env node
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-explorer",
version: "1.0.0",
});
const GITHUB_TOKEN = process.env.GITHUB_TOKEN || "";
async function githubFetch(endpoint: string) {
const headers: Record<string, string> = {
"Accept": "application/vnd.github.v3+json",
"User-Agent": "mcp-github-explorer",
};
if (GITHUB_TOKEN) {
headers["Authorization"] = `Bearer ${GITHUB_TOKEN}`;
}
const res = await fetch(`https://api.github.com${endpoint}`, { headers });
if (!res.ok) throw new Error(`GitHub API error: ${res.status}`);
return res.json();
}
// ツール: リポジトリ情報取得
server.tool(
"get_repo",
"GitHubリポジトリの詳細情報を取得する",
{
owner: z.string().describe("リポジトリオーナー"),
repo: z.string().describe("リポジトリ名"),
},
async ({ owner, repo }) => {
const data = await githubFetch(`/repos/${owner}/${repo}`);
const info = [
`📦 ${data.full_name}`,
`⭐ ${data.stargazers_count.toLocaleString()} stars`,
`🍴 ${data.forks_count.toLocaleString()} forks`,
`📝 ${data.description || "説明なし"}`,
`🔧 言語: ${data.language || "不明"}`,
`📅 最終更新: ${new Date(data.updated_at).toLocaleDateString("ja-JP")}`,
`📄 ライセンス: ${data.license?.spdx_id || "不明"}`,
].join("\n");
return { content: [{ type: "text", text: info }] };
}
);
// ツール: リポジトリ検索
server.tool(
"search_repos",
"キーワードでGitHubリポジトリを検索する",
{
query: z.string().describe("検索キーワード"),
sort: z.enum(["stars", "forks", "updated"]).optional().default("stars"),
limit: z.number().optional().default(10),
},
async ({ query, sort, limit }) => {
const data = await githubFetch(
`/search/repositories?q=${encodeURIComponent(query)}&sort=${sort}&per_page=${limit}`
);
const results = data.items.map((item: any, i: number) =>
`${i + 1}. ${item.full_name} (⭐${item.stargazers_count}) - ${item.description || ""}`
);
return {
content: [{ type: "text", text: `検索結果 (${data.total_count}件中${limit}件):\n\n${results.join("\n")}` }],
};
}
);
// ツール: Issue一覧取得
server.tool(
"list_issues",
"リポジトリのオープンIssue一覧を取得する",
{
owner: z.string(),
repo: z.string(),
limit: z.number().optional().default(10),
},
async ({ owner, repo, limit }) => {
const data = await githubFetch(`/repos/${owner}/${repo}/issues?state=open&per_page=${limit}`);
const issues = data.map((issue: any) =>
`#${issue.number} ${issue.title} (${issue.labels.map((l: any) => l.name).join(", ") || "ラベルなし"})`
);
return {
content: [{ type: "text", text: issues.join("\n") || "オープンIssueなし" }],
};
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(console.error);
3パターンの使い分け
| パターン | 通信先 | セキュリティ | 代表的な用途 |
|---|---|---|---|
| ファイル操作 | ローカルFS | ディレクトリトラバーサル防止 | プロジェクト探索、ドキュメント管理 |
| DB連携 | SQLite/PostgreSQL | SELECT文のみ許可 | データ分析、レポート生成 |
| 外部API | REST/GraphQL | APIキー管理、レート制限 | GitHub連携、Slack通知 |
MCPサーバーをAIクライアントに接続する方法
Claude Codeとの接続
Claude Codeは claude mcp add コマンドで簡単にMCPサーバーを登録できる。
# TypeScriptサーバーを登録
claude mcp add file-explorer node /path/to/build/index.js
# 環境変数付きで登録
claude mcp add github-explorer \
-e GITHUB_TOKEN=ghp_xxxxxxxxxxxx \
node /path/to/build/index.js
# Pythonサーバーを登録
claude mcp add db-explorer python /path/to/server.py
# 登録済みサーバー一覧
claude mcp list
# サーバー削除
claude mcp remove file-explorer
MCPサーバーの設定ファイルは ~/.claude/claude_desktop_config.json に保存される。手動で編集することも可能だ。
{
"mcpServers": {
"file-explorer": {
"command": "node",
"args": ["/path/to/build/index.js"],
"env": {
"MCP_ROOT_DIR": "/home/user/projects"
}
},
"db-explorer": {
"command": "python",
"args": ["/path/to/server.py"]
}
}
}
Cursorとの接続
Cursorは .cursor/mcp.json をプロジェクトルートに配置する。AIコーディングツールの選び方については、Claude CodeとCursorの違い比較記事も参照してほしい。
{
"mcpServers": {
"file-explorer": {
"command": "node",
"args": ["./build/index.js"]
}
}
}
VS Code + Copilotとの接続
VS Codeの設定(settings.json)に追加する。
{
"mcp": {
"servers": {
"file-explorer": {
"command": "node",
"args": ["./build/index.js"]
}
}
}
}
claude mcp add"] CU["Cursor
.cursor/mcp.json"] VS["VS Code Copilot
settings.json"] end subgraph Servers["自作MCPサーバー"] S1["file-explorer
ファイル操作"] S2["db-explorer
DB連携"] S3["github-explorer
API連携"] end CC --> S1 CC --> S2 CC --> S3 CU --> S1 CU --> S3 VS --> S1 style CC fill:#D4A574,color:#000 style CU fill:#4A90D9,color:#fff style VS fill:#007ACC,color:#fff style S1 fill:#50C878,color:#fff style S2 fill:#50C878,color:#fff style S3 fill:#50C878,color:#fff
MCPサーバー開発のセキュリティベストプラクティス
MCPサーバーはAIにシステムリソースへのアクセス権を与えるため、セキュリティ設計が極めて重要だ。AgentSealの調査によると、1,808のMCPサーバーのうち66%に脆弱性が発見され、そのうち43%がシェルインジェクションだった。OWASPもGen AIセキュリティプロジェクトでMCPサーバー開発ガイドを公開している。
入力バリデーション
// ❌ 悪い例:パストラバーサル脆弱性
server.tool("read", { path: z.string() }, async ({ path }) => {
const content = await fs.readFile(path, "utf-8"); // ../../etc/passwd が読める
return { content: [{ type: "text", text: content }] };
});
// ✅ 良い例:ルートディレクトリ制限
server.tool("read", { path: z.string() }, async ({ path }) => {
const fullPath = path.resolve(ROOT_DIR, path);
if (!fullPath.startsWith(ROOT_DIR)) {
throw new Error("Access denied");
}
const content = await fs.readFile(fullPath, "utf-8");
return { content: [{ type: "text", text: content }] };
});
セキュリティチェックリスト
| チェック項目 | 対策 |
|---|---|
| パストラバーサル | path.resolve() + プレフィックスチェック |
| SQLインジェクション | パラメータバインディング、SELECT文のみ許可 |
| APIキー漏洩 | 環境変数で管理、レスポンスに含めない |
| 過剰なアクセス | 最小権限の原則(読み取り専用等) |
| レート制限 | 外部API呼び出しにはスロットリングを実装 |
| 大量データ | 結果の最大行数を制限 |
認証とアクセス制御
import os
from functools import wraps
def require_auth(func):
"""環境変数でAPIキーを検証するデコレータ"""
@wraps(func)
def wrapper(*args, **kwargs):
api_key = os.environ.get("MCP_API_KEY")
if not api_key:
return "エラー: MCP_API_KEY 環境変数が未設定です"
return func(*args, **kwargs)
return wrapper
@mcp.tool()
@require_auth
def sensitive_operation(query: str) -> str:
"""認証が必要な操作"""
# ...
MCPサーバーのデプロイとSSE(Server-Sent Events)対応
ローカルのstdio接続だけでなく、リモートからHTTP経由でアクセスできるSSEトランスポートも利用可能だ。チーム全体で1つのMCPサーバーを共有する場合に有用。
TypeScript SSEサーバー
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
const app = express();
const server = new McpServer({ name: "remote-server", version: "1.0.0" });
// ツール定義は同じ
server.tool("ping", {}, async () => ({
content: [{ type: "text", text: "pong" }],
}));
// SSEエンドポイント
let transport: SSEServerTransport;
app.get("/sse", (req, res) => {
transport = new SSEServerTransport("/messages", res);
server.connect(transport);
});
app.post("/messages", (req, res) => {
transport.handlePostMessage(req, res);
});
app.listen(3001, () => {
console.log("MCPサーバー起動: http://localhost:3001/sse");
});
Streamable HTTP(2025年3月仕様追加)
MCPプロトコルの最新仕様では、SSEに代わるStreamable HTTPトランスポートが追加された。単一のHTTPエンドポイントでリクエスト・レスポンスを処理でき、ステートレスなデプロイが容易になっている。
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
});
app.post("/mcp", async (req, res) => {
await transport.handleRequest(req, res);
});
| トランスポート | 接続方式 | 用途 |
|---|---|---|
| stdio | 標準入出力 | ローカル開発、CLI統合 |
| SSE | HTTP(Server-Sent Events) | リモートサーバー共有 |
| Streamable HTTP | HTTP POST/レスポンス | 本番デプロイ、ステートレス |
公式MCPサーバーの活用と既存エコシステム
自前で作る前に、公式リポジトリ modelcontextprotocol/servers(83,000+ Star)に収録されている既存サーバーを確認するのが効率的だ。2026年4月時点で200以上の公式・コミュニティサーバーが登録されている。
主要な公式サーバー
# ファイルシステム操作
npx -y @modelcontextprotocol/server-filesystem /path/to/allowed/dir
# GitHub連携
npx -y @modelcontextprotocol/server-github
# PostgreSQL
npx -y @modelcontextprotocol/server-postgres postgresql://localhost/mydb
# Google Drive
npx -y @modelcontextprotocol/server-gdrive
# Slack
npx -y @modelcontextprotocol/server-slack
サイト上では、Unity開発環境をMCPで拡張する方法や、LINE BotをMCPサーバーとして構築する事例、セキュリティ診断MCPサーバーなど、実践的なMCPサーバーの解説記事も公開している。
コミュニティサーバーの探し方
MCP公式のサーバーディレクトリは以下で確認できる。
- 公式サーバーリポジトリ:
github.com/modelcontextprotocol/servers - Awesome MCP Servers:
github.com/punkpeye/awesome-mcp-servers(コミュニティキュレーション、Star 40,000超) - Smithery.ai: MCPサーバーのレジストリサービス(2,000以上登録)
- mcp.so: MCPサーバーの検索・比較サイト
ファイル操作"] GH["github
GitHub連携"] PG["postgres
DB連携"] GD["gdrive
Google Drive"] end subgraph Community["コミュニティ"] UN["unity-mcp
Unity開発"] LN["line-bot-mcp
LINE Bot"] SC["mcp-for-security
セキュリティ"] TR["tradingview-mcp
トレード"] end subgraph Registry["レジストリ"] SM["Smithery.ai
2,000+サーバー"] AW["awesome-mcp-servers
Star 40K+"] end Official --> Registry Community --> Registry style FS fill:#4A90D9,color:#fff style GH fill:#4A90D9,color:#fff style PG fill:#4A90D9,color:#fff style GD fill:#4A90D9,color:#fff
MCPサーバー開発のトラブルシューティング
開発中によく遭遇するエラーと解決策をまとめる。
よくあるエラーと解決策
| エラー | 原因 | 解決策 |
|---|---|---|
Server disconnected |
サーバープロセスがクラッシュ | console.error でログ出力。stdoutにデバッグログを出すとJSON-RPCが壊れる |
Tool not found |
ツール名の不一致 | MCP Inspectorで登録済みツールを確認 |
Invalid params |
Zodスキーマと引数の不一致 | スキーマ定義を確認。z.optional() の付け忘れに注意 |
ENOENT |
ファイルパスの解決失敗 | path.resolve() で絶対パスに変換 |
EACCES |
権限不足 | chmod でファイル権限を確認 |
デバッグのコツ
# MCP Inspectorで対話的にテスト
npx @modelcontextprotocol/inspector node ./build/index.js
# Claude Codeのデバッグログ
claude mcp list # 登録状況確認
# Pythonサーバーのデバッグ
mcp dev server.py # 開発モードで起動
重要: MCPサーバーの console.log() はJSON-RPCのstdioストリームに書き込まれるため、デバッグ出力には必ず console.error() を使うこと。
// ❌ stdoutに出力 → JSON-RPCが壊れる
console.log("デバッグ情報");
// ✅ stderrに出力 → 安全
console.error("デバッグ情報");
まとめ:MCPサーバー構築の次のステップ
MCPサーバーの構築は、TypeScript SDKなら McpServer クラスに server.tool() でツールを登録するだけ、Python SDKなら FastMCP に @mcp.tool() デコレータを付けるだけで始められる。
本記事で紹介した3パターンを組み合わせることで、AIアシスタントにファイル操作・データベース検索・外部API連携のあらゆる機能を追加できる。
次のステップとして推奨するのは以下の3つだ。
- 既存の公式サーバーを使ってみる —
npx -y @modelcontextprotocol/server-filesystemから始めるのが最も簡単 - MCP Inspectorでデバッグ環境を整える — ブラウザでツールをテストできる
- 自分の業務に特化したサーバーを作る — 社内API、独自データベース、プロジェクト管理ツールとの連携
AIエージェント開発の全体像を把握するには、フレームワーク比較記事も参考にしてほしい。MCPサーバーは単体でも強力だが、エージェントフレームワークと組み合わせることで、より複雑なワークフローを構築できる。