branch:
server.ts
3342 bytesRaw
import { createMcpHandler } from "agents/mcp";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
import { openApiMcpServer } from "@cloudflare/codemode/mcp";
const CLOUDFLARE_SPEC_URL =
"https://raw.githubusercontent.com/cloudflare/api-schemas/main/openapi.json";
let specCache: Record<string, unknown> | null = null;
async function getSpec(): Promise<Record<string, unknown>> {
if (specCache) return specCache;
const res = await fetch(CLOUDFLARE_SPEC_URL);
if (!res.ok) throw new Error(`Failed to fetch spec: ${res.status}`);
specCache = (await res.json()) as Record<string, unknown>;
return specCache;
}
export default {
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext
): Promise<Response> {
// Extract API token from Authorization header
const authHeader = request.headers.get("Authorization");
const token = authHeader?.startsWith("Bearer ")
? authHeader.slice(7)
: null;
if (!token) {
return new Response(
JSON.stringify({
error: "Authorization header with Bearer token required"
}),
{ status: 401, headers: { "Content-Type": "application/json" } }
);
}
const spec = await getSpec();
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });
const server = openApiMcpServer({
spec,
executor,
name: "cloudflare",
description: `This server wraps the Cloudflare API. Replace path parameters like {account_id} and {zone_id} with real values from a prior search or list call.
// List all zones (accounts the token can access)
async () => {
return await codemode.request({ method: "GET", path: "/zones" });
}
// List Workers scripts in an account
async () => {
const zones = await codemode.request({ method: "GET", path: "/zones" });
const accountId = zones.result[0].account.id;
return await codemode.request({
method: "GET",
path: \`/accounts/\${accountId}/workers/scripts\`
});
}
// Create a DNS record
async () => {
return await codemode.request({
method: "POST",
path: "/zones/{zone_id}/dns_records",
body: { type: "A", name: "example.com", content: "1.2.3.4", ttl: 3600 }
});
}`,
// This is where you call your API. Runs on the host — auth, base URL,
// headers are all yours. The sandbox never sees tokens or secrets.
request: async (opts) => {
const url = new URL(`https://api.cloudflare.com/client/v4${opts.path}`);
if (opts.query) {
for (const [key, value] of Object.entries(opts.query)) {
if (value !== undefined) url.searchParams.set(key, String(value));
}
}
const headers: Record<string, string> = {
Authorization: `Bearer ${token}`
};
if (opts.contentType) {
headers["Content-Type"] = opts.contentType;
} else if (opts.body) {
headers["Content-Type"] = "application/json";
}
const res = await fetch(url.toString(), {
method: opts.method,
headers,
body: opts.body
? opts.rawBody
? (opts.body as string)
: JSON.stringify(opts.body)
: undefined
});
return await res.json();
}
});
return createMcpHandler(server)(request, env, ctx);
}
};