Skip to main content
sunpeak API

Overview

Simulations define test scenarios for your resources—combining tool definitions, mock data, and platform state. They can be used with the Inspector for local development or runMCPServer for platform testing with hosts like ChatGPT and Claude. Simulations are JSON files that live in the tests/simulations/ directory. Each simulation references a tool file by name — tool metadata and resource linkage are handled by the tool files in src/tools/.

File Convention

Simulation files live in a flat tests/simulations/ directory:
tests/simulations/*.json
For example:
  • tests/simulations/show-albums.json
  • tests/simulations/review-diff.json
  • tests/simulations/review-post.json
Any *.json filename works. Multiple simulations can reference the same tool with different fixture data. Tool files live in src/tools/:
  • Each .ts file exports tool (metadata with optional resource link), schema (Zod), and a default handler.
Resource files live in src/resources/{name}/:
  • Component + Metadata: src/resources/{name}/{name}.tsx (exports both the React component and export const resource: ResourceConfig)

JSON Schema

Each simulation JSON file contains:
// tests/simulations/show-example.json
{
  "tool": "show-example",
  "userMessage": "Show me an example",
  "toolInput": {
    "query": "example search"
  },
  "toolResult": {
    "structuredContent": {
      "items": ["item1", "item2"]
    }
  }
}
The tool field is a string referencing a tool filename (without .ts) in src/tools/. Tool metadata (name, description, schema, annotations, visibility) lives in the tool file, not in the simulation.

Auto-Discovery

The framework automatically discovers and links everything:
  1. Tools: Scans src/tools/*.ts for tool files with metadata, schemas, and handlers
  2. Simulations: Scans tests/simulations/*.json and links each to its tool via the tool string field
  3. Resources: Each tool file references its resource by directory name (e.g., resource: 'albums')
This means you never need to:
  • Duplicate tool metadata in simulations
  • Import resource metadata into simulations
  • Maintain an index file of simulations or tools

Multiple Scenarios

Create multiple simulations for the same tool to test different scenarios:
src/resources/albums/
  albums.tsx                       # Component + resource metadata

src/tools/
  show-albums.ts                   # Tool metadata, schema, handler

tests/simulations/
  show-albums.json                 # Default view
  show-albums-empty.json           # Empty state
  show-albums-error.json           # Error state
Each simulation can have different toolInput and toolResult.structuredContent to test various data scenarios.

Mocking Server Tool Calls

When a resource calls callServerTool (e.g., the review resource calling a backend-only “review” tool), the inspector needs mock responses. Instead of separate simulation files for each backend tool response, you define mock responses inline using the serverTools field on the resource’s simulation.
// tests/simulations/review-purchase.json
{
  "tool": "review-purchase",
  "toolResult": { "structuredContent": { "..." } },
  "serverTools": {
    "review": [
      {
        "when": { "confirmed": true },
        "result": {
          "content": [{ "type": "text", "text": "Completed." }],
          "structuredContent": { "status": "success", "message": "Completed." }
        }
      },
      {
        "when": { "confirmed": false },
        "result": {
          "content": [{ "type": "text", "text": "Cancelled." }],
          "structuredContent": { "status": "cancelled", "message": "Cancelled." }
        }
      }
    ]
  }
}
The serverTools map supports two forms: Simple form — always return the same result:
"serverTools": {
  "review": {
    "content": [{ "type": "text", "text": "Done." }],
    "structuredContent": { "status": "success", "message": "Done." }
  }
}
Conditional form — match against call arguments:
"serverTools": {
  "review": [
    { "when": { "confirmed": true }, "result": { "..." } },
    { "when": { "confirmed": false }, "result": { "..." } }
  ]
}
The when object does shallow key matching against the tool call arguments. The first entry whose keys all match wins. Use structuredContent in the result to return structured data to the calling resource. For example, the review tool returns { status: 'success', message: '...' } or { status: 'cancelled', message: '...' } — the review resource reads status to determine success/error styling and displays message. Use isError: true only for actual tool execution failures.

Properties

name
string
required
Unique identifier for the simulation. Auto-generated from the filename.
userMessage
string
A decorative message shown in the inspector interface. Has no functional purpose.
tool
string
required
A string referencing a tool filename (without .ts) in src/tools/.
{ "tool": "show-albums" }
toolInput
Record<string, unknown>
Mock input parameters for the tool call. Accessible via useToolData().
toolResult
object
Mock data for the tool response. The structuredContent property is passed to your component via useToolData().
{
  content?: Array<{ type: string; text: string }>;
  structuredContent?: unknown;
  isError?: boolean;
}
hostContext
Partial<McpUiHostContext>
Initial host context for the simulation. Accessible via useHostContext().
serverTools
Record<string, ServerToolMock>
Mock responses for callServerTool calls made by the resource. Keys are tool names. Values are either a single CallToolResult (always returned) or an array of { when, result } entries for argument-based conditional matching.
"serverTools": {
  "review": [
    { "when": { "confirmed": true }, "result": { "structuredContent": { "status": "success" } } },
    { "when": { "confirmed": false }, "result": { "structuredContent": { "status": "cancelled" } } }
  ]
}

MCP SDK Types

The simulation interface uses official types from @modelcontextprotocol/sdk:

Tool

interface Tool {
  name: string;
  description?: string;
  inputSchema: JSONSchema;
  title?: string;
  annotations?: {
    readOnlyHint?: boolean;
    destructiveHint?: boolean;
    idempotentHint?: boolean;
    openWorldHint?: boolean;
  };
  _meta?: Record<string, unknown>;
}

Tool Visibility

The tool._meta.ui.visibility field controls which contexts can invoke the tool:
type McpUiToolVisibility = ('model' | 'app')[];
  • "model" — The AI model can call this tool
  • "app" — The app can call this tool (via useCallServerTool)
When set in the simulation JSON, the MCP server preserves this metadata when registering tools with the host.

Resource

interface Resource {
  name: string;
  uri: string;
  title?: string;
  description?: string;
  mimeType?: string;
  _meta?: Record<string, unknown>;
}

Resource Metadata (_meta.ui)

The resource._meta.ui field configures resource rendering behavior:
interface McpUiResourceMeta {
  /** Whether the host should render a border around the resource */
  prefersBorder?: boolean;

  /** Origin isolation domain for the resource iframe (used by web hosts) */
  domain?: string;

  /** Sandbox permissions for the resource iframe */
  permissions?: McpUiResourcePermissions;

  /** Content Security Policy for the resource iframe */
  csp?: McpUiResourceCsp;
}
Permissions
interface McpUiResourcePermissions {
  camera?: boolean;
  microphone?: boolean;
  geolocation?: boolean;
  clipboardWrite?: boolean;
}
The inspector maps these to iframe allow attribute directives. Only declared permissions are enabled.
CSP
interface McpUiResourceCsp {
  connectDomains?: string[];     // Allowed fetch/XHR/WebSocket origins
  resourceDomains?: string[];    // Allowed image/media/font origins
  frameDomains?: string[];       // Allowed nested iframe origins
  baseUriDomains?: string[];     // Allowed base URI origins
}
Example resource config with metadata:
// src/resources/map/map.tsx — name auto-derived as 'map' from directory
export const resource: ResourceConfig = {
  description: 'Interactive map',
  _meta: {
    ui: {
      prefersBorder: true,
      permissions: { geolocation: true },
      csp: {
        connectDomains: ['https://api.mapbox.com'],
        resourceDomains: ['https://tiles.mapbox.com'],
        frameDomains: ['https://embed.mapbox.com'],
      },
    },
  },
};

See Also

Inspector

Component API reference.

runMCPServer

MCP server API reference.