Skip to content
Testing
Skill

/create-sunpeak-app

Use when working with sunpeak, or when the user asks to "build an MCP App", "build a ChatGPT App", "add a UI to an MCP tool", "create an interactive resource for Claude Connector or ChatGPT", "build a React UI for an MCP server", or needs guidance on MCP App resources,

BOOST
From plugin
sunpeak
2172 skills
Install
$ npx -y skills add Alignbase/sunpeak --skill create-sunpeak-app --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/create-sunpeak-app

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when working with sunpeak, or when the user asks to "build an MCP App", "build a ChatGPT App", "add a UI to an MCP tool", "create an interactive resource for Claude Connector or ChatGPT", "build a React UI for an MCP server", or needs guidance on MCP App resources,

SKILL.md

create-sunpeak-app.SKILL.md
name: create-sunpeak-app
description: Use when working with sunpeak, or when the user asks to "build an MCP App", "build a ChatGPT App", "add a UI to an MCP tool", "create an interactive resource for Claude Connector or ChatGPT", "build a React UI for an MCP server", or needs guidance on MCP App resources, tool-to-UI data flow, simulation files, host context, platform-specific ChatGPT/Claude features, or production builds. For testing (e2e, visual regression, live tests, evals), see the test-mcp-server skill.

Create Sunpeak App

Sunpeak is a React framework built on `@modelcontextprotocol/ext-apps` for building MCP Apps with interactive UIs that run inside AI chat hosts (ChatGPT, Claude). It provides React hooks, a dev inspector, a CLI (`sunpeak dev` / `sunpeak build` / `sunpeak start`), and a structured project convention. ChatGPT apps are now submitted and published as plugins, but the app portion remains an MCP-backed app, so sunpeak's server, tools, resources, and runtime stay the same.

Getting Reference Code

Clone the sunpeak repo for working examples:

git clone --depth 1 https://github.com/Alignbase/sunpeak /tmp/sunpeak

Template app lives at `/tmp/sunpeak/packages/sunpeak/template/`. This is the canonical project structure — read it first.

Project Structure

sunpeak-app/
├── src/
│   ├── resources/
│   │   └── {name}/
│   │       └── {name}.tsx            # Resource component + ResourceConfig export
│   ├── tools/
│   │   └── {name}.ts                 # Tool metadata, Zod schema, handler
│   ├── server.ts                     # Optional server entry (auth, identity, icons, instructions)
│   └── styles/
│       └── globals.css               # Tailwind imports
├── tests/
│   ├── simulations/
│   │   └── *.json                    # Simulation fixture files (flat directory)
│   ├── e2e/
│   │   └── {name}.spec.ts            # Playwright inspector tests
│   ├── evals/
│   │   ├── eval.config.ts            # Eval config (models, runs, defaults)
│   │   ├── .env                      # API keys (gitignored)
│   │   └── {name}.eval.ts            # Eval specs (one per resource or tool)
│   └── live/
│       ├── playwright.config.ts      # Live test config (long timeouts, single worker)
│       └── {name}.spec.ts            # Live tests against real ChatGPT (one per resource)
├── package.json
└── (vite.config.ts, tsconfig.json, etc. managed by sunpeak CLI)

Discovery is convention-based:

  • Resources: `src/resources/{name}/{name}.tsx` (name derived from directory)
  • Tools: `src/tools/{name}.ts` (name derived from filename)
  • Simulations: `tests/simulations/*.json` (flat directory, `"tool"` string references tool filename)

Resource Component Pattern

Every resource file exports two things:

1. **`resource`** — A `ResourceConfig` object with [MCP resource](https://sunpeak.ai/docs/mcp-apps/mcp/resources) metadata (name is auto-derived from directory) 2. **A named React component** — The UI (`{Name}Resource`)

import { useToolData, useHostContext, useDisplayMode, SafeArea } from 'sunpeak';
import type { ResourceConfig } from 'sunpeak';

// MCP resource metadata (name auto-derived from directory: src/resources/weather/)
export const resource: ResourceConfig = {
  title: 'Weather',
  description: 'Show current weather conditions',
  mimeType: 'text/html;profile=mcp-app',
  _meta: {
    ui: {
      csp: {
        resourceDomains: ['https://cdn.example.com'],
      },
    },
  },
};

// Type definitions
interface WeatherInput {
  city: string;
  units?: 'metric' | 'imperial';
}

interface WeatherOutput {
  temperature: number;
  condition: string;
  humidity: number;
}

// React component
export function WeatherResource() {
  // All hooks must be called before any early return
  const { input, output, isLoading } = useToolData<WeatherInput, WeatherOutput>();
  const context = useHostContext();
  const displayMode = useDisplayMode();

  if (isLoading) return <div className="p-4 text-[var(--color-text-secondary)]">Loading...</div>;

  const isFullscreen = displayMode === 'fullscreen';
  const hasTouch = context?.deviceCapabilities?.touch ?? false;

  return (
    <SafeArea className={isFullscreen ? 'flex flex-col h-screen' : undefined}>
      <div className="p-4">
        <h1 className="text-[var(--color-text-primary)] font-semibold">{input?.city}</h1>
        <p className={`${hasTouch ? 'text-base' : 'text-sm'} text-[var(--color-text-secondary)]`}>
          {output?.temperature}° — {output?.condition}
        </p>
      </div>
    </SafeArea>
  );
}

**Rules:**

  • Always wrap in `<SafeArea>` to respect host insets
  • Use MCP standard CSS variables via Tailwind arbitrary values: `text-[var(--color-text-primary)]`, `text-[var(--color-text-secondary)]`, `bg-[var(--color-background-primary)]`, `border-[var(--color-border-tertiary)]`
  • `useToolData<TInput, TOutput>()` — provide types for both input and output
  • All hooks must be called before any early `return` (React rules of hooks)
  • Do NOT mutate `app` directly inside hooks — use `eslint-disable-next-line react-hooks/immutability` for class setters

Tool Files

Each tool `.ts` file exports metadata, a Zod schema, an optional output schema, and a handler. The `resource` field links a tool to its UI — omit it for data-only tools:

// src/tools/show-weather.ts
import { z } from 'zod';
import type { AppToolConfig, ToolHandlerExtra } from 'sunpeak/mcp';

// 1. Tool metadata (resource links to src/resources/weather/ — omit for tools without a UI)
export const tool: AppToolConfig = {
  resource: 'weather',
  title: 'Show Weather',
  description: 'Show current weather conditions',
  annotations: { readOnlyHint: true },
  _meta: { ui: { visibility: ['model', 'app'] } },
};

// 2. Zod schema (auto-converted to JSON Schema for MCP)
export const schema = {
  city: z.string().describe('City name'),
  units: z.enum(['metric', 'imperial']).describe('Temperature units'),
};

// 3. Optional output schema (en
Read more
Ships withsunpeak

Server-agnostic MCP testing framework and full-stack MCP App framework for ChatGPT Apps, Claude Connectors, and more.

Get the whole plugin
Stats
218
Stars
12
Forks
Active
Maintenance
TypeScript
Language
MIT
License
3d ago
Last commit
11mo ago
Created
3h ago
Added

Repo: Alignbase/sunpeak

Other skills on sunpeak.