> ## Documentation Index
> Fetch the complete documentation index at: https://sunpeak.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Inspector

> API reference for the Inspector component.

<Badge color="yellow">sunpeak API</Badge>

## Overview

The `Inspector` component provides a local development environment that replicates the MCP App runtime used by hosts like ChatGPT and Claude. It renders your resources inside iframes, matching the production hosting behavior. The inspector supports multiple host shells — switch between ChatGPT and Claude conversation chrome, theming, and reported capabilities from the sidebar or via URL parameters.

The tool picker includes both tools with UI resources and backend-only tools. When you select a backend-only tool outside a model conversation, the preview shows `Tool does not render a UI`; you can still edit Tool Input, click **Run**, and inspect Tool Result from the right sidebar.

<Note>
  Dropping the Inspector into your own React app? See [Embedding the
  Inspector](/testing/inspector-embedding) for the `app` prop, the static sandbox file you host on
  your CDN, and the CSS isolation story.
</Note>

## CLI Usage

```bash theme={null}
sunpeak inspect --server <url-or-command>
```

### CLI Flags

| Flag                     | Description                                                         |
| ------------------------ | ------------------------------------------------------------------- |
| `--server <url>`         | HTTP URL or stdio command for the MCP server                        |
| `--env KEY=VALUE`        | Set an environment variable for stdio server processes. Repeatable. |
| `--cwd <path>`           | Working directory for stdio server processes                        |
| `--header "Name: value"` | Set an HTTP header for HTTP MCP server requests. Repeatable.        |

Examples:

```bash theme={null}
# HTTP server
sunpeak inspect --server http://localhost:8000/mcp

# HTTP server with a bearer token
sunpeak inspect --server http://localhost:8000/mcp -H "Authorization: Bearer $TOKEN"

# Python stdio server with env vars
sunpeak inspect --server "python server.py" --env DATABASE_URL=sqlite:///test.db --env DEBUG=1

# Stdio server with a working directory
sunpeak inspect --server "python server.py" --cwd ./my-server
```

### Authentication

sunpeak auto-negotiates MCP OAuth when connecting to servers that return 401 Unauthorized. For servers with auto-approved (anonymous) OAuth, this happens without user interaction. For servers requiring interactive login, sunpeak opens the authorization URL in your browser and waits for the callback.

No configuration is needed. sunpeak follows the [MCP OAuth specification](https://spec.modelcontextprotocol.io), discovering metadata from `/.well-known/oauth-protected-resource` and registering a client dynamically.

If you already have a token, pass it directly:

```bash theme={null}
sunpeak inspect --server http://localhost:8000/mcp -H "Authorization: Bearer $TOKEN"
```

## Import

```tsx theme={null}
import 'sunpeak/style.css'; // Required to style the inspector.
import { Inspector } from 'sunpeak/inspector';
```

## Basic Usage

For projects using the sunpeak framework, simulations are auto-discovered. Just run:

<Tabs>
  <Tab title="pnpm">
    ```bash theme={null}
    pnpm dev
    ```
  </Tab>

  <Tab title="npm">
    ```bash theme={null}
    npm run dev
    ```
  </Tab>

  <Tab title="yarn">
    ```bash theme={null}
    yarn dev
    ```
  </Tab>
</Tabs>

For custom setups, you can manually configure the inspector:

```tsx theme={null}
import { Inspector } from 'sunpeak/inspector';
import { resource } from './resources/example/example';
import exampleSimulation from './simulations/show-example.json';

// Combine simulation with resource
const simulations = [{
  ...exampleSimulation,
  resource,
  resourceUrl: 'http://localhost:3000/resources/example',
}];

<Inspector
  simulations={simulations}
  appName="My App"
  appIcon="🌄"
/>
```

## Props

<ResponseField name="app" type="InspectorApp">
  Hierarchical input for [embedding the Inspector](/testing/inspector-embedding) in your own React
  app: an MCP App with its resources, tools, and saved simulations. When provided, the Inspector
  switches to embedded mode — the MCP Server URL input, Authentication section, and Prod Resources
  checkbox are hidden, and no `/__sunpeak/*` network calls are made. Mutually exclusive with
  `simulations`.
</ResponseField>

<ResponseField name="simulations" type="Record<string, Simulation>">
  Flat simulation map (the CLI codepath's input). Object mapping simulation names to their
  definitions. See [Simulation API Reference](/testing/simulations) for details.
</ResponseField>

<ResponseField name="appName" type="string" default="Sunpeak App">
  Name of the app displayed in the inspector UI.
</ResponseField>

<ResponseField name="appIcon" type="string">
  Optional icon (emoji) displayed in the inspector UI.
</ResponseField>

<ResponseField name="children" type="React.ReactNode">
  Optional children to render when no simulations are selected.
</ResponseField>

<ResponseField name="onCallTool" type="(params: { name: string; arguments?: Record<string, unknown> }) => Promise<CallToolResult> | CallToolResult">
  Handler for tool calls. When the user clicks **Run**, or when the app calls `callServerTool`,
  calls are forwarded to this callback. The `sunpeak dev` command wires this up automatically.
</ResponseField>

<ResponseField name="onCallToolDirect" type="(params: { name: string; arguments?: Record<string, unknown> }) => Promise<CallToolResult> | CallToolResult">
  Direct tool handler call, bypassing MCP server mock data. Used by the **Run** button to call real
  handlers. Falls back to `onCallTool` if not provided.
</ResponseField>

<ResponseField name="defaultProdResources" type="boolean" default="false">
  Initial state of the Prod Resources toggle. When `true`, the inspector loads resources from
  `dist/` production builds instead of HMR. Set by the `--prod-resources` CLI flag.
</ResponseField>

<ResponseField name="hideInspectorModes" type="boolean" default="false">
  Hide the Prod Resources checkbox in the sidebar. Used in standalone inspect mode where Prod
  Resources is not applicable.
</ResponseField>

## URL Parameters

The Inspector supports URL parameters for configuring the initial state. This is especially useful for automated testing and sharing specific configurations.

### Supported Parameters

| Parameter        | Type                                                                      | Default             | Description                                                                                                                                                   |
| ---------------- | ------------------------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tool`           | `string`                                                                  | First tool          | Tool name to select. When specified without `simulation`, no mock data is loaded ("Press Run" state).                                                         |
| `simulation`     | `string`                                                                  | First fixture       | Simulation fixture name to load. Mock data renders immediately.                                                                                               |
| `host`           | `'chatgpt' \| 'claude'`                                                   | `'chatgpt'`         | Host shell (conversation chrome, theming, capabilities)                                                                                                       |
| `theme`          | `'light' \| 'dark'`                                                       | `'dark'`            | Color theme                                                                                                                                                   |
| `displayMode`    | `'inline' \| 'pip' \| 'fullscreen'`                                       | `'inline'`          | App display mode                                                                                                                                              |
| `devicePreset`   | `'iphone-se' \| 'iphone-15' \| 'iphone-15-pro-max' \| 'ipad' \| 'custom'` | `'custom'`          | Named mobile fullscreen preview preset                                                                                                                        |
| `locale`         | `string`                                                                  | `'en-US'`           | Locale for i18n                                                                                                                                               |
| `maxHeight`      | `number`                                                                  | `600`               | Max height in PiP mode                                                                                                                                        |
| `maxWidth`       | `number`                                                                  | Measured chat width | Max width for the app container                                                                                                                               |
| `deviceType`     | `'mobile' \| 'tablet' \| 'desktop' \| 'unknown'`                          | `'desktop'`         | Device type                                                                                                                                                   |
| `hover`          | `'true' \| 'false'`                                                       | `'true'`            | Hover capability                                                                                                                                              |
| `touch`          | `'true' \| 'false'`                                                       | `'false'`           | Touch capability                                                                                                                                              |
| `safeAreaTop`    | `number`                                                                  | `0`                 | Top safe area inset                                                                                                                                           |
| `safeAreaBottom` | `number`                                                                  | `0`                 | Bottom safe area inset                                                                                                                                        |
| `safeAreaLeft`   | `number`                                                                  | `0`                 | Left safe area inset                                                                                                                                          |
| `safeAreaRight`  | `number`                                                                  | `0`                 | Right safe area inset                                                                                                                                         |
| `prodResources`  | `'true' \| 'false'`                                                       | `'false'`           | Enable Prod Resources mode (dist/ bundles)                                                                                                                    |
| `sidebar`        | `'true' \| 'false'`                                                       | `'true'`            | Show the inspector sidebar. Set to `false` for headless testing or embedding as a pure resource viewer.                                                       |
| `devOverlay`     | `'true' \| 'false'`                                                       | `'true'`            | Show the dev overlay (resource timestamp + tool timing) inside resources. Set to `false` in e2e tests to prevent it from interfering with element assertions. |

### Example URLs

```
# Load albums simulation with mock data
http://localhost:3000/?simulation=show-albums&theme=light

# Select a tool with no mock data (user clicks Run for real handler)
http://localhost:3000/?tool=show-albums&theme=dark

# Fullscreen dark mode
http://localhost:3000/?simulation=review-diff&theme=dark&displayMode=fullscreen

# Fullscreen iPhone preview
http://localhost:3000/?simulation=review-diff&devicePreset=iphone-15-pro-max

# Mobile simulation with touch
http://localhost:3000/?simulation=show-map&deviceType=mobile&touch=true&hover=false

# Claude host in light mode
http://localhost:3000/?simulation=show-albums&host=claude&theme=light

# Hide sidebars (app viewer only)
http://localhost:3000/?simulation=show-albums&sidebar=false

# Hide dev overlay (for automated tests)
http://localhost:3000/?simulation=show-albums&devOverlay=false
```

## createInspectorUrl

For type-safe URL generation in tests, use the `createInspectorUrl` utility:

### Import

```typescript theme={null}
import { createInspectorUrl } from 'sunpeak/inspector';
```

### Usage

```typescript theme={null}
// Basic usage
createInspectorUrl({ simulation: 'show-albums', theme: 'light' });
// Returns: '/?simulation=show-albums&theme=light'

// With display mode
createInspectorUrl({
  simulation: 'review-diff',
  theme: 'dark',
  displayMode: 'fullscreen',
});
// Returns: '/?simulation=review-diff&theme=dark&displayMode=fullscreen'

// With device simulation
createInspectorUrl({
  simulation: 'show-map',
  deviceType: 'mobile',
  touch: true,
  hover: false,
});
// Returns: '/?simulation=show-map&deviceType=mobile&touch=true&hover=false'

// With safe area insets (for notch simulation)
createInspectorUrl({
  simulation: 'show-carousel',
  safeAreaTop: 44,
  safeAreaBottom: 34,
});
// Returns: '/?simulation=show-carousel&safeAreaTop=44&safeAreaBottom=34'
```

### InspectorUrlParams Interface

```typescript theme={null}
interface InspectorUrlParams {
  tool?: string;
  simulation?: string;
  host?: string;
  theme?: 'light' | 'dark';
  displayMode?: 'inline' | 'pip' | 'fullscreen';
  devicePreset?: 'iphone-se' | 'iphone-15' | 'iphone-15-pro-max' | 'ipad' | 'custom';
  locale?: string;
  maxHeight?: number;
  maxWidth?: number;
  deviceType?: 'mobile' | 'tablet' | 'desktop' | 'unknown';
  hover?: boolean;
  touch?: boolean;
  safeAreaTop?: number;
  safeAreaBottom?: number;
  safeAreaLeft?: number;
  safeAreaRight?: number;
  prodResources?: boolean;
  sidebar?: boolean;
  devOverlay?: boolean;
}
```

### E2E Testing Example

E2E tests use the `tests/e2e/helpers.ts` wrapper which sets `devOverlay: false` by default so the dev overlay doesn't interfere with element assertions:

```typescript theme={null}
// tests/e2e/helpers.ts (generated by sunpeak new)
import {
  createInspectorUrl as _createInspectorUrl,
  type InspectorUrlParams,
} from 'sunpeak/chatgpt';

export function createInspectorUrl(params: InspectorUrlParams, basePath?: string): string {
  return _createInspectorUrl({ devOverlay: false, ...params }, basePath);
}
```

```typescript theme={null}
// tests/e2e/albums.spec.ts
import { test, expect } from '@playwright/test';
import { createInspectorUrl } from './helpers';

test.describe('Albums Resource', () => {
  test('should render in light mode', async ({ page }) => {
    await page.goto(
      createInspectorUrl({
        simulation: 'show-albums',
        theme: 'light',
      })
    );

    const iframe = page.frameLocator('iframe').frameLocator('iframe');
    const albumCard = iframe.locator('button:has-text("Summer Slice")');
    await expect(albumCard).toBeVisible();
  });
});
```

## Sidebar Controls

The inspector sidebar provides interactive controls for:

* **Prod Tools**: Toggle Prod Tools mode — calls real tool handlers instead of simulation mocks (requires `onCallTool` prop)
* **Prod Resources**: Toggle Prod Resources mode — loads production-built HTML from `dist/` instead of Vite HMR
* **Host**: Switch between ChatGPT and Claude conversation shells
* **Simulation / Tool**: Select which simulation (or tool, in Prod Tools mode) to display
* **Width**: Toggle between mobile (375px, 393px, 425px, 430px), tablet (768px, 820px), and full width (1024px)
* **Theme**: Toggle between light and dark themes
* **Display Mode**: Switch between inline, PiP, and fullscreen modes
* **Device**: Apply iPhone and iPad fullscreen host-context presets
* **Locale**: Set the locale string
* **Max Height**: Configure PiP mode height
* **Device Type**: Configure device type and capabilities (hover, touch)
* **Time Zone**: Set the IANA time zone (e.g. `America/New_York`)
* **User Agent**: Set the user agent string
* **Safe Area Insets**: Configure safe area insets for notch simulation
* **JSON Editors**: Edit tool input, tool result, and app context (model context) directly
* **Send as Partial (Streaming)**: Send the current tool input JSON as a `tool-input-partial` notification to test streaming input handling

## ChatGPT Mock Runtime

When the **ChatGPT** host is selected, the inspector automatically injects a mock `window.openai` runtime into the resource iframe. This enables:

* **`isChatGPT()`** returns `true` — host detection works correctly
* **ChatGPT-specific hooks** (`useUploadFile`, `useRequestModal`, `useRequestCheckout`) work with stub implementations
* All mock method calls are logged to the browser console with a `[Inspector]` prefix

When a different host (e.g. Claude) is selected, `window.openai` is not injected and ChatGPT hooks will throw if called.

See [ChatGPT Hooks](/app-framework/hooks/chatgpt-hooks) for the full API reference.
