Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,6 @@ The SDK's main entry (`app.d.ts`) uses `export * from "./types"` to re-export al
- Resources discovered from `src/resources/{name}/{name}.tsx`
- Tools discovered from `src/tools/{name}.ts` (each exports `tool: AppToolConfig`, `schema`, optional `outputSchema`, `default` handler)
- Simulations discovered from `tests/simulations/*.json` (flat directory, `"tool"` string field references tool filename)
- Optional server entry at `src/server.ts` (exports `server: ServerConfig` for identity/icons, `auth()` for request authentication)
- Optional server entry at `src/server.ts` (exports `server: ServerConfig` for identity/icons/instructions, `auth()` for request authentication). `instructions` is a server-wide string sent in the MCP `initialize` response that hosts may inject into the model's system prompt — for cross-tool workflows and constraints.
- Hook file naming: `use-{kebab-name}.ts` → export `use{PascalName}` (e.g., `use-download-file.ts` → `useDownloadFile`)
- SDK re-exports in `src/index.ts` are organized into five sections: core classes/functions, app options/tool registration/Standard Schema types, method constants, Zod schemas, protocol types
2 changes: 1 addition & 1 deletion docs/app-framework/tools/production-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ app.listen(3000);
</ResponseField>

<ResponseField name="serverInfo" type="ServerConfig">
Full server identity. Overrides `name` and `version` when provided. Supports additional fields like `title`, `description`, `websiteUrl`, and `icons`.
Full server identity. Overrides `name` and `version` when provided. Supports additional fields like `title`, `description`, `websiteUrl`, `icons`, and `instructions` (sent in the MCP `initialize` response so hosts can inject it into the model's system prompt). See [Server Entry](/app-framework/tools/server-entry#server-object-optional) for the full field list.
</ResponseField>

<ResponseField name="tools" type="ProductionTool[]" required>
Expand Down
28 changes: 28 additions & 0 deletions docs/app-framework/tools/server-entry.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,34 @@ export const server = { name: 'My App', version: '1.0.0' };
Server version.
</ResponseField>

<ResponseField name="title" type="string">
Human-readable display name. Shown by hosts when more polish is needed than `name`.
</ResponseField>

<ResponseField name="description" type="string">
Short description of what the server does.
</ResponseField>

<ResponseField name="websiteUrl" type="string">
URL of the server's homepage.
</ResponseField>

<ResponseField name="icons" type="Icon[]">
64x64 PNG icons (data URIs preferred). Light and dark variants supported via the `theme` field on each icon.
</ResponseField>

<ResponseField name="instructions" type="string">
Server-wide instructions sent in the MCP `initialize` response. Hosts (ChatGPT, Claude) may surface this to the model, typically by injecting it into the system prompt. Use it for guidance that spans multiple tools or describes the server as a whole — e.g., "Always call `get_user` before `update_user`" or "This server is read-only between 5pm and 9am UTC". Per-tool guidance still belongs in each tool's `description`.

```ts
export const server: ServerConfig = {
name: 'My App',
version: '1.0.0',
instructions: 'Call list_orders before fulfill_order. Refunds require a manager scope.',
};
```
</ResponseField>

## Using Auth in Tool Handlers

The `authInfo` from the server entry is available in every tool handler:
Expand Down
2 changes: 1 addition & 1 deletion docs/mcp-apps/mcp/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ sequenceDiagram
```

1. The client sends `initialize` with its capabilities and preferred protocol version.
2. The server responds with its own capabilities and the negotiated protocol version.
2. The server responds with its own capabilities and the negotiated protocol version. The response can also include an optional `instructions` string the host may surface to the model (e.g., as part of the system prompt) to describe cross-tool relationships and constraints.
3. The client sends `notifications/initialized` to confirm the session is ready.
4. Requests flow in both directions for the duration of the session.

Expand Down
2 changes: 1 addition & 1 deletion examples/albums-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"clsx": "^2.1.1",
"embla-carousel-react": "^8.6.0",
"embla-carousel-wheel-gestures": "^8.1.0",
"sunpeak": "^0.20.16",
"sunpeak": "^0.20.17",
"tailwind-merge": "^3.5.0",
"zod": "^4.4.3"
},
Expand Down
5 changes: 5 additions & 0 deletions examples/albums-example/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ export async function auth(req: IncomingMessage): Promise<AuthInfo | null> {
* to embed the icon inline (no external fetch required by the host):
*
* icons: [{ src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'] }]
*
* `instructions` is a server-wide hint hosts may inject into the model's
* system prompt — useful for cross-tool workflows or constraints that
* don't fit in any single tool's `description`.
*/
export const server: ServerConfig = {
// name defaults to package.json "name" field when omitted
version: '1.0.0',
description: 'A sunpeak MCP app',
// instructions: 'Always call get_user before update_user.',
// icons: [{ src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'] }],
};
2 changes: 1 addition & 1 deletion examples/carousel-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"clsx": "^2.1.1",
"embla-carousel-react": "^8.6.0",
"embla-carousel-wheel-gestures": "^8.1.0",
"sunpeak": "^0.20.16",
"sunpeak": "^0.20.17",
"tailwind-merge": "^3.5.0",
"zod": "^4.4.3"
},
Expand Down
5 changes: 5 additions & 0 deletions examples/carousel-example/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ export async function auth(req: IncomingMessage): Promise<AuthInfo | null> {
* to embed the icon inline (no external fetch required by the host):
*
* icons: [{ src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'] }]
*
* `instructions` is a server-wide hint hosts may inject into the model's
* system prompt — useful for cross-tool workflows or constraints that
* don't fit in any single tool's `description`.
*/
export const server: ServerConfig = {
// name defaults to package.json "name" field when omitted
version: '1.0.0',
description: 'A sunpeak MCP app',
// instructions: 'Always call get_user before update_user.',
// icons: [{ src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'] }],
};
2 changes: 1 addition & 1 deletion examples/map-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"clsx": "^2.1.1",
"embla-carousel-react": "^8.6.0",
"mapbox-gl": "^3.23.1",
"sunpeak": "^0.20.16",
"sunpeak": "^0.20.17",
"tailwind-merge": "^3.5.0",
"zod": "^4.4.3"
},
Expand Down
5 changes: 5 additions & 0 deletions examples/map-example/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ export async function auth(req: IncomingMessage): Promise<AuthInfo | null> {
* to embed the icon inline (no external fetch required by the host):
*
* icons: [{ src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'] }]
*
* `instructions` is a server-wide hint hosts may inject into the model's
* system prompt — useful for cross-tool workflows or constraints that
* don't fit in any single tool's `description`.
*/
export const server: ServerConfig = {
// name defaults to package.json "name" field when omitted
version: '1.0.0',
description: 'A sunpeak MCP app',
// instructions: 'Always call get_user before update_user.',
// icons: [{ src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'] }],
};
2 changes: 1 addition & 1 deletion examples/review-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
"dependencies": {
"clsx": "^2.1.1",
"sunpeak": "^0.20.16",
"sunpeak": "^0.20.17",
"tailwind-merge": "^3.5.0",
"zod": "^4.4.3"
},
Expand Down
5 changes: 5 additions & 0 deletions examples/review-example/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ export async function auth(req: IncomingMessage): Promise<AuthInfo | null> {
* to embed the icon inline (no external fetch required by the host):
*
* icons: [{ src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'] }]
*
* `instructions` is a server-wide hint hosts may inject into the model's
* system prompt — useful for cross-tool workflows or constraints that
* don't fit in any single tool's `description`.
*/
export const server: ServerConfig = {
// name defaults to package.json "name" field when omitted
version: '1.0.0',
description: 'A sunpeak MCP app',
// instructions: 'Always call get_user before update_user.',
// icons: [{ src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'] }],
};
5 changes: 4 additions & 1 deletion packages/sunpeak/src/mcp/production-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,10 @@ export function createProductionMcpServer(config: ProductionServerConfig): McpSe
},
],
},
{ capabilities: { resources: {}, tools: {} } }
{
capabilities: { resources: {}, tools: {} },
...(serverInfo?.instructions ? { instructions: serverInfo.instructions } : {}),
}
);

// Build resource lookup: resource name → ProductionResource
Expand Down
5 changes: 4 additions & 1 deletion packages/sunpeak/src/mcp/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,10 @@ function createAppServer(
},
],
},
{ capabilities: { resources: {}, tools: {} } }
{
capabilities: { resources: {}, tools: {} },
...(serverInfo?.instructions ? { instructions: serverInfo.instructions } : {}),
}
);

// Capture the connecting host's clientInfo.name after MCP initialization.
Expand Down
27 changes: 27 additions & 0 deletions packages/sunpeak/src/mcp/stateless.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,33 @@ describe('createHandler stateless mode', () => {
});
});

describe('serverInfo.instructions', () => {
it('includes instructions in initialize result when set', async () => {
const text = 'Always call get_user before update_user.';
const handler = createHandler({
...baseWebConfig,
serverInfo: { name: 'test', version: '1.0.0', instructions: text },
});
const res = await handler(makePostRequest(initializeBody()));
expect(res.status).toBe(200);

const body = await res.json();
expect(body.result?.instructions).toBe(text);
});

it('omits instructions from initialize result when unset', async () => {
const handler = createHandler({
...baseWebConfig,
serverInfo: { name: 'test', version: '1.0.0' },
});
const res = await handler(makePostRequest(initializeBody()));
expect(res.status).toBe(200);

const body = await res.json();
expect(body.result?.instructions).toBeUndefined();
});
});

describe('createHandler stateful mode (default)', () => {
it('returns 404 for unknown session ID', async () => {
const handler = createHandler({ ...baseWebConfig, stateless: false });
Expand Down
22 changes: 21 additions & 1 deletion packages/sunpeak/src/mcp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,40 @@ export type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
*
* If omitted, a default sunpeak icon is used.
*
* `instructions` is sent in the MCP initialize response. Hosts may inject it
* into the model's system prompt to teach cross-tool relationships, workflow
* patterns, and constraints that don't fit in per-tool descriptions.
*
* @example
* ```ts
* export const server: ServerConfig = {
* name: 'my-app',
* version: '1.0.0',
* description: 'My MCP app',
* instructions: 'Always call get_user before update_user. Read-only after 5pm UTC.',
* icons: [
* { src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'] },
* { src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'], theme: 'dark' },
* ],
* };
* ```
*/
export type ServerConfig = Partial<Implementation>;
export type ServerConfig = Partial<Implementation> & {
/**
* Server-wide instructions sent in the MCP initialize response.
*
* Hosts (ChatGPT, Claude) may surface this string to the model, typically
* by injecting it into the system prompt. Use it for guidance that spans
* multiple tools or describes the server as a whole, e.g.:
*
* - "Always call `get_user` before `update_user`"
* - "This server is read-only between 5pm and 9am UTC"
* - "Prefer `search_albums` over listing all albums for queries with a name"
*
* Per-tool guidance still belongs in each tool's `description`.
*/
instructions?: string;
};

/**
* Extra context passed to tool handlers as the second argument.
Expand Down
5 changes: 5 additions & 0 deletions packages/sunpeak/template/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ export async function auth(req: IncomingMessage): Promise<AuthInfo | null> {
* to embed the icon inline (no external fetch required by the host):
*
* icons: [{ src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'] }]
*
* `instructions` is a server-wide hint hosts may inject into the model's
* system prompt — useful for cross-tool workflows or constraints that
* don't fit in any single tool's `description`.
*/
export const server: ServerConfig = {
// name defaults to package.json "name" field when omitted
version: '1.0.0',
description: 'A sunpeak MCP app',
// instructions: 'Always call get_user before update_user.',
// icons: [{ src: 'data:image/png;base64,...', mimeType: 'image/png', sizes: ['64x64'] }],
};
2 changes: 1 addition & 1 deletion skills/create-sunpeak-app/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ sunpeak-app/
│ │ └── {name}.tsx # Resource component + ResourceConfig export
│ ├── tools/
│ │ └── {name}.ts # Tool metadata, Zod schema, handler
│ ├── server.ts # Optional server entry (auth, config, icons)
│ ├── server.ts # Optional server entry (auth, identity, icons, instructions)
│ └── styles/
│ └── globals.css # Tailwind imports
├── tests/
Expand Down
Loading