Introduction
Hello everyone!
I recently properly studied Cloudflare Workers for the first time, so I'm writing this article to share my findings!
This post will cover what I tried during implementation and how to deploy an MCP server to Cloudflare Workers!
What is Cloudflare Workers?
Cloudflare Workers is a serverless computing platform provided by Cloudflare.
While there are some constraints like bundle size, its charm lies in the ease of deploying TypeScript/JavaScript apps with a frontend-like feel! It also integrates seamlessly with other major Cloudflare services like KV and D1.
Great Compatibility with Hono
Hono is a lightweight, fast, and modern web framework for developing web applications and APIs, primarily in TypeScript/JavaScript. Being fast and lightweight, it is extremely compatible with Cloudflare Workers!
What is Vibe Kanban?
Vibe Kanban is a tool that manages AI coding agents (like Claude Code or Codex) in a Kanban format (task visualization) to realize automated development flows!
Development Workflow with cc-sdd + VibeKanban + GitWorkTree
I practiced the following development workflow for this implementation:
- 0. Formulate product vision and concept.
- 1. Create requirements, design documents, and task lists with
cc-sdd. - 2. Register tasks in VibeKanban (register as GitHub Issues).
- 3. Prepare working directories with
git worktree.
- 4. Parallel execution of tasks.
- 5. Self-review deliverables and create PRs.
I used CodeRabbit for code reviews!

Converting and Registering cc-sdd Tasks to VibeKanban
I used a prompt like this to convert tasks generated by cc-sdd into VibeKanban tasks:
Review the task list generated by @(cc-sdd) and register the work plan as Tasks in vibe_kanban.
Each task should include:
- Refer to @design.md for design details.
- Specific work content for this task.
- Dependencies on other tasks or if parallel work is possible.
- If it can be executed in parallel, add a + to the title.
Register tasks in descending order.
The best part is being able to turn them directly into GitHub Issues!
What is x402?
x402 is a standard protocol for stablecoin payments announced by Coinbase, a prominent cryptocurrency exchange in the US.
True to its name, it adopts the HTTP status code 402 Payment Required and has gained significant attention for its compliance with the HTTP protocol.
Cloudflare has not only co-founded the x402 Foundation but, given its compatibility with AI agents, it's a technology that has garnered immense interest within various blockchain tech stacks.
About the App I Built
Overview
I developed and deployed an x402 backend server and an MCP server on Cloudflare Workers. I created a sample app where stablecoin payments are processed simultaneously when weather information is retrieved through a chat interface within a GPT App!
What it can do:
- Call the
get_weathertool from a GPT App to retrieve weather information. - Access
/weatheronly for requests that have passed x402 payment verification. - Deploy
x402serverandmcpserverseparately on Cloudflare Workers. - Verify the integrated operation of
mcpserver -> x402FetchClient -> x402servervia E2E tests.
Sample Code
The source code for this project is available in the following GitHub repository:
mashharuki
/
vibekanban-gitworktree-sample
VibeKanbanとGitWorktreewo
vibekanban-gitworktree-sample
VibeKanbanとGitWorktreeを掛け合わせたサンプルアプリ
概要
x402バックエンドサーバーとMCPサーバーを使ってGPT App内のチャットインターフェースから天気予報の情報を取得すると同時にステーブルコイン支払いが行われるサンプルアプリ。
このプロジェクトでできること
- GPT App から
get_weatherツールを呼び出して天気情報を取得 -
x402による支払い検証を通過したリクエストのみ/weatherにアクセス - Cloudflare Workers 上で
x402serverとmcpserverを分離デプロイ - E2E テストで
mcpserver -> x402FetchClient -> x402serverの結合動作を検証
構成
リポジトリ構成
| パス | 役割 | 主な技術 |
|---|---|---|
pkgs/x402server |
天気 API と x402 決済検証を提供するバックエンド | Hono / x402 / Cloudflare Workers / TypeScript |
pkgs/mcpserver |
GPT App から呼び出される MCP サーバー。x402server を決済付きで呼び出す |
Hono MCP / MCP SDK / x402 fetch / Cloudflare Workers / TypeScript |
pkgs/*/__tests__ |
単体・結合テスト群 | Vitest |
ルート (package.json) |
monorepo の共通スクリプト・ワークスペース管理 | pnpm workspace |
リクエストの流れ
- GPT App から MCP ツール
get_weatherを実行 -
mcpserverがx402-fetch-clientで/weatherを呼び出し -
x402serverがpaymentMiddlewareで支払いを検証 - 検証後に天気データを返却
機能一覧
| 機能 | 概要 | 提供パッケージ | 補足 |
|---|---|---|---|
| ヘルスチェック API | サービス稼働確認 (/, /health) |
x402server, mcpserver
|
監視・疎通確認に利用 |
| 天気情報取得 API | 都市名を受けて天気を返却 (/weather) |
x402server |
都市未登録時は 404 |
| x402 課金付きアクセス制御 |
/weather を課金保護し未決済時は 402 を返却 |
x402server |
価格・ネットワークは環境変数で設定 |
| MCP ツール公開 |
get_weather ツールを外部クライアントへ公開 |
mcpserver |
入力検証・エラー整形を実施 |
| x402 決済付き fetch クライアント | 支払い情報付きで x402server を呼び出す |
mcpserver |
Service Binding |
Repository Structure
| Path | Role | Key Technologies |
|---|---|---|
pkgs/x402server |
Backend providing Weather API and x402 payment verification. | Hono / x402 / Cloudflare Workers / TypeScript |
pkgs/mcpserver |
MCP server called by GPT App. Calls x402server with payment. |
Hono MCP / MCP SDK / x402 fetch / Cloudflare Workers / TypeScript |
Root (package.json) |
Common scripts and workspace management for the monorepo. | pnpm workspace |
List of Implemented Features
| Feature | Overview | Provided Package | Notes |
|---|---|---|---|
| Health Check API | Service availability check (/, /health). |
x402server, mcpserver
|
Used for monitoring and connectivity checks. |
| Weather Info API | Returns weather based on city name (/weather). |
x402server |
Returns 404 if city is not registered. |
| x402 Paid Access Control | Protects /weather and returns 402 if unpaid. |
x402server |
Price and network configured via env vars. |
| MCP Tool Exposure | Exposes get_weather tool to external clients. |
mcpserver |
Performs input validation and error formatting. |
| x402 Payment-enabled Fetch Client | Calls x402server with payment information. |
mcpserver |
Supports both Service Binding and URLs. |
| E2E Integration Test | Verifies flow from MCP to backend. |
mcpserver tests |
Confirms 402/404/Success cases. |
Key Implementation Points
Let's pick up and introduce some important implementation parts.
x402 Server
First, the x402 server! You need to set environment variables in wrangler.jsonc. Since there's no highly sensitive information here, I'm not using the Secret feature for these.
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "x402server",
"main": "src/index.ts",
"compatibility_date": "2026-02-23",
"compatibility_flags": ["nodejs_compat"],
"vars": {
"SERVER_WALLET_ADDRESS": "0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072",
"FACILITATOR_URL": "https://x402.org/facilitator",
"X402_PRICE_USD": "$0.01",
"X402_NETWORK": "eip155:84532"
}
}
The x402 server is built on Hono. The main code is in src/app.ts. x402 middleware is applied only to specific routes.
import { paymentMiddleware } from "@x402/hono";
import { Hono } from "hono";
import { createRoutes } from "./route";
import { createResourceServer, resolvePaymentOptions } from "./utils/config";
import type { CreateAppOptions, ErrorResponse, WeatherService } from "./utils/types";
import { createMockWeatherService } from "./weather/service";
const toErrorResponse = (statusCode: number, message: string): ErrorResponse => ({
statusCode,
message,
});
export const createApp = (
weatherService: WeatherService = createMockWeatherService(),
options: CreateAppOptions = {},
): Hono => {
const app = new Hono();
const enablePayment = options.enablePayment ?? true;
if (enablePayment) {
const paymentOptions = resolvePaymentOptions(options.payment);
const resourceServer = createResourceServer(paymentOptions);
const routes = createRoutes(paymentOptions);
const protectedRouteKeys = new Set(Object.keys(routes));
let resourceServerInitialization: Promise<void> | null = null;
app.use(async (c, next) => {
const routeKey = `${c.req.method.toUpperCase()} ${c.req.path}`;
if (!protectedRouteKeys.has(routeKey)) return next();
if (!resourceServerInitialization) {
resourceServerInitialization = resourceServer.initialize().catch((error) => {
resourceServerInitialization = null;
throw error;
});
}
await resourceServerInitialization;
return next();
});
app.use(paymentMiddleware(routes, resourceServer, undefined, undefined, false));
}
app.get("/", (c) => c.json({ status: "ok" }, 200));
app.get("/health", (c) => c.json({ status: "ok" }, 200));
app.get("/weather", async (c) => {
const city = c.req.query("city")?.trim();
if (!city) return c.json(toErrorResponse(400, "city query parameter is required"), 400);
try {
const weather = await weatherService.getWeatherByCity(city);
if (!weather) return c.json(toErrorResponse(404, "city not found"), 404);
return c.json(weather, 200);
} catch {
return c.json(toErrorResponse(503, "weather service unavailable"), 503);
}
});
return app;
};
Regarding the weather retrieval logic: for verification purposes, I've implemented it to return hardcoded demo data instead of hitting an external API. In a production setting, this would be where you call an external service.
import { WeatherData, WeatherService } from "../utils/types";
const MOCK_WEATHER_DATA: ReadonlyArray<WeatherData> = [
{ city: "Tokyo", condition: "Sunny", temperatureC: 28, humidity: 60 },
{ city: "Osaka", condition: "Cloudy", temperatureC: 26, humidity: 65 },
{ city: "New York", condition: "Rainy", temperatureC: 22, humidity: 72 },
];
const normalizeCity = (city: string): string => {
const trimmed = city.trim().replace(/^['\"]+|['\"]+$/g, "");
const withoutCountry = trimmed.split(",")[0]?.trim() ?? trimmed;
return withoutCountry.toLowerCase();
};
export const createMockWeatherService = (): WeatherService => {
return {
async getWeatherByCity(city: string): Promise<WeatherData | null> {
const normalized = normalizeCity(city);
return MOCK_WEATHER_DATA.find((item) => normalizeCity(item.city) === normalized) ?? null;
},
};
};
x402-specific settings are consolidated in src/utils/config.ts. Implementing an x402 server requires configuring a facilitator and a resource server.
Briefly, a facilitator acts as a bridge between the API you want to apply x402 to and the blockchain, handling signature verification and transaction submission for payments. Facilitators are recommended to save development effort.
MCP Server
The MCP server is also based on Hono.
import { StreamableHTTPTransport } from "@hono/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { Hono } from "hono";
import { cors } from "hono/cors";
// ... (imports)
export const createApp = (options: CreateAppOptions = {}): Hono => {
const app = new Hono();
const mcpServer = new McpServer({ name: "x402-weather-payment-mcpserver", version: "1.0.0" });
// ... (logic to handle MCP via Streamable HTTP transport)
return app;
};
The x402 client setup is implemented in x402-fetch-client.ts.
// ... (X402FetchClient implementation using wrapFetch from @x402/fetch)
And the tool for retrieving weather information:
// ... (get_weather tool registration using McpServer.registerTool)
The Point I Got Stuck On
I learned this for the first time: when calling one Worker from another, you cannot simply specify the URL; you must use Bindings. You need to register this in your wrangler.jsonc.
{
"services": [
{
"binding": "X402SERVER",
"service": "x402server"
}
]
}
How to Deploy to Cloudflare Workers!
Now that the code is explained, let's look at the deployment!
Setup
Install dependencies:
pnpm i
x402 Backend Server
Deploy:
pnpm x402server run deploy
MCP Server
Condition: x402server must already be deployed! Register the private key for the x402 client and the x402server endpoint using Secrets.
pnpm mcpserver run secret CLIENT_PRIVATE_KEY --name mcpserver
pnpm mcpserver run secret X402_SERVER_URL --name mcpserver
Deploy:
pnpm mcpserver run deploy
Register https://mcpserver.<unique-id>.workers.dev/mcp as the GPT App URL to enable x402 payments from chat!
How to Call from Within ChatGPT
- Register the MCP server endpoint in your GPT App.
- Add the app from the + button and ask for the weather.
- If the weather is returned and USDC payment is processed, you're set!
Check the block explorer to confirm the stablecoin payment!
Summary
That's it for now! While you can achieve similar results using AWS Lambda or AgentCore, Cloudflare Workers is highly recommended for quick and easy experimentation. The sample code for x402 is abundant and fits perfectly!
Thank you for reading until the end!
References
- (Included original Japanese references)
- Official Site - Vibekanban
- GitHub - Vibekanban




Top comments (0)