This guide is the xmcp entry from our seven-framework deploy comparison. We shipped the same example on Manufact Cloud: an echo tool and a greet_widget that returns a MCP Apps view. Below is the deploy path we used; the reference server code is in the second half if you want to reproduce the example.
If you want to run the same deploy pipeline on your repo, connect it in the dashboard. Open the dashboard.
Deploy to Manufact
xmcp deploys cleanly on Manufact Cloud once you set the right port. Because xmcp bakes the port into the build (Zod default 3001), the platform side has to match.
Push the repo to GitHub
Open the new-server flow
Go to manufact.com/cloud/<your-org>/servers/new and pick Deploy from GitHub. The framework probe labels the repo xmcp.
Set the port to 3001
The preset fills npm run build / npm start correctly, but the port field defaults to 3000. Change it to 3001 to match xmcp's hardcoded runtime port. This makes Fly route traffic to the right port and sets PORT=3001 for the container (which xmcp ignores anyway, but consistency matters).
Click Deploy
Build takes ~60s (Rspack pulls in @rspack/binding-linux-x64-musl for the Alpine image: Rspack ships musl wheels, so no node-gyp issues). Once you see Server status: running, the URL is live.
Smoke-test the URL
greet_widget._meta["ui/resourceUri"] should be ui://app/greet_widget.html, and resources/list returns the auto-generated HTML resource with mimeType: "text/html;profile=mcp-app".
Example repo: manufacts/mcp-detect-xmcp
Live /mcp: keen-steel-ix5iy.run.mcp-use.com/mcp — serverInfo.name is xmcp server (example repo mcp-detect-xmcp; Manufact server status: running).
Why port 3001?
xmcp serializes its config at build time. The Zod schema port: z.number().default(3001) injects 3001 even if you set http: true (no port): process.env.PORT is never read at runtime. Either set the platform port to 3001 (what we did) or set http.port: 3000 in xmcp.config.ts and rebuild for that target.
The steps above are what we used for the live demo. Your repo can use the same GitHub deploy flow. Open the dashboard.
Reference server
Use this section if you are following along with the same example. If you already have an MCP app to deploy, the GitHub steps above are enough.
What we deployed
- An xmcp project scaffolded from the official
mcp-apptemplate - An
echotool (plain.tsfile: no UI) - A
greet_widgettool (.tsx: React handler, auto-registered as MCP App view)
Project setup
The starter ships with one weather widget; we'll replace it.
Project layout:
package.json scripts:
Configure the transport
xmcp.config.ts:
A subtle gotcha: xmcp serializes this config at build time. Its Zod schema has port: z.number().default(3001), so omitting port still bakes 3001 into the build. The deploy target needs to be told port=3001, or you bake a different port at build time. There's no runtime process.env.PORT override path. (More on this in the field report.)
The echo tool
src/tools/echo.ts:
Three exports: schema, metadata, default handler. xmcp turns a string return into a text/content MCP response automatically.
The widget tool
src/tools/greet_widget.tsx:
Three things to notice:
- The
.tsxextension flips xmcp into widget mode: it builds the React component into a self-contained HTML resource, registers atext/html;profile=mcp-appresource, and writes_meta.ui.resourceUrion the tool. _meta.ui.csp.connectDomainsdeclares the widget's allowed network origins for the host's Content Security Policy. Empty means "no outgoing requests."connectDomains: ["https://api.example.com"]would whitelist that origin.prefersBorder: trueis a hint to the host to render the widget inside a bordered card.
The widget's name prop is the tool's input: xmcp passes args straight through to the React component.
What the host sees
After npm run build && npm start:
tools/list returns:
resources/list returns the auto-bundled HTML resource:
You wrote a single .tsx file. xmcp produced the resource, the metadata, the bundled HTML, and the wiring.
Run it
Or in dev with hot reload:
The dev server watches src/tools/, rebuilds on change, and reconnects clients.
When to reach for it
Pick this when file-based routing matters more to you than runtime flexibility, and you're comfortable with the Rspack toolchain and the build-time-baked port. The file-based router and npx xmcp create widget <name> scaffolder genuinely remove decisions if you stay on the happy path. The friction shows up as soon as you don't: Rspack-only build, port hardcoded by Zod default, no runtime process.env.PORT override.
If you want widget ergonomics with fewer baked-in opinions, mcp-use keeps the React-component-per-widget pattern without the build-time port and toolchain lock-in. The full comparison is in Deploying Seven MCP Frameworks.









