Cookie preferences

We use cookies to improve your experience. See our Privacy Policy.

Manufact

How to Deploy mcp-use to Production

Enrico Toniato
Enrico ToniatoCTO
How to Deploy mcp-use to Production

This guide is the mcp-use 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 was built to run mcp-use servers natively: the framework detector recognizes mcp-use as a dependency and the preset wires the right build/start commands automatically.

Push the repo to GitHub

git init && git add . && git commit -m "Initial commit" gh repo create my-org/mcp-use-greet --private --source=. --push

Make sure resources/greet-widget/widget.tsx is committed: mcp-use bundles it at server boot from the repo, not from any build artifact.

Open the new-server flow

Go to manufact.com/cloud/<your-org>/servers/new and pick Deploy from GitHub. The framework detection step labels the repo mcp-use SDK.

Confirm build and start

The preset fills:

  • Build command: npm run build
  • Start command: npm start
  • Port: 3000

Leave them. The cloud's generated Dockerfile installs deps (including mcp-use's Vite peer for widget bundling), runs tsc, and runs node dist/index.js.

Click Deploy

The first build is 60–120 seconds: mcp-use pulls down its widget toolchain on npm install. Once you see Server status: running, the URL is live.

Open the inspector at the live URL

Every mcp-use deployment ships a built-in inspector at /inspector:

https://<your-slug>.run.mcp-use.com/inspector

Call greet_widget from the inspector's tools panel: the widget renders inline. You're seeing exactly what ChatGPT or Claude will render when they call your server.

Smoke-test the protocol

# Initialize first to get a session ID curl -s -i -X POST -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"1"}}}' \ https://<your-slug>.run.mcp-use.com/mcp

Grab the mcp-session-id header from the response and reuse it for tools/list. mcp-use is stateful by default; if your client can't initialize, set statelessHttp: true on the constructor and redeploy.

Example repo: manufacts/mcp-detect-mcp-use

Live /mcp: wild-forge-dj9bw.run.mcp-use.com/mcpserverInfo.name is mcp-detect-mcp-use (Manufact server status: running).

Tip

Public chat for free

Manufact's per-server Public Chat turns your deployment into a hosted conversational UI at https://<slug>.run.mcp-use.com/chat. Toggle it on from the server's Public Chat tab: useful for sharing the widget with non-technical users without writing a frontend.

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 MCPServer.listen() and a built-in inspector
  • An echo tool that returns text via the text() helper
  • A greet_widget tool whose response is a React widget rendered in the host

Project setup

mkdir my-server && cd my-server npm init -y npm pkg set type=module npm pkg set scripts.build="tsc" npm pkg set scripts.start="node dist/index.js" npm install mcp-use zod npm install -D typescript @types/node

tsconfig.json:

{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "rootDir": "src", "outDir": "dist", "strict": true, "skipLibCheck": true, "esModuleInterop": true }, "include": ["src/**/*.ts"] }

The resources/ directory at the repo root is not included in tsc: mcp-use bundles it separately at runtime via Vite, so it's free to use JSX without leaking React types into your server build.

The server

src/index.ts:

import { MCPServer, text, widget } from "mcp-use/server"; import { z } from "zod"; const server = new MCPServer({ name: "my-server", version: "0.1.0", description: "Greeting demo with mcp-use widgets.", host: "0.0.0.0", // bind on all interfaces for cloud deploy }); server.tool( { name: "echo", title: "Echo", description: "Echo the input back as text.", schema: z.object({ text: z.string() }), }, async ({ text: t }) => text(t), ); server.tool( { name: "greet_widget", title: "Greet (widget)", description: "Greet someone and render a Greet widget.", schema: z.object({ name: z.string() }), widget: { name: "greet-widget", // matches resources/greet-widget/widget.tsx invoking: "Preparing greeting...", invoked: "Greeting ready", }, }, async ({ name }) => widget({ props: { name }, // structuredContent for the widget output: text(`Hello, ${name}!`), // text content for the model }), ); const port = Number(process.env.PORT ?? "3000"); await server.listen(port);

The widget

resources/greet-widget/widget.tsx:

import { useWidget, type WidgetMetadata } from "mcp-use/react"; import { z } from "zod"; const propSchema = z.object({ name: z.string(), }); export const widgetMetadata: WidgetMetadata = { description: "Greet a user with a small card.", props: propSchema, }; const GreetWidget: React.FC = () => { const { props, isPending, theme } = useWidget<z.infer<typeof propSchema>>(); const isDark = theme === "dark"; if (isPending) return <div>Preparing greeting…</div>; return ( <div style={{ background: isDark ? "#1a1a2e" : "#f0f4ff", color: isDark ? "#fff" : "#101032", borderRadius: 16, padding: 24, fontFamily: "system-ui, sans-serif", }} > <h1 style={{ margin: "0 0 8px" }}>Hello, {props.name}!</h1> <p style={{ margin: 0, opacity: 0.75, fontSize: 13 }}> Greeting widget served by my-server. </p> </div> ); }; export default GreetWidget;

useWidget() returns the props sent in the tool's widget({ props }) call, plus theme, isPending, and a few helpers for client-driven state. Theming is built in.

What the host sees

When you call tools/list, mcp-use auto-attaches all the _meta keys hosts care about:

{ "name": "greet_widget", "_meta": { "ui": { "resourceUri": "ui://widget/greet-widget.html" }, "ui/resourceUri": "ui://widget/greet-widget.html", "openai/outputTemplate": "ui://widget/greet-widget.html", "openai/widgetCSP": { "connect_domains": ["http://localhost:3000"] }, "openai/description": "Greet a user with a small card." } }

resources/list includes the auto-bundled HTML resource:

{ "uri": "ui://widget/greet-widget.html", "mimeType": "text/html;profile=mcp-app" }

You wrote a tool definition, a React component, and one helper call inside the handler. mcp-use produced all of the above.

Run it

npm run build npm start

Boot logs print every tool, every widget, and the inspector URL:

📋 Server exposes: Tools: 2 - echo - greet_widget Resources: 2 - greet-widget - greet-widget-dynamic [INSPECTOR] UI available at http://0.0.0.0:3000/inspector [SERVER] Listening on http://0.0.0.0:3000 [MCP] Endpoints: http://0.0.0.0:3000/mcp

The inspector is a full MCP client at /inspector with BYOK chat: call your tools and see the widget render in the same window.

When to reach for it

You want the lowest possible distance between "I have an idea for a widget" and "users can call it." The widget DSL is opinionated but unobtrusive: the React component is just a React component, and you can drop down to manual _meta whenever you need protocol-level control. For teams shipping multiple widgets a week, this is what we built mcp-use for.

The full comparison across all seven frameworks is in Deploying Seven MCP Frameworks.

Share