このエントリーは with Advent Calendar 2025 の 5日目の記事になります。
はじめに
MCP(Model Context Protocol)は、AIエージェントが外部ツールと連携するためのプロトコルです。通常、MCPサーバーはデータベースへのアクセスやファイル操作など、プログラムが自動で処理を行います。
今日は私がMCPサーバーになってみました。
Claude Codeから呼び出されると、ターミナルに質問が表示され、人間が回答を入力する。そういうスクリプトを作って、自分がMCPサーバーとして動作してみました。
仕組み
構成はシンプルです:
Claude Code
↓
HTTP リクエスト
↓
MCP サーバー (Express)
↓
ターミナルで人間に質問
↓
回答を返す
実装
サーバーのセットアップ
まず、MCPサーバーとExpressアプリを作成します:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express, { type Request, type Response } from "express";
import { randomUUID } from "node:crypto";
import readline from "node:readline";
import { z } from "zod";
const PORT = 54321;
// MCP サーバーを作成
const mcpServer = new McpServer({
name: "human-mcp",
version: "1.0.0",
});
人間に質問する関数
人間をツールとして呼び出す部分です。Node.jsのreadlineを使って、ターミナルで人間に入力を促します。
// 人間に質問して回答を待つ
function askHuman(prompt: string): Promise<string> {
return new Promise((resolve) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
console.log(`\n${"=".repeat(60)}`);
console.log("🧑 Claude Code からリクエストが来ました!");
console.log("=".repeat(60));
console.log(`\n${prompt}\n`);
console.log("=".repeat(60));
rl.question("\n👤 あなたの回答: ", (answer) => {
rl.close();
console.log("\n✅ 回答を送信しました!\n");
console.log("次のリクエストを待機中...\n");
resolve(answer);
});
});
}
※ 複数のAIエージェントから同時にリクエストが来た場合のキューイングは実装していません。人間は一度に一つの質問にしか答えられないので、本来はリクエストを順番に処理する仕組みが必要です。
ツールの登録
MCPサーバーにツールを登録します:
// ツールを登録
mcpServer.registerTool(
"ask_human",
{
description:
"人間に質問して回答を得ます。AIでは判断できないことや、人間の意見が必要な場合に使用してください。",
inputSchema: {
question: z.string().describe("人間に聞きたい質問"),
},
},
async ({ question }) => {
const prompt = `【質問】\n${question}`;
const answer = await askHuman(prompt);
return { content: [{ type: "text", text: answer }] };
}
);
HTTPエンドポイント
Claude CodeはHTTP経由でMCPサーバーと通信します。セッション管理を含むエンドポイントを設定します:
// Express アプリを作成
const app = express();
// セッション管理用のトランスポートマップ
const transports = new Map<string, StreamableHTTPServerTransport>();
// MCP エンドポイント
app.all("/mcp", async (req: Request, res: Response) => {
// セッション ID を取得または生成
const sessionId = req.headers["mcp-session-id"] as string | undefined;
let transport: StreamableHTTPServerTransport;
if (sessionId && transports.has(sessionId)) {
// 既存セッション
transport = transports.get(sessionId)!;
} else if (!sessionId && req.method === "POST") {
// 新規セッション(初期化リクエスト)
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: (newSessionId) => {
transports.set(newSessionId, transport);
},
});
transport.onclose = () => {
if (transport.sessionId) {
transports.delete(transport.sessionId);
}
};
await mcpServer.connect(transport);
} else {
res.status(400).send("Bad request");
return;
}
await transport.handleRequest(req, res);
});
// サーバーを起動
app.listen(PORT, () => {
console.log("=".repeat(60));
console.log("🎭 Human MCP Server - あなたは今から MCP サーバーです!");
console.log("=".repeat(60));
console.log(`\nHTTP サーバーがポート ${PORT} で起動しました`);
console.log("\nClaude Code からの接続を待っています...\n");
});
※ MCPではセッションという概念があり、クライアント(Claude Code)とサーバー間の一連のやり取りを管理します。セッションIDをHTTPヘッダーで送受信することで、同じ会話の中で複数回ツールを呼び出しても状態を維持できます。
使い方
1. サーバーを起動
npx tsx src/server.ts
起動すると以下のような表示が出ます:
============================================================
🎭 Human MCP Server - あなたは今から MCP サーバーです!
============================================================
HTTP サーバーがポート 54321 で起動しました
Claude Code からの接続を待っています...
2. Claude Codeに登録
Claude Codeの設定ファイル(.mcp.jsonなど)に以下を追加:
{
"mcpServers": {
"human": {
"type": "http",
"url": "http://localhost:54321/mcp"
}
}
}
3. 動作開始
Claude Codeで「人間に聞いてみて」などと頼むと、ターミナルに質問が表示されます:
============================================================
🧑 Claude Code からリクエストが来ました!
============================================================
【質問】
今日の気分はどうですか?
============================================================
👤 あなたの回答:
回答を入力すると、Claude Codeに返されます。
動作の様子
やってみた感想
AIからの質問を待っている間、妙な緊張感がありました。呼び出されると「早く答えなきゃ」と焦ります。自分の回答がAIの思考に組み込まれていくのを見ると、なんだか不思議な気持ちになりました。
普段はAIに質問する側ですが、逆もまた悪くないですね。
ただ、しばらく待機していて、こんなことを考え始めました。将来、人間はAIから呼び出されるだけの存在になるのではないかと。
呼び出されている間は忙しいですが、呼び出されないとき、私は自分に価値を感じられるでしょうか。たぶん、AIに選んでもらえるように、もっとたくさんのツールを公開しようと頑張り始めるのだと思います。「私はこんなこともできます」「こんな知識もあります」と。
でも、AIのコンテキストウィンドウには限りがあります。登録できるMCPサーバーの数にも上限がある。我々が入り込める余地は、思っているほど広くないのかもしれません。知性のメインストリームから、人間は少しずつ外れていく。
今からでも遅くないです。AIに必要とされるMCPサーバーを目指しましょう。
まとめ
MCPの仕組みを理解するのに、自分がMCPサーバーになるのは意外と効果的でした。プロトコルの流れを体感できます。
興味があれば試してみてください。あなたも今日からMCPサーバーです。
