Skip to main content

Overview

Simulations define test scenarios for your resources—combining tool definitions, mock data, and platform state. They can be used with the Simulator for local development or runMCPServer for platform testing with hosts like ChatGPT and Claude.
In the sunpeak framework, simulations are JSON files that live in the tests/simulations/ directory. The resource URL and metadata are automatically linked based on directory structure.

File Naming Convention

Simulation files live in tests/simulations/{name}/:
tests/simulations/{name}/{name}-{scenario}-simulation.json
For example:
  • tests/simulations/albums/albums-show-simulation.json
  • tests/simulations/review/review-diff-simulation.json
  • tests/simulations/review/review-post-simulation.json
Resource files live in src/resources/{name}/:
  • Component + Metadata: src/resources/{name}/{name}-resource.tsx (exports both the React component and export const resource: ResourceConfig)
You can have multiple simulations per resource (e.g., albums-show-simulation.json and albums-empty-simulation.json in the same folder).

JSON Schema

Each simulation JSON file contains:
// tests/simulations/example/example-show-simulation.json
{
  "userMessage": "Show me an example",
  "tool": {
    "name": "show-example",
    "description": "Show an example",
    "inputSchema": { "type": "object", "properties": {}, "additionalProperties": false },
    "title": "Show Example",
    "annotations": { "readOnlyHint": true },
    "_meta": {
      "ui": {
        "visibility": ["model", "app"]
      }
    }
  },
  "toolResult": {
    "structuredContent": {
      "items": ["item1", "item2"]
    },
    "_meta": {}
  }
}

Auto-Discovery

The framework automatically discovers and links simulations:
  1. Discovery: Scans tests/simulations/{name}/ folders for *-simulation.json files
  2. Linking: Simulations are automatically linked to the corresponding resource in src/resources/{name}/
  3. No imports needed: Everything is wired automatically
This means you never need to:
  • Import resource metadata into simulations
  • Specify resource in the simulation JSON
  • Maintain an index file of simulations

Multiple Scenarios

Create multiple simulations per resource to test different scenarios:
src/resources/albums/
  albums-resource.tsx              # Component + resource metadata

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

Properties

name
string
required
Unique identifier for the simulation. Used to select simulations in the UI and URL parameters. (Library only - auto-generated from filename in framework)
resourceUrl
string
URL to the resource HTML page for dev mode (with Vite HMR). (Library only - auto-linked in framework)
resourceScript
string
URL to the resource JS bundle for prod mode. (Library only - auto-linked in framework)
userMessage
string
A decorative message shown in the simulator interface. Has no functional purpose.
tool
Tool
required
MCP Tool definition from @modelcontextprotocol/sdk. Defines the tool’s name, description, input schema, and metadata.
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
resource
Resource
required
MCP Resource definition from @modelcontextprotocol/sdk. Defines the resource’s name, URI, MIME type, and metadata. (Library only - auto-linked in framework)
import type { Resource } from '@modelcontextprotocol/sdk/types.js';
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().

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 simulator 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:
export const resource: ResourceConfig = {
  name: 'map',
  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

Simulator

Component API reference.

runMCPServer

MCP server API reference.