Skip to main content
Heads up. Some of the diagrams and file paths below predate the chat-v2 / hostConfig consolidation. The current architecture is summarized in the HostConfig consolidation section directly below; treat that as the canonical reference when it conflicts with the older sections.

HostConfig consolidation

Playground (live chat) and Evals share a single, portable hostConfig core that lives in @mcpjam/sdk/host-config/. Both surfaces are wired onto the same canonicalizer, host-execution policy resolver, OpenAI-Apps compat resolver, and filterAppOnlyTools predicate — there is no playground fork. Key consumption points in the inspector server:
  • server/utils/chat-v2-orchestration.ts:prepareChatV2 — the live playground entry point. Imports filterAppOnlyTools from @mcpjam/sdk/host-config/internal (Stage 3); the local copy is deleted. The @modelcontextprotocol/ext-apps dep stays only because three other server files still use it.
  • server/services/evals/host-execution-policy.tsdeleted after Stage 4. The eval path imports extractHostExecutionPolicy and buildHostIterationMetadata directly from @mcpjam/sdk/host-config/internal.
  • server/services/evals/compat-runtime.ts — slimmed to only loadSuiteHostConfig (Convex-bound). resolveOpenAiCompatForHostConfig and friends moved to the SDK.
The MCPJam backend (mcpjam-backend/convex/lib/hostConfigV2.ts) imports the canonicalizer + hasher from the same @mcpjam/sdk/host-config/internal subpath — one source of truth, no hand-mirror. See evals-architecture.mdx → HostConfig consolidation for the cross-surface picture. Two distinct sandbox layers. The persisted host-config sandbox shape (mcpProfile.apps.sandbox.csp + .permissions) is allowlist-only — no deny. The runtime CSP resolver in sandbox-policy.ts carries deny plus a hosted clamp. They share only leaf subtypes; do not reuse the resolver’s policy types for persisted configs.

Overview

The Playground (Chat Tab) is MCPJam Inspector’s interactive testing environment for MCP servers with LLM integration. It enables real-time conversation with AI models while automatically invoking MCP tools, handling elicitations, and streaming responses. Key Features:
  • Multi-provider LLM support (OpenAI, Anthropic, DeepSeek, Google, Ollama)
  • Free chat via MCPJam-provided models (powered by MCPJam backend)
  • Real-time MCP tool execution with OpenAI Apps SDK compatibility
  • MCP-UI rendering for custom interactive components
  • Server-Sent Events (SSE) for streaming responses
  • Interactive elicitation support (MCP servers requesting user input)
  • Multi-server MCP integration with automatic tool routing
Location:
  • Frontend: client/src/components/ChatTabV2.tsx (new resizable layout)
  • Legacy: client/src/components/ChatTab.tsx (original single-panel layout)
  • Backend: server/routes/mcp/chat.ts
  • Hook: client/src/hooks/use-chat.ts

Architecture Overview

UI Layout (ChatTabV2)

The playground uses a resizable split-panel layout powered by ResizablePanelGroup:
Key Features:
  • Chat panel (left): Message history, tool execution, input form
  • Logger panel (right): JSON-RPC messages from MCP servers
  • Resizable divider: Users can adjust panel sizes
  • Responsive: Minimum widths prevent UI collapse

System Components


Chat Flow: Local vs Backend

The Playground supports two execution paths based on the selected model:

1. Local Execution (User API Keys)

Used when the user selects models requiring their own API keys (OpenAI, Anthropic, DeepSeek, Google, or Ollama). Flow:
Key Files:
  • server/routes/mcp/chat.ts:207-403 - createStreamingResponse()
  • server/utils/chat-helpers.ts - createLlmModel()
  • client/src/hooks/use-chat.ts:181-327 - SSE event handling

2. Backend Execution (Free Models via MCPJamBackend)

Used when the user selects MCPJam-provided models (identified by isMCPJamProvidedModel()). Flow:
Key Files:
  • server/routes/mcp/chat.ts:405-589 - sendMessagesToBackend()
  • shared/backend-conversation.ts - runBackendConversation()
  • shared/http-tool-calls.ts - executeToolCallsFromMessages()
MCPJam-Provided Models (defined in shared/types.ts:109-118):
  • meta-llama/llama-3.3-70b-instruct
  • openai/gpt-oss-120b
  • x-ai/grok-4-fast
  • openai/gpt-5-nano

Free Chat: MCPJamBackend Integration

MCPJam Inspector offers free chat powered by MCPJamBackend at CONVEX_HTTP_URL.

How It Works

1. Model Selection:
2. Authentication:
3. Backend Routing:
4. MCPJamBackend Communication:
5. Response Format:
Environment Variables:
  • CONVEX_HTTP_URL - MCPJamBackend backend endpoint (required for free chat)
  • Authenticated users get access via WorkOS tokens

MCP Integration via MCPClientManager

The Playground uses MCPClientManager to orchestrate MCP server connections and tool execution.

Tool Retrieval

What getToolsForAiSdk() does (see docs/contributing/mcp-client-manager.mdx):
  1. Fetches tools from specified servers (or all if undefined)
  2. Converts MCP tool schemas to AI SDK format
  3. Attaches _serverId metadata to each tool
  4. Wires up tool.execute() to call mcpClientManager.executeTool()
  5. Caches tool _meta fields for OpenAI Apps SDK
Example Tool Object:

Tool Execution Flow

Local Execution (AI SDK):
Backend Execution (MCPJamBackend):

Server Selection

Users can select which MCP servers to use in the chat:
Passed to backend:

Streaming Implementation (SSE)

The Playground uses Server-Sent Events (SSE) for real-time streaming of LLM responses and tool execution.

Event Types

Defined in shared/sse.ts:

Server-Side Streaming

Helper Function:

Client-Side Parsing

Event Handlers (client/src/hooks/use-chat.ts:181-327):

Elicitation Support

Elicitation allows MCP servers to request interactive input from the user during tool execution.

Flow

User Response

Client Side (client/src/hooks/use-chat.ts:563-619):
UI Component: client/src/components/ElicitationDialog.tsx

OpenAI Apps SDK Integration

MCPJam Inspector supports OpenAI Apps SDK via _meta field preservation in tool results.

Why _meta?

The OpenAI Apps SDK uses _meta to pass rendering hints to OpenAI’s UI (e.g., chart data, markdown formatting, images). Tools can return:

Implementation

1. Tool Metadata Caching (MCPClientManager):
2. Result Preservation (shared/http-tool-calls.ts:154-168):
3. Backend Conversation (shared/backend-conversation.ts:125-142):
4. SSE Emission (server/routes/mcp/chat.ts:512-528):

Accessing Tool Metadata

Device Globals for ChatGPT Apps

As of PR #1026, the playground provides device context to ChatGPT Apps and MCP Apps through configurable device globals. These settings are only applied when the UI Playground is active; outside the playground, defaults are used. Device Settings:
  • Device Type - 'mobile', 'tablet', or 'desktop'. Controlled by the device type selector in the playground toolbar. Outside the playground, automatically detected from window size via getDeviceType().
  • Device Capabilities - Input method support:
    • hover (boolean) - Whether hover interactions are supported
    • touch (boolean) - Whether touch input is supported
    Controlled by hover and touch toggle buttons in the playground toolbar. Outside the playground, defaults to { hover: true, touch: false }.
  • Safe Area Insets - Device notches, rounded corners, and gesture areas in pixels:
    • top, bottom, left, right (numbers)
    Configured through the safe area editor with presets for common devices (iPhone notch, Dynamic Island, Android gesture navigation). Outside the playground, all insets default to 0.
Implementation: The playground store (ui-playground-store.ts) maintains these settings and provides them to the ChatGPT app renderer. When widgets are initialized, these values are passed through the widget data store and injected into the window.openai API:
Widgets can use these values to adapt their layout and interactions for different device contexts. For example, disabling hover-based tooltips on touch devices or adjusting padding for safe area insets.

Server Instructions Integration

As of PR #948, MCP server instructions are automatically included in the chat context as system messages. This enables the LLM to understand server-specific guidance and capabilities. Flow:
  1. Extract instructions from connectedServerConfigs[serverName]?.initializationInfo?.instructions
  2. Create system messages with metadata { source: "server-instruction", serverName }
  3. Keep instruction messages in sync with selected servers via useEffect
  4. Filter out old instruction messages when servers change
  5. Prepend instruction messages to conversation history
Implementation (client/src/components/ChatTabV2.tsx:170-182):
Synchronization (client/src/components/ChatTabV2.tsx:252-279):
Use Cases:
  • LLM understanding server-specific capabilities and constraints
  • Contextual guidance for tool usage patterns
  • Server-defined best practices and limitations
  • Multi-server coordination with distinct instruction sets

Widget State Propagation to Model

As of PR #891, widget state changes from OpenAI Apps are automatically propagated to the LLM model as hidden assistant messages. This enables the AI to understand and reason about widget interactions. Flow:
  1. Widget calls window.openai.setWidgetState(state) in iframe
  2. chatgpt-app-renderer.tsx receives openai:setWidgetState postMessage
  3. State is deduped by comparing serialized JSON
  4. onWidgetStateChange callback is invoked with (toolCallId, state)
  5. ChatTabV2.tsx adds/updates hidden assistant message with ID widget-state-${toolCallId}
  6. Message contains text: "The state of widget ${toolCallId} is: ${JSON.stringify(state)}"
  7. thread.tsx hides messages starting with widget-state- from UI
  8. Model receives state updates in conversation context
Implementation (client/src/components/ChatTabV2.tsx:281-326):
Use Cases:
  • LLM understanding user interactions with charts/dashboards
  • Contextual follow-up questions based on widget selections
  • Multi-turn conversations referencing widget state
  • Debugging widget behavior through model awareness
See OpenAI SDK Architecture - Widget State Propagation for detailed implementation.

Technical Details

Agent Loop

Both local and backend execution use an agent loop pattern:
Constants (server/routes/mcp/chat.ts:57-58):
  • MAX_AGENT_STEPS = 10 - Max iterations
  • ELICITATION_TIMEOUT = 300000 - 5 minutes

Message Format

Client Messages (shared/types.ts:19-28):
AI SDK Messages (server/routes/mcp/chat.ts:223-234):

Content Blocks

Used for rich UI rendering:

Model Selection

Available Models (shared/types.ts:167-260):
  • Anthropic: Claude Opus 4, Sonnet 4, Sonnet 3.7/3.5, Haiku 3.5
  • OpenAI: GPT-4.1, GPT-4.1 Mini/Nano, GPT-4o, GPT-4o Mini
  • DeepSeek: Chat, Reasoner
  • Google: Gemini 2.5 Pro/Flash, 2.0 Flash Exp, 1.5 Pro/Flash variants
  • Meta: Llama 3.3 70B (Free)
  • X.AI: Grok 4 Fast (Free)
  • OpenAI: GPT-OSS 120B, GPT-5 Nano (Free)
  • Ollama: User-defined local models
Model Provider Keys (client/src/hooks/use-chat.ts:159-179):

Temperature & System Prompt

Passed to backend:

Key Files Reference

Frontend

  • client/src/components/ChatTabV2.tsx - Main chat UI with resizable layout
  • client/src/components/ChatTab.tsx - Legacy single-panel layout
  • client/src/components/logging/json-rpc-logger-view.tsx - MCP protocol viewer
  • client/src/components/ui/resizable.tsx - Resizable panel components
  • client/src/hooks/use-chat.ts - Chat state management
  • client/src/components/chat/message.tsx - Message rendering
  • client/src/components/chat/chat-input.tsx - Input component
  • client/src/components/ElicitationDialog.tsx - Elicitation UI
  • client/src/lib/sse.ts - SSE parsing utilities

Backend

  • server/routes/mcp/chat.ts - Chat endpoint (593 lines)
  • server/utils/chat-helpers.ts - LLM model creation

Shared

  • shared/types.ts - Type definitions
  • shared/sse.ts - SSE event types
  • shared/backend-conversation.ts - Backend conversation orchestration
  • shared/http-tool-calls.ts - Tool execution logic

SDK

  • sdk/mcp-client-manager/index.ts - MCP orchestration
  • sdk/mcp-client-manager/tool-converters.ts - AI SDK conversion
  • See docs/contributing/mcp-client-manager.mdx for full docs

Development Patterns

Adding New Model Providers

  1. Add provider to shared/types.ts:
  1. Add models to SUPPORTED_MODELS array
  2. Implement in server/utils/chat-helpers.ts:
  1. Add API key handling in client/src/hooks/use-ai-provider-keys.ts

Adding New SSE Event Types

  1. Define in shared/sse.ts:
  1. Emit in backend:
  1. Handle in client:

Debugging Tips

Enable RPC Logging:
Monitor SSE Events:
Check Backend Logs:

MCP-UI Integration

MCPJam Inspector supports rendering custom UI components from MCP servers using the MCP-UI specification.

Detection and Rendering

Located in client/src/components/chat-v2/thread.tsx:151-172:

MCP-UI Component

The MCPUIResourcePart component uses @mcp-ui/client library to render UI resources:

Supported Action Types

MCP-UI components can trigger the following actions:
  • tool: Request tool execution (converted to chat message)
  • link: Open external URLs in new tab
  • prompt: Send text prompt as follow-up message
  • intent: Send intent string as follow-up message
  • notify: Display notification (converted to chat message)

Component Library

The implementation uses the basic component library from @mcp-ui/client:
  • Button: Interactive buttons with action handlers
  • Text: Text display with formatting
  • Stack: Layout container for vertical/horizontal stacking
  • Card: Container with border and padding
  • Image: Image display with alt text

MCP-UI vs OpenAI Apps SDK

MCPJam Inspector supports both MCP-UI and OpenAI Apps SDK for custom UI rendering: See OpenAI SDK Architecture for details on OpenAI Apps SDK implementation.

UI Playground (Apps Builder)

The UI Playground is a specialized testing environment for ChatGPT Apps and MCP Apps, providing device emulation, locale testing, and widget debugging capabilities.

Playground Controls

Located in client/src/components/ui-playground/PlaygroundMain.tsx, the playground header includes: Device Selector - Toggle between mobile (430×932), tablet (820×1180), and desktop (1280×800) viewports to test responsive layouts. Locale Selector - Choose from common BCP 47 locales for internationalization testing:
  • English: en-US, en-GB
  • European: es-ES, es-MX, fr-FR, de-DE, it-IT, pt-BR
  • Asian: ja-JP, zh-CN, zh-TW, ko-KR, hi-IN
  • Other: ar-SA, ru-RU, nl-NL
The selected locale is passed to widgets via window.openai.locale and included in the openai:set_globals message. Theme Toggle - Switch between light and dark modes. Theme changes are automatically propagated to widgets.

Widget Debugging UI

As of PR #1022, the debug interface uses icon buttons instead of tabs for a more compact layout: Located in client/src/components/chat-v2/thread.tsx:416-568: Debug Controls:
  • Data (database icon) - View tool input, output, and error details
  • Widget State (box icon) - Inspect current widget state with last updated timestamp
  • Globals (globe icon) - View global values (theme, displayMode, locale, maxHeight)
Display Mode Controls:
  • Inline (layout icon) - Default message flow rendering
  • Picture-in-Picture (picture-in-picture icon) - Floating overlay at top of screen
  • Fullscreen (maximize icon) - Full viewport expansion
All controls include tooltips for better discoverability. Clicking a debug icon toggles the corresponding panel; clicking again closes it.

Locale Propagation Flow

Implementation Details:
  1. Locale Selection (client/src/components/ui-playground/PlaygroundMain.tsx:69-89):
    • Dropdown with 16 common locales
    • Stored in useUIPlaygroundStore globals
    • Fallback to navigator.language if not set
  2. Widget Storage (client/src/components/chat-v2/chatgpt-app-renderer.tsx:284-349):
    • Locale included in widget storage payload
    • Passed to backend for iframe initialization
    • Available as window.openai.locale in widget
  3. Global Synchronization (client/src/components/chat-v2/chatgpt-app-renderer.tsx:831-850):
    • Locale sent via openai:set_globals message
    • Updates when user changes locale selector
    • Propagated to both inline and modal views

Debug Panel State Management

The debug UI uses local state to track which panel is active:
This provides a cleaner UX compared to the previous tabs-based approach, reducing visual clutter and improving mobile responsiveness.