This guide is the tmcp 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
Manufact Cloud detects tmcp by the tmcp dependency and labels the repo accordingly. The deploy is the standard Node.js path (npm run build, npm start, port 3000) with no framework-specific quirks.
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 tmcp.
Confirm build and start
Preset fills:
- Build command:
npm run build - Start command:
npm start - Port:
3000
The cloud's generated Dockerfile clones the repo, runs npm install, runs tsc, and starts node dist/index.js.
Click Deploy
First build is fast (~30s); tmcp has minimal dependencies. Once you see Server status: running, the URL is live.
Smoke-test the URL
Confirm greet_widget._meta.ui.resourceUri and _meta["ui/resourceUri"] both point at your view URI, and resources/list returns it with mimeType: "text/html;profile=mcp-app".
Example repo: manufacts/mcp-detect-tmcp
Live /mcp: fast-wave-zubi2.run.mcp-use.com/mcp — serverInfo.name is mcp-detect-tmcp (Manufact server status: running).
Switch to Workers later
Because tmcp's HTTP transport returns a fetch Response, you can move the same handler to Cloudflare Workers without rewriting tool registration; only the listener changes. The Manufact deployment uses Node + Fly today; the tmcp code itself is portable.
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 MCP server with
McpServerfromtmcpand a Zod-v4 adapter - An
echotool - A
greet_widgettool that follows the MCP Apps protocol manually: your own HTML, your own resource, your own metadata
Project setup
@tmcp/transport-http returns a Response; @remix-run/node-fetch-server adapts that into a Node http.createServer listener. Here zod is v4, which matches what the tmcp Zod adapter expects.
tsconfig.json:
The server
src/index.ts:
A few details worth noting:
outputSchemais required when you returnstructuredContent. tmcp validates that the response shape matches what you declared. Hosts that readoutputSchemause it to type the widget'suseToolInfohook (where applicable).- Both metadata keys.
_meta.ui.resourceUriis the modern key;_meta["ui/resourceUri"]is the legacy key. Newer hosts read the first; older hosts read the second. Set both. - The transport handler returns
Response | null. Null means "not my path." Your HTTP layer decides what to do with non-MCP routes.
What the host sees
Returns:
And resources/list returns the view at the matching URI with the right MIME type.
Run it
Then probe:
You should see { structuredContent: { name: "World" }, content: [{ type: "text", text: "Hello, World!" }] }.
When to reach for it
Pick this when you're embedding MCP into a host you already have (a Worker, a Hono app, a Fastify plugin), you want a small schema-agnostic core, and you're fine writing the MCP Apps wiring by hand. The fetch-shaped transport is portable across Node, Bun, Deno, and Workers without a rewrite, which matters when the MCP server is one piece of a larger system.
For a standalone MCP server that ships widgets as its main job, mcp-use gives you the same protocol fidelity with a widget() helper that auto-registers the resource so you don't repeat the wiring on every tool. The full comparison is in Deploying Seven MCP Frameworks.









