Refine JSDoc examples for consistency and type safety
Follow-up to the examples extraction: pass dependencies as function parameters instead of using global stub declarations, rename region names to match method names (e.g., `AppBridge_guestCapabilities_*` → `AppBridge_getAppCapabilities_*`), and simplify examples using optional chaining. Also clarifies `RESOURCE_URI_META_KEY` documentation to indicate it's for tool definition metadata (from `tools/list`), not tool result metadata, and removes the redundant `setNotificationHandler` example in favor of the simpler event setter approach. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ad04e89745
commit
f0c7cc1b14
11 changed files with 197 additions and 250 deletions
|
|
@ -17,6 +17,7 @@ import {
|
||||||
ListPromptsResultSchema,
|
ListPromptsResultSchema,
|
||||||
} from "@modelcontextprotocol/sdk/types.js";
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { AppBridge, PostMessageTransport } from "./app-bridge.js";
|
import { AppBridge, PostMessageTransport } from "./app-bridge.js";
|
||||||
|
import type { McpUiDisplayMode } from "./types.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Basic usage of the AppBridge class with PostMessageTransport.
|
* Example: Basic usage of the AppBridge class with PostMessageTransport.
|
||||||
|
|
@ -65,7 +66,6 @@ function AppBridge_constructor_withMcpClient(mcpClient: Client) {
|
||||||
{ openLinks: {}, serverTools: {}, logging: {} },
|
{ openLinks: {}, serverTools: {}, logging: {} },
|
||||||
);
|
);
|
||||||
//#endregion AppBridge_constructor_withMcpClient
|
//#endregion AppBridge_constructor_withMcpClient
|
||||||
return bridge;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -83,42 +83,43 @@ function AppBridge_constructor_withoutMcpClient() {
|
||||||
return { content: [] };
|
return { content: [] };
|
||||||
};
|
};
|
||||||
//#endregion AppBridge_constructor_withoutMcpClient
|
//#endregion AppBridge_constructor_withoutMcpClient
|
||||||
return bridge;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Check Guest UI capabilities after initialization.
|
* Example: Check Guest UI capabilities after initialization.
|
||||||
*/
|
*/
|
||||||
function AppBridge_guestCapabilities_checkAfterInit(bridge: AppBridge) {
|
function AppBridge_getAppCapabilities_checkAfterInit(bridge: AppBridge) {
|
||||||
//#region AppBridge_guestCapabilities_checkAfterInit
|
//#region AppBridge_getAppCapabilities_checkAfterInit
|
||||||
bridge.oninitialized = () => {
|
bridge.oninitialized = () => {
|
||||||
const caps = bridge.getAppCapabilities();
|
const caps = bridge.getAppCapabilities();
|
||||||
if (caps?.tools) {
|
if (caps?.tools) {
|
||||||
console.log("Guest UI provides tools");
|
console.log("Guest UI provides tools");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//#endregion AppBridge_guestCapabilities_checkAfterInit
|
//#endregion AppBridge_getAppCapabilities_checkAfterInit
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Log Guest UI information after initialization.
|
* Example: Log Guest UI information after initialization.
|
||||||
*/
|
*/
|
||||||
function AppBridge_guestInfo_logAfterInit(bridge: AppBridge) {
|
function AppBridge_getAppVersion_logAfterInit(bridge: AppBridge) {
|
||||||
//#region AppBridge_guestInfo_logAfterInit
|
//#region AppBridge_getAppVersion_logAfterInit
|
||||||
bridge.oninitialized = () => {
|
bridge.oninitialized = () => {
|
||||||
const appInfo = bridge.getAppVersion();
|
const appInfo = bridge.getAppVersion();
|
||||||
if (appInfo) {
|
if (appInfo) {
|
||||||
console.log(`Guest UI: ${appInfo.name} v${appInfo.version}`);
|
console.log(`Guest UI: ${appInfo.name} v${appInfo.version}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//#endregion AppBridge_guestInfo_logAfterInit
|
//#endregion AppBridge_getAppVersion_logAfterInit
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Handle Guest UI initialization and send tool input.
|
* Example: Handle Guest UI initialization and send tool input.
|
||||||
*/
|
*/
|
||||||
function AppBridge_oninitialized_sendToolInput(bridge: AppBridge) {
|
function AppBridge_oninitialized_sendToolInput(
|
||||||
const toolArgs = { location: "NYC" };
|
bridge: AppBridge,
|
||||||
|
toolArgs: Record<string, unknown>,
|
||||||
|
) {
|
||||||
//#region AppBridge_oninitialized_sendToolInput
|
//#region AppBridge_oninitialized_sendToolInput
|
||||||
bridge.oninitialized = () => {
|
bridge.oninitialized = () => {
|
||||||
console.log("Guest UI ready");
|
console.log("Guest UI ready");
|
||||||
|
|
@ -162,18 +163,11 @@ declare function showDialog(options: {
|
||||||
buttons: string[];
|
buttons: string[];
|
||||||
}): Promise<boolean>;
|
}): Promise<boolean>;
|
||||||
|
|
||||||
// Stub for example code - represents a hypothetical model context storage
|
// Stub for example code - represents a hypothetical model context manager
|
||||||
declare let modelContext: {
|
declare const modelContextManager: {
|
||||||
type: string;
|
update(context: { content?: unknown; structuredContent?: unknown }): void;
|
||||||
content: unknown;
|
|
||||||
structuredContent: unknown;
|
|
||||||
timestamp: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Stub for example code - represents a hypothetical MCP client that can be passed to AppBridge
|
|
||||||
// Using Client type directly since AppBridge expects Client | null
|
|
||||||
declare const mcpClient: Client;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Handle external link requests from the Guest UI.
|
* Example: Handle external link requests from the Guest UI.
|
||||||
*/
|
*/
|
||||||
|
|
@ -209,13 +203,8 @@ function AppBridge_onupdatemodelcontext_storeContext(bridge: AppBridge) {
|
||||||
{ content, structuredContent },
|
{ content, structuredContent },
|
||||||
extra,
|
extra,
|
||||||
) => {
|
) => {
|
||||||
// Update the model context with the new snapshot
|
// Store the context snapshot for inclusion in the next model request
|
||||||
modelContext = {
|
modelContextManager.update({ content, structuredContent });
|
||||||
type: "app_context",
|
|
||||||
content,
|
|
||||||
structuredContent,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
};
|
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
//#endregion AppBridge_onupdatemodelcontext_storeContext
|
//#endregion AppBridge_onupdatemodelcontext_storeContext
|
||||||
|
|
@ -224,11 +213,14 @@ function AppBridge_onupdatemodelcontext_storeContext(bridge: AppBridge) {
|
||||||
/**
|
/**
|
||||||
* Example: Forward tool calls to the MCP server.
|
* Example: Forward tool calls to the MCP server.
|
||||||
*/
|
*/
|
||||||
function AppBridge_oncalltool_forwardToServer(bridge: AppBridge) {
|
function AppBridge_oncalltool_forwardToServer(
|
||||||
|
bridge: AppBridge,
|
||||||
|
mcpClient: Client,
|
||||||
|
) {
|
||||||
//#region AppBridge_oncalltool_forwardToServer
|
//#region AppBridge_oncalltool_forwardToServer
|
||||||
bridge.oncalltool = async ({ name, arguments: args }, extra) => {
|
bridge.oncalltool = async (params, extra) => {
|
||||||
return mcpClient.request(
|
return mcpClient.request(
|
||||||
{ method: "tools/call", params: { name, arguments: args } },
|
{ method: "tools/call", params },
|
||||||
CallToolResultSchema,
|
CallToolResultSchema,
|
||||||
{ signal: extra.signal },
|
{ signal: extra.signal },
|
||||||
);
|
);
|
||||||
|
|
@ -239,7 +231,10 @@ function AppBridge_oncalltool_forwardToServer(bridge: AppBridge) {
|
||||||
/**
|
/**
|
||||||
* Example: Forward list resources requests to the MCP server.
|
* Example: Forward list resources requests to the MCP server.
|
||||||
*/
|
*/
|
||||||
function AppBridge_onlistresources_returnResources(bridge: AppBridge) {
|
function AppBridge_onlistresources_returnResources(
|
||||||
|
bridge: AppBridge,
|
||||||
|
mcpClient: Client,
|
||||||
|
) {
|
||||||
//#region AppBridge_onlistresources_returnResources
|
//#region AppBridge_onlistresources_returnResources
|
||||||
bridge.onlistresources = async (params, extra) => {
|
bridge.onlistresources = async (params, extra) => {
|
||||||
return mcpClient.request(
|
return mcpClient.request(
|
||||||
|
|
@ -254,11 +249,14 @@ function AppBridge_onlistresources_returnResources(bridge: AppBridge) {
|
||||||
/**
|
/**
|
||||||
* Example: Forward read resource requests to the MCP server.
|
* Example: Forward read resource requests to the MCP server.
|
||||||
*/
|
*/
|
||||||
function AppBridge_onreadresource_returnResource(bridge: AppBridge) {
|
function AppBridge_onreadresource_returnResource(
|
||||||
|
bridge: AppBridge,
|
||||||
|
mcpClient: Client,
|
||||||
|
) {
|
||||||
//#region AppBridge_onreadresource_returnResource
|
//#region AppBridge_onreadresource_returnResource
|
||||||
bridge.onreadresource = async ({ uri }, extra) => {
|
bridge.onreadresource = async (params, extra) => {
|
||||||
return mcpClient.request(
|
return mcpClient.request(
|
||||||
{ method: "resources/read", params: { uri } },
|
{ method: "resources/read", params },
|
||||||
ReadResourceResultSchema,
|
ReadResourceResultSchema,
|
||||||
{ signal: extra.signal },
|
{ signal: extra.signal },
|
||||||
);
|
);
|
||||||
|
|
@ -269,7 +267,10 @@ function AppBridge_onreadresource_returnResource(bridge: AppBridge) {
|
||||||
/**
|
/**
|
||||||
* Example: Forward list prompts requests to the MCP server.
|
* Example: Forward list prompts requests to the MCP server.
|
||||||
*/
|
*/
|
||||||
function AppBridge_onlistprompts_returnPrompts(bridge: AppBridge) {
|
function AppBridge_onlistprompts_returnPrompts(
|
||||||
|
bridge: AppBridge,
|
||||||
|
mcpClient: Client,
|
||||||
|
) {
|
||||||
//#region AppBridge_onlistprompts_returnPrompts
|
//#region AppBridge_onlistprompts_returnPrompts
|
||||||
bridge.onlistprompts = async (params, extra) => {
|
bridge.onlistprompts = async (params, extra) => {
|
||||||
return mcpClient.request(
|
return mcpClient.request(
|
||||||
|
|
@ -281,14 +282,6 @@ function AppBridge_onlistprompts_returnPrompts(bridge: AppBridge) {
|
||||||
//#endregion AppBridge_onlistprompts_returnPrompts
|
//#endregion AppBridge_onlistprompts_returnPrompts
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stub for example code - represents a hypothetical iframe element
|
|
||||||
declare const iframe: HTMLIFrameElement;
|
|
||||||
|
|
||||||
// Stub for example code - represents a hypothetical host context
|
|
||||||
declare const hostContext: {
|
|
||||||
availableDisplayModes?: Array<"inline" | "fullscreen" | "pip">;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Handle ping requests from the Guest UI.
|
* Example: Handle ping requests from the Guest UI.
|
||||||
*/
|
*/
|
||||||
|
|
@ -303,7 +296,10 @@ function AppBridge_onping_handleRequest(bridge: AppBridge) {
|
||||||
/**
|
/**
|
||||||
* Example: Handle size change notifications from the Guest UI.
|
* Example: Handle size change notifications from the Guest UI.
|
||||||
*/
|
*/
|
||||||
function AppBridge_onsizechange_handleResize(bridge: AppBridge) {
|
function AppBridge_onsizechange_handleResize(
|
||||||
|
bridge: AppBridge,
|
||||||
|
iframe: HTMLIFrameElement,
|
||||||
|
) {
|
||||||
//#region AppBridge_onsizechange_handleResize
|
//#region AppBridge_onsizechange_handleResize
|
||||||
bridge.onsizechange = ({ width, height }) => {
|
bridge.onsizechange = ({ width, height }) => {
|
||||||
if (width != null) {
|
if (width != null) {
|
||||||
|
|
@ -319,18 +315,16 @@ function AppBridge_onsizechange_handleResize(bridge: AppBridge) {
|
||||||
/**
|
/**
|
||||||
* Example: Handle display mode requests from the Guest UI.
|
* Example: Handle display mode requests from the Guest UI.
|
||||||
*/
|
*/
|
||||||
function AppBridge_onrequestdisplaymode_handleRequest(bridge: AppBridge) {
|
function AppBridge_onrequestdisplaymode_handleRequest(
|
||||||
|
bridge: AppBridge,
|
||||||
|
currentDisplayMode: McpUiDisplayMode,
|
||||||
|
availableDisplayModes: McpUiDisplayMode[],
|
||||||
|
) {
|
||||||
//#region AppBridge_onrequestdisplaymode_handleRequest
|
//#region AppBridge_onrequestdisplaymode_handleRequest
|
||||||
type McpUiDisplayMode = "inline" | "fullscreen" | "pip";
|
|
||||||
let currentDisplayMode: McpUiDisplayMode = "inline";
|
|
||||||
|
|
||||||
bridge.onrequestdisplaymode = async ({ mode }, extra) => {
|
bridge.onrequestdisplaymode = async ({ mode }, extra) => {
|
||||||
const availableModes = hostContext.availableDisplayModes ?? ["inline"];
|
if (availableDisplayModes.includes(mode)) {
|
||||||
if (availableModes.includes(mode)) {
|
|
||||||
currentDisplayMode = mode;
|
currentDisplayMode = mode;
|
||||||
return { mode };
|
|
||||||
}
|
}
|
||||||
// Return current mode if requested mode not available
|
|
||||||
return { mode: currentDisplayMode };
|
return { mode: currentDisplayMode };
|
||||||
};
|
};
|
||||||
//#endregion AppBridge_onrequestdisplaymode_handleRequest
|
//#endregion AppBridge_onrequestdisplaymode_handleRequest
|
||||||
|
|
@ -342,9 +336,8 @@ function AppBridge_onrequestdisplaymode_handleRequest(bridge: AppBridge) {
|
||||||
function AppBridge_onloggingmessage_handleLog(bridge: AppBridge) {
|
function AppBridge_onloggingmessage_handleLog(bridge: AppBridge) {
|
||||||
//#region AppBridge_onloggingmessage_handleLog
|
//#region AppBridge_onloggingmessage_handleLog
|
||||||
bridge.onloggingmessage = ({ level, logger, data }) => {
|
bridge.onloggingmessage = ({ level, logger, data }) => {
|
||||||
const prefix = logger ? `[${logger}]` : "[Guest UI]";
|
|
||||||
console[level === "error" ? "error" : "log"](
|
console[level === "error" ? "error" : "log"](
|
||||||
`${prefix} ${level.toUpperCase()}:`,
|
`[${logger ?? "Guest UI"}] ${level.toUpperCase()}:`,
|
||||||
data,
|
data,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -354,7 +347,10 @@ function AppBridge_onloggingmessage_handleLog(bridge: AppBridge) {
|
||||||
/**
|
/**
|
||||||
* Example: Gracefully tear down the Guest UI before unmounting.
|
* Example: Gracefully tear down the Guest UI before unmounting.
|
||||||
*/
|
*/
|
||||||
async function AppBridge_teardownResource_gracefulShutdown(bridge: AppBridge) {
|
async function AppBridge_teardownResource_gracefulShutdown(
|
||||||
|
bridge: AppBridge,
|
||||||
|
iframe: HTMLIFrameElement,
|
||||||
|
) {
|
||||||
//#region AppBridge_teardownResource_gracefulShutdown
|
//#region AppBridge_teardownResource_gracefulShutdown
|
||||||
try {
|
try {
|
||||||
await bridge.teardownResource({});
|
await bridge.teardownResource({});
|
||||||
|
|
@ -422,21 +418,16 @@ function AppBridge_sendToolInputPartial_streaming(bridge: AppBridge) {
|
||||||
*/
|
*/
|
||||||
async function AppBridge_sendToolResult_afterExecution(
|
async function AppBridge_sendToolResult_afterExecution(
|
||||||
bridge: AppBridge,
|
bridge: AppBridge,
|
||||||
mcpClientParam: Client,
|
mcpClient: Client,
|
||||||
args: Record<string, unknown>,
|
args: Record<string, unknown>,
|
||||||
) {
|
) {
|
||||||
//#region AppBridge_sendToolResult_afterExecution
|
//#region AppBridge_sendToolResult_afterExecution
|
||||||
// import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
||||||
|
|
||||||
const result = await mcpClient.request(
|
const result = await mcpClient.request(
|
||||||
{ method: "tools/call", params: { name: "get_weather", arguments: args } },
|
{ method: "tools/call", params: { name: "get_weather", arguments: args } },
|
||||||
CallToolResultSchema,
|
CallToolResultSchema,
|
||||||
);
|
);
|
||||||
bridge.sendToolResult(result);
|
bridge.sendToolResult(result);
|
||||||
//#endregion AppBridge_sendToolResult_afterExecution
|
//#endregion AppBridge_sendToolResult_afterExecution
|
||||||
|
|
||||||
// Make the parameter used to satisfy linting
|
|
||||||
void mcpClientParam;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -466,7 +457,8 @@ function AppBridge_sendToolCancelled_systemLevel(bridge: AppBridge) {
|
||||||
* Example: Connect with MCP client for automatic forwarding.
|
* Example: Connect with MCP client for automatic forwarding.
|
||||||
*/
|
*/
|
||||||
async function AppBridge_connect_withMcpClient(
|
async function AppBridge_connect_withMcpClient(
|
||||||
mcpClientParam: Client,
|
iframe: HTMLIFrameElement,
|
||||||
|
mcpClient: Client,
|
||||||
hostInfo: { name: string; version: string },
|
hostInfo: { name: string; version: string },
|
||||||
capabilities: { openLinks: {}; serverTools: {}; logging: {} },
|
capabilities: { openLinks: {}; serverTools: {}; logging: {} },
|
||||||
toolArgs: Record<string, unknown>,
|
toolArgs: Record<string, unknown>,
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,7 @@ export class AppBridge extends Protocol<
|
||||||
* @returns Guest UI capabilities, or `undefined` if not yet initialized
|
* @returns Guest UI capabilities, or `undefined` if not yet initialized
|
||||||
*
|
*
|
||||||
* @example Check Guest UI capabilities after initialization
|
* @example Check Guest UI capabilities after initialization
|
||||||
* {@includeCode ./app-bridge.examples.ts#AppBridge_guestCapabilities_checkAfterInit}
|
* {@includeCode ./app-bridge.examples.ts#AppBridge_getAppCapabilities_checkAfterInit}
|
||||||
*
|
*
|
||||||
* @see {@link McpUiAppCapabilities} for the capabilities structure
|
* @see {@link McpUiAppCapabilities} for the capabilities structure
|
||||||
*/
|
*/
|
||||||
|
|
@ -297,7 +297,7 @@ export class AppBridge extends Protocol<
|
||||||
* @returns Guest UI implementation info, or `undefined` if not yet initialized
|
* @returns Guest UI implementation info, or `undefined` if not yet initialized
|
||||||
*
|
*
|
||||||
* @example Log Guest UI information after initialization
|
* @example Log Guest UI information after initialization
|
||||||
* {@includeCode ./app-bridge.examples.ts#AppBridge_guestInfo_logAfterInit}
|
* {@includeCode ./app-bridge.examples.ts#AppBridge_getAppVersion_logAfterInit}
|
||||||
*/
|
*/
|
||||||
getAppVersion(): Implementation | undefined {
|
getAppVersion(): Implementation | undefined {
|
||||||
return this._appInfo;
|
return this._appInfo;
|
||||||
|
|
|
||||||
|
|
@ -7,43 +7,43 @@
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
||||||
import {
|
import type {
|
||||||
App,
|
McpServer,
|
||||||
PostMessageTransport,
|
ToolCallback,
|
||||||
McpUiToolInputNotificationSchema,
|
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
RESOURCE_URI_META_KEY,
|
import { App, PostMessageTransport, RESOURCE_URI_META_KEY } from "./app.js";
|
||||||
} from "./app.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: How MCP servers use RESOURCE_URI_META_KEY (server-side, not in Apps).
|
* Example: How MCP servers use RESOURCE_URI_META_KEY (server-side, not in Apps).
|
||||||
*/
|
*/
|
||||||
function RESOURCE_URI_META_KEY_serverSide(): CallToolResult {
|
function RESOURCE_URI_META_KEY_serverSide(
|
||||||
|
server: McpServer,
|
||||||
|
handler: ToolCallback,
|
||||||
|
) {
|
||||||
//#region RESOURCE_URI_META_KEY_serverSide
|
//#region RESOURCE_URI_META_KEY_serverSide
|
||||||
// In an MCP server's tool handler:
|
server.registerTool(
|
||||||
return {
|
"weather",
|
||||||
content: [{ type: "text", text: "Result" }],
|
{
|
||||||
_meta: {
|
description: "Get weather forecast",
|
||||||
[RESOURCE_URI_META_KEY]: "ui://weather/forecast",
|
_meta: {
|
||||||
|
[RESOURCE_URI_META_KEY]: "ui://weather/forecast",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
handler,
|
||||||
|
);
|
||||||
//#endregion RESOURCE_URI_META_KEY_serverSide
|
//#endregion RESOURCE_URI_META_KEY_serverSide
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: How hosts check for RESOURCE_URI_META_KEY metadata (host-side).
|
* Example: How hosts check for RESOURCE_URI_META_KEY metadata (host-side).
|
||||||
*/
|
*/
|
||||||
async function RESOURCE_URI_META_KEY_hostSide(mcpClient: {
|
function RESOURCE_URI_META_KEY_hostSide(tool: Tool) {
|
||||||
callTool: (args: {
|
|
||||||
name: string;
|
|
||||||
arguments: object;
|
|
||||||
}) => Promise<CallToolResult>;
|
|
||||||
}) {
|
|
||||||
//#region RESOURCE_URI_META_KEY_hostSide
|
//#region RESOURCE_URI_META_KEY_hostSide
|
||||||
const result = await mcpClient.callTool({ name: "weather", arguments: {} });
|
// Check tool definition metadata (from tools/list response):
|
||||||
const uiUri = result._meta?.[RESOURCE_URI_META_KEY];
|
const uiUri = tool._meta?.[RESOURCE_URI_META_KEY];
|
||||||
if (uiUri) {
|
if (typeof uiUri === "string" && uiUri.startsWith("ui://")) {
|
||||||
// Load and display the UI resource
|
// Fetch the resource and display the UI
|
||||||
}
|
}
|
||||||
//#endregion RESOURCE_URI_META_KEY_hostSide
|
//#endregion RESOURCE_URI_META_KEY_hostSide
|
||||||
}
|
}
|
||||||
|
|
@ -72,20 +72,12 @@ async function App_basicUsage() {
|
||||||
{}, // capabilities
|
{}, // capabilities
|
||||||
);
|
);
|
||||||
|
|
||||||
// Register notification handler using setter (simpler)
|
// Register handlers before connecting to ensure no notifications are missed
|
||||||
app.ontoolinput = (params) => {
|
app.ontoolinput = (params) => {
|
||||||
console.log("Tool arguments:", params.arguments);
|
console.log("Tool arguments:", params.arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
// OR using inherited setNotificationHandler (more explicit)
|
await app.connect();
|
||||||
app.setNotificationHandler(
|
|
||||||
McpUiToolInputNotificationSchema,
|
|
||||||
(notification) => {
|
|
||||||
console.log("Tool arguments:", notification.params.arguments);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await app.connect(new PostMessageTransport(window.parent, window.parent));
|
|
||||||
//#endregion App_basicUsage
|
//#endregion App_basicUsage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,95 +96,62 @@ async function App_sendMessage(app: App) {
|
||||||
/**
|
/**
|
||||||
* Example: Check host capabilities after connection.
|
* Example: Check host capabilities after connection.
|
||||||
*/
|
*/
|
||||||
async function App_hostCapabilities_checkAfterConnection(
|
async function App_getHostCapabilities_checkAfterConnection(app: App) {
|
||||||
app: App,
|
//#region App_getHostCapabilities_checkAfterConnection
|
||||||
transport: PostMessageTransport,
|
await app.connect();
|
||||||
) {
|
if (app.getHostCapabilities()?.serverTools) {
|
||||||
//#region App_hostCapabilities_checkAfterConnection
|
|
||||||
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");
|
console.log("Host supports server tool calls");
|
||||||
}
|
}
|
||||||
//#endregion App_hostCapabilities_checkAfterConnection
|
//#endregion App_getHostCapabilities_checkAfterConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Log host information after connection.
|
* Example: Log host information after connection.
|
||||||
*/
|
*/
|
||||||
async function App_hostInfo_logAfterConnection(
|
async function App_getHostVersion_logAfterConnection(
|
||||||
app: App,
|
app: App,
|
||||||
transport: PostMessageTransport,
|
transport: PostMessageTransport,
|
||||||
) {
|
) {
|
||||||
//#region App_hostInfo_logAfterConnection
|
//#region App_getHostVersion_logAfterConnection
|
||||||
await app.connect(transport);
|
await app.connect(transport);
|
||||||
const host = app.getHostVersion();
|
const { name, version } = app.getHostVersion() ?? {};
|
||||||
if (host === undefined) {
|
console.log(`Connected to ${name} v${version}`);
|
||||||
console.error("Not connected");
|
//#endregion App_getHostVersion_logAfterConnection
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(`Connected to ${host.name} v${host.version}`);
|
|
||||||
//#endregion App_hostInfo_logAfterConnection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Access host context after connection.
|
* Example: Access host context after connection.
|
||||||
*/
|
*/
|
||||||
async function App_hostContext_accessAfterConnection(
|
async function App_getHostContext_accessAfterConnection(
|
||||||
app: App,
|
app: App,
|
||||||
transport: PostMessageTransport,
|
transport: PostMessageTransport,
|
||||||
) {
|
) {
|
||||||
//#region App_hostContext_accessAfterConnection
|
//#region App_getHostContext_accessAfterConnection
|
||||||
await app.connect(transport);
|
await app.connect(transport);
|
||||||
const context = app.getHostContext();
|
const context = app.getHostContext();
|
||||||
if (context === undefined) {
|
if (context?.theme === "dark") {
|
||||||
console.error("Not connected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (context.theme === "dark") {
|
|
||||||
document.body.classList.add("dark-theme");
|
document.body.classList.add("dark-theme");
|
||||||
}
|
}
|
||||||
if (context.toolInfo) {
|
if (context?.toolInfo) {
|
||||||
console.log("Tool:", context.toolInfo.tool.name);
|
console.log("Tool:", context.toolInfo.tool.name);
|
||||||
}
|
}
|
||||||
//#endregion App_hostContext_accessAfterConnection
|
//#endregion App_getHostContext_accessAfterConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Using the ontoolinput setter (simpler approach).
|
* Example: Using the ontoolinput setter (simpler approach).
|
||||||
*/
|
*/
|
||||||
async function App_ontoolinput_setter(
|
async function App_ontoolinput_setter(app: App) {
|
||||||
app: App,
|
|
||||||
transport: PostMessageTransport,
|
|
||||||
) {
|
|
||||||
//#region App_ontoolinput_setter
|
//#region App_ontoolinput_setter
|
||||||
// Register before connecting to ensure no notifications are missed
|
// Register before connecting to ensure no notifications are missed
|
||||||
app.ontoolinput = (params) => {
|
app.ontoolinput = (params) => {
|
||||||
console.log("Tool:", params.arguments);
|
console.log("Tool:", params.arguments);
|
||||||
// Update your UI with the tool arguments
|
// Update your UI with the tool arguments
|
||||||
};
|
};
|
||||||
await app.connect(transport);
|
await app.connect();
|
||||||
//#endregion App_ontoolinput_setter
|
//#endregion App_ontoolinput_setter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Example: Using setNotificationHandler for tool input (more explicit approach).
|
|
||||||
*/
|
|
||||||
function App_ontoolinput_setNotificationHandler(app: App) {
|
|
||||||
//#region App_ontoolinput_setNotificationHandler
|
|
||||||
app.setNotificationHandler(
|
|
||||||
McpUiToolInputNotificationSchema,
|
|
||||||
(notification) => {
|
|
||||||
console.log("Tool:", notification.params.arguments);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
//#endregion App_ontoolinput_setNotificationHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Progressive rendering of tool arguments using ontoolinputpartial.
|
* Example: Progressive rendering of tool arguments using ontoolinputpartial.
|
||||||
*/
|
*/
|
||||||
|
|
@ -211,11 +170,10 @@ function App_ontoolinputpartial_progressiveRendering(app: App) {
|
||||||
function App_ontoolresult_displayResults(app: App) {
|
function App_ontoolresult_displayResults(app: App) {
|
||||||
//#region App_ontoolresult_displayResults
|
//#region App_ontoolresult_displayResults
|
||||||
app.ontoolresult = (params) => {
|
app.ontoolresult = (params) => {
|
||||||
if (params.content) {
|
|
||||||
console.log("Tool output:", params.content);
|
|
||||||
}
|
|
||||||
if (params.isError) {
|
if (params.isError) {
|
||||||
console.error("Tool execution failed");
|
console.error("Tool execution failed:", params.content);
|
||||||
|
} else if (params.content) {
|
||||||
|
console.log("Tool output:", params.content);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//#endregion App_ontoolresult_displayResults
|
//#endregion App_ontoolresult_displayResults
|
||||||
|
|
@ -228,14 +186,11 @@ function App_ontoolcancelled_handleCancellation(app: App) {
|
||||||
//#region App_ontoolcancelled_handleCancellation
|
//#region App_ontoolcancelled_handleCancellation
|
||||||
app.ontoolcancelled = (params) => {
|
app.ontoolcancelled = (params) => {
|
||||||
console.log("Tool cancelled:", params.reason);
|
console.log("Tool cancelled:", params.reason);
|
||||||
showCancelledMessage(params.reason ?? "Operation was cancelled");
|
// Update your UI to show cancellation state
|
||||||
};
|
};
|
||||||
//#endregion App_ontoolcancelled_handleCancellation
|
//#endregion App_ontoolcancelled_handleCancellation
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stub for example
|
|
||||||
declare function showCancelledMessage(message: string): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Respond to theme changes using onhostcontextchanged.
|
* Example: Respond to theme changes using onhostcontextchanged.
|
||||||
*/
|
*/
|
||||||
|
|
@ -291,7 +246,7 @@ function App_onlisttools_returnTools(app: App) {
|
||||||
//#region App_onlisttools_returnTools
|
//#region App_onlisttools_returnTools
|
||||||
app.onlisttools = async (params, extra) => {
|
app.onlisttools = async (params, extra) => {
|
||||||
return {
|
return {
|
||||||
tools: ["calculate", "convert", "format"],
|
tools: ["greet", "calculate", "format"],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
//#endregion App_onlisttools_returnTools
|
//#endregion App_onlisttools_returnTools
|
||||||
|
|
@ -324,13 +279,17 @@ async function App_callServerTool_fetchWeather(app: App) {
|
||||||
async function App_sendMessage_textFromInteraction(app: App) {
|
async function App_sendMessage_textFromInteraction(app: App) {
|
||||||
//#region App_sendMessage_textFromInteraction
|
//#region App_sendMessage_textFromInteraction
|
||||||
try {
|
try {
|
||||||
await app.sendMessage({
|
const result = await app.sendMessage({
|
||||||
role: "user",
|
role: "user",
|
||||||
content: [{ type: "text", text: "Show me details for item #42" }],
|
content: [{ type: "text", text: "Show me details for item #42" }],
|
||||||
});
|
});
|
||||||
|
if (result.isError) {
|
||||||
|
console.error("Host rejected the message");
|
||||||
|
// Handle rejection appropriately for your app
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to send message:", error);
|
console.error("Failed to send message:", error);
|
||||||
// Handle error appropriately for your app
|
// Handle transport/protocol error
|
||||||
}
|
}
|
||||||
//#endregion App_sendMessage_textFromInteraction
|
//#endregion App_sendMessage_textFromInteraction
|
||||||
}
|
}
|
||||||
|
|
@ -375,11 +334,11 @@ async function App_updateModelContext_structuredContent(app: App) {
|
||||||
*/
|
*/
|
||||||
async function App_openLink_documentation(app: App) {
|
async function App_openLink_documentation(app: App) {
|
||||||
//#region App_openLink_documentation
|
//#region App_openLink_documentation
|
||||||
try {
|
const { isError } = await app.openLink({ url: "https://docs.example.com" });
|
||||||
await app.openLink({ url: "https://docs.example.com" });
|
if (isError) {
|
||||||
} catch (error) {
|
// Host denied the request (e.g., blocked domain, user cancelled)
|
||||||
console.error("Failed to open link:", error);
|
|
||||||
// Optionally show fallback: display URL for manual copy
|
// Optionally show fallback: display URL for manual copy
|
||||||
|
console.warn("Link request denied");
|
||||||
}
|
}
|
||||||
//#endregion App_openLink_documentation
|
//#endregion App_openLink_documentation
|
||||||
}
|
}
|
||||||
|
|
|
||||||
36
src/app.ts
36
src/app.ts
|
|
@ -60,14 +60,16 @@ export {
|
||||||
} from "./styles";
|
} from "./styles";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata key for associating a resource URI with a tool call.
|
* Metadata key for associating a UI resource URI with a tool.
|
||||||
*
|
*
|
||||||
* MCP servers include this key in tool call result metadata to indicate which
|
* MCP servers include this key in tool definition metadata (via `tools/list`)
|
||||||
* UI resource should be displayed for the tool. When hosts receive a tool result
|
* to indicate which UI resource should be displayed when the tool is called.
|
||||||
* containing this metadata, they resolve and render the corresponding {@link App}.
|
* When hosts see a tool with this metadata, they fetch and render the
|
||||||
|
* corresponding {@link App}.
|
||||||
*
|
*
|
||||||
* **Note**: This constant is provided for reference. MCP servers set this metadata
|
* **Note**: This constant is provided for reference. App developers typically
|
||||||
* in their tool handlers; App developers typically don't need to use it directly.
|
* don't need to use it directly. Prefer using {@link server-helpers!registerAppTool}
|
||||||
|
* with the `_meta.ui.resourceUri` format instead.
|
||||||
*
|
*
|
||||||
* @example How MCP servers use this key (server-side, not in Apps)
|
* @example How MCP servers use this key (server-side, not in Apps)
|
||||||
* {@includeCode ./app.examples.ts#RESOURCE_URI_META_KEY_serverSide}
|
* {@includeCode ./app.examples.ts#RESOURCE_URI_META_KEY_serverSide}
|
||||||
|
|
@ -200,7 +202,7 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
||||||
* @returns Host capabilities, or `undefined` if not yet connected
|
* @returns Host capabilities, or `undefined` if not yet connected
|
||||||
*
|
*
|
||||||
* @example Check host capabilities after connection
|
* @example Check host capabilities after connection
|
||||||
* {@includeCode ./app.examples.ts#App_hostCapabilities_checkAfterConnection}
|
* {@includeCode ./app.examples.ts#App_getHostCapabilities_checkAfterConnection}
|
||||||
*
|
*
|
||||||
* @see {@link connect} for the initialization handshake
|
* @see {@link connect} for the initialization handshake
|
||||||
* @see {@link McpUiHostCapabilities} for the capabilities structure
|
* @see {@link McpUiHostCapabilities} for the capabilities structure
|
||||||
|
|
@ -219,7 +221,7 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
||||||
* @returns Host implementation info, or `undefined` if not yet connected
|
* @returns Host implementation info, or `undefined` if not yet connected
|
||||||
*
|
*
|
||||||
* @example Log host information after connection
|
* @example Log host information after connection
|
||||||
* {@includeCode ./app.examples.ts#App_hostInfo_logAfterConnection}
|
* {@includeCode ./app.examples.ts#App_getHostVersion_logAfterConnection}
|
||||||
*
|
*
|
||||||
* @see {@link connect} for the initialization handshake
|
* @see {@link connect} for the initialization handshake
|
||||||
*/
|
*/
|
||||||
|
|
@ -240,7 +242,7 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
||||||
* @returns Host context, or `undefined` if not yet connected
|
* @returns Host context, or `undefined` if not yet connected
|
||||||
*
|
*
|
||||||
* @example Access host context after connection
|
* @example Access host context after connection
|
||||||
* {@includeCode ./app.examples.ts#App_hostContext_accessAfterConnection}
|
* {@includeCode ./app.examples.ts#App_getHostContext_accessAfterConnection}
|
||||||
*
|
*
|
||||||
* @see {@link connect} for the initialization handshake
|
* @see {@link connect} for the initialization handshake
|
||||||
* @see {@link onhostcontextchanged} for context change notifications
|
* @see {@link onhostcontextchanged} for context change notifications
|
||||||
|
|
@ -264,12 +266,9 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
||||||
*
|
*
|
||||||
* @param callback - Function called with the tool input params ({@link McpUiToolInputNotification.params})
|
* @param callback - Function called with the tool input params ({@link McpUiToolInputNotification.params})
|
||||||
*
|
*
|
||||||
* @example Using the setter (simpler)
|
* @example
|
||||||
* {@includeCode ./app.examples.ts#App_ontoolinput_setter}
|
* {@includeCode ./app.examples.ts#App_ontoolinput_setter}
|
||||||
*
|
*
|
||||||
* @example Using setNotificationHandler (more explicit)
|
|
||||||
* {@includeCode ./app.examples.ts#App_ontoolinput_setNotificationHandler}
|
|
||||||
*
|
|
||||||
* @see {@link setNotificationHandler} for the underlying method
|
* @see {@link setNotificationHandler} for the underlying method
|
||||||
* @see {@link McpUiToolInputNotification} for the notification structure
|
* @see {@link McpUiToolInputNotification} for the notification structure
|
||||||
*/
|
*/
|
||||||
|
|
@ -607,9 +606,9 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
||||||
*
|
*
|
||||||
* @param params - Message role and content
|
* @param params - Message role and content
|
||||||
* @param options - Request options (timeout, etc.)
|
* @param options - Request options (timeout, etc.)
|
||||||
* @returns Result indicating success or error (no message content returned)
|
* @returns Result with optional `isError` flag indicating host rejection
|
||||||
*
|
*
|
||||||
* @throws {Error} If the host rejects the message
|
* @throws {Error} If the request times out or the connection is lost
|
||||||
*
|
*
|
||||||
* @example Send a text message from user interaction
|
* @example Send a text message from user interaction
|
||||||
* {@includeCode ./app.examples.ts#App_sendMessage_textFromInteraction}
|
* {@includeCode ./app.examples.ts#App_sendMessage_textFromInteraction}
|
||||||
|
|
@ -662,6 +661,7 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
||||||
* @param options - Request options (timeout, etc.)
|
* @param options - Request options (timeout, etc.)
|
||||||
*
|
*
|
||||||
* @throws {Error} If the host rejects the context update (e.g., unsupported content type)
|
* @throws {Error} If the host rejects the context update (e.g., unsupported content type)
|
||||||
|
* @throws {Error} If the request times out or the connection is lost
|
||||||
*
|
*
|
||||||
* @example Update model context with current app state
|
* @example Update model context with current app state
|
||||||
* {@includeCode ./app.examples.ts#App_updateModelContext_appState}
|
* {@includeCode ./app.examples.ts#App_updateModelContext_appState}
|
||||||
|
|
@ -689,19 +689,19 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
||||||
* Request the host to open an external URL in the default browser.
|
* 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.
|
* The host may deny this request based on user preferences or security policy.
|
||||||
* Apps should handle rejection gracefully.
|
* Apps should handle rejection gracefully by checking `result.isError`.
|
||||||
*
|
*
|
||||||
* @param params - URL to open
|
* @param params - URL to open
|
||||||
* @param options - Request options (timeout, etc.)
|
* @param options - Request options (timeout, etc.)
|
||||||
* @returns Result indicating success or error
|
* @returns Result with `isError: true` if the host denied the request (e.g., blocked domain, user cancelled)
|
||||||
*
|
*
|
||||||
* @throws {Error} If the host denies the request (e.g., blocked domain, user cancelled)
|
|
||||||
* @throws {Error} If the request times out or the connection is lost
|
* @throws {Error} If the request times out or the connection is lost
|
||||||
*
|
*
|
||||||
* @example Open documentation link
|
* @example Open documentation link
|
||||||
* {@includeCode ./app.examples.ts#App_openLink_documentation}
|
* {@includeCode ./app.examples.ts#App_openLink_documentation}
|
||||||
*
|
*
|
||||||
* @see {@link McpUiOpenLinkRequest} for request structure
|
* @see {@link McpUiOpenLinkRequest} for request structure
|
||||||
|
* @see {@link McpUiOpenLinkResult} for result structure
|
||||||
*/
|
*/
|
||||||
openLink(params: McpUiOpenLinkRequest["params"], options?: RequestOptions) {
|
openLink(params: McpUiOpenLinkRequest["params"], options?: RequestOptions) {
|
||||||
return this.request(
|
return this.request(
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ function PostMessageTransport_constructor_guestUI() {
|
||||||
*/
|
*/
|
||||||
function PostMessageTransport_constructor_host() {
|
function PostMessageTransport_constructor_host() {
|
||||||
//#region PostMessageTransport_constructor_host
|
//#region PostMessageTransport_constructor_host
|
||||||
const iframe = document.getElementById("app") as HTMLIFrameElement;
|
const iframe = document.getElementById("app-iframe") as HTMLIFrameElement;
|
||||||
const transport = new PostMessageTransport(
|
const transport = new PostMessageTransport(
|
||||||
iframe.contentWindow!,
|
iframe.contentWindow!,
|
||||||
iframe.contentWindow!,
|
iframe.contentWindow!,
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useApp, McpUiToolInputNotificationSchema } from "./index.js";
|
import { useState } from "react";
|
||||||
|
import type { McpUiHostContext } from "../types.js";
|
||||||
|
import { useApp } from "./index.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Register a notification handler in onAppCreated.
|
* Example: Register an event handler in onAppCreated.
|
||||||
*/
|
*/
|
||||||
function useApp_registerHandler() {
|
function useApp_registerHandler() {
|
||||||
//#region useApp_registerHandler
|
//#region useApp_registerHandler
|
||||||
|
|
@ -15,40 +17,49 @@ function useApp_registerHandler() {
|
||||||
appInfo: { name: "MyApp", version: "1.0.0" },
|
appInfo: { name: "MyApp", version: "1.0.0" },
|
||||||
capabilities: {},
|
capabilities: {},
|
||||||
onAppCreated: (app) => {
|
onAppCreated: (app) => {
|
||||||
app.setNotificationHandler(
|
app.ontoolresult = (result) => {
|
||||||
McpUiToolInputNotificationSchema,
|
console.log("Tool result:", result);
|
||||||
(notification) => {
|
};
|
||||||
console.log("Tool input:", notification.params.arguments);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
//#endregion useApp_registerHandler
|
//#endregion useApp_registerHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Basic usage of useApp hook.
|
* Example: Basic usage of useApp hook with common event handlers.
|
||||||
*/
|
*/
|
||||||
function useApp_basicUsage() {
|
function useApp_basicUsage() {
|
||||||
//#region useApp_basicUsage
|
//#region useApp_basicUsage
|
||||||
function MyApp() {
|
function MyApp() {
|
||||||
|
const [hostContext, setHostContext] = useState<
|
||||||
|
McpUiHostContext | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
const { app, isConnected, error } = useApp({
|
const { app, isConnected, error } = useApp({
|
||||||
appInfo: { name: "MyApp", version: "1.0.0" },
|
appInfo: { name: "MyApp", version: "1.0.0" },
|
||||||
capabilities: {},
|
capabilities: {},
|
||||||
onAppCreated: (app) => {
|
onAppCreated: (app) => {
|
||||||
// Register handlers before connection
|
app.ontoolinput = (input) => {
|
||||||
app.setNotificationHandler(
|
console.log("Tool input:", input);
|
||||||
McpUiToolInputNotificationSchema,
|
};
|
||||||
(notification) => {
|
app.ontoolresult = (result) => {
|
||||||
console.log("Tool input:", notification.params.arguments);
|
console.log("Tool result:", result);
|
||||||
},
|
};
|
||||||
);
|
app.ontoolcancelled = (params) => {
|
||||||
|
console.log("Tool cancelled:", params.reason);
|
||||||
|
};
|
||||||
|
app.onerror = (error) => {
|
||||||
|
console.log("Error:", error);
|
||||||
|
};
|
||||||
|
app.onhostcontextchanged = (params) => {
|
||||||
|
setHostContext((prev) => ({ ...prev, ...params }));
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) return <div>Error: {error.message}</div>;
|
if (error) return <div>Error: {error.message}</div>;
|
||||||
if (!isConnected) return <div>Connecting...</div>;
|
if (!isConnected) return <div>Connecting...</div>;
|
||||||
return <div>Connected!</div>;
|
return <div>Theme: {hostContext?.theme}</div>;
|
||||||
}
|
}
|
||||||
//#endregion useApp_basicUsage
|
//#endregion useApp_basicUsage
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ export interface UseAppOptions {
|
||||||
*
|
*
|
||||||
* @param app - The newly created `App` instance
|
* @param app - The newly created `App` instance
|
||||||
*
|
*
|
||||||
* @example Register a notification handler
|
* @example Register an event handler
|
||||||
* {@includeCode ./useApp.examples.tsx#useApp_registerHandler}
|
* {@includeCode ./useApp.examples.tsx#useApp_registerHandler}
|
||||||
*/
|
*/
|
||||||
onAppCreated?: (app: App) => void;
|
onAppCreated?: (app: App) => void;
|
||||||
|
|
@ -69,7 +69,7 @@ export interface AppState {
|
||||||
* initialization, the `error` field will contain the error (typically connection
|
* initialization, the `error` field will contain the error (typically connection
|
||||||
* timeouts, initialization handshake failures, or transport errors).
|
* timeouts, initialization handshake failures, or transport errors).
|
||||||
*
|
*
|
||||||
* @example Basic usage
|
* @example Basic usage of useApp hook with common event handlers
|
||||||
* {@includeCode ./useApp.examples.tsx#useApp_basicUsage}
|
* {@includeCode ./useApp.examples.tsx#useApp_basicUsage}
|
||||||
*
|
*
|
||||||
* @see {@link App.connect} for the underlying connection method
|
* @see {@link App.connect} for the underlying connection method
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,12 @@
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import {
|
import {
|
||||||
useApp,
|
useApp,
|
||||||
useHostStyleVariables,
|
useHostStyleVariables,
|
||||||
useHostFonts,
|
useHostFonts,
|
||||||
useHostStyles,
|
useHostStyles,
|
||||||
App,
|
|
||||||
} from "./index.js";
|
} from "./index.js";
|
||||||
import type { McpUiHostContext } from "../types.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Basic usage of useHostStyleVariables.
|
* Example: Basic usage of useHostStyleVariables.
|
||||||
|
|
@ -20,7 +17,7 @@ import type { McpUiHostContext } from "../types.js";
|
||||||
function useHostStyleVariables_basicUsage() {
|
function useHostStyleVariables_basicUsage() {
|
||||||
//#region useHostStyleVariables_basicUsage
|
//#region useHostStyleVariables_basicUsage
|
||||||
function MyApp() {
|
function MyApp() {
|
||||||
const { app, isConnected } = useApp({
|
const { app } = useApp({
|
||||||
appInfo: { name: "MyApp", version: "1.0.0" },
|
appInfo: { name: "MyApp", version: "1.0.0" },
|
||||||
capabilities: {},
|
capabilities: {},
|
||||||
});
|
});
|
||||||
|
|
@ -43,44 +40,37 @@ function useHostStyleVariables_basicUsage() {
|
||||||
function useHostFonts_basicUsage() {
|
function useHostFonts_basicUsage() {
|
||||||
//#region useHostFonts_basicUsage
|
//#region useHostFonts_basicUsage
|
||||||
function MyApp() {
|
function MyApp() {
|
||||||
const { app, isConnected } = useApp({
|
const { app } = useApp({
|
||||||
appInfo: { name: "MyApp", version: "1.0.0" },
|
appInfo: { name: "MyApp", version: "1.0.0" },
|
||||||
capabilities: {},
|
capabilities: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Automatically apply host fonts
|
// Apply host fonts - pass initial context to apply fonts from connect() immediately
|
||||||
useHostFonts(app);
|
useHostFonts(app, app?.getHostContext());
|
||||||
|
|
||||||
return <div style={{ fontFamily: "var(--font-sans)" }}>Hello!</div>;
|
return <div style={{ fontFamily: "var(--font-sans)" }}>Hello!</div>;
|
||||||
}
|
}
|
||||||
//#endregion useHostFonts_basicUsage
|
//#endregion useHostFonts_basicUsage
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Example: useHostFonts with initial context.
|
|
||||||
*/
|
|
||||||
function useHostFonts_withInitialContext(app: App) {
|
|
||||||
//#region useHostFonts_withInitialContext
|
|
||||||
const [hostContext, setHostContext] = useState<McpUiHostContext | null>(null);
|
|
||||||
|
|
||||||
// ... get initial context from app.connect() result
|
|
||||||
|
|
||||||
useHostFonts(app, hostContext);
|
|
||||||
//#endregion useHostFonts_withInitialContext
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Basic usage of useHostStyles.
|
* Example: Basic usage of useHostStyles.
|
||||||
*/
|
*/
|
||||||
function useHostStyles_basicUsage() {
|
function useHostStyles_basicUsage() {
|
||||||
const appInfo = { name: "MyApp", version: "1.0.0" };
|
|
||||||
//#region useHostStyles_basicUsage
|
//#region useHostStyles_basicUsage
|
||||||
function MyApp() {
|
function MyApp() {
|
||||||
const { app } = useApp({ appInfo, capabilities: {} });
|
const { app } = useApp({
|
||||||
|
appInfo: { name: "MyApp", version: "1.0.0" },
|
||||||
|
capabilities: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply all host styles - pass initial context to apply styles from connect() immediately
|
||||||
useHostStyles(app, app?.getHostContext());
|
useHostStyles(app, app?.getHostContext());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ background: "var(--color-background-primary)" }}>...</div>
|
<div style={{ background: "var(--color-background-primary)" }}>
|
||||||
|
Hello!
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
//#endregion useHostStyles_basicUsage
|
//#endregion useHostStyles_basicUsage
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,6 @@ export function useHostStyleVariables(
|
||||||
* @example Basic usage with useApp
|
* @example Basic usage with useApp
|
||||||
* {@includeCode ./useHostStyles.examples.tsx#useHostFonts_basicUsage}
|
* {@includeCode ./useHostStyles.examples.tsx#useHostFonts_basicUsage}
|
||||||
*
|
*
|
||||||
* @example With initial context
|
|
||||||
* {@includeCode ./useHostStyles.examples.tsx#useHostFonts_withInitialContext}
|
|
||||||
*
|
|
||||||
* @see {@link applyHostFonts} for the underlying fonts function
|
* @see {@link applyHostFonts} for the underlying fonts function
|
||||||
* @see {@link useHostStyleVariables} for applying style variables and theme
|
* @see {@link useHostStyleVariables} for applying style variables and theme
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,12 @@
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
import * as fs from "node:fs/promises";
|
||||||
|
import type {
|
||||||
|
McpServer,
|
||||||
|
ToolCallback,
|
||||||
|
ReadResourceCallback,
|
||||||
|
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import {
|
import {
|
||||||
registerAppTool,
|
registerAppTool,
|
||||||
|
|
@ -24,17 +29,14 @@ declare function updateCartItem(
|
||||||
itemId: string,
|
itemId: string,
|
||||||
quantity: number,
|
quantity: number,
|
||||||
): Promise<{ items: unknown[]; total: number }>;
|
): Promise<{ items: unknown[]; total: number }>;
|
||||||
declare const fs: { readFile(path: string, encoding: string): Promise<string> };
|
|
||||||
declare function readCallback(): Promise<{
|
|
||||||
contents: { uri: string; mimeType: string; text: string }[];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Module overview showing basic registration of tools and resources.
|
* Example: Module overview showing basic registration of tools and resources.
|
||||||
*/
|
*/
|
||||||
function index_overview(
|
function index_overview(
|
||||||
server: McpServer,
|
server: McpServer,
|
||||||
handler: () => Promise<{ content: { type: "text"; text: string }[] }>,
|
toolCallback: ToolCallback,
|
||||||
|
readCallback: ReadResourceCallback,
|
||||||
) {
|
) {
|
||||||
//#region index_overview
|
//#region index_overview
|
||||||
// Register a tool that displays a widget
|
// Register a tool that displays a widget
|
||||||
|
|
@ -45,7 +47,7 @@ function index_overview(
|
||||||
description: "Get weather forecast",
|
description: "Get weather forecast",
|
||||||
_meta: { ui: { resourceUri: "ui://weather/widget.html" } },
|
_meta: { ui: { resourceUri: "ui://weather/widget.html" } },
|
||||||
},
|
},
|
||||||
handler,
|
toolCallback,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Register the HTML resource the tool references
|
// Register the HTML resource the tool references
|
||||||
|
|
@ -175,12 +177,11 @@ function registerAppResource_withCsp(
|
||||||
uri: "ui://music/player.html",
|
uri: "ui://music/player.html",
|
||||||
mimeType: RESOURCE_MIME_TYPE,
|
mimeType: RESOURCE_MIME_TYPE,
|
||||||
text: musicPlayerHtml,
|
text: musicPlayerHtml,
|
||||||
// CSP must be on the content item, not the resource config
|
|
||||||
_meta: {
|
_meta: {
|
||||||
ui: {
|
ui: {
|
||||||
csp: {
|
csp: {
|
||||||
connectDomains: ["https://api.example.com"], // For fetch/WebSocket
|
|
||||||
resourceDomains: ["https://cdn.example.com"], // For scripts/styles/images
|
resourceDomains: ["https://cdn.example.com"], // For scripts/styles/images
|
||||||
|
connectDomains: ["https://api.example.com"], // For fetch/WebSocket
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { McpUiHostContext } from "./types.js";
|
|
||||||
import { App } from "./app.js";
|
import { App } from "./app.js";
|
||||||
import {
|
import {
|
||||||
getDocumentTheme,
|
getDocumentTheme,
|
||||||
|
|
@ -16,24 +15,20 @@ import {
|
||||||
applyHostFonts,
|
applyHostFonts,
|
||||||
} from "./styles.js";
|
} from "./styles.js";
|
||||||
|
|
||||||
// Stub declarations for examples
|
|
||||||
declare const app: App;
|
|
||||||
declare const hostContext: McpUiHostContext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Check current theme.
|
* Example: Check current theme.
|
||||||
*/
|
*/
|
||||||
function getDocumentTheme_checkCurrent() {
|
function getDocumentTheme_checkCurrent() {
|
||||||
//#region getDocumentTheme_checkCurrent
|
//#region getDocumentTheme_checkCurrent
|
||||||
const theme = getDocumentTheme();
|
const theme = getDocumentTheme();
|
||||||
console.log(`Current theme: ${theme}`);
|
const isDark = theme === "dark";
|
||||||
//#endregion getDocumentTheme_checkCurrent
|
//#endregion getDocumentTheme_checkCurrent
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Apply theme from host context.
|
* Example: Apply theme from host context.
|
||||||
*/
|
*/
|
||||||
function applyDocumentTheme_fromHostContext() {
|
function applyDocumentTheme_fromHostContext(app: App) {
|
||||||
//#region applyDocumentTheme_fromHostContext
|
//#region applyDocumentTheme_fromHostContext
|
||||||
app.onhostcontextchanged = (params) => {
|
app.onhostcontextchanged = (params) => {
|
||||||
if (params.theme) {
|
if (params.theme) {
|
||||||
|
|
@ -46,7 +41,7 @@ function applyDocumentTheme_fromHostContext() {
|
||||||
/**
|
/**
|
||||||
* Example: Apply style variables from host context.
|
* Example: Apply style variables from host context.
|
||||||
*/
|
*/
|
||||||
function applyHostStyleVariables_fromHostContext() {
|
function applyHostStyleVariables_fromHostContext(app: App) {
|
||||||
//#region applyHostStyleVariables_fromHostContext
|
//#region applyHostStyleVariables_fromHostContext
|
||||||
app.onhostcontextchanged = (params) => {
|
app.onhostcontextchanged = (params) => {
|
||||||
if (params.styles?.variables) {
|
if (params.styles?.variables) {
|
||||||
|
|
@ -59,19 +54,21 @@ function applyHostStyleVariables_fromHostContext() {
|
||||||
/**
|
/**
|
||||||
* Example: Apply to a specific element.
|
* Example: Apply to a specific element.
|
||||||
*/
|
*/
|
||||||
function applyHostStyleVariables_toElement() {
|
function applyHostStyleVariables_toElement(app: App) {
|
||||||
//#region applyHostStyleVariables_toElement
|
//#region applyHostStyleVariables_toElement
|
||||||
const container = document.getElementById("app-root");
|
app.onhostcontextchanged = (params) => {
|
||||||
if (container && hostContext.styles?.variables) {
|
const container = document.getElementById("app-root");
|
||||||
applyHostStyleVariables(hostContext.styles.variables, container);
|
if (container && params.styles?.variables) {
|
||||||
}
|
applyHostStyleVariables(params.styles.variables, container);
|
||||||
|
}
|
||||||
|
};
|
||||||
//#endregion applyHostStyleVariables_toElement
|
//#endregion applyHostStyleVariables_toElement
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: Apply fonts from host context.
|
* Example: Apply fonts from host context.
|
||||||
*/
|
*/
|
||||||
function applyHostFonts_fromHostContext() {
|
function applyHostFonts_fromHostContext(app: App) {
|
||||||
//#region applyHostFonts_fromHostContext
|
//#region applyHostFonts_fromHostContext
|
||||||
app.onhostcontextchanged = (params) => {
|
app.onhostcontextchanged = (params) => {
|
||||||
if (params.styles?.css?.fonts) {
|
if (params.styles?.css?.fonts) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue