# How to Deploy mcp-use to Production

Enrico Toniato·CTO·May 10, 2026

This guide is the **mcp-use** entry from our [seven-framework deploy comparison](/content/blog/deploying-seven-mcp-frameworks/index.html). We shipped the same example on Manufact Cloud: an `echo` tool and a `greet_widget` that returns a [MCP Apps](https://github.com/modelcontextprotocol/ext-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](/content/login/index.html).

## 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.

1

### 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.

2

### Open the new-server flow

Go to [`manufact.com/cloud/<your-org>/servers/new`](/content/site-root.html) and pick **Deploy from GitHub**. The framework detection step labels the repo **mcp-use SDK**.

3

### 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`.

4

### 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.

5

### 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.

6

### 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`](https://github.com/manufacts/mcp-detect-mcp-use)

**Live `/mcp`:** [`wild-forge-dj9bw.run.mcp-use.com/mcp`](https://wild-forge-dj9bw.run.mcp-use.com/mcp) — `serverInfo.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](/content/login/index.html).

## 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](/content/blog/deploying-seven-mcp-frameworks/index.html).
