Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions examples/cloudflare-workers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,22 @@ This writes these files into the project root:
- `worker.js` - The bundled Cloudflare Worker
- `wrangler.jsonc` - Wrangler configuration template (only if you don't already have `wrangler.toml/jsonc`)

3. Test locally with Wrangler:
3. Start development mode (watch + rebuild worker output) and Wrangler:

```bash
pnpm dev
```

This runs `xmcp dev --cf` (rebuilds `worker.js`) and `wrangler dev` together.

4. Test with curl:

```bash
pnpm preview
# or
npx wrangler dev
```

4. Test with curl:

```bash
# Health check
curl http://localhost:8787/health
Expand Down
5 changes: 3 additions & 2 deletions examples/cloudflare-workers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"type": "module",
"scripts": {
"build": "xmcp build --cf",
"dev": "xmcp dev",
"dev": "concurrently \"xmcp dev --cf\" \"npx wrangler dev\"",
"deploy": "npx wrangler deploy",
"preview": "npx wrangler dev"
},
Expand All @@ -16,7 +16,8 @@
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20241112.0",
"concurrently": "^9.2.0",
"typescript": "^5.7.2",
"wrangler": "^4.61.1"
"wrangler": "^4.62.0"
}
}
6 changes: 6 additions & 0 deletions packages/create-xmcp-app/src/helpers/cloudflare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ export function applyCloudflareSettings(projectPath: string): void {

packageJson.scripts = packageJson.scripts ?? {};
packageJson.scripts.build = "xmcp build --cf";
if (!packageJson.scripts.dev || packageJson.scripts.dev === "xmcp dev") {
packageJson.scripts.dev =
"concurrently \"xmcp dev --cf\" \"npx wrangler dev\"";
}
Comment on lines 90 to 93

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid backgrounding xmcp dev in npm script

Using xmcp dev --cf & npx wrangler dev is shell-specific: in Windows' default npm shell (cmd.exe), & is just a command separator, so xmcp dev --cf blocks forever and wrangler dev never starts. On POSIX shells, xmcp dev is backgrounded, so Ctrl+C only stops wrangler dev and leaves the xmcp watcher running. This makes pnpm dev unreliable or hard to stop depending on the environment. Consider using a cross-platform process runner (e.g., concurrently) or a script that properly manages both processes.

Useful? React with 👍 / 👎.

packageJson.scripts.deploy =
packageJson.scripts.deploy ?? "npx wrangler deploy";
packageJson.scripts.preview =
Expand All @@ -97,6 +101,8 @@ export function applyCloudflareSettings(projectPath: string): void {
}

packageJson.devDependencies = packageJson.devDependencies ?? {};
packageJson.devDependencies.concurrently =
packageJson.devDependencies.concurrently ?? "^9.2.0";
packageJson.devDependencies.wrangler =
packageJson.devDependencies.wrangler ?? DEFAULT_WRANGLER_VERSION;
packageJson.devDependencies["@cloudflare/workers-types"] =
Expand Down
9 changes: 6 additions & 3 deletions packages/xmcp/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@ program.name("xmcp").description("The MCP framework CLI").version("0.0.1");
program
.command("dev")
.description("Start development mode")
.action(async () => {
.option("--cf", "Enable Cloudflare Workers output in development")
.action(async (options) => {
console.log(`${xmcpLogo} Starting development mode...`);
const isCloudflareDev = options.cf || process.env.CF_PAGES === "1";
await compilerContextProvider(
{
mode: "development",
// Ignore platforms on dev mode
platforms: {},
platforms: {
cloudflare: isCloudflareDev,
},
},
async () => {
await compile();
Expand Down
16 changes: 14 additions & 2 deletions packages/xmcp/src/compiler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { isValidPath } from "@/utils/path-validation";
import { getResolvedPathsConfig } from "./config/utils";
import { pathToToolName } from "./utils/path-utils";
import { transpileClientComponent } from "./client/transpile";
import { buildCloudflareOutput } from "../platforms/build-cloudflare-output";
const { version: XMCP_VERSION } = require("../../package.json");
dotenv.config();

Expand All @@ -43,7 +44,7 @@ export interface CompileOptions {
}

export async function compile({ onBuild }: CompileOptions = {}) {
const { mode, toolPaths, promptPaths, resourcePaths } =
const { mode, toolPaths, promptPaths, resourcePaths, platforms } =
compilerContext.getContext();
const startTime = Date.now();
let compilerStarted = false;
Expand Down Expand Up @@ -177,7 +178,7 @@ export async function compile({ onBuild }: CompileOptions = {}) {
// Generate all code (including client bundles) BEFORE bundler runs
await generateCode();

rspack(bundlerConfig, (err, stats) => {
rspack(bundlerConfig, async (err, stats) => {
// Track compilation time
let compilationTime: number;
if (stats?.endTime && stats?.startTime) {
Expand Down Expand Up @@ -280,6 +281,17 @@ export async function compile({ onBuild }: CompileOptions = {}) {
}
}

if (mode === "development" && platforms.cloudflare) {
try {
await buildCloudflareOutput({ log: firstBuild });
} catch (error) {
console.error(
chalk.red("❌ Failed to sync Cloudflare Workers output:"),
error
);
}
}

if (mode === "production") {
logBuildSuccess({
...collectBaseTelemetryData(),
Expand Down
21 changes: 15 additions & 6 deletions packages/xmcp/src/platforms/build-cloudflare-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ import fs from "fs";

const rootDir = process.cwd();

interface BuildCloudflareOutputOptions {
log?: boolean;
}

/**
* Build the Cloudflare Workers output structure.
* Writes Cloudflare Worker files to the project root:
* - worker.js
* - wrangler.jsonc (only if not already present)
*/
async function buildCloudflareOutput() {
async function buildCloudflareOutput(
options: BuildCloudflareOutputOptions = {}
) {
const { log = true } = options;
const outputDir = rootDir;
const buildDir = path.join(rootDir, ".xmcp", "cloudflare");

Expand All @@ -32,11 +39,13 @@ async function buildCloudflareOutput() {
fs.writeFileSync(wranglerJsoncPath, wranglerConfig);
}

console.log("Cloudflare Workers output structure created successfully");
console.log("");
console.log("Next steps:");
console.log(" 1. npx wrangler dev # Test locally");
console.log(" 2. npx wrangler deploy # Deploy to Cloudflare");
if (log) {
console.log("Cloudflare Workers output structure created successfully");
console.log("");
console.log("Next steps:");
console.log(" 1. npx wrangler dev # Test locally");
console.log(" 2. npx wrangler deploy # Deploy to Cloudflare");
}
}

/**
Expand Down
Loading