Model Context Protocol (MCP)はLLMアプリケーションとデータソースやツールを標準化された方法で連携させるためのプロトコルです。AIアプリケーションのための「USB-C」のようなものと考えると理解しやすいです(公式より)。
このメモでは、TypeScript SDKを使用してMCPサーバーを最小限のステップで構築する手順をまとめます。
前提環境
- Node.js: v18.0.0以上
- TypeScript: v5.0.0以上
- MCP SDK: v1.8.0以上
1. プロジェクトセットアップ
新しいディレクトリを作成し、必要なパッケージをインストール。
mkdir minimal-mcp-server
cd minimal-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
パッケージの役割:
-
@modelcontextprotocol/sdk
: MCP用TypeScript実装 -
zod
: 型安全なスキーマ検証ライブラリ -
typescript
: TSコンパイラ -
@types/node
: Node.js型定義
2. TypeScript設定
tsconfig.json
を作成。
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["./**/*"],
"exclude": ["node_modules"]
}
重要点:
- ES2022ターゲット
- Node16モジュール設定
- 出力先は
./build
- ルートは
./src
- 厳格な型チェック有効
3. package.json設定
package.json
を編集し、ESMモジュール形式設定とスクリプトを追加。
{
"name": "minimal-mcp-server",
"version": "1.0.0",
"main": "index.js",
"type": "module", // 追加
"scripts": {
"build": "tsc", // 追加
"start": "node build/index.js" // 追加
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.8.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^22.0.2",
"typescript": "^5.5.4"
}
}
重要点:
-
"type": "module"
でESM形式指定 - ビルド・実行用スクリプト追加 (build, start)
4. MCPサーバー実装
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: "minimal-mcp-server",
version: "1.0.0",
});
// エコーツール定義
server.tool(
"echo",
"入力されたメッセージをそのまま返す簡単なツール",
{
message: z.string().describe("エコーするメッセージ"),
},
async ({ message }) => {
return {
content: [
{
type: "text",
text: `Echo: ${message}`,
},
],
};
}
);
// 日時ツール追加
server.tool(
"get-current-time",
"現在の日時を取得するツール",
{},
async () => {
const now = new Date();
return {
content: [
{
type: "text",
text: `現在の日時: ${now.toLocaleString()}`,
},
],
};
}
);
// サーバー起動
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.log("MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});
MCPツールの構成要素:
- 名前:一意の識別子
- 説明:機能説明
- パラメータスキーマ:入力パラメータはZodで検証
- ハンドラ関数:実際の処理
公式の例はZodを使っていますがArkTypeでも可です
5. ビルドと実行
サーバーをビルドして実行。
npm run build
node build/index.js
「MCP Server running on stdio」メッセージが表示されれば成功。
6. テストと検証
MCP Inspectorでテスト。
npx @modelcontextprotocol/inspector node build/index.js
Inspectorが起動し、ツールやリソースが確認できる。
7. LLMクライアント接続
Claude Desktop設定
設定ファイルを編集:
- MacOS:
~/Library/Application Support/Claude/claude_desktop_config.json
- Windows:
%AppData%\Claude\claude_desktop_config.json
{
"mcpServers": {
"minimal-mcp": {
"command": "node",
"args": ["/絶対パス/minimal-mcp-server/build/index.js"]
}
}
}
Cursor設定
- Settings → MCPセクション → 「Add new MCP server」
- 入力情報:
- Name: minimal-mcp
- Type: command
- Command:
node /絶対パス/minimal-mcp-server/build/index.js
8. 拡張方法
リソース追加
リソースについて、私も十分に理解しているわけではないですが、Claude Desktopではメッセージへの添付などがしやすくなります(プラグのアイコンから)
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
// 静的リソース
server.resource(
"info",
"info://server",
async (uri) => ({
contents: [{
uri: uri.href,
text: "このサーバーに関する情報"
}]
})
);
// 動的リソース
server.resource(
"user-info",
new ResourceTemplate("users://{userId}", { list: undefined }),
async (uri, { userId }) => ({
contents: [{
uri: uri.href,
text: `ユーザーID: ${userId} の情報`
}]
})
);
HTTP/SSEトランスポート
import express from "express";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
const app = express();
const transports = {};
app.get("/sse", async (_, res) => {
const transport = new SSEServerTransport('/messages', res);
transports[transport.sessionId] = transport;
res.on("close", () => {
delete transports[transport.sessionId];
});
await server.connect(transport);
});
app.post("/messages", async (req, res) => {
const sessionId = req.query.sessionId;
const transport = transports[sessionId];
if (transport) {
await transport.handlePostMessage(req, res);
} else {
res.status(400).send('セッションが見つかりません');
}
});
app.listen(3000, () => {
console.log("MCP Server running on http://localhost:3000");
});