---
title: "Integration"
description: "Mount the inspector on Express or Hono applications"
icon: "plug-2"
---

The MCP Inspector can be integrated directly into your Express or Hono applications, making it available at a custom path alongside your MCP server.

## Auto-Mounting with mcp-use

When using `mcp-use` to create your MCP server, the inspector is automatically available at `/inspector`.

### Example

```typescript
import { MCPServer } from 'mcp-use/server'

const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
})

// Add your tools, resources, prompts...
server.tool(/* ... */)

// Start the server
server.listen(3000)

// 🎉 Inspector automatically available at http://localhost:3000/inspector
```

<Note>
The inspector is automatically mounted when using `mcp-use/server`. No additional configuration needed.
</Note>

## Manual Integration

### Express Integration

Mount the inspector on an Express application:

```typescript
import express from 'express'
import { mountInspector } from '@mcp-use/inspector'

const app = express()

// Your Express routes
app.get('/api/health', (req, res) => {
  res.json({ status: 'ok' })
})

// Mount inspector at /inspector
mountInspector(app)

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000')
  console.log('Inspector available at http://localhost:3000/inspector')
})
```

### Hono Integration

Mount the inspector on a Hono application:

```typescript
import { Hono } from 'hono'
import { mountInspector } from '@mcp-use/inspector'

const app = new Hono()

// Your Hono routes
app.get('/api/health', (c) => {
  return c.json({ status: 'ok' })
})

// Mount inspector at /inspector
mountInspector(app)

// Start server with your Hono adapter
export default app
```

## Path Customization

The inspector is mounted at `/inspector` by default. To use a different path, you can use a path prefix with your framework:

### Express with Custom Path

```typescript
import express from 'express'
import { mountInspector } from '@mcp-use/inspector'

const app = express()

// Mount at custom path using Express router
const inspectorRouter = express.Router()
mountInspector(inspectorRouter)

app.use('/debug', inspectorRouter)
// Inspector now available at http://localhost:3000/debug/inspector
```

### Hono with Custom Path

```typescript
import { Hono } from 'hono'
import { mountInspector } from '@mcp-use/inspector'

const app = new Hono()

// Create sub-app for inspector
const inspectorApp = new Hono()
mountInspector(inspectorApp)

app.route('/debug', inspectorApp)
// Inspector now available at http://localhost:3000/debug/inspector
```

## Configuration Options

### `mountInspector` Options

The `mountInspector` function accepts an optional configuration object:

```typescript
import { mountInspector } from '@mcp-use/inspector'

mountInspector(app, {
  autoConnectUrl: 'http://localhost:3000/mcp',  // Auto-connect to an MCP server
  devMode: true,                                  // Enable development mode
  sandboxOrigin: 'https://sandbox.example.com',   // Custom sandbox origin
})
```

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `autoConnectUrl` | `string \| null` | `undefined` | MCP server URL to auto-connect on load |
| `devMode` | `boolean` | `true` if not production | Enables same-origin sandbox for MCP Apps widgets (no subdomain required) |
| `sandboxOrigin` | `string \| null` | `undefined` | Override the sandbox origin for MCP Apps widgets |

#### `devMode`

When `devMode` is `true`, the inspector uses same-origin for the MCP Apps widget sandbox instead of requiring a `sandbox-{hostname}` subdomain. This is essential when running behind reverse proxies (ngrok, E2B, Cloudflare tunnels) where that subdomain does not exist.

When using `mcp-use/server`, `devMode` is automatically set to `true` in development and `false` in production.

#### `sandboxOrigin`

Override the sandbox origin used for MCP Apps widget iframes. Use this when you have a specific domain configured for sandboxed content in production.

## Embeddable Chat Components

The inspector also exports client-side chat UI components from `@mcp-use/inspector/client` for embedding into your own React app.

### `ChatTab` Quick Start

Use `ChatTab` when you want the full chat experience with the smallest integration surface.

```typescript
import { ChatTab } from '@mcp-use/inspector/client'

<ChatTab
  connection={connection}
  isConnected={isConnected}
  prompts={prompts}
  serverId={serverId}
  callPrompt={callPrompt}
  readResource={readResource}
  hideTitle
  hideModelBadge
  hideServerUrl
  clearButtonLabel="Start over"
  clearButtonHideShortcut
  clearButtonVariant="ghost"
/>
```

### `ChatTab` Customization Props

| Prop | Type | Description |
|------|------|-------------|
| `hideTitle` | `boolean` | Hides the chat header title (`Chat`) |
| `hideModelBadge` | `boolean` | Hides the selected model badge in the landing state |
| `hideServerUrl` | `boolean` | Hides the MCP server URL shown in the landing state |
| `clearButtonLabel` | `string` | Custom label for the clear/new-chat button |
| `clearButtonHideIcon` | `boolean` | Hides the clear/new-chat button icon |
| `clearButtonHideShortcut` | `boolean` | Hides the keyboard shortcut hint on the clear/new-chat button |
| `clearButtonVariant` | `"default" \| "secondary" \| "ghost" \| "outline"` | Visual button variant for the clear/new-chat button |
| `hideToolSelector` | `boolean` | Hides the tool selector in the chat input (when visible, disabled tools are sent to the stream API as `disabledTools`) |
| `streamProtocol` | `"sse" \| "data-stream"` | Wire format for the streaming endpoint: Inspector SSE (default) or Vercel AI SDK data-stream |
| `credentials` | `RequestCredentials` | `fetch` credentials, e.g. `"include"` for cross-origin cookie sessions |
| `extraHeaders` | `Record<string, string>` | Additional headers on every streaming `POST` |
| `body` | `(messages) => unknown` | Custom JSON body builder; use to send only `{ messages }` (or your own shape) to a backend you control |

### Advanced Composition (`MessageList` / `ChatHeader` / `ChatLandingForm`)

For custom layouts, compose lower-level exports from `@mcp-use/inspector/client`.

When using `MessageList`, pass `serverBaseUrl` from your MCP connection URL so widget resource rendering uses the correct server origin:

```typescript
import { MessageList } from '@mcp-use/inspector/client'

<MessageList
  messages={messages}
  isLoading={isLoading}
  serverId={connection.url}
  serverBaseUrl={connection.url}
  tools={connection.tools}
  readResource={readResource}
  sendMessage={(text) => sendMessage(text, [])}
/>
```

### Environment Detection

The inspector automatically detects the framework:

- **Express**: Detects Express app instance
- **Hono**: Detects Hono app instance
- **Auto-detection**: Works with both frameworks

### Client Files

The inspector includes built client files. If files are missing, you'll see a warning:

```
⚠️  MCP Inspector client files not found at /path/to/files
   Run 'yarn build' in the inspector package to build the UI
```

**Solution:** Ensure the inspector package is properly built before mounting.

## Best Practices

### Development vs Production

**Development:**
- Mount inspector directly in development
- Use default `/inspector` path
- Enable hot reload for inspector UI

**Production:**
- Consider mounting only in staging environments
- Use custom paths for security
- Restrict access with authentication middleware

### Security Considerations

<Warning>
The inspector provides full access to your MCP server. In production, consider:
- Adding authentication middleware
- Restricting to internal networks
- Using custom paths
- Disabling in production entirely
</Warning>

### Example with Authentication

```typescript
import express from 'express'
import { mountInspector } from '@mcp-use/inspector'

const app = express()

// Authentication middleware
const requireAuth = (req, res, next) => {
  // Your auth logic
  if (!req.user) {
    return res.status(401).json({ error: 'Unauthorized' })
  }
  next()
}

// Mount inspector behind authentication
app.use('/inspector', requireAuth)
mountInspector(app)
```

### Multiple MCP Servers

When mounting the inspector, it can connect to multiple MCP servers:

1. Mount inspector once on your main application
2. Connect to different MCP servers from the inspector UI
3. Each server appears as a separate connection

### CORS Configuration

If your MCP server is on a different origin, configure CORS:

```typescript
import express from 'express'
import cors from 'cors'
import { mountInspector } from '@mcp-use/inspector'

const app = express()

// Configure CORS for inspector
app.use(cors({
  origin: ['http://localhost:3000', 'https://your-domain.com'],
  credentials: true
}))

mountInspector(app)
```

### Reverse Proxy Support

The inspector and MCP Apps widgets work behind reverse proxies such as ngrok, E2B sandboxes, and Cloudflare tunnels. To set this up:

1. **Set `MCP_URL`** to the public-facing URL of your server. This ensures widget asset URLs and Vite HMR WebSocket connections route correctly through the proxy.

```bash
MCP_URL=https://abc123.ngrok.io npx @mcp-use/cli dev
```

2. **`devMode` is enabled automatically** in development, so MCP Apps widgets use same-origin sandboxing (no `sandbox-{hostname}` subdomain required).

3. **Vite HMR works through the proxy**  -  a WebSocket proxy forwards HMR connections from the main HTTP server to Vite's internal port automatically.

See the [CLI Environment Variables](/inspector/cli#mcp_url) documentation for more details on `MCP_URL`.

### Widget Server URL

Widgets rendered via MCP Apps have access to a `window.__mcpServerUrl` global variable containing the server's origin URL. Use this to construct API URLs dynamically instead of hardcoding `localhost`:

```typescript
// Inside a widget
const data = await fetch(`${window.__mcpServerUrl}/api/data`)
```

This global is injected automatically and works correctly behind reverse proxies.

### Embedding Widget Iframes

If you need to embed inspector widgets in iframes from your own application (e.g., embedding widgets in your dashboard or admin panel), you can configure allowed origins using the `MCP_INSPECTOR_FRAME_ANCESTORS` environment variable.

```bash
# Allow embedding from specific domains
MCP_INSPECTOR_FRAME_ANCESTORS="https://app.example.com https://admin.example.com" node server.js

# Or in development, allow all origins
MCP_INSPECTOR_FRAME_ANCESTORS="*" npm run dev
```

**Security Note:**

<Note>
By default, the inspector uses:
- **Development mode** (when `devServerBaseUrl` is detected): `frame-ancestors *` (allows all origins)
- **Production mode**: `frame-ancestors 'self'` (same-origin only)

This prevents unauthorized sites from embedding your inspector widgets while making development easier.
</Note>

For more details, see the [CLI Environment Variables](/inspector/cli#mcpinspectorframeancestors) documentation.

## Troubleshooting

### Inspector Not Loading

**Issue:** Inspector page shows blank or errors.

**Solutions:**
1. Check that client files are built: `yarn build` in inspector package
2. Verify the mount path is correct
3. Check browser console for errors
4. Ensure no route conflicts with `/inspector`

### Route Conflicts

**Issue:** Your routes conflict with inspector paths.

**Solution:** Use a custom path or ensure your routes don't overlap with `/inspector/*`.

### Build Errors

**Issue:** Inspector fails to mount with build errors.

**Solutions:**
1. Ensure `@mcp-use/inspector` is properly installed
2. Check Node.js version compatibility
3. Verify all dependencies are installed
4. Rebuild the inspector package

## Related Documentation

- [Getting Started](/inspector/index) - Basic inspector usage
- [CLI Usage](/inspector/cli) - Standalone inspector usage
- [Self-Hosting](/inspector/self-hosting) - Docker deployment

