08. MCP の HTTP API 化

この資料は 2025 年 11 月時点での田中正吾の雑感として書いたものです。あくまでもハッカソンでザっと AI の連携を作れるようにするために、動かして進められるような説明用の資料とサンプル群を用意した次第です。

07の MCP サーバーを HTTP API として公開しましょう。06と同じように /message エンドポイントで外から会話できるようにします。

学べること

  • MCP サーバーの HTTP API 化
  • Express + MCP の組み合わせ
  • 会話記録を持つ HTTP API の実装
  • 07 のツールを HTTP 経由で使う方法
  • 06(Function Calling)と 08(MCP)の対になっている構造

準備するもの

  • Node.js (v18 以上)
  • TypeScript の基本知識
  • 06 と 07 の理解
  • OpenAI API キー

セットアップ

サンプルコードは本教材の Codespace(リンク集を参照)の 08_mcp-api-wrapper/ にあります。

フォルダ移動

まず、ターミナルに表示されているカレントディレクトリ(現在の作業フォルダ)を確認しましょう。

  • /workspaces/hackathon-ai-and-mcp-sample-202511-codespace/ の場合は、以下のコマンドでフォルダ移動します。
cd 08_mcp-api-wrapper
  • 続けて 07 から勉強している場合は、カレントディレクトリが /workspaces/hackathon-ai-and-mcp-sample-202511-codespace/07_mcp-basics で一つ階層が深いので、以下のコマンドでフォルダ移動します。
cd ../08_mcp-api-wrapper

手元の Visual Studio Code で作業されている方は、現在作業中のカレントディレクトリを確認いただいて上記を参考にフォルダを移動しましょう。

※フォルダ移動について詳しくは「Codespace でのフォルダ移動」を参照してください。

インストール

カレントディレクトリの移動が確認できたら、関連ライブラリをインストールして準備します。

npm install

これ以降はこのカレントフォルダでコマンドを打って作業していきましょう。

サンプル一覧

このサンプルと 07 の関係

このサンプルには、07 で学んだ MCP サーバーと同じプログラムが含まれています。ただし、ファイル名が異なります:

  • 07: 01_simple-server.ts → 08: 01_simple-mcp-server.ts(動作は全く同じ)
  • 07: 02_weather-server.ts → 08: 02_weather-mcp-server.ts(動作は全く同じ)

なぜファイル名を変えたのでしょうか?

この章では「HTTP サーバー」と「MCP サーバー」という2種類のサーバーが登場するため、混乱を避けるために明示的に mcp-server という名前にしました。

07 では MCP サーバーしか登場しなかったので simple-server.ts でも問題ありませんでしたが、08 では HTTP サーバー (00_http-server.ts) も登場するため、どちらが「サーバー」なのか分かりにくくなります。

この章のファイル構成:

src/
├── 00_http-server.ts           ← HTTP サーバー(Web からリクエストを受け取る)
├── 01_simple-mcp-server.ts     ← MCP サーバー(HTTP サーバーから呼ばれる)
└── 02_weather-mcp-server.ts    ← MCP サーバー(HTTP サーバーから呼ばれる)

ファイル名で役割が明確になり、00_http-server.ts01_simple-mcp-server.ts を呼び出す、という順序関係も分かりやすくなります。

「サーバー」という言葉について

このサンプルには2種類の「サーバー」が登場します。

HTTP サーバー (00_http-server.ts):

  • Web ブラウザや curl からのリクエストを受け取る
  • Express で実装された、いわゆる「Web サーバー」
  • /message エンドポイントでメッセージを受け付ける

MCP サーバー (01_simple-mcp-server.ts, 02_weather-mcp-server.ts):

  • MCP クライアント(この場合は HTTP サーバー)からのリクエストを受け取る
  • ツール(関数)を提供するブリッジ的な存在
  • 他の仕組みや API への橋渡しをする

どちらも「データの受け手」という意味では「サーバー」ですが、役割が異なります。この章では、HTTP サーバーが MCP クライアントとして振る舞い、MCP サーバーに接続してツールを利用します。

MCP サーバー(シンプル版)

ファイル: src/01_simple-mcp-server.ts

3つのツールを提供する MCP サーバーです(07 からコピー):

  • add: 2つの数値を足し算
  • multiply: 2つの数値を掛け算
  • greet: 指定された名前で挨拶

このファイルは直接実行しません。HTTP サーバー(00_http-server.ts)から自動的に起動されます。

MCP サーバー(天気 API 版)

ファイル: src/02_weather-mcp-server.ts

外部 API(wttr.in)を呼び出して天気情報を提供する MCP サーバーです(07 からコピー):

  • get_weather: 指定された場所の天気情報を取得

このファイルは直接実行しません。HTTP サーバーから呼び出すときに、サーバー切り替えの設定が必要です(後述)。

HTTP API サーバー(会話記録あり)

ファイル: src/00_http-server.ts

MCP サーバーを HTTP API として公開します。06 と同じように /message エンドポイントで会話を継続できます。

API キーの設定:

以下のコマンドでエディタで開くか、エクスプローラからファイルをダブルクリックして開きましょう。

code src/00_http-server.ts

ファイルの先頭にある API キー設定箇所を、お使いの OpenAI API キーに書き換えてください。

// OpenAI API キー
const OPENAI_API_KEY = "ここにあなたのAPIキーを入力";

実行:

npx tsx src/00_http-server.ts

デフォルトでは 01_simple-mcp-server.ts を使います。

天気サーバーに切り替えるには:

src/00_http-server.ts をエディタで開き、以下の箇所を変更してください:

// 変更前
const mcpServerScript = "01_simple-mcp-server.ts";

// 変更後
const mcpServerScript = "02_weather-mcp-server.ts";

保存してから、再度 npx tsx src/00_http-server.ts で実行してください。

エンドポイント一覧

POST /message

メッセージを送信して返答を得ます。会話履歴を保持します。

curl -X POST http://localhost:3000/message \
  -H "Content-Type: application/json" \
  -d '{"message":"5と3を足して"}'
curl -X POST http://localhost:3000/message \
  -H "Content-Type: application/json" \
  -d '{"message":"さっきの結果を2倍にして"}'

POST /clear

会話履歴をクリアします。

curl -X POST http://localhost:3000/clear

GET /

サーバー情報を取得します。

curl http://localhost:3000/

06 と 08 の比較

共通点(対になっている構造)

どちらも:

  • Express で HTTP API を実装
  • /message エンドポイントで会話を継続
  • /clear エンドポイントで履歴をクリア
  • 会話記録を保持

違い

06_interactive-chat-server

  • Function Calling を直接実装
  • ツール定義をコード内に記述
  • ツール実行もコード内で処理

08_mcp-http-wrapper

  • MCP サーバー経由でツールを実行
  • ツール定義は 07 の MCP サーバーから取得
  • ツール実行は MCP サーバーに委譲

使い分けの例

06(Function Calling)が適している場合:

  • ツールが少ない(1-3個程度)
  • シンプルに実装したい
  • ツールの変更が少ない

08(MCP)が適している場合:

  • ツールが多い、または増える予定
  • ツールを他のクライアントでも使いたい
  • Claude Desktop / Cline と同じツールを共有したい

両方作る:

  • ハッカソンでは最も柔軟
  • 状況に応じて使い分けられる

教材の流れ(復習)

標準入出力(会話記録あり) → HTTP API化(会話記録あり)
  05                      →   06   (Function Calling)
  07                      →   08   (MCP)

05→06と07→08は対になっている関係です。どちらも「標準入出力 → HTTP API 化」という同じパターンを学びます。

コードの説明

MCP クライアントのセットアップ

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

async function setupMCPClient() {
  // MCP サーバーを起動
  const transport = new StdioClientTransport({
    command: "npx",
    args: ["tsx", `./01_simple-mcp-server.ts`],
  });

  const client = new Client(
    {
      name: "mcp-http-wrapper",
      version: "1.0.0",
    },
    {
      capabilities: {},
    }
  );

  await client.connect(transport);

  // ツール一覧を取得
  const toolsResult = await client.listTools();
  return { client, tools: toolsResult.tools };
}

起動時に MCP サーバーを子プロセスとして起動し、ツール一覧を取得します。

Express エンドポイントの実装

import express from "express";
import OpenAI from "openai";

const app = express();
app.use(express.json());

// 会話履歴を保持
let messages: any[] = [];

// POST /message
app.post("/message", async (req, res) => {
  const { message } = req.body;

  // ユーザーのメッセージを履歴に追加
  messages.push({ role: "user", content: message });

  // OpenAI API に質問
  const response = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    messages: messages,
    tools: mcpTools, // MCP サーバーから取得したツール
  });

  const responseMessage = response.choices[0].message;

  // ツール呼び出しがある場合
  if (responseMessage.tool_calls) {
    // アシスタントのメッセージを履歴に追加
    messages.push(responseMessage);

    // 各ツールを MCP サーバーで実行
    for (const toolCall of responseMessage.tool_calls) {
      const result = await mcpClient.callTool({
        name: toolCall.function.name,
        arguments: JSON.parse(toolCall.function.arguments),
      });

      // ツール実行結果を履歴に追加
      messages.push({
        role: "tool",
        tool_call_id: toolCall.id,
        content: result.content[0].text,
      });
    }

    // 最終的な返答を生成
    const finalResponse = await openai.chat.completions.create({
      messages: messages,
      model: "gpt-4o-mini",
    });

    const finalContent = finalResponse.choices[0].message.content;

    // 最終的な返答も履歴に追加
    messages.push({
      role: "assistant",
      content: finalContent,
    });

    return res.json({ response: finalContent });
  } else {
    // ツールを使わない返答
    messages.push({
      role: "assistant",
      content: responseMessage.content,
    });

    return res.json({ response: responseMessage.content });
  }
});

app.listen(3000);

06とほぼ同じ構造ですが、ツール実行を MCP サーバーに委譲しています。

ハッカソンでの活用パターン

パターン1: Function Calling 直接

Webアプリ → OpenAI API(Function Calling)
  • シンプル
  • ツールが少ない場合に最適
  • 06 の実装パターン

パターン2: MCP 経由

Webアプリ → OpenAI API → MCP サーバー → 外部API/DB
  • ツールの再利用性が高い
  • Claude Desktop/Cline と同じツールを共有できる
  • 複雑なツールの管理がしやすい
  • 08 の実装パターン

パターン3: ハイブリッド

Webアプリ → OpenAI API
              ├─ Function Calling(シンプルなツール)
              └─ MCP サーバー(複雑なツール)
  • 状況に応じて使い分け
  • ハッカソンでは最も柔軟

MCP サーバーの切り替え

src/00_http-server.ts の中でサーバーファイル名を変更することで、異なる MCP サーバーを使えます。

この章に含まれる MCP サーバー(01_simple-mcp-server.ts02_weather-mcp-server.ts)を切り替えて HTTP API 化できます。

トラブルシューティング

MCP サーバーへの接続エラー

Error: Cannot connect to MCP server

MCP サーバーファイルのパスが正しいか確認してください。00_http-server.ts と同じディレクトリに MCP サーバーファイルがあることを確認しましょう。

ツールが実行されない

(ツールは呼び出されませんでした)

ChatGPT がツールを使う必要がないと判断している可能性があります。質問をより具体的にしてみてください。

API キーエラー

Error: Invalid API Key

コード内のAPIキーを確認してください。

次のステップ

これで MCP の基礎から HTTP API 化まで学びました。ハッカソンでは、この 08 のパターンをベースに、独自の MCP サーバーを追加していくと良いでしょう。