---
title: "Testing & publishing"
description: "Test MCP Apps locally, pick a compatible host, and ship HTTPS endpoints for ChatGPT, Claude, and IDE clients."
canonical_url: "https://mcp-toolkit.nuxt.dev/apps/testing-publishing"
---
# Testing & publishing

> Test MCP Apps locally, pick a compatible host, and ship HTTPS endpoints for ChatGPT, Claude, and IDE clients.

## Host compatibility

<table>
<thead>
  <tr>
    <th>
      Host
    </th>
    
    <th>
      Render
    </th>
    
    <th>
      <code>
        sendPrompt
      </code>
    </th>
    
    <th>
      <code>
        callTool
      </code>
    </th>
    
    <th>
      <code>
        openLink
      </code>
    </th>
    
    <th>
      Notes
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <strong>
        Cursor
      </strong>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      Tested with the JSON-RPC bridge and legacy ready/resize messages.
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        ChatGPT (Apps SDK)
      </strong>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ⚠️
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      Uses <code>
        window.openai
      </code>
      
       when available. Follow-ups are sent, but the next tool is not always rendered inline.
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        MCP UI / Inspector-style hosts
      </strong>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      varies
    </td>
    
    <td>
      varies
    </td>
    
    <td>
      varies
    </td>
    
    <td>
      The bridge emits spec JSON-RPC messages plus legacy <code>
        mcp-ui
      </code>
      
       envelopes where useful. Verify each host before documenting support.
    </td>
  </tr>
</tbody>
</table>

The bridge auto-detects the host on handshake and adapts to its protocol — modern JSON-RPC for MCP UI hosts, the legacy `mcp-ui` envelope for older clients, the ChatGPT Apps SDK globals when present. Your code just calls `useMcpApp()`.

## Local test loop

MCP Apps use the same streamable HTTP endpoint as your tools, resources, and prompts. The default route is `/mcp` unless you set `mcp.route` in `nuxt.config`.

Start your Nuxt app:

<code-group>

```bash [pnpm]
pnpm dev
```

```bash [npm]
npm run dev
```

```bash [yarn]
yarn dev
```

```bash [bun]
bun dev
```

</code-group>

Your local MCP URL is:

```txt
http://localhost:3000/mcp
```

Use the [MCP Inspector](https://mcp-toolkit.nuxt.dev/getting-started/inspector), `npx @modelcontextprotocol/inspector@latest`, or the **MCP** panel in **Nuxt DevTools** first. Confirm that:

- the app SFC appears as a tool, for example `color-picker` or `release-control`;
- calling the tool returns `structuredContent`;
- the tool result includes a `text/html;profile=mcp-app` resource;
- the resource URI looks like `ui://mcp-app/<name>`.

Some clients can call the tool but cannot render the iframe. Use a host with MCP Apps support when validating the UI itself.

## Test in ChatGPT with ngrok

ChatGPT requires a public HTTPS endpoint, even for local development. Tunnel your local Nuxt server:

```bash
ngrok http 3000
```

ngrok prints a forwarding URL:

```txt
Forwarding https://your-subdomain.ngrok-free.dev -> http://localhost:3000
```

Use the `/mcp` route on that origin:

```txt
https://your-subdomain.ngrok-free.dev/mcp
```

In ChatGPT:

1. Enable **Developer Mode** in **Settings** → **Apps** → **Advanced settings**.
2. Open **Settings** → **Apps**.
3. Click **Create app**.
4. Enter your public MCP URL, including `/mcp`.
5. Enable the app and start a new chat.
6. Select your app from the composer before prompting the model.

Mention the app with `@` so ChatGPT routes the prompt through your connector:

```txt
@Weather App What is the weather in London?
```

Then test the iframe behaviour:

- click a button that calls `callTool()` and confirm the widget updates without a new chat turn;
- click a button that calls `sendPrompt()` and confirm a follow-up message appears in the conversation;
- click a button that calls `openLink()` and confirm ChatGPT opens or prompts for the external URL;
- watch your Nuxt terminal and ngrok dashboard for POST requests to `/mcp`.

If you expose local dev through ngrok, allow the tunnel host in Vite:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  vite: {
    server: {
      allowedHosts: ['.ngrok-free.dev', '.ngrok.app'],
    },
  },
})
```

For cross-origin hosts such as ChatGPT, configure MCP origin checks intentionally:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  mcp: {
    security: {
      allowedOrigins: '*',
    },
  },
})
```

Use `'*'` only for demos or public read-only surfaces. For production apps with private data or real side effects, use a list of trusted origins.

## Deploy to Vercel or another cloud

MCP Apps do **not** use a second URL. Deploy the Nuxt app and connect ChatGPT to the same MCP route:

```txt
https://your-project.vercel.app/mcp
```

The toolkit auto-detects `_meta.ui.domain` from common hosting variables:

- `NUXT_PUBLIC_APP_URL`;
- Vercel: `VERCEL_PROJECT_PRODUCTION_URL`, `VERCEL_URL`;
- Netlify: `URL`;
- Render: `RENDER_EXTERNAL_URL`;
- Railway: `RAILWAY_PUBLIC_DOMAIN`;
- Fly: `FLY_APP_NAME`.

If you need to override the detected value, set it explicitly:

```vue [app/mcp/my-app.vue]
<script setup lang="ts">
defineMcpApp({
  _meta: {
    ui: {
      domain: 'https://my-app.example.com',
    },
  },
})
</script>
```

For Vercel, make sure your build includes the MCP Apps bundling dependencies from the toolkit version you deploy. If you test an unreleased package, use a fresh `pkg.pr.new` build or install the required build dependencies in the app until the package is published.

## Prepare for ChatGPT submission

OpenAI reviews apps before broad distribution. Before submitting, check:

- your MCP server is publicly reachable over HTTPS;
- each app resource returns `text/html;profile=mcp-app`;
- each app tool has clear `description`, `inputSchema`, and behavior annotations;
- `_meta.ui.resourceUri` is present (the toolkit generates it);
- `_meta.ui.domain` is set or auto-detected;
- CSP is explicit for external images, scripts, fonts, APIs, or iframes;
- `structuredContent` is concise and safe for the model to read;
- large or widget-only data goes in `_meta`, not in `structuredContent`;
- OAuth, privacy policy, app name, screenshots, and test prompts are ready if your app needs public distribution.

When the host exposes the **Apps SDK** (`window.openai`) or enforces widget CSP, the toolkit mirrors your `csp` to **_meta.ui.csp** and **openai/widgetCSP**. Behaviour, especially `sendPrompt` and follow-up re-renders, can change between ChatGPT releases. Re-test your app before submitting or announcing support.

## Troubleshooting

### `403 Forbidden` from `/mcp`

The request reached your server but failed an origin or host check.

- For ChatGPT/ngrok demos, set `mcp.security.allowedOrigins` to `'*'` or to a trusted origin list.
- For Vite dev server tunnels, add the tunnel domain to `vite.server.allowedHosts`.
- Restart the Nuxt dev server after changing `nuxt.config.ts`.

### Widget does not render

Check the tool result:

- The resource MIME type must be `text/html;profile=mcp-app`.
- The resource URI should match `_meta.ui.resourceUri`.
- The host must support MCP Apps. Some MCP clients can call the tool but only display text.

### No `ui/*` messages arrive

The iframe loaded, but the host did not enable the MCP Apps bridge.

- Confirm the host renders the HTML as an MCP App resource, not as a plain file.
- Check the browser console inside the host devtools for CSP or sandbox errors.
- Test in Cursor or ChatGPT to compare host behaviour.

### CSP blocks images or fetch calls

Add explicit allow-lists:

```ts
defineMcpApp({
  csp: {
    resourceDomains: ['https://images.example.com'],
    connectDomains: ['https://api.example.com'],
  },
})
```

The toolkit mirrors these to `_meta.ui.csp` and `openai/widgetCSP` for hosts that enforce widget CSP.

### Vercel cannot find generated MCP App files

Use a toolkit version that includes the MCP Apps build fixes. Older unreleased builds may import `.nuxt/mcp-apps/gen/*.ts` at runtime, which does not exist inside the deployed Vercel function.

### `vite-plugin-singlefile` or `@vitejs/plugin-vue` is missing

Use a toolkit version that ships the MCP Apps bundling dependencies, or install them in the app while testing an unreleased build.

### ChatGPT keeps showing an old widget

ChatGPT can cache widget templates by URI. If you make a breaking UI change, change the resource URI or file name so hosts load a fresh template.

## Other clients

**Claude Code**, **Claude Desktop** (where remote MCP is enabled), **Cursor**, **VS Code**, and **Codex**-style clients connect to your server with the same MCP URL you use for tools. There is no separate “apps-only” endpoint.

Web and mobile ChatGPT/Claude apps may not render MCP UI widgets on every release channel. Always verify inline iframes on the product version you support.

Prefer Cursor as the main development vehicle when debugging the JSON-RPC UI bridge; it is the most exercised environment in this module’s test suite.

## Related

- [CSP & build pipeline](https://mcp-toolkit.nuxt.dev/apps/csp-and-wiring) — how HTML and `_meta` are produced.
- [Patterns & limits](https://mcp-toolkit.nuxt.dev/apps/patterns-reference) — iframe constraints and API cheat sheet.
- [Handlers](https://mcp-toolkit.nuxt.dev/handlers/overview) — optional `https://…/mcp/apps` surface if you split apps from other tools.
- [OpenAI: Build your MCP server](https://developers.openai.com/apps-sdk/build/mcp-server)
- [OpenAI: Connect from ChatGPT](https://developers.openai.com/apps-sdk/deploy/connect-chatgpt)
- [OpenAI: Test your integration](https://developers.openai.com/apps-sdk/deploy/testing)
- [OpenAI: Submit and maintain your app](https://developers.openai.com/apps-sdk/deploy/submission)
- [OpenAI: App submission guidelines](https://developers.openai.com/apps-sdk/app-submission-guidelines)


## Sitemap

See the full [sitemap](https://mcp-toolkit.nuxt.dev/sitemap.md) for all pages.
