Skip to main content
registerTool adds an action the model can call to your server, optionally rendering its result through a view.

Example

search-products searches the store catalog for the signed-in user and renders the matches in the carousel view.
server.ts

Signature

Config

The first argument declares the tool.

name, title, description

All three are the tool’s prompt surface: the model reads them to decide when to call it, so write each for the model. name is also the identifier the call uses, and title a short display name.

icons

The tool’s icons, a standard MCP field using the Icon type (src, mimeType, sizes, theme). Hosts choose whether and where to show them. ChatGPT asks for them on every tool with an entrypoint, as a monochrome SVG on a 20x20 viewport that draws with currentColor. Without them it falls back to the server’s icons for a local server, or to the logo registered with OpenAI for a hosted app.

inputSchema, outputSchema

Shapes of Standard Schema validators that can emit JSON Schema: Zod 4.2+ and ArkType out of the box, valibot through @valibot/to-json-schema. inputSchema validates the call arguments and types the handler’s input. Add .describe() to a field to tell the model what it’s for. outputSchema tells the model the shape to expect back, it does not type the returned value.

annotations

Standard MCP hints describing what the tool does, so the host can label it and order calls. They are hints: the host may surface or ignore them, and never gates a call on them.

view

Bind the tool to a React view to render its result instead of plain text. Each view backs exactly one tool.

csp

A view runs in a sandboxed iframe; your server’s domain is allowlisted automatically. Add external origins per directive:

openai

This is an OpenAI MCP extension, not part of MCP or MCP Apps. Only ChatGPT supports it, and other hosts ignore it.
The options need a view: Skybridge writes them to _meta["openai/ui"] on the tool and on its view resource, and throws at startup when the tool has no view.
A sidebar entry and a conversation tab call the tool without arguments, so it takes no required input:
A file entrypoint calls the tool with the file the user opened, so it gets its own tool with a required file input. registerFileViewer is a shortcut for this:
Every field is optional, and Skybridge only emits what you set: ChatGPT applies the spec defaults for the rest. When both display-mode fields are omitted, the view supports both modes. When only preferredDisplayMode is set, it becomes the only available mode, so pair it with availableDisplayModes to keep the other. ChatGPT calls a tool with {} from a global or thread entrypoint, so Skybridge throws at startup when the tool’s input schema rejects {}. From a file entrypoint it passes { file: { name, resourceUri } }, which the view reads and saves with useFileResource. Skybridge also throws when a file extension doesn’t start with ..
ChatGPT opens global, thread and file entrypoints fullscreen. The display modes apply when the model calls the tool in a conversation.
settings, quickAction and preferredModelDisplayMode come from OpenAI’s reference package, not from the written spec, so their behavior in ChatGPT may change.

auth

The recommended way to declare a tool’s auth. With the oauth field set, Skybridge enforces the declaration for you (anonymous or under-scoped calls are rejected before the handler runs). Declaring any { allowsAnonymous: true } tool turns the server mixed: it serves anonymous callers, and every other tool stays sign-in-gated by default. Requiring sign-in ({ scopes }) needs the oauth provider and throws without it; { allowsAnonymous: true } works either way. Omit auth entirely for the secure default (sign-in required whenever the server has auth).

securitySchemes

The low-level form behind auth, for cases it can’t express (several alternative oauth2 schemes, or pure noauth that never accepts a token). Mutually exclusive with auth.
  • Across the array, match any. Each entry is an alternative.
  • Within an oauth2 entry’s scopes, match all. The token must carry every listed scope.
  • noauth and oauth2 together means “works anonymously, but auth unlocks more.”
With the oauth field, Skybridge enforces securitySchemes like auth. With hand-wired middleware (requireBearerAuth or optionalBearerAuth) it stays client-facing metadata: enforce the tool’s requirements in the handler from extra.http?.authInfo.

_meta

Metadata on the tool. Skybridge recognizes the keys below; any other key you set is forwarded on the tool’s _meta untouched.
The openai/* keys are read only on ChatGPT. openai/widgetAccessibleis deprecated, prefer ui.visibility instead.

Handler

Runs when the tool is called. It receives the validated input (typed from inputSchema) and the request context extra:

extra

The request context.

Return

For a view tool, Skybridge adds a viewUUID to _meta, a reserved key used for state persistence.

Client hints

ChatGPT attaches conversation context to extra.mcpReq._meta. They are hints: tolerate their absence and never use them for authorization.
These hints are ChatGPT only.
Any MCP client can send openai/resource. Resolve the path and check that it stays inside a directory you allow before reading or writing the file.

Content helpers

skybridge/server exports helpers that build ContentBlocks for the handler’s content:
Returning plain ContentBlock objects works too; the helpers are optional.

McpServer

The server you register tools on

useToolInfo

Read the tool’s result in the view

FileRef

Pass files in and out of a tool