This spec outlines every extension and exactly how it builds on top of the existing MCP spec. In most cases, it is easier to use these extensions via our TypeScript and Python SDKs:
- TypeScript SDK (MCP Servers and MCP Apps)
- Python SDK (MCP Servers only)
MCP.<Type> refers to the corresponding type in the MCP schema reference.
Web refers to the Work browser, excluding classic ChatGPT. Asterisks link to remaining platform limitations.
| Feature | Desktop | Web | iOS | Android |
|---|---|---|---|---|
| Global entrypoint | Supported | Supported | Supported | Supported |
| Deep links | Supported | Supported | Supported | Not supported |
| Thread entrypoint | Supported | Supported | Supported | Supported |
| File entrypoint | Supported | Not supported | Not supported | Not supported |
| Resource display modes | Supported | Supported | Supported | Supported |
| Plugin onboarding | Supported | Supported | Supported | Supported |
| Structured settings | Supported | Supported | Supported | Supported |
| Extended model context | Supported | Supported* | Supported* | Supported |
| WebMCP | Supported | Not supported | Not supported | Not supported |
| Composer at-mentions | Supported | Supported | Not supported | Not supported |
| Extended message sending | Supported | Supported | Supported* | Supported |
| Extended forms | Supported | Supported* | Not supported | Not supported |
| File resources | Supported | Not supported | Not supported | Not supported |
| File opening | Supported | Not supported | Not supported | Not supported |
Servers advertise named capabilities at these locations:
| MCP Protocol version | Result | Capability location |
|---|---|---|
2026-07-28 or newer |
server/discover |
capabilities["extensions"] |
2025-11-25 or earlier |
initialize |
capabilities["extensions"] or legacy capabilities["experimental"] |
Normally, MCP Apps can only be invoked via the model. However, it may be convenient to allow users to open your MCP App via static entrypoints.
You can define up to three entrypoints on any MCP App:
- Global: Available in the primary sidebar navigation.
- Thread: Available as a content tab within a thread.
- File: File extension handler to render matching files referenced from any part of any thread.
- Entrypoints are registered by adding one or more entries to
_meta["openai/ui"]["entrypoints"], as defined below. _meta["ui"]["visibility"]normally controls model versus user visibility. This field is ignored when an MCP App is invoked as an entrypoint.
An icon helps users identify the MCP Server in navigation.
Entrypoint icons are resolved in the following priority order:
- The
iconsproperty in the MCP App’stools/listentry. - The MCP Server icon for local MCP Servers, or the registered app logo for hosted MCP Apps. Server icons come from
_meta["io.modelcontextprotocol/serverInfo"]["icons"]inserver/discover, orserverInfo["icons"]ininitializefor MCP Servers that do not implementserver/discover. - A generic fallback icon.
All entrypoints will be rendered with a title.
- The title comes from the first defined property in the following list on your MCP App’s
tools/listentry:titleannotations["title"]name
Global entrypoints expose apps that users can open fullscreen from global navigation.
- On Desktop: requests that target the current thread, such as
ui/update-model-contextandui/message, use the global entrypoint's thread. - The server MUST accept
{}as the tool arguments when the entrypoint opens.
interface GlobalEntrypoint {
type: "global";
}tools/list response item:
{
"name": "cad.library",
"inputSchema": {
"type": "object"
},
"_meta": {
"ui": {
"resourceUri": "ui://bits-and-bolts/library"
},
"openai/ui": {
"entrypoints": [
{
"type": "global"
}
]
}
}
}Deep links navigate directly to a particular page within a global entrypoint.
- On Desktop: use the
codexURI scheme. - On iOS: use the
chatgptURI scheme. - URI format:
{scheme}://plugins/{pluginId}@{marketplace}/app/{toolName}?path={encodedAppRelativePath}pluginId- MUST be percent-encoded.
- Can be found via the public ChatGPT plugin URL (
https://chatgpt.com/plugins/{pluginId}).
marketplace- A custom
marketplace.jsonname. @{marketplace}MUST be omitted for plugins published directly to ChatGPT.
- A custom
toolName- MUST be percent-encoded as a single path segment.
encodedAppRelativePath- The complete app-relative URL, including any query string, MUST be percent-encoded as the
pathquery value. - The decoded app-relative URL MUST begin with
/and MUST NOT contain a fragment. - If
pathis omitted, the app-relative URL defaults to/.
- The complete app-relative URL, including any query string, MUST be percent-encoded as the
- MCP Apps receive the specified app-relative URL in
hostContext["openai/deepLink"]during initialization and through subsequentui/notifications/host-context-changednotifications, following MCP Apps host-context update semantics.
Web link format:
https://chatgpt.com/plugins/<plugin-id>/app/<tool-name>?path=<encoded-app-relative-url>
interface DeepLinkHostState {
/** App-relative URL, including any query. */
url: string;
}Deep link with two query parameters:
codex://plugins/bits-and-bolts/app/cad.browse?path=%2Fparts%3Ftag%3Dbolt%26sort%3Dasc
MCP App initialize result:
{
"jsonrpc": "2.0",
"id": 0,
"result": {
"protocolVersion": "2026-01-26",
"hostInfo": { "name": "example-host", "version": "1.0.0" },
"hostCapabilities": {},
"hostContext": {
"openai/deepLink": {
"url": "/parts?tag=bolt&sort=asc"
}
}
}
}Notification when the user clicks a deep link to a different page:
codex://plugins/bits-and-bolts/app/cad.browse?path=%2Fparts%2Fhex-bolt
{
"jsonrpc": "2.0",
"method": "ui/notifications/host-context-changed",
"params": {
"openai/deepLink": {
"url": "/parts/hex-bolt"
}
}
}Thread entrypoints let users manually open an MCP App as a new content tab within a thread.
- The server receives
{}as the tool arguments when the entrypoint opens. - Every thread will have a different instance of the entrypoint.
interface ThreadEntrypoint {
type: "thread";
}tools/list response item:
{
"name": "cad.tray",
"inputSchema": {
"type": "object",
"properties": {}
},
"_meta": {
"ui": {
"resourceUri": "ui://bits-and-bolts/tray"
},
"openai/ui": {
"entrypoints": [
{
"type": "thread"
}
]
}
}
}File extension entrypoints let MCP Apps advertise support for file types (for example, .ipynb or .stl) and provide a custom viewer in place of the default file viewer.
File extension entrypoints use extended MCP Resource APIs for reading, subscribing to, and updating the files that invoked them.
MCP App initialize result:
{
"jsonrpc": "2.0",
"id": 0,
"result": {
"protocolVersion": "2026-01-26",
"hostInfo": { "name": "example-host", "version": "1.0.0" },
"hostCapabilities": { "experimental": { "openai/resource": {} } },
"hostContext": {}
}
}- Servers MUST use the file-extension form (a string that starts with ".") of HTML's
acceptattribute for each value inextensions. - The MCP Server tool receives
FileInputas its arguments when the entrypoint opens.resourceUri- Opaque URI that WILL NOT directly map to a filesystem path.
- Standard MCP resource methods such as
resources/readandresources/subscribework. However, they will be intercepted and handled by ChatGPT instead of the MCP Server.
- The MCP App receives
ui/notifications/tool-inputwith the sameFileInputarguments.
interface FileEntrypoint {
type: "file";
extensions: string[];
}
interface FileInput {
file: {
/** Non-empty name of the opened file. Includes the extension but not the path. */
name: string;
/** Non-blank opaque URI for access through the host. */
resourceUri: string;
};
}tools/list response item:
{
"name": "cad.open",
"inputSchema": {
"type": "object",
"properties": {
"file": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"resourceUri": {
"type": "string"
}
},
"required": ["name", "resourceUri"]
}
},
"required": ["file"]
},
"_meta": {
"ui": {
"resourceUri": "ui://bits-and-bolts/viewer"
},
"openai/ui": {
"entrypoints": [
{
"type": "file",
"extensions": [".stl", ".step"]
}
]
}
}
}Notification:
{
"jsonrpc": "2.0",
"method": "ui/notifications/tool-input",
"params": {
"arguments": {
"file": {
"name": "hex-bolt.stl",
"resourceUri": "host-resource://hex-bolt"
}
}
}
}Because MCP Apps can execute untrusted JavaScript, ChatGPT will never directly provide raw filesystem paths to MCP Apps. Instead, MCP resource methods should be used.
For more advanced filesystem usage, such as reading relative files or parsing very large files that can't be transferred via a single MCP resource request, use dedicated tools handled by the MCP Server.
Tool calls from an MCP App to its MCP Server within a file extension entrypoint will be intercepted and amended with _meta["openai/resource"]["path"]. Its value is the absolute filesystem path of the opened resource.
Once the MCP Server has the path, it can do anything with it, including sending the path back to the MCP App if it is trusted.
sequenceDiagram
participant App as MCP App
participant Host
participant Server as MCP Server
alt Resource operation
App->>Host: Read, subscribe, or write using the opaque URI
Host-->>App: Result or resource update
else Tool call
App->>Host: tools/call
Host->>Server: tools/call + _meta["openai/resource"].path
Server-->>Host: Tool result
Host-->>App: Tool result
end
By default, MCP Servers determine whether the right representation for a resource is text or a base64-encoded blob. However, a resources/read call for a file extension entrypoint is handled by ChatGPT, which cannot infer what the correct representation should be. To specify the ideal representation, set _meta["openai/resource"]["representation"] to either "text" or "blob".
- If the representation is unspecified, an MCP App SHOULD be able to handle either text or blob results.
- If a representation is requested from a URI other than the one that represents the file extension entrypoint, it is up to the MCP Server to respect the representation request.
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "resources/read",
"params": {
"uri": "host-resource://hex-bolt",
"_meta": { "openai/resource": { "representation": "text" } }
}
}MCP Apps receive change notifications through notifications/resources/updated.
- MCP Apps MAY subscribe to changes to their
resourceUriwithresources/subscribeand stop receiving updates withresources/unsubscribe.
The MCP protocol normally only allows you to read resources and subscribe to changes to them. File extension entrypoints support updating the resource to enable rich editor experiences within ChatGPT via an openai/resources/write method.
- MCP Apps MAY receive
_meta["openai/resource"]on a content item in aresources/readresult for a file opened via a file extension entrypoint. - MCP Apps MAY call
openai/resources/writewith thefile["resourceUri"]given to them via a file extension entrypoint. - MCP Apps MAY NOT call
openai/resources/writewith any other URI. - MCP Apps MAY NOT call
openai/resources/writeifwritablewas not set to true when reading the file. - If
ifMatchis provided, the resource will only be updated if the ETag provided matches the latest value.
/** `resources/read` `_meta["openai/resource"]` for a file extension entrypoint URI. */
interface ResourceMetadata {
/** A hint as to whether the host supports writes. Defaults to false. */
writable?: boolean;
/** Opaque version token for the returned contents. */
etag?: string;
}
/** `openai/resources/write` params. */
type ResourceWriteParams = {
/** Non-blank host-provided URI of the opened resource. */
uri: string;
/** Non-empty ETag to match. Omit to write without a version check. */
ifMatch?: string;
} & (
| { text: string }
| {
/** Base64-encoded replacement contents. */
blob: string;
}
);
/** `openai/resources/write` result. */
type ResourceWriteResult =
| {
outcome: "saved";
/** ETag of the saved version. */
etag: string;
}
| {
outcome: "conflict";
/** Not saved. ETag of the current version. */
etag: string;
}
| {
outcome: "too-large";
/** Not saved. Size limit exceeded by UTF-8 text or base64-decoded blob bytes. */
maxBytes: number;
};resources/read response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"contents": [
{
"uri": "host-resource://hex-bolt",
"text": "solid hex-bolt\nendsolid hex-bolt\n",
"_meta": {
"openai/resource": { "etag": "version-1", "writable": true }
}
}
]
}
}Request:
{
"jsonrpc": "2.0",
"id": 2,
"method": "openai/resources/write",
"params": {
"uri": "host-resource://hex-bolt",
"text": "solid edited-hex-bolt\nendsolid edited-hex-bolt\n",
"ifMatch": "version-1"
}
}Response:
{
"jsonrpc": "2.0",
"id": 2,
"result": { "outcome": "saved", "etag": "version-2" }
}Structured settings let MCP Apps contribute settings to the plugin details page within ChatGPT. ChatGPT will render the settings with native controls that match the look and feel of ChatGPT on the current platform.
Servers advertise openai/settings through server capabilities.
initialize result (MCP 2025-11-25):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-11-25",
"serverInfo": {
"name": "bits-and-bolts",
"version": "1.0.0"
},
"capabilities": {
"tools": {},
"experimental": {
"openai/settings": {
"readTool": "settings.read",
"updateTool": "settings.update"
}
}
}
}
}server/discover result:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "complete",
"supportedVersions": ["2026-07-28"],
"_meta": {
"io.modelcontextprotocol/serverInfo": {
"name": "bits-and-bolts",
"version": "1.0.0"
}
},
"capabilities": {
"tools": {},
"extensions": {
"openai/settings": {
"readTool": "settings.read",
"updateTool": "settings.update"
}
}
}
}
}SettingsCapabilityreturns two tools:readToolreads the type-safe schema, current values, and layout for the settings.updateToolreceives updates for changed settings from ChatGPT.
- MCP Servers are responsible for persisting settings.
/** capabilities["extensions"]["openai/settings"] */
interface SettingsCapability {
/** Non-blank name of the read settings tool. */
readTool: string;
/** Non-blank name of the update settings tool. */
updateTool: string;
}Settings combine primitive fields and tool actions.
- Servers MUST accept
{}for the read tool, and calls to it must be read-only. outputSchema- Servers MUST declare an
outputSchemadescribingSettingsReadResult. - The example response contains an
outputSchemayou can use.
- Servers MUST declare an
values- Servers MUST provide a current or default value in
valuesfor every schema property.
- Servers MUST provide a current or default value in
layout- Servers MAY use
layoutto group settings and add tool actions into logical groupings. - Fields omitted from
layoutappear in an "Other settings" group after the listed groups.
- Servers MAY use
- Primitive settings only support boolean, string (with optional enum), number, and integer types.
- Tool settings may correspond to MCP App tools or regular tools.
- Tool actions use the referenced tool's display name:
title, thenannotations.title, thenname. - Regular tools: when the button is clicked, ChatGPT will render a spinner followed by a tooltip containing text from the tool call response.
- MCP App tools: when the button is clicked, ChatGPT will render the MCP App in a modal on top of settings. This can be used for bespoke settings such as a payment method entry form.
- Tool actions use the referenced tool's display name:
interface SettingsReadResult {
schema: {
type: "object";
properties: Record<string, SettingSchema>;
/** Required property names. All properties still need an effective value in `values`. */
required?: string[];
};
/** Current or default values for every schema property. */
values: Record<string, unknown>;
/** Groups appear in array order. */
layout?: SettingsGroup[];
}
type SettingSchema = {
title: string;
description?: string;
} & (
| { type: "boolean" }
| {
type: "string";
enum?: string[];
/** Minimum string length. Nonnegative integer. */
minLength?: number;
/** Maximum string length. Nonnegative integer. */
maxLength?: number;
/** Regular expression that the value must match. */
pattern?: string;
}
| {
type: "number" | "integer";
/** Inclusive lower bound. */
minimum?: number;
/** Inclusive upper bound. */
maximum?: number;
/** Positive number whose multiples are valid values. */
multipleOf?: number;
}
);
interface SettingsGroup {
kind: "group";
title: string;
items: (
| { kind: "property"; property: string }
| {
kind: "tool";
/** Same-server tool accepting {}. */
tool: string;
/** @deprecated Set the title on the referenced MCP tool. Hosts ignore this field. */
title?: string;
description?: string;
}
)[];
}Tool definition:
{
"name": "settings.read",
"inputSchema": { "type": "object", "additionalProperties": false },
"annotations": { "readOnlyHint": true },
"outputSchema": {
"type": "object",
"properties": {
"schema": { "type": "object" },
"values": { "type": "object" },
"layout": { "type": "array", "items": { "type": "object" } }
},
"required": ["schema", "values"]
}
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [],
"structuredContent": {
"schema": {
"type": "object",
"properties": {
"units": {
"type": "string",
"title": "Measurement units",
"enum": ["mm", "in"]
},
"showGrid": {
"type": "boolean",
"title": "Show grid"
}
}
},
"values": {
"units": "in",
"showGrid": false
},
"layout": [
{
"kind": "group",
"title": "Display",
"items": [
{
"kind": "property",
"property": "units"
},
{
"kind": "tool",
"tool": "cad.library"
}
]
}
]
}
}
}The server receives updated settings values through the tool named by updateTool in the settings capability.
- ChatGPT will only call
updateToolwith the settings properties that have changed.
interface SettingsUpdateArguments {
set: Record<string, string | number | boolean>;
}Tool definition:
{
"name": "settings.update",
"inputSchema": {
"type": "object",
"properties": {
"set": {
"type": "object",
"properties": {
"units": { "type": "string", "enum": ["mm", "in"] },
"showGrid": { "type": "boolean" }
},
"minProperties": 1,
"additionalProperties": false
}
},
"required": ["set"],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": { "values": { "type": "object" } },
"required": ["values"]
}
}Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "settings.update",
"arguments": {
"set": {
"showGrid": true
}
}
}
}tools/call response retaining units:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [],
"structuredContent": {
"values": {
"units": "in",
"showGrid": true
}
}
}
}The MCP Apps specification defines display modes to specify where MCP Apps can be rendered. However, appCapabilities.availableDisplayModes is available only after app initialization, which can take time. ChatGPT also recognizes _meta["openai/ui"].availableDisplayModes on the UI resource content item so it can choose a supported mode immediately.
type ChatGPTUIDisplayMode = "inline" | "fullscreen";
/** UI resource content item's `_meta["openai/ui"]`. */
interface DisplayModes {
/** Display modes the app supports. */
availableDisplayModes?: ChatGPTUIDisplayMode[];
/** Preferred initial display mode. */
preferredDisplayMode?: ChatGPTUIDisplayMode;
}- ChatGPT supports
inlineandfullscreen, but notpip. - When the model invokes an MCP App, ChatGPT considers
preferredDisplayModewhen choosing the initial display mode. - If
availableDisplayModesis omitted from_meta["openai/ui"], ChatGPT assumes the app supports all ChatGPT display modes. - If
availableDisplayModesincludes bothinlineandfullscreen, ChatGPT defaults tofullscreenunlesspreferredDisplayModespecifies otherwise. - If
_meta["openai/ui"]is omitted entirely, ChatGPT preserves legacy behavior and defaults toinline. - ChatGPT uses
fullscreenfor all entrypoints.
resources/read content item that opens fullscreen by default:
{
"uri": "ui://bits-and-bolts/library",
"mimeType": "text/html;profile=mcp-app",
"text": "<!doctype html><html>...</html>",
"_meta": {
"openai/ui": {
"availableDisplayModes": ["inline", "fullscreen"]
}
}
}Onboarding provides a setup skill for users to run after installation. Setup invokes the onboarding skill in a new conversation, or in the existing thread if the plugin was installed during a conversation.
- Plugins MAY set
extensions["com.openai"]["onboardingSkill"]in their manifest to a path relative to the plugin root, referencing a packaged skill.
Manifest:
{
"name": "bits-and-bolts",
"skills": "./skills/",
"extensions": {
"com.openai": {
"onboardingSkill": "./skills/setup/SKILL.md"
}
}
}This extension clarifies and extends the behavior of ui/update-model-context.
MCP App initialize result:
{
"jsonrpc": "2.0",
"id": 0,
"result": {
"protocolVersion": "2026-01-26",
"hostInfo": { "name": "example-host", "version": "1.0.0" },
"hostCapabilities": {
"experimental": { "openai/modelContext": {} },
"updateModelContext": {
"text": {},
"image": {},
"resourceLink": {},
"resource": {},
"structuredContent": {}
}
},
"hostContext": {}
}
}- On Web: image content in model context is not yet supported.
- Content block
_metais excluded from model input. ui/update-model-contextis idempotent. Each call replaces the model context previously supplied by the same MCP App instance.
Group metadata labels related content blocks.
type ModelContextGroup = {
/** Title shared by related content blocks. */
title: string;
};MCP Apps MAY set _meta["openai/group"] to a ModelContextGroup object on any content block in ui/update-model-context.
The group title MUST be a non-empty string. Content blocks without a usable group title use the "Context" group.
{
"jsonrpc": "2.0",
"id": 1,
"method": "ui/update-model-context",
"params": {
"content": [
{
"type": "text",
"text": "Selected: Bug keycap (1U)",
"_meta": { "openai/group": { "title": "Keycaps" } }
},
{
"type": "text",
"text": "Selected: OpenAI keycap (1U)",
"_meta": { "openai/group": { "title": "Keycaps" } }
},
{
"type": "text",
"text": "Selected: Codex Micro joystick cap",
"_meta": { "openai/group": { "title": "Controls" } }
}
]
}
}Context change notifications provide updates from the host when model context changes, such as when a user removes an attachment.
- MCP Apps receive their attached context in
hostContext["openai/modelContext"]on initialization or remount.
type ModelContextHostState = {
/** Non-empty ID, renewed any time content or structuredContent changes. */
updateId: string;
content?: MCP.ContentBlock[];
structuredContent?: Record<string, unknown>;
} | null;Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "ui/update-model-context",
"params": {
"content": [{ "type": "text", "text": "Selected: M6 hex bolt" }]
}
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"_meta": {
"openai/modelContext": {
"updateId": "update-1"
}
}
}
}Notification (context cleared):
{
"jsonrpc": "2.0",
"method": "ui/notifications/host-context-changed",
"params": {
"openai/modelContext": null
}
}Titles provide human-readable labels for text attachments and alt text for images.
MCP Apps MAY set _meta["openai/title"] to a non-empty string on text and image content blocks.
Text block in ui/update-model-context with a title:
{
"type": "text",
"text": "Selected: agent dial",
"_meta": {
"openai/title": "Agent dial"
}
}Thumbnails give text attachments a visual representation of their context.
MCP Apps MAY set _meta["openai/thumbnail"] to an MCP.Icon on text content blocks in ui/update-model-context.
- On iOS:
openai/thumbnailis not yet supported.
Text block in ui/update-model-context with a thumbnail:
{
"type": "text",
"text": "Selected: agent dial",
"_meta": {
"openai/thumbnail": {
"src": "https://example.com/agent-dial.png"
}
}
}Sometimes, it is useful to provide context to the model without showing a removable composer attachment to the user.
- Content blocks marked with
annotations.audience: ["assistant"]are hidden from the user.- All model context is sent to the model regardless of audience.
- MCP Apps returning serialized
structuredContentin a text content block for MCP backward compatibility SHOULD set that block'sannotations.audienceto["assistant"].
Request:
{
"type": "text",
"text": "Current part: hex-bolt",
"annotations": {
"audience": ["assistant"]
}
}ui/update-model-context and ui/message support the following content blocks:
| Content block | Support |
|---|---|
| Text | Supported |
| Image | Supported |
| Resource link | Supported |
| Embedded resource | Supported |
| Audio | Not supported |
Agents can discover and call tools registered by expanded MCP Apps through document.modelContext.
- WebMCP is available only on Desktop.
- ChatGPT does not support the MCP Apps SDK’s
App.registerTool().
This extension adds metadata to ui/message to control its behavior in ChatGPT.
Support for this extension is indicated by "openai/message": {}.
MCP App initialize result:
{
"jsonrpc": "2.0",
"id": 0,
"result": {
"protocolVersion": "2026-01-26",
"hostInfo": { "name": "example-host", "version": "1.0.0" },
"hostCapabilities": {
"experimental": { "openai/message": {} },
"message": {
"text": {}
}
},
"hostContext": {}
}
}- On iOS:
ui/messagedoes not support resource links. ui/messagesupports the same content types asui/update-model-context.
Messages support the same content titles as model context.
Untitled text becomes ordinary message text, while titled text becomes a labeled inline item that can be removed as a single unit.
MCP Apps SHOULD NOT display titled text items as selected. Apps are not notified when an item is removed from the composer.
This extension adds metadata to ui/message to control its behavior in ChatGPT.
- Defaults when undefined:
{ target: "active", send: true }. - For supported target/send combinations,
send: falseappends content to the active draft or opens a fresh editable draft whentarget: "new", without submitting it.
interface MessageParams {
role: "user";
content: MCP.ContentBlock[];
_meta?: {
"openai/message"?: MessageOptions;
};
}
type MessageOptions =
| {
/** Which conversation receives the content. */
target: "new";
/** Whether to send the content immediately. */
send?: boolean;
}
| {
target?: "active";
send?: boolean;
};Request sending a message to a new conversation:
{
"jsonrpc": "2.0",
"id": 1,
"method": "ui/message",
"params": {
"role": "user",
"content": [
{
"type": "text",
"text": "Help me design a bracket."
}
],
"_meta": {
"openai/message": {
"target": "new"
}
}
}
}Request opening a new editable draft:
{
"jsonrpc": "2.0",
"id": 2,
"method": "ui/message",
"params": {
"role": "user",
"content": [
{
"type": "text",
"text": "Help me design a bracket."
}
],
"_meta": {
"openai/message": {
"target": "new",
"send": false
}
}
}
}Typically, an MCP App will not have access to raw filesystem paths. However, if its MCP Server provides an absolute filesystem path, it can request that ChatGPT open that file.
MCP Apps receive hostCapabilities["experimental"]["openai/files"]: {} in the ui/initialize result when opening local files is available.
MCP App initialize result:
{
"jsonrpc": "2.0",
"id": 0,
"result": {
"protocolVersion": "2026-01-26",
"hostInfo": { "name": "example-host", "version": "1.0.0" },
"hostCapabilities": {
"experimental": {
"openai/files": {}
}
},
"hostContext": {}
}
}interface FileOpenParams {
/** Absolute filesystem path on the execution host. */
path: string;
}Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "openai/files/open",
"params": {
"path": "/workspace/parts/hex-bolt.stl"
}
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {}
}Normally, users can only at-mention a plugin or MCP Server. The at-mention capability allows your plugin to expose a searchable list of items (people, files, channels, etc.) that the user can mention individually in their prompt.
- The at-mention tool is responsible for handling the at-mention request, which includes a typeahead search query, and returning items for the user to choose from.
- Returned items may be resource links.
- Servers advertise
openai/mentions: { searchTool: string }through server capabilities.searchToolis the non-blank name of a read-only tool on that server. - The tool named by
searchToolMUST setannotations.readOnlyHinttotrue. - At-mention tools MUST include
"app"in_meta["ui"]["visibility"].
Deprecated:
_meta["openai/extensions"]["mentions/search"]: {}remains supported when theopenai/mentionscapability is absent. Advertise the capability instead.
Mention-search capability:
{
"extensions": {
"openai/mentions": { "searchTool": "cad.mentions" }
}
}The at-mention tool accepts these arguments:
interface MentionSearchParams {
/** Search text, which may be empty. */
query: string;
}
interface MentionSearchResult {
items: ResourceLink[];
}Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": { "name": "cad.mentions", "arguments": { "query": "hex-bolt" } }
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [],
"structuredContent": {
"items": [
{
"type": "resource_link",
"uri": "cad://parts/hex-bolt",
"name": "hex-bolt"
}
]
}
}
}Extended forms expand on MCP form elicitation with additional elicitation types and display modes.
NOTE: OpenAI-registered MCP servers require MCP 2026-07-28 or later with multi-round-trip requests (MRTR) for form elicitation. Direct MCP connections still support legacy forms.
MCP Host initialize request (MCP 2025-11-25):
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {
"extensions": {
"openai/elicitation": {
"form": {}
}
}
},
"clientInfo": { "name": "example-host", "version": "1.0.0" }
}
}- On connections that negotiate
2026-07-28or later, MCP Servers SHOULD use multi-round-trip elicitation. - On connections that negotiate an earlier protocol version, MCP Servers MUST use
openai/elicitation/create. openai/elicitation/createis a superset ofelicitation/create.- Forms containing unsupported input types are reported as unsupported. They are not partially displayed.
This section extends multi-round-trip requests for form elicitation.
- MCP Servers MUST require both
elicitation.form: {}andextensions["openai/elicitation"].form: {}in the request's client capabilities. - MCP Servers MUST specify an empty object
requestedSchemainelicitation/createinputRequests:requestedSchema:{ "type": "object", "properties": {} }.
- MCP Servers MUST put the
requestedSchemain_meta["openai/elicitation"]["requestedSchema"]: - Missing or unsupported schemas will be reported as unsupported without displaying the empty core form.
Multi round-trip input_required response (2026-07-28 or later):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "input_required",
"inputRequests": {
"details": {
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "Enter a project code",
"requestedSchema": { "type": "object", "properties": {} },
"_meta": {
"openai/elicitation": {
"requestedSchema": {
"type": "object",
"properties": {
"resource": {
"type": "string",
"format": "uri",
"x-openai-input": {
"type": "resource",
"options": [],
"userOptions": {}
}
}
}
}
}
}
}
}
}
}
}On legacy connections (2025-11-25 or older), openai/elicitation/create is a drop-in replacement for elicitation/create.
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "openai/elicitation/create",
"params": {
"mode": "form",
"message": "Choose a CAD part to inspect",
"requestedSchema": {
"type": "object",
"properties": {
"resource": {
"type": "string",
"format": "uri",
"x-openai-input": {
"type": "resource",
"options": [],
"userOptions": {}
}
}
}
}
}
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"action": "accept",
"content": { "resource": "file:///parts/hex-bolt.stl" }
}
}Hosts MUST submit answers for integer and number fields as JSON numbers.
Servers that require exact numeric text, such as large integers or high-precision decimals, MUST request a string field. Hosts MUST preserve those string answers without numeric conversion. Servers are responsible for parsing and validating the text, and MAY use pattern to constrain its format.
Servers MAY add pattern to MCP's StringSchema.
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "openai/elicitation/create",
"params": {
"mode": "form",
"message": "Choose a CAD part to inspect",
"requestedSchema": {
"type": "object",
"properties": {
"part": {
"type": "string",
"title": "Reference file",
"format": "uri",
"pattern": "^(cad|file):"
}
}
}
}
}Titled const options support an optional description.
part field with supporting text for an option (description):
{
"type": "string",
"oneOf": [
{
"const": "hex-bolt",
"title": "M6 hex bolt",
"description": "A fastener for the main joint."
}
]
}Thumbnails allow you to specify an image as the user-visible choice instead of just text.
- Titled
constoptions support an optionalx-openai-thumbnail: MCP.Icon. - Servers MUST use an HTTPS URL or base64-encoded image data URI for
src. - Thumbnails use MCP's Icon type.
- If ANY items for a given property have a thumbnail, ALL items for that property will be rendered with an image UI.
- Items without a thumbnail will render a fallback image.
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "openai/elicitation/create",
"params": {
"mode": "form",
"message": "Choose a CAD part to inspect",
"requestedSchema": {
"type": "object",
"properties": {
"part": {
"type": "string",
"title": "Part",
"oneOf": [
{
"const": "hex-bolt",
"title": "M6 hex bolt",
"x-openai-thumbnail": {
"src": "https://example.com/hex-bolt.png"
}
},
{
"const": "washer",
"title": "M6 washer",
"x-openai-thumbnail": { "src": "https://example.com/washer.png" }
}
]
}
}
}
}
}Suggested values let users choose from predefined options or enter free-form text. Array fields support selecting multiple options and adding custom text. The same field constraints apply to suggested and entered values.
- MCP Servers MAY add
x-openai-suggestionsto a string field or an array's stringitemsschema. Each suggestion uses the same titledconstoption format as option descriptions.
part field with a suggested value:
{
"type": "string",
"minLength": 1,
"x-openai-suggestions": [
{
"const": "hex-bolt",
"title": "M6 hex bolt"
}
]
}accessories field with suggested values:
{
"type": "array",
"items": {
"type": "string",
"x-openai-suggestions": [
{
"const": "washer",
"title": "M6 washer"
}
]
}
}Elicitation result with suggested and entered values:
{
"action": "accept",
"content": {
"accessories": ["washer", "custom-spacer", "custom-gasket"]
}
}Resource selection supports supplied resources, user-added files or directories, or both.
- On Web:
- Directory selection is unsupported.
- Forms requested through MCP Apps support only explicit resource selection without user uploads.
- Servers declare resource inputs by adding an
x-openai-inputto astring(single-select) orarray(multi-select) form field. - Single-select fields submit a URI string.
- Multi-select fields submit an array of URI strings.
- Implicit selection always allows user uploads. When
userOptionsis omitted, it defaults to{ kind: "file" }with no file-type restrictions. Explicit selection shows no upload input whenuserOptionsis omitted. - Resource options may include a thumbnail (
_meta["openai/thumbnail"]: MCP.Icon), a preview (_meta["openai/preview"]: { target: PreviewTarget }), or both.
| Mode | Behavior |
|---|---|
"explicit" |
Items are chosen by selecting or deselecting them. |
"implicit" |
Items are chosen by adding or removing them. All items that remain when the form is submitted are included in the result. |
- Selection modes apply only to multi-select (
type: array) fields. - Servers MUST NOT specify selection mode for single-selection fields.
- Servers MUST use only URIs from
optionsfor resource selection defaults. - Servers MUST NOT specify
defaultwith"implicit"selection.
/** x-openai-input */
interface ResourceInput {
type: "resource";
/** Multi-select only. Defaults to "explicit". */
selection?: "explicit" | "implicit";
/** Server-provided entries. May be empty. */
options: MCP.Resource[];
/** Configures user-added resources. */
userOptions?: UserResourceOptions;
}
interface UserResourceOptions {
/** Defaults to "file". */
kind?: "file" | "directory";
/**
* Allowed filename extensions or MIME types, including MIME wildcards.
* https://html.spec.whatwg.org/multipage/input.html#attr-input-accept
*/
accept?: string[];
}Request (single selection):
{
"jsonrpc": "2.0",
"id": 1,
"method": "openai/elicitation/create",
"params": {
"mode": "form",
"message": "Choose a CAD part to inspect",
"requestedSchema": {
"type": "object",
"properties": {
"part": {
"type": "string",
"title": "Reference file",
"format": "uri",
"x-openai-input": {
"type": "resource",
"options": [
{
"uri": "cad://parts/hex-bolt",
"name": "hex-bolt",
"title": "M6 hex bolt",
"_meta": {
"openai/thumbnail": {
"src": "https://example.com/hex-bolt.png"
},
"openai/preview": {
"target": {
"type": "mcp_app_tool",
"name": "cad.open",
"arguments": {
"file": {
"name": "hex-bolt.stl",
"resourceUri": "cad://parts/hex-bolt"
}
}
}
}
}
},
{
"uri": "cad://parts/washer",
"name": "washer",
"title": "M6 washer"
}
],
"userOptions": { "kind": "file", "accept": [".stl", ".step"] }
},
"default": "cad://parts/hex-bolt"
}
}
}
}
}Request (explicit selection):
{
"jsonrpc": "2.0",
"id": 1,
"method": "openai/elicitation/create",
"params": {
"mode": "form",
"message": "Choose CAD parts to inspect",
"requestedSchema": {
"type": "object",
"properties": {
"parts": {
"type": "array",
"items": { "type": "string", "format": "uri" },
"x-openai-input": {
"type": "resource",
"selection": "explicit",
"options": [
{
"uri": "cad://parts/hex-bolt",
"name": "hex-bolt",
"title": "M6 hex bolt"
},
{
"uri": "cad://parts/washer",
"name": "washer",
"title": "M6 washer"
}
]
},
"default": ["cad://parts/hex-bolt"]
}
}
}
}
}Request (implicit selection):
{
"jsonrpc": "2.0",
"id": 1,
"method": "openai/elicitation/create",
"params": {
"mode": "form",
"message": "Choose CAD parts to inspect",
"requestedSchema": {
"type": "object",
"properties": {
"parts": {
"type": "array",
"items": { "type": "string", "format": "uri" },
"x-openai-input": {
"type": "resource",
"selection": "implicit",
"options": [
{
"uri": "cad://parts/hex-bolt",
"name": "hex-bolt",
"title": "M6 hex bolt"
},
{
"uri": "cad://parts/washer",
"name": "washer",
"title": "M6 washer"
}
]
}
}
}
}
}
}Previews open expanded details for resource options.
- MCP Servers MAY add
_meta["openai/preview"]: { target: PreviewTarget }to resource options, with or without a thumbnail.
// Opens on the originating server.
type PreviewTarget =
| {
type: "mcp_app_tool";
// Non-blank name of the MCP App tool to call.
name: string;
// Defaults to {}.
arguments?: Record<string, unknown>;
}
// Reads the linked MCP resource.
| MCP.ResourceLink;Resource option with a preview:
{
"uri": "cad://parts/hex-bolt",
"name": "M6 hex bolt",
"_meta": {
"openai/preview": {
"target": {
"type": "resource_link",
"uri": "cad://parts/hex-bolt",
"name": "M6 hex bolt"
}
}
}
}