ChatGPTって外部API叩けなくて不便だな…せや、自分でMCPっぽいの作ってコネクタに登録したろ!

結論から言うと、できました。公開APIなら、ChatGPTのチャットからなんでも叩ける。
MCPサーバを自分で1枚かますだけ。意外とあっさり。

目次

そもそもの動機:ChatGPT、外部APIを直接叩けない

ChatGPTと喋ってると、しょっちゅうこうなる。

「今のドル円教えて」→ 「リアルタイムの為替は取得できません…」
「この郵便番号の住所は?」→ 「最新のデータにはアクセスできません…」

いや知ってるけど! そりゃ ChatGPT は基本箱の中で喋ってるだけなので、こっちが用意した好きな REST API を勝手に叩いてはくれない。
昔のプラグインも畳まれたし、「自前のAPIに手を伸ばす手段」がパッと見ない。地味に不便。

…と思ってたら、カスタムコネクタっていう口があるじゃん。で、調べたらこのコネクタの正体、MCP(Model Context Protocol) だった。これ使えるのでは?

ひらめき

コネクタ=MCPサーバ。だったらMCPサーバを自作して、そこに「公開APIを叩くツール」を生やせばよくない? ChatGPTはそのツールを呼ぶだけ。ツールの中身はただの fetch()
つまりサーバ側で叩ける公開APIは、ぜんぶChatGPTから叩けることになる。
要はChatGPTに「手」を生やすイメージ。

仕組みは超シンプル

やることを絵にするとこれだけ。ChatGPTとあなたのAPIの間に、薄いMCPサーバを1枚はさむ。

MCPは「AIにツール(関数)を生やすための共通規格」くらいのザックリ理解でOK。

ChatGPTのリモートコネクタは Streamable HTTP って方式で喋るので、結局やることは POST /mcp を1本立てるだけ。身構えなくていい。

最小のMCPサーバを書く

TypeScript+公式SDK(@modelcontextprotocol/sdk)+Expressでサクッと。
依存はこれだけ。軽い。

"@modelcontextprotocol/sdk": "^1.29.0",
"express": "^5.2.1",
"zod": "^4.4.3"

まず「ツール」を定義する。ここが本体っちゃ本体。といっても registerTool のハンドラの中で 好きな公開APIを fetch するだけ
今回は例として、認証不要で使える為替APIを叩いてみる。

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

export function buildMcpServer() {
  const server = new McpServer({ name: 'my-api', version: '0.1.0' });

  server.registerTool(
    'get_exchange_rate',
    {
      title: 'Get exchange rate',
      description: '2通貨間の最新の為替レートを返す。例: base=USD, quote=JPY',
      inputSchema: {
        base:  z.string().length(3).describe('基準通貨 (例: USD)'),
        quote: z.string().length(3).describe('相手通貨 (例: JPY)'),
      },
    },
    async ({ base, quote }) => {
      // ★ここが心臓部:好きな公開APIをそのまま叩く
      const r = await fetch(
        `https://api.frankfurter.app/latest?from=${base}&to=${quote}`
      );
      const data = await r.json();
      const rate = data.rates?.[quote];

      return {
        content: [{ type: 'text', text: `1 ${base} = ${rate} ${quote}` }],
      };
    },
  );

  return server;
}

地味に大事なのが「ツールの説明文(description / describe)」をちゃんと書くこと。 ChatGPTはこの自然言語の説明だけを読んで「お、今ユーザー為替聞いてるな。じゃあこのツールを base=USD, quote=JPY で呼ぶか」って判断してる。
なので説明が雑だと普通にスルーされる。ここケチると動かないので注意。

HTTPの口を立てる

あとは POST /mcp を1本生やすだけ。Streamable HTTPのトランスポートにさっきのサーバを繋ぐ。ステートレスで雑にOK。

import express from 'express';
import { StreamableHTTPServerTransport }
  from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { buildMcpServer } from './mcp-tool.js';

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

app.get('/healthz', (_req, res) => res.json({ ok: true }));

app.post('/mcp', async (req, res) => {
  const server = buildMcpServer();
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
  res.on('close', () => { transport.close(); server.close(); });
  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

app.listen(3000, () => console.log('MCP on :3000'));

これで http://localhost:3000/mcp に喋るMCPサーバが完成。MCP Inspector みたいなローカルクライアントで tools/list を叩くと、ちゃんと get_exchange_rate が見える。

おお、できてる。ここまでで「箱」は完成。

HTTPSで外に公開する(コネクタはhttps必須)

ChatGPTのコネクタは 公開された https:// のURL しか登録できない。localhost はもちろんダメ。
というわけで、どっかのVM(無料枠のクラウドで十分)に置いてTLS付けて外に出す。

証明書を手で取るのはダルいので Caddy に丸投げ。
設定これだけで Let’s Encrypt を勝手に取ってきてくれる。神。

api.example.com {
    reverse_proxy localhost:3000
}

DNSで api.example.com をVMのIPに向けて(証明書発行のため一旦プロキシはオフ=DNSだけ通す)、Caddy起動。
あとはサーバを systemd でデーモン化しとけば、VM再起動しても勝手に立ち上がってくれる。これで https://api.example.com/mcp が世界に開通。

ChatGPTにコネクタとして登録する

ここまで来たら、あとはChatGPT側の設定 → コネクタ → カスタムコネクタを追加、でさっきのURLを入れるだけ。

  1. MCPサーバのURLに https://api.example.com/mcp を入力
  2. 登録方法は DCR(動的クライアント登録)、スコープは自分で決めた文字列(例 mcp
  3. 接続すると、ChatGPTが tools/list でツール一覧を取りに来る → get_exchange_rateが見えれば成功

で、チャットで「今のドル円は?」って聞く。するとChatGPTが裏で get_exchange_rate(base:"USD", quote:"JPY") を勝手に呼んで、自作サーバが公開APIを叩いて、生きた数字を返してくる。

「1ドル ≒ 158.2円です」とChatGPTが即答。
でもこの数字、ChatGPTは本当は知らない。自作サーバが今この瞬間に公開APIから取ってきた値。
地味だけど、ちょっと感動する。ChatGPTに手が生えた瞬間です。

そして本当の沼は、この先(OAuth)にあった

…と、ここまでは正直サクサクだった。問題はこの後。
ChatGPTにコネクタとして繋ごうとすると、「まず認証通してね」と OAuth を要求してくる。 「はいはいSDKに認証ルータ入ってるんでしょ、貼るだけっしょ」と舐めてたら、ここで半日溶けた。

リバースプロキシ背後の罠とか、トークンの有効期限を返し忘れて「再認証→失敗→再認証」の無限ループに叩き込まれたりとか、まあまあエグいハマり方を何点かしまして…。

OAuthのハマりどころは、別記事に切り出す予定

この記事の本題(=公開APIを叩く)からは脱線するし、何よりOAuthの沼だけで普通に1記事ぶんのボリュームになったので。 → 【OAuth編】ChatGPTコネクタの認証で半日溶かした話(trust proxy / expiresAt 無限ループ)(執筆中)

ちなみに——自分専用でいい/他人には使わせないなら、実はOAuthまで作り込まなくても、さっきの「公開APIを叩くツール」までで普通に遊べます。

OAuthが効いてくるのは「他人にも配る」「ユーザーごとにデータを分ける」みたいな段階。
まず動かしてニヤニヤするぶんには、認証なしで十分。

まとめ:ChatGPTに「手」を生やせた

やったことを振り返るとシンプルで、

  1. 公式SDKで MCPサーバを1枚書く(ツールの中身は普通の fetch
  2. VMに置いて Caddyで HTTPS 化して外に出す
  3. ChatGPTにカスタムコネクタとして登録する

たったこれだけで、ChatGPTから好きな公開APIが叩けるようになる
為替でも天気でも、社内の在庫APIでも、自分で書いたAPIでも、サーバ側で fetch できるものは全部「ChatGPTの手の届く範囲」。

「ChatGPTって外部API叩けなくて不便〜」とか言ってた自分をひっぱたきたい。
コネクタ=MCPっていう口に気づいた瞬間、拡張性が一気に青天井になった。 気になるAPIがあったら、ツールを1個生やすだけ。

週末の工作にちょうどいいので、ぜひ。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次