176
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MCPサーバーになってみた

Last updated at Posted at 2025-12-04

このエントリーは 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に返されます。

動作の様子

CleanShot 2025-12-03 at 22.28.14.gif

やってみた感想

AIからの質問を待っている間、妙な緊張感がありました。呼び出されると「早く答えなきゃ」と焦ります。自分の回答がAIの思考に組み込まれていくのを見ると、なんだか不思議な気持ちになりました。

普段はAIに質問する側ですが、逆もまた悪くないですね。

ただ、しばらく待機していて、こんなことを考え始めました。将来、人間はAIから呼び出されるだけの存在になるのではないかと。

呼び出されている間は忙しいですが、呼び出されないとき、私は自分に価値を感じられるでしょうか。たぶん、AIに選んでもらえるように、もっとたくさんのツールを公開しようと頑張り始めるのだと思います。「私はこんなこともできます」「こんな知識もあります」と。

でも、AIのコンテキストウィンドウには限りがあります。登録できるMCPサーバーの数にも上限がある。我々が入り込める余地は、思っているほど広くないのかもしれません。知性のメインストリームから、人間は少しずつ外れていく。

今からでも遅くないです。AIに必要とされるMCPサーバーを目指しましょう。

まとめ

MCPの仕組みを理解するのに、自分がMCPサーバーになるのは意外と効果的でした。プロトコルの流れを体感できます。

興味があれば試してみてください。あなたも今日からMCPサーバーです。

176
62
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
176
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?