Create a new MCP App instance.
App identification (name and version)
Features and capabilities this app provides
Configuration options including autoResize behavior
OptionalfallbackA handler to invoke for any notification types that do not have their own handler installed.
OptionalfallbackA handler to invoke for any request types that do not have their own handler installed.
OptionaloncloseCallback for when the connection is closed for any reason.
This is invoked when close() is called as well.
OptionalonerrorCallback for when an error occurs.
Note that errors are not necessarily fatal; they are used for reporting any kind of exceptional condition out of band.
Request the host to open an external URL in the default browser.
The host may deny this request based on user preferences or security policy. Apps should handle rejection gracefully.
URL to open
Optionaloptions: RequestOptionsRequest options (timeout, etc.)
Result indicating success or error
try {
await app.openLink({ url: "https://docs.example.com" });
} catch (error) {
console.error("Failed to open link:", error);
// Optionally show fallback: display URL for manual copy
}
McpUiOpenLinkRequest for request structure
Use openLink instead
Convenience handler for tool call requests from the host.
Set this property to register a handler that will be called when the host requests this app to execute a tool. This enables apps to provide their own tools that can be called by the host or LLM.
The app must declare tool capabilities in the constructor to use this handler.
This setter is a convenience wrapper around setRequestHandler() that
automatically handles the request schema and extracts the params for you.
Register handlers before calling connect to avoid missing requests.
Async function that executes the tool and returns the result. The callback will only be invoked if the app declared tool capabilities in the constructor.
app.oncalltool = async (params, extra) => {
if (params.name === "greet") {
const name = params.arguments?.name ?? "World";
return { content: [{ type: "text", text: `Hello, ${name}!` }] };
}
throw new Error(`Unknown tool: ${params.name}`);
};
setRequestHandler for the underlying method
Convenience handler for host context changes (theme, viewport, locale, etc.).
Set this property to register a handler that will be called when the host's context changes, such as theme switching (light/dark), viewport size changes, locale changes, or other environmental updates. Apps should respond by updating their UI accordingly.
This setter is a convenience wrapper around setNotificationHandler() that
automatically handles the notification schema and extracts the params for you.
Register handlers before calling connect to avoid missing notifications.
Function called with the updated host context
app.onhostcontextchanged = (params) => {
if (params.theme === "dark") {
document.body.classList.add("dark-theme");
} else {
document.body.classList.remove("dark-theme");
}
};
Convenience handler for listing available tools.
Set this property to register a handler that will be called when the host requests a list of tools this app provides. This enables dynamic tool discovery by the host or LLM.
The app must declare tool capabilities in the constructor to use this handler.
This setter is a convenience wrapper around setRequestHandler() that
automatically handles the request schema and extracts the params for you.
Register handlers before calling connect to avoid missing requests.
Async function that returns the list of available tools. The callback will only be invoked if the app declared tool capabilities in the constructor.
app.onlisttools = async (params, extra) => {
return {
tools: ["calculate", "convert", "format"]
};
};
Convenience handler for graceful shutdown requests from the host.
Set this property to register a handler that will be called when the host requests the app to prepare for teardown. This allows the app to perform cleanup operations (save state, close connections, etc.) before being unmounted.
The handler can be sync or async. The host will wait for the returned promise to resolve before proceeding with teardown.
This setter is a convenience wrapper around setRequestHandler() that
automatically handles the request schema.
Register handlers before calling connect to avoid missing requests.
Function called when teardown is requested. Can return void or a Promise that resolves when cleanup is complete.
app.onteardown = async () => {
await saveState();
closeConnections();
console.log("App ready for teardown");
};
Convenience handler for receiving tool cancellation notifications from the host.
Set this property to register a handler that will be called when the host notifies that tool execution was cancelled. This can occur for various reasons including user action, sampling error, classifier intervention, or other interruptions. Apps should update their state and display appropriate feedback.
This setter is a convenience wrapper around setNotificationHandler() that
automatically handles the notification schema and extracts the params for you.
Register handlers before calling connect to avoid missing notifications.
Function called when tool execution is cancelled
app.ontoolcancelled = (params) => {
console.log("Tool cancelled:", params.reason);
showCancelledMessage(params.reason ?? "Operation was cancelled");
};
Convenience handler for receiving complete tool input from the host.
Set this property to register a handler that will be called when the host sends a tool's complete arguments. This is sent after a tool call begins and before the tool result is available.
This setter is a convenience wrapper around setNotificationHandler() that
automatically handles the notification schema and extracts the params for you.
Register handlers before calling connect to avoid missing notifications.
Function called with the tool input params
// Register before connecting to ensure no notifications are missed
app.ontoolinput = (params) => {
console.log("Tool:", params.arguments);
// Update your UI with the tool arguments
};
await app.connect(transport);
app.setNotificationHandler(
McpUiToolInputNotificationSchema,
(notification) => {
console.log("Tool:", notification.params.arguments);
}
);
Convenience handler for receiving streaming partial tool input from the host.
Set this property to register a handler that will be called as the host streams partial tool arguments during tool call initialization. This enables progressive rendering of tool arguments before they're complete.
This setter is a convenience wrapper around setNotificationHandler() that
automatically handles the notification schema and extracts the params for you.
Register handlers before calling connect to avoid missing notifications.
Function called with each partial tool input update
app.ontoolinputpartial = (params) => {
console.log("Partial args:", params.arguments);
// Update your UI progressively as arguments stream in
};
Convenience handler for receiving tool execution results from the host.
Set this property to register a handler that will be called when the host sends the result of a tool execution. This is sent after the tool completes on the MCP server, allowing your app to display the results or update its state.
This setter is a convenience wrapper around setNotificationHandler() that
automatically handles the notification schema and extracts the params for you.
Register handlers before calling connect to avoid missing notifications.
Function called with the tool result
app.ontoolresult = (params) => {
if (params.content) {
console.log("Tool output:", params.content);
}
if (params.isError) {
console.error("Tool execution failed");
}
};
Asserts that a request handler has not already been set for the given method, in preparation for a new one being automatically installed.
InternalVerify that the host supports the capability required for the given request method.
InternalVerify that the app supports the capability required for the given notification method.
InternalVerify that the app declared the capability required for the given request method.
ProtectedassertInternalVerify that task creation is supported for the given request method.
ProtectedassertInternalVerify that task handler is supported for the given method.
Call a tool on the originating MCP server (proxied through the host).
Apps can call tools to fetch fresh data or trigger server-side actions. The host proxies the request to the actual MCP server and returns the result.
Tool name and arguments
Optionaloptions: RequestOptionsRequest options (timeout, etc.)
Tool execution result
If the host rejects the request
Note: Tool-level execution errors are returned in the result with isError: true
rather than throwing exceptions. Always check result.isError to distinguish
between transport failures (thrown) and tool execution failures (returned).
try {
const result = await app.callServerTool({
name: "get_weather",
arguments: { location: "Tokyo" }
});
if (result.isError) {
console.error("Tool returned error:", result.content);
} else {
console.log(result.content);
}
} catch (error) {
console.error("Tool call failed:", error);
}
ProtectedcancelExperimentalCancels a specific task.
Use client.experimental.tasks.cancelTask() to access this method.
Optionaloptions: RequestOptionsCloses the connection.
Establish connection with the host and perform initialization handshake.
This method performs the following steps:
ui/initialize request with app info and capabilitiesui/notifications/initialized notificationIf initialization fails, the connection is automatically closed and an error is thrown.
Transport layer (typically PostMessageTransport)
Optionaloptions: RequestOptionsRequest options for the initialize request
const app = new App(
{ name: "MyApp", version: "1.0.0" },
{}
);
try {
await app.connect(new PostMessageTransport(window.parent));
console.log("Connected successfully!");
} catch (error) {
console.error("Failed to connect:", error);
}
Get the host's capabilities discovered during initialization.
Returns the capabilities that the host advertised during the
connect handshake. Returns undefined if called before
connection is established.
Host capabilities, or undefined if not yet connected
await app.connect(transport);
const caps = app.getHostCapabilities();
if (caps === undefined) {
console.error("Not connected");
return;
}
if (caps.serverTools) {
console.log("Host supports server tool calls");
}
Get the host context discovered during initialization.
Returns the host context that was provided in the initialization response,
including tool info, theme, viewport, locale, and other environment details.
This context is automatically updated when the host sends
ui/notifications/host-context-changed notifications.
Returns undefined if called before connection is established.
Host context, or undefined if not yet connected
await app.connect(transport);
const context = app.getHostContext();
if (context === undefined) {
console.error("Not connected");
return;
}
if (context.theme === "dark") {
document.body.classList.add("dark-theme");
}
if (context.toolInfo) {
console.log("Tool:", context.toolInfo.tool.name);
}
Get the host's implementation info discovered during initialization.
Returns the host's name and version as advertised during the
connect handshake. Returns undefined if called before
connection is established.
Host implementation info, or undefined if not yet connected
await app.connect(transport);
const host = app.getHostVersion();
if (host === undefined) {
console.error("Not connected");
return;
}
console.log(`Connected to ${host.name} v${host.version}`);
connect for the initialization handshake
ProtectedgetExperimentalGets the current status of a task.
Use client.experimental.tasks.getTask() to access this method.
Optionaloptions: RequestOptionsProtectedgetExperimentalRetrieves the result of a completed task.
Use client.experimental.tasks.getTaskResult() to access this method.
Optionaloptions: RequestOptionsProtectedlistExperimentalLists tasks, optionally starting from a pagination cursor.
Use client.experimental.tasks.listTasks() to access this method.
Optionalparams: { cursor?: string }Optionaloptions: RequestOptionsEmits a notification, which is a one-way message that does not expect a response.
Optionaloptions: NotificationOptionsRequest the host to open an external URL in the default browser.
The host may deny this request based on user preferences or security policy. Apps should handle rejection gracefully.
URL to open
Optionaloptions: RequestOptionsRequest options (timeout, etc.)
Result indicating success or error
try {
await app.openLink({ url: "https://docs.example.com" });
} catch (error) {
console.error("Failed to open link:", error);
// Optionally show fallback: display URL for manual copy
}
McpUiOpenLinkRequest for request structure
Removes the notification handler for the given method.
Removes the request handler for the given method.
Sends a request and waits for a response.
Do not use this method to emit notifications! Use notification() instead.
Optionaloptions: RequestOptionsRequest a change to the display mode.
Requests the host to change the UI container to the specified display mode
(e.g., "inline", "fullscreen", "pip"). The host will respond with the actual
display mode that was set, which may differ from the requested mode if
the requested mode is not available (check availableDisplayModes in host context).
The display mode being requested
Optionaloptions: RequestOptionsRequest options (timeout, etc.)
Result containing the actual display mode that was set
const context = app.getHostContext();
if (context?.availableDisplayModes?.includes("fullscreen")) {
const result = await app.requestDisplayMode({ mode: "fullscreen" });
console.log("Display mode set to:", result.mode);
}
ProtectedrequestExperimentalSends a request and returns an AsyncGenerator that yields response messages. The generator is guaranteed to end with either a 'result' or 'error' message.
Optionaloptions: RequestOptionsconst stream = protocol.requestStream(request, resultSchema, options);
for await (const message of stream) {
switch (message.type) {
case 'taskCreated':
console.log('Task created:', message.task.taskId);
break;
case 'taskStatus':
console.log('Task status:', message.task.status);
break;
case 'result':
console.log('Final result:', message.result);
break;
case 'error':
console.error('Error:', message.error);
break;
}
}
Use client.experimental.tasks.requestStream() to access this method.
Send log messages to the host for debugging and telemetry.
Logs are not added to the conversation but may be recorded by the host for debugging purposes.
Log level and message
Promise that resolves when the log notification is sent
Send a message to the host's chat interface.
Enables the app to add messages to the conversation thread. Useful for user-initiated messages or app-to-conversation communication.
Message role and content
Optionaloptions: RequestOptionsRequest options (timeout, etc.)
Result indicating success or error (no message content returned)
try {
await app.sendMessage({
role: "user",
content: [{ type: "text", text: "Show me details for item #42" }]
});
} catch (error) {
console.error("Failed to send message:", error);
// Handle error appropriately for your app
}
McpUiMessageRequest for request structure
Notify the host of UI size changes.
Apps can manually report size changes to help the host adjust the container.
If autoResize is enabled (default), this is called automatically.
New width and height in pixels
Optionalheight?: numberOptionalwidth?: numberPromise that resolves when the notification is sent
McpUiSizeChangedNotification for notification structure
Registers a handler to invoke when this protocol object receives a notification with the given method.
Note that this will replace any previous notification handler for the same method.
Registers a handler to invoke when this protocol object receives a request with the given method.
Note that this will replace any previous request handler for the same method.
Set up automatic size change notifications using ResizeObserver.
Observes both document.documentElement and document.body for size changes
and automatically sends ui/notifications/size-changed notifications to the host.
The notifications are debounced using requestAnimationFrame to avoid duplicates.
Note: This method is automatically called by connect() if the autoResize
option is true (default). You typically don't need to call this manually unless
you disabled autoResize and want to enable it later.
Cleanup function to disconnect the observer
Main class for MCP Apps to communicate with their host.
The App class provides a framework-agnostic way to build interactive MCP Apps that run inside host applications. It extends the MCP SDK's Protocol class and handles the connection lifecycle, initialization handshake, and bidirectional communication with the host.
Architecture
Guest UIs (Apps) act as MCP clients connecting to the host via PostMessageTransport. The host proxies requests to the actual MCP server and forwards responses back to the App.
Lifecycle
connect()to establish transport and perform handshakeInherited Methods
As a subclass of Protocol, App inherits key methods for handling communication:
setRequestHandler()- Register handlers for requests from hostsetNotificationHandler()- Register handlers for notifications from hostSee
Protocol from @modelcontextprotocol/sdk for all inherited methods
Notification Setters
For common notifications, the App class provides convenient setter properties that simplify handler registration:
ontoolinput- Complete tool arguments from hostontoolinputpartial- Streaming partial tool argumentsontoolresult- Tool execution resultsonhostcontextchanged- Host context changes (theme, viewport, etc.)These setters are convenience wrappers around
setNotificationHandler(). Both patterns work; use whichever fits your coding style better.Example: Basic usage with PostMessageTransport
Example: Sending a message to the host's chat