📞

Cloudflare リモートMCPサーバーを利用してAPIを叩くMCPツールを作る

に公開

はじめに

この記事は【LLM+RAG】自分自身と会話できるナレッジベースシステムを作ってみたのナレッジベースシステムのsearch API をMCPから叩けるようにした記事です。

画像赤枠の部分について書いています。

できること

  • MCPからsearch APIを通じてナレッジベースにアクセスし、情報を返す
  • リモートMCPサーバーをホストすることで誰でもアクセスできる

リモートMCPサーバーのホスティング

MCPですが、サーバーについてはローカル環境で構築することは簡単ですが、環境に捉われず誰でも利用できるMCPサーバーを立てたいです。

CloudflareがHTTPで接続しMCPを利用できるサービスを提供しているのでそれを使うことにしました。

以下ページに詳しい内容が載っています。

https://blog.cloudflare.com/remote-model-context-protocol-servers-mcp/

初期設定

Cloudflare DocsのBuild a Remote MCP serverに沿って作っていきます。

対象のリポジトリで以下を実行します。

$ npm create cloudflare@latest -- mcp-server

対話形式で設定を確認されるため以下のように進めていきます。

├ In which directory do you want to create your application?
│ dir ./mcp-server
│
╰ What would you like to start with?
  ● Hello World example <-選択
  ○ Framework Starter
  ○ Application Starter
  ○ Template from a GitHub repo
  ◁ Go back


╰ Which template would you like to use?
  ○ Worker only
  ○ Static site
  ○ SSR / full-stack app
  ● Worker + Durable Objects <-選択
  ○ Worker + Durable Objects + Assets
  ○ Workflow
  ○ Scheduled Worker (Cron Trigger)
  ○ Queue consumer & producer Worker
  ○ API starter (OpenAPI compliant)
  ◁ Go back

╰ Which language do you want to use?
  ● TypeScript <-選択
  ○ JavaScript
  ○ Python (beta)
  ◁ Go back

┐ Installing dependencies ..


╰ Do you want to use git for version control?
  Yes / No <- 既に対象リポジトリがGit管理のためNo

╰ Do you want to deploy your application?
  Yes / No <- 後程設定してdeployしたいのでNo

必要なライブラリをインストールします

$ npm install agents @modelcontextprotocol/sdk zod

MCPツールの実装

ナレッジベースのsearch APIを叩き、結果を返す内容を実装します。

以下のページをとても参考にしました。

https://azukiazusa.dev/blog/cloudflare-mcp-server/

./src/MyMcp.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { McpAgent } from 'agents/mcp';
import { z } from 'zod';

export class MyMCP extends McpAgent {
	server = new McpServer({
		name: 'MyMCP Server',
		version: '0.1.0',
	});

	async init() {
		this.server.tool(
			// ツールの名前
			'quarkgabber-knowledge-base-search',
			// ツールの説明
			'QuarkgabberナレッジベースAPI searchにアクセスしレスポンスを受け取ります。',
			// ツールの引数のスキーマ
            { query: z.string().describe('検索クエリ')},
			// ツールの実行関数
            async (args: any) => {
                const { query } = args;
                // search APIをPOSTリクエストする
                const response = await fetch('https://home.quark-hardcore.com/personal-knowledge-base/api/v1/search', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        query: query,
                        k: 10
                    })
                });

                const data = await response.json();
                // 結果を返す
                return {
                    content: [{ type: 'text', text: JSON.stringify(data, null, 2)}]
                };
        });
    }
}
./src/index.ts
import { MyMCP } from './MyMcp.js';

// Durable Objects のエクスポート
export { MyMCP };

export default {
	fetch(request: Request, env: Env, ctx: ExecutionContext) {
		const url = new URL(request.url);

		// /sse エンドポイントの場合は SSE で応答する
		if (url.pathname === '/sse' || url.pathname === '/sse/message') {
			// @ts-ignore
			return MyMCP.serveSSE('/sse').fetch(request, env, ctx);
		}

		// /mcp エンドポイントの場合は Streamable HTTP で応答する
		if (url.pathname === '/mcp') {
			// @ts-ignore
			return MyMCP.serve('/mcp').fetch(request, env, ctx);
		}

		return new Response('Not found', { status: 404 });
	},
};
wrangler.jsonc
{
	"$schema": "node_modules/wrangler/config-schema.json",
	"name": "mcp-server",
	"main": "src/index.ts",
	"compatibility_date": "2025-09-02",
	"compatibility_flags": ["nodejs_compat"],
	"migrations": [
		{
			"new_sqlite_classes": [
				"MyMCP"
			],
			"tag": "v1"
		}
	],
	"durable_objects": {
		"bindings": [
			{
				"class_name": "MyMCP",
				"name": "MCP_OBJECT"
			}
		]
	}
}

動作チェック

まずはローカル環境で確認します。

サーバーを立ち上げます。

$ npm run dev
[wrangler:info] Ready on http://localhost:8787

ブラウザからMCPサーバーに接続してツールを呼び出せるMCP Inspectorを実行します。

$ npx @modelcontextprotocol/inspector@latest


ブラウザからこのような画面が開く

デフォルトではlocalhost:6274でブラウザが開き、リクエストはlocalhost:6277でプロキシしているみたいです。

  • Transport Type -> SSE
  • URL -> http://localhost:8787/sse

と設定しconnectを押します。

右上のToolsList Toolsボタンを押します。

queryに質問文を入れて、Run Toolを押します。

成功しました!

Transport Type -> SSE
URL -> http://localhost:8787/sse
と設定しconnectを押します。

こちらの設定ですが、リモートMCPクライアントではSSE(Server-Sent Events)かStreamable HTTPで通信します。

  • Transport Type -> Streamable HTTP
  • URL -> http://localhost:8787/mcp

でも同様に成功することを確認します。

成功したら変更を対象リポジトリにPushしておきます。

CloudflareにDeploy

Cloudflareにアカウントを作ります。

npm run deployコマンドをwrangler.jsoncがあるディレクトリで実行します。初回はOAuthブラウザ認証が入ります。

$ npm run deploy
Total Upload: 1237.91 KiB / gzip: 198.40 KiB
Worker Startup Time: 30 ms
Your Worker has access to the following bindings:
Binding                     Resource
env.MCP_OBJECT (MyMCP)      Durable Object

Uploaded mcp-server (4.35 sec)
Deployed mcp-server triggers (0.30 sec)
  https://mcp-server.quark-hardcore.workers.dev

Cloudflareのダッシュボード「コンピューティング」→「Workers & Pages」をみるとWorkerが作られています。

Workers AI LLM PlaygroundでDeployしたMCPサーバーを試すことができます。

npm run deployで発行されたURL + /sse を「Enter MCP server URL」にいれてconnectします。(URLはCloudflare対象workerの設定タブからも確認できます。)

このMCPはsearch APIでOpenSearchから類似コンテンツを返すもので、その後の解釈はAIが行うので、AIの能力に依存します。

Workers AI LLM Playgroundで提供されているモデルでは、あまり思ったような返事にはなりませんでした。


GPT5やSonnet 4ではtimestampを使って答えてくれる

Claude DesktopからリモートMCPサーバーに接続して使う

Claude DesktopはMCPに対応しているのでそちらで使ってみます。

「設定」→「コネクタ」→「カスタムコネクタを追加」をクリック

名前とURLを設定します。

検索とツールでツールを有効にします。

チャットから誕生日を聞いてみます

うまくいきました!

参考

https://azukiazusa.dev/blog/cloudflare-mcp-server/

https://speakerdeck.com/syumai/cloudflare-workersdejin-merurimotomcphuo-yong

https://dev.classmethod.jp/articles/mcp-server-without-local-credentials-cloudflare/

GitHubで編集を提案

Discussion