Skip to main content
The mcp-use Inspector provides comprehensive support for debugging widgets built with both MCP Apps (SEP-1865) and ChatGPT Apps SDK protocols. Test your tools, render widgets, switch protocols, and verify interactions all within the inspector interface.
Dual-Protocol Support: The inspector supports both widget protocols:
  • MCP Apps - Standard MCP protocol (SEP-1865) with JSON-RPC communication
  • ChatGPT Apps SDK - OpenAI’s protocol with window.openai API emulation
Use the Protocol Toggle to switch between protocols and test cross-compatibility.

Overview

What are Widget Protocols?

MCP servers can provide interactive widgets using two protocols:
ProtocolStandardClientsCommunication
MCP AppsSEP-1865 (MCP standard)Claude, Goose, MCP clientsJSON-RPC over postMessage
ChatGPT Apps SDKOpenAI proprietaryChatGPTwindow.openai API
With mcp-use, you can build widgets that support both protocols simultaneously using type: "mcpApps".

How the Inspector Helps

The inspector provides:
  • Dual-Protocol Testing: Switch between MCP Apps and ChatGPT protocols
  • Protocol Toggle: Test cross-compatibility with one click
  • Widget Rendering: Preview widgets in both protocols
  • Debug Controls: Test different devices, locales, CSP modes
  • Display Modes: Test inline, picture-in-picture, fullscreen
  • Interactive Debugging: Test widget interactions and tool calls
  • Dev Mode Support: Hot reload for widget development

Protocol Toggle

For widgets that support both protocols (using type: "mcpApps"), the inspector shows a Protocol Toggle that lets you switch between:
  • MCP Apps - Tests the SEP-1865 protocol with JSON-RPC communication
  • ChatGPT Apps - Tests the Apps SDK protocol with window.openai emulation
The toggle only appears for dual-protocol widgets. Single-protocol widgets (type: "appsSdk") only render using ChatGPT protocol.

MCP Apps Debug Controls

When testing MCP Apps protocol widgets, the inspector provides comprehensive debug controls:

Device Emulation

Switch between device types to test responsive behavior:
  • Desktop - Large viewport, hover support
  • Mobile - Small viewport, touch support
  • Tablet - Medium viewport, both touch and hover

Locale & Timezone

Test internationalization:
  • Locale Selector - Choose from 100+ locales (en-US, es-ES, ja-JP, etc.)
  • Timezone Selector - Test timezone-specific behavior (MCP Apps only)

CSP Mode Toggle

Test Content Security Policy enforcement:
  • Permissive - Relaxed CSP for debugging
  • Widget-Declared - Enforces widget’s declared CSP (production mode)
CSP violations are logged in the console. Use Widget-Declared mode to catch CSP issues before production deployment.

Capabilities Testing

Toggle device capabilities:
  • Touch - Enable/disable touch input simulation
  • Hover - Enable/disable hover state detection

Safe Area Insets

Configure safe area insets for testing mobile layouts:
  • Top - Notch or status bar area
  • Bottom - Home indicator area
  • Left/Right - Display cutouts

Display Mode Controls

Test different display modes:
  • Inline - Default embedded view
  • Picture-in-Picture - Floating window (click PiP button)
  • Fullscreen - Full browser window (click fullscreen button)

Props Management

Test widgets with different inputs:
  • Use Tool Input - Use current tool call parameters
  • Select Preset - Choose from saved prop configurations
  • Create Preset - Save current props for reuse
  • Edit Props - Manually modify prop values

ChatGPT Apps SDK: window.openai API Emulation

This section covers the ChatGPT Apps SDK protocol specifically. For MCP Apps protocol widgets, the inspector uses JSON-RPC over postMessage instead.Your widget code can work with both protocols by using the useWidget() hook from mcp-use/react, which abstracts protocol differences.
When testing ChatGPT Apps SDK protocol widgets, the inspector provides complete emulation of the window.openai API. This API is automatically injected into widget iframes, allowing your components to work identically in the inspector and in ChatGPT.

API Overview

The window.openai object provides:
  • Global Properties: Theme, display mode, tool data, widget state
  • Methods: Tool calls, follow-up messages, display mode requests
  • Events: Global change notifications via openai:set_globals events

Global Properties

These properties are available on window.openai and update reactively:

toolInput

The input parameters passed to the tool that triggered this widget.
const input = window.openai.toolInput;
// { city: "San Francisco", category: "pizza" }

toolOutput

The structured output from the tool execution. This is the primary data source for your widget.
const output = window.openai.toolOutput;
// { places: [...], metadata: {...} }

toolResponseMetadata

Additional metadata from the tool response (currently null in inspector).

widgetState

Persistent state for this widget instance. State is scoped to the specific widget and conversation message.
// Read current state
const state = window.openai.widgetState;

// Update state (persists across widget interactions)
await window.openai.setWidgetState({ favorites: [...] });
Widget state persists in browser localStorage and is rehydrated when the widget loads. State is scoped to the widget instance and doesn’t travel across different widgets or conversation turns.

displayMode

Current display mode: "inline", "pip", or "fullscreen".
const mode = window.openai.displayMode;
// "inline" | "pip" | "fullscreen"

theme

Current theme: "light" or "dark". Automatically syncs with inspector theme.
const theme = window.openai.theme;
// "light" | "dark"

maxHeight

Maximum height available for the widget container (in pixels).
const height = window.openai.maxHeight;
// 600 (default)

locale

User’s locale setting.
const locale = window.openai.locale;
// "en-US" (default)

safeArea

Safe area insets for mobile devices.
const safeArea = window.openai.safeArea;
// { insets: { top: 0, bottom: 0, left: 0, right: 0 } }

userAgent

User agent information including device type and capabilities.
const userAgent = window.openai.userAgent;
// { device: { type: "desktop" }, capabilities: { hover: true, touch: false } }

API Methods

callTool(name, params)

Call an MCP tool directly from the widget. Returns a Promise with the tool result.
const result = await window.openai.callTool("get_restaurants", {
  city: "San Francisco",
  category: "pizza",
});

// Result format matches OpenAI's expected structure:
// { content: [{ type: "text", text: "..." }] }
Implementation:
  • Sends postMessage to parent (inspector)
  • Inspector executes tool via MCP connection
  • Result is formatted to match OpenAI’s expected format
  • MCP contents array is converted to OpenAI content format
  • 30-second timeout for tool calls
Tools must be marked as callable by components in your MCP server. The inspector forwards all tool calls to the connected MCP server.

sendFollowUpMessage(args)

Send a follow-up message to ChatGPT as if the user typed it.
await window.openai.sendFollowUpMessage({
  prompt: "Show me more details about the first restaurant",
});
Implementation:
  • Dispatches custom event mcp-inspector:widget-followup
  • Message appears in Chat tab
  • Can be used to continue conversation from widget interactions

setWidgetState(state)

Persist widget state across interactions. State is visible to ChatGPT and can influence future tool calls.
await window.openai.setWidgetState({
  favorites: ["restaurant-1", "restaurant-2"],
  filters: { price: "$$" },
});
Implementation:
  • Stores state in browser localStorage
  • Keyed by widget instance ID
  • State is rehydrated on widget load
  • Sent to parent via postMessage for inspector awareness
Keep widget state under 4k tokens for performance. State is sent to ChatGPT and can influence model behavior.

requestDisplayMode(options)

Request a different display mode for the widget.
const result = await window.openai.requestDisplayMode({
  mode: "fullscreen",
});
// Returns: { mode: "fullscreen" }
Supported modes:
  • "inline" - Default embedded view
  • "pip" - Picture-in-Picture floating window
  • "fullscreen" - Full browser window
Implementation:
  • Uses native Fullscreen API when available
  • Falls back to CSS-based fullscreen
  • On mobile, PiP may be coerced to fullscreen
  • Updates displayMode property and dispatches events

openExternal(payload)

Open an external link in a new window/tab.
window.openai.openExternal({
  href: "https://example.com",
});

// Or with string
window.openai.openExternal("https://example.com");
Implementation:
  • Uses window.open() with security flags
  • Opens in new tab with noopener,noreferrer

notifyIntrinsicHeight(height)

Notify OpenAI about intrinsic height changes for auto-sizing. This allows widgets to dynamically resize based on content.
await window.openai.notifyIntrinsicHeight(800);
Implementation:
  • Sends postMessage with type openai:notifyIntrinsicHeight
  • Inspector updates iframe height accordingly
  • Height is capped based on display mode (fullscreen/pip respect viewport)
  • Used by McpUseProvider with autoSize={true} for automatic height updates
Usage Example:
// Manual height notification
const container = document.getElementById("widget-content");
const height = container.scrollHeight;
await window.openai.notifyIntrinsicHeight(height);

// Or use McpUseProvider with autoSize for automatic updates
<McpUseProvider autoSize>
  <MyWidget />
</McpUseProvider>;

Console Proxy Toggle

The inspector provides a console proxy feature that allows you to forward iframe console logs to the page console for easier debugging.

Enabling Console Proxy

  1. Open the Console panel in the inspector (click the terminal icon)
  2. Toggle “Proxy logs to page console” switch
  3. Console logs from the widget iframe will now appear in your browser’s developer console

Features

  • Persistent Preference: Your preference is saved in localStorage and persists across sessions
  • Formatted Output: Logs are prefixed with [WIDGET CONSOLE] for easy identification
  • Log Level Preservation: Error, warn, info, debug, and trace levels are preserved
  • JSON Formatting: Objects are automatically stringified for better readability

Use Cases

  • Debugging Widget Issues: See all console logs in one place
  • Development Workflow: Use browser DevTools features (filtering, searching)
  • Error Tracking: Easier to spot errors and warnings

Widget State Inspection

The inspector provides built-in widget state inspection capabilities through the WidgetInspectorControls component.

Viewing Widget State

When a widget is rendered in the inspector, you can inspect:
  • Props: Current widget props from toolInput
  • Output: Tool output data (toolOutput)
  • Metadata: Response metadata (toolResponseMetadata)
  • State: Persistent widget state (widgetState)
  • Theme: Current theme (light/dark)
  • Display Mode: Current display mode (inline/pip/fullscreen)
  • Safe Area: Safe area insets for mobile
  • User Agent: Device capabilities
  • Locale: User locale

Debug Information Display

The inspector automatically displays debug information when widgets use McpUseProvider with debugger={true} or when using WidgetControls component. This provides:
  • Real-time state updates
  • Interactive tool testing
  • State modification capabilities
  • Full widget context visibility

State Inspection API

Widgets can respond to state inspection requests:
// Inspector sends: mcp-inspector:getWidgetState
window.addEventListener("message", (event) => {
  if (event.data?.type === "mcp-inspector:getWidgetState") {
    window.parent.postMessage(
      {
        type: "mcp-inspector:widgetStateResponse",
        toolId: event.data.toolId,
        state: window.openai.widgetState,
      },
      "*",
    );
  }
});
This allows the inspector to display current widget state even when the widget is in an iframe.

Events

openai:set_globals

Custom event dispatched when any global property changes. React components can listen to this for reactive updates.
window.addEventListener("openai:set_globals", (event) => {
  const { globals } = event.detail;
  // globals contains all updated properties
  console.log("Theme changed:", globals.theme);
  console.log("Display mode:", globals.displayMode);
});
When dispatched:
  • Initial widget load
  • Display mode changes
  • Theme changes
  • Any global property update from parent
Event detail structure:
{
  globals: {
    toolInput: {...},
    toolOutput: {...},
    toolResponseMetadata: null,
    widgetState: {...},
    displayMode: "inline",
    maxHeight: 600,
    theme: "dark",
    locale: "en-US",
    safeArea: {...},
    userAgent: {...}
  }
}

React Helper Hooks

The inspector’s API emulation is compatible with React hooks that use useSyncExternalStore to subscribe to global changes. Here’s an example pattern:
function useOpenAiGlobal(key) {
  return useSyncExternalStore(
    (onChange) => {
      const handleSetGlobal = (event) => {
        if (event.detail.globals[key] !== undefined) {
          onChange();
        }
      };
      window.addEventListener("openai:set_globals", handleSetGlobal);
      return () => {
        window.removeEventListener("openai:set_globals", handleSetGlobal);
      };
    },
    () => window.openai[key],
  );
}

// Usage
function MyWidget() {
  const theme = useOpenAiGlobal("theme");
  const toolOutput = useOpenAiGlobal("toolOutput");

  return (
    <div className={theme === "dark" ? "dark" : "light"}>
      {/* Widget content */}
    </div>
  );
}

Implementation Details

API Injection

The window.openai API is injected into widget iframes via server-side HTML generation:
  1. Widget HTML is fetched from MCP server
  2. Inspector injects API script before widget content
  3. API object is attached to window.openai and window.webplus (for compatibility)
  4. Initial globals event is dispatched
  5. Message listeners are set up for parent communication

Communication Protocol

Widget-to-inspector communication uses postMessage: Widget → Inspector:
  • openai:callTool - Tool execution requests
  • openai:sendFollowup - Follow-up messages
  • openai:requestDisplayMode - Display mode changes
  • openai:setWidgetState - State updates
Inspector → Widget:
  • openai:callTool:response - Tool call results
  • openai:globalsChanged - Global property updates
  • openai:displayModeChanged - Display mode changes (legacy)

State Persistence

Widget state is persisted in browser localStorage:
  • Key format: mcp-inspector-widget-state-${toolId}
  • Storage: Browser localStorage (scoped to inspector domain)
  • Lifetime: Persists across page reloads
  • Scope: Per widget instance

Tool Result Formatting

MCP tool results are automatically converted to OpenAI’s expected format: MCP Format:
{
  "contents": [{ "type": "text", "text": "Result" }]
}
OpenAI Format (returned to widget):
{
  "content": [{ "type": "text", "text": "Result" }]
}

Compatibility

The inspector maintains compatibility with:
  • OpenAI Apps SDK: Full API compatibility
  • Legacy APIs: sendFollowupTurn (aliased to sendFollowUpMessage)
  • React Router: URL normalization for routing
  • Multiple display modes: Inline, PiP, Fullscreen

Differences from ChatGPT

While the inspector provides full API compatibility, there are some differences:
  1. User Agent: Inspector provides mock user agent data
  2. Safe Area: Defaults to zero insets (not mobile-specific)
  3. Locale: Defaults to “en-US” (not user-specific)
  4. Tool Results: Converted from MCP format to OpenAI format
  5. Follow-ups: Appear in inspector Chat tab instead of ChatGPT

Testing Your Widget

To test widget compatibility:
  1. Develop locally with inspector
  2. Test all API methods in your widget
  3. Verify state persistence across interactions
  4. Test display mode transitions
  5. Verify tool calls work correctly
  6. Check theme adaptation
  7. Test in ChatGPT for final verification

Official Documentation

For the complete OpenAI Apps SDK API reference, see:

Connecting Your ChatGPT App

Setting Up Your MCP Server

Your ChatGPT App needs an MCP server that exposes tools and optionally widgets:
import { MCPServer } from "mcp-use/server";

const server = new MCPServer({
  name: "my-chatgpt-app",
  version: "1.0.0",
});

// Add a tool that returns a widget
server.addTool({
  name: "create_dashboard",
  description: "Create an interactive dashboard",
  inputSchema: {
    type: "object",
    properties: {
      title: { type: "string" },
      data: { type: "object" },
    },
  },
  handler: async (args) => {
    // Return widget URI in metadata
    return {
      contents: [
        {
          type: "text",
          text: "Dashboard created",
        },
      ],
      _meta: {
        "openai/outputTemplate": "dashboard-widget-123",
      },
    };
  },
});

Connecting via Inspector

  1. Start your MCP server
  2. Open the inspector (local or hosted)
  3. Connect to your server URL:
    • Local: http://localhost:3000/mcp
    • Remote: https://your-server.com/mcp
  4. Server appears in Connected Servers list

Authentication Setup

If your ChatGPT App requires authentication:
  1. Configure OAuth in connection settings
  2. Or add custom headers with API keys
  3. Complete authentication flow
  4. Inspector stores credentials securely

Testing Tools

Executing Tools

Test your ChatGPT App tools directly:
  1. Navigate to Tools tab
  2. Find your tool in the list
  3. Click to select it
  4. Enter test parameters
  5. Click Execute
  6. View results in real-time

Viewing Tool Results

Tool results show:
  • Text Output: Plain text responses
  • Structured Data: JSON responses
  • Widget References: Links to OpenAI Apps SDK widgets
  • Metadata: Tool execution metadata

Testing with Different Parameters

Test edge cases and variations:
  1. Execute tool with different parameters
  2. Save successful requests for replay
  3. Test error handling
  4. Verify parameter validation
Use saved requests to quickly test the same tool with different parameters.

Widget/Component Testing

OpenAI Apps SDK Widget Support

The inspector fully supports OpenAI Apps SDK widgets:
  • Widget Rendering: Interactive widget display
  • Dev Mode: Hot reload during development
  • Display Modes: Inline, Picture-in-Picture, Fullscreen
  • CSP Handling: Content Security Policy support

Rendering Widgets

When a tool returns a widget reference:
  1. Tool executes successfully
  2. Inspector detects widget URI in metadata
  3. Widget automatically loads and renders
  4. Interactive components become available
Widget detection:
  • Looks for openai/outputTemplate in tool metadata
  • Fetches widget resource from MCP server
  • Renders in dedicated widget container

Widget Lifecycle Testing

Widgets render before tool execution completes, allowing them to show loading states. The mcp-use Inspector properly emulates this lifecycle:
  1. Tool Called → Widget iframe created immediately
  2. Widget RendersisPending=true, shows loading state
  3. Tool Completes → Widget receives result via window.openai updates
  4. Widget UpdatesisPending=false, shows data
Testing isPending Transitions: To verify your widget handles the pending state correctly:
  1. Use a tool that takes >2 seconds to complete
  2. Widget should show loading state immediately (not after tool completes)
  3. Open browser DevTools console
  4. Look for logs showing isPending: true then isPending: false
  5. Verify widget transitions from loading UI to data display
Example Console Output:
[MyWidget] isPending: true, props: {}           // Immediate render
[MyWidget] isPending: false, props: {city: ...} // After tool completes
Use the Tools tab to test widget lifecycle - execute a slow tool and watch the widget render in pending state before the tool completes.
See Widget Lifecycle for complete details on isPending behavior and implementation patterns.

Dev Mode for Widgets

Enable hot reload for widget development:
  1. Set widget metadata with dev flag:
    _meta: {
      'mcp-use/widget': {
        name: 'my-widget',
        html: 'widget.html',
        dev: true  // Enable dev mode
      }
    }
    
  2. Inspector uses dev server URL
  3. Changes reload automatically
  4. Console logs visible in inspector

Widget Display Modes

Widgets support three display modes: Inline:
  • Default mode
  • Embedded in result panel
  • Scrollable content
Picture-in-Picture (PiP):
  • Floating window
  • Stays visible while scrolling
  • Resizable and draggable
Fullscreen:
  • Full browser window
  • Maximum visibility
  • Exit with ESC or close button
Switching modes:
  • Widget can request mode changes
  • Inspector handles transitions
  • State persists during session

Widget CSP Handling

Content Security Policy is automatically handled:
  • CSP metadata from tool results
  • Applied to widget iframe
  • Secure sandbox environment
  • Script execution allowed

Interactive Widget Features

Tool Calls from Widgets

Widgets can call MCP tools:
  1. Widget uses window.openai.callTool()
  2. Inspector intercepts the call
  3. Executes tool via MCP connection
  4. Returns result to widget
  5. Widget updates with response
Example widget code:
// In your widget
const result = await window.openai.callTool("get_data", {
  userId: "123",
});

Follow-up Messages

Widgets can send follow-up messages to ChatGPT:
  1. Widget calls window.openai.sendFollowup()
  2. Inspector captures the message
  3. Message appears in Chat tab
  4. ChatGPT processes the follow-up
  5. Conversation continues
Use cases:
  • User interactions in widget
  • Dynamic conversation flow
  • Context-aware responses

Widget State Management

Widget state is managed automatically:
  • Tool Input: Parameters passed to tool
  • Tool Output: Results from tool execution
  • Widget Data: Resource content for widget
  • Display State: Current display mode

Console Logging from Widgets

View widget console output:
  1. Widget console logs appear in inspector
  2. Access via console panel in widget container
  3. Filter by log level
  4. Debug widget issues
Console features:
  • Real-time log streaming
  • Log level filtering
  • Error highlighting
  • Stack trace display

Debugging Workflow

Step-by-Step Process

  1. Connect to Server
    • Add your MCP server in inspector
    • Verify connection status
  2. Test Tools
    • Execute each tool independently
    • Verify parameters and responses
    • Check for errors
  3. Test Widgets
    • Execute tools that return widgets
    • Verify widget rendering
    • Use Protocol Toggle to test both protocols (if dual-protocol)
    • Test widget interactions and tool calls
    • Test different display modes (inline, PiP, fullscreen)
  4. Test Device Compatibility
    • Use Device Emulation (Desktop/Mobile/Tablet)
    • Test different locales and timezones
    • Verify CSP compliance (Widget-Declared mode)
    • Test touch and hover capabilities
  5. Test Integration
    • Use Chat tab with LLM
    • Verify tool calls from AI
    • Check widget rendering in context
  6. Debug Issues
    • Check console logs
    • Review tool results
    • Verify widget metadata
    • Test error scenarios

Common Issues and Solutions

Widget Not Rendering:
  • MCP Apps: Check _meta.ui.resourceUri or _meta["ui/resourceUri"] in metadata
  • ChatGPT: Check openai/outputTemplate in metadata
  • Verify widget resource exists at the specified path
  • Check CSP settings match widget’s network requirements
  • Use Protocol Toggle to test if it works in the other protocol
  • Review console for errors
Tool Calls Failing:
  • Verify tool name matches
  • Check parameter schema
  • Review authentication
  • Check server logs
Widget Interactions Not Working:
  • ChatGPT: Verify window.openai API availability in widget
  • MCP Apps: Check JSON-RPC postMessage communication
  • Check widget iframe sandbox permissions
  • Review console for errors
  • Test in different display modes
  • Try switching protocols with Protocol Toggle

Testing Widget Interactions

  1. Render Widget: Execute tool to load widget
  2. Interact: Click buttons, fill forms in widget
  3. Monitor: Watch console for tool calls
  4. Verify: Check tool results and widget updates
  5. Iterate: Fix issues and retest

Verifying Tool Outputs

  • Preview Mode: See formatted widget output
  • JSON Mode: View raw tool response
  • Metadata: Check widget references
  • Structure: Verify data format

Best Practices

Development vs Production Widgets

Development:
  • Use dev mode for hot reload
  • Enable console logging and verbose debugging
  • Use Permissive CSP mode for easier debugging
  • Test in all display modes and device types
  • Test both protocols (MCP Apps and ChatGPT)
  • Verify error handling and fallbacks
Production:
  • Disable dev mode
  • Use Widget-Declared CSP mode to catch violations
  • Minimize console output
  • Test with production CSP restrictions
  • Verify performance across device types
  • Test internationalization with different locales

Testing Widget Responsiveness

  • Test in different display modes (inline, PiP, fullscreen)
  • Use Device Emulation to test Desktop/Mobile/Tablet
  • Verify mobile layouts with safe area insets
  • Check resize behavior and auto-resize (MCP Apps)
  • Test PiP mode transitions
  • Test with different locales and timezones

Handling Widget Errors

  • Implement error boundaries
  • Show user-friendly messages
  • Log errors to console
  • Provide fallback UI

Performance Considerations

  • Optimize widget loading
  • Minimize initial bundle size
  • Lazy load components
  • Cache widget resources

Next Steps