Move JSDoc examples to type-checked .examples.ts files
Extract inline code examples from JSDoc comments into companion
`.examples.ts`/`.examples.tsx` files. Examples are now:
- Type-checked by wrapping them in functions
- Referenced via `{@includeCode ./file.examples.ts#regionName}` tags
- Organized using `//#region` and `//#endregion` markers
This keeps the main source files cleaner while ensuring examples
compile correctly. Updated TypeDoc config with `exampleTag: false`
to support title text on `@example` tags.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e5c90c7385
commit
ad04e89745
1 changed files with 1661 additions and 838 deletions
509
src/app-bridge.examples.ts
Normal file
509
src/app-bridge.examples.ts
Normal file
|
|
@ -0,0 +1,509 @@
|
|||
/**
|
||||
* Type-checked examples for {@link AppBridge}.
|
||||
*
|
||||
* These examples are included in the API documentation via `@includeCode` tags.
|
||||
* Each function's region markers define the code snippet that appears in the docs.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
||||
import {
|
||||
CallToolResult,
|
||||
CallToolResultSchema,
|
||||
ListResourcesResultSchema,
|
||||
ReadResourceResultSchema,
|
||||
ListPromptsResultSchema,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { AppBridge, PostMessageTransport } from "./app-bridge.js";
|
||||
|
||||
/**
|
||||
* Example: Basic usage of the AppBridge class with PostMessageTransport.
|
||||
*/
|
||||
async function AppBridge_basicUsage(serverTransport: Transport) {
|
||||
//#region AppBridge_basicUsage
|
||||
// Create MCP client for the server
|
||||
const client = new Client({
|
||||
name: "MyHost",
|
||||
version: "1.0.0",
|
||||
});
|
||||
await client.connect(serverTransport);
|
||||
|
||||
// Create bridge for the Guest UI
|
||||
const bridge = new AppBridge(
|
||||
client,
|
||||
{ name: "MyHost", version: "1.0.0" },
|
||||
{ openLinks: {}, serverTools: {}, logging: {} },
|
||||
);
|
||||
|
||||
// Set up iframe and connect
|
||||
const iframe = document.getElementById("app") as HTMLIFrameElement;
|
||||
const transport = new PostMessageTransport(
|
||||
iframe.contentWindow!,
|
||||
iframe.contentWindow!,
|
||||
);
|
||||
|
||||
bridge.oninitialized = () => {
|
||||
console.log("Guest UI initialized");
|
||||
// Now safe to send tool input
|
||||
bridge.sendToolInput({ arguments: { location: "NYC" } });
|
||||
};
|
||||
|
||||
await bridge.connect(transport);
|
||||
//#endregion AppBridge_basicUsage
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Creating an AppBridge with an MCP client for automatic forwarding.
|
||||
*/
|
||||
function AppBridge_constructor_withMcpClient(mcpClient: Client) {
|
||||
//#region AppBridge_constructor_withMcpClient
|
||||
const bridge = new AppBridge(
|
||||
mcpClient,
|
||||
{ name: "MyHost", version: "1.0.0" },
|
||||
{ openLinks: {}, serverTools: {}, logging: {} },
|
||||
);
|
||||
//#endregion AppBridge_constructor_withMcpClient
|
||||
return bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Creating an AppBridge without an MCP client, using manual handlers.
|
||||
*/
|
||||
function AppBridge_constructor_withoutMcpClient() {
|
||||
//#region AppBridge_constructor_withoutMcpClient
|
||||
const bridge = new AppBridge(
|
||||
null,
|
||||
{ name: "MyHost", version: "1.0.0" },
|
||||
{ openLinks: {}, serverTools: {}, logging: {} },
|
||||
);
|
||||
bridge.oncalltool = async (params, extra) => {
|
||||
// Handle tool calls manually
|
||||
return { content: [] };
|
||||
};
|
||||
//#endregion AppBridge_constructor_withoutMcpClient
|
||||
return bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Check Guest UI capabilities after initialization.
|
||||
*/
|
||||
function AppBridge_guestCapabilities_checkAfterInit(bridge: AppBridge) {
|
||||
//#region AppBridge_guestCapabilities_checkAfterInit
|
||||
bridge.oninitialized = () => {
|
||||
const caps = bridge.getAppCapabilities();
|
||||
if (caps?.tools) {
|
||||
console.log("Guest UI provides tools");
|
||||
}
|
||||
};
|
||||
//#endregion AppBridge_guestCapabilities_checkAfterInit
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Log Guest UI information after initialization.
|
||||
*/
|
||||
function AppBridge_guestInfo_logAfterInit(bridge: AppBridge) {
|
||||
//#region AppBridge_guestInfo_logAfterInit
|
||||
bridge.oninitialized = () => {
|
||||
const appInfo = bridge.getAppVersion();
|
||||
if (appInfo) {
|
||||
console.log(`Guest UI: ${appInfo.name} v${appInfo.version}`);
|
||||
}
|
||||
};
|
||||
//#endregion AppBridge_guestInfo_logAfterInit
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Handle Guest UI initialization and send tool input.
|
||||
*/
|
||||
function AppBridge_oninitialized_sendToolInput(bridge: AppBridge) {
|
||||
const toolArgs = { location: "NYC" };
|
||||
//#region AppBridge_oninitialized_sendToolInput
|
||||
bridge.oninitialized = () => {
|
||||
console.log("Guest UI ready");
|
||||
bridge.sendToolInput({ arguments: toolArgs });
|
||||
};
|
||||
//#endregion AppBridge_oninitialized_sendToolInput
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Handle message requests from the Guest UI.
|
||||
*/
|
||||
function AppBridge_onmessage_logMessage(bridge: AppBridge) {
|
||||
//#region AppBridge_onmessage_logMessage
|
||||
bridge.onmessage = async ({ role, content }, extra) => {
|
||||
try {
|
||||
await chatManager.addMessage({ role, content, source: "app" });
|
||||
return {}; // Success
|
||||
} catch (error) {
|
||||
console.error("Failed to add message:", error);
|
||||
return { isError: true };
|
||||
}
|
||||
};
|
||||
//#endregion AppBridge_onmessage_logMessage
|
||||
}
|
||||
|
||||
// Stub for example code - represents a hypothetical chat manager
|
||||
declare const chatManager: {
|
||||
addMessage(message: {
|
||||
role: string;
|
||||
content: unknown;
|
||||
source: string;
|
||||
}): Promise<void>;
|
||||
};
|
||||
|
||||
// Stub for example code - represents a hypothetical URL validator
|
||||
declare function isAllowedDomain(url: string): boolean;
|
||||
|
||||
// Stub for example code - represents a hypothetical dialog API
|
||||
declare function showDialog(options: {
|
||||
message: string;
|
||||
buttons: string[];
|
||||
}): Promise<boolean>;
|
||||
|
||||
// Stub for example code - represents a hypothetical model context storage
|
||||
declare let modelContext: {
|
||||
type: string;
|
||||
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.
|
||||
*/
|
||||
function AppBridge_onopenlink_handleRequest(bridge: AppBridge) {
|
||||
//#region AppBridge_onopenlink_handleRequest
|
||||
bridge.onopenlink = async ({ url }, extra) => {
|
||||
if (!isAllowedDomain(url)) {
|
||||
console.warn("Blocked external link:", url);
|
||||
return { isError: true };
|
||||
}
|
||||
|
||||
const confirmed = await showDialog({
|
||||
message: `Open external link?\n${url}`,
|
||||
buttons: ["Open", "Cancel"],
|
||||
});
|
||||
|
||||
if (confirmed) {
|
||||
window.open(url, "_blank", "noopener,noreferrer");
|
||||
return {};
|
||||
}
|
||||
|
||||
return { isError: true };
|
||||
};
|
||||
//#endregion AppBridge_onopenlink_handleRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Store model context updates from the Guest UI.
|
||||
*/
|
||||
function AppBridge_onupdatemodelcontext_storeContext(bridge: AppBridge) {
|
||||
//#region AppBridge_onupdatemodelcontext_storeContext
|
||||
bridge.onupdatemodelcontext = async (
|
||||
{ content, structuredContent },
|
||||
extra,
|
||||
) => {
|
||||
// Update the model context with the new snapshot
|
||||
modelContext = {
|
||||
type: "app_context",
|
||||
content,
|
||||
structuredContent,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
return {};
|
||||
};
|
||||
//#endregion AppBridge_onupdatemodelcontext_storeContext
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Forward tool calls to the MCP server.
|
||||
*/
|
||||
function AppBridge_oncalltool_forwardToServer(bridge: AppBridge) {
|
||||
//#region AppBridge_oncalltool_forwardToServer
|
||||
bridge.oncalltool = async ({ name, arguments: args }, extra) => {
|
||||
return mcpClient.request(
|
||||
{ method: "tools/call", params: { name, arguments: args } },
|
||||
CallToolResultSchema,
|
||||
{ signal: extra.signal },
|
||||
);
|
||||
};
|
||||
//#endregion AppBridge_oncalltool_forwardToServer
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Forward list resources requests to the MCP server.
|
||||
*/
|
||||
function AppBridge_onlistresources_returnResources(bridge: AppBridge) {
|
||||
//#region AppBridge_onlistresources_returnResources
|
||||
bridge.onlistresources = async (params, extra) => {
|
||||
return mcpClient.request(
|
||||
{ method: "resources/list", params },
|
||||
ListResourcesResultSchema,
|
||||
{ signal: extra.signal },
|
||||
);
|
||||
};
|
||||
//#endregion AppBridge_onlistresources_returnResources
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Forward read resource requests to the MCP server.
|
||||
*/
|
||||
function AppBridge_onreadresource_returnResource(bridge: AppBridge) {
|
||||
//#region AppBridge_onreadresource_returnResource
|
||||
bridge.onreadresource = async ({ uri }, extra) => {
|
||||
return mcpClient.request(
|
||||
{ method: "resources/read", params: { uri } },
|
||||
ReadResourceResultSchema,
|
||||
{ signal: extra.signal },
|
||||
);
|
||||
};
|
||||
//#endregion AppBridge_onreadresource_returnResource
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Forward list prompts requests to the MCP server.
|
||||
*/
|
||||
function AppBridge_onlistprompts_returnPrompts(bridge: AppBridge) {
|
||||
//#region AppBridge_onlistprompts_returnPrompts
|
||||
bridge.onlistprompts = async (params, extra) => {
|
||||
return mcpClient.request(
|
||||
{ method: "prompts/list", params },
|
||||
ListPromptsResultSchema,
|
||||
{ signal: extra.signal },
|
||||
);
|
||||
};
|
||||
//#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.
|
||||
*/
|
||||
function AppBridge_onping_handleRequest(bridge: AppBridge) {
|
||||
//#region AppBridge_onping_handleRequest
|
||||
bridge.onping = (params, extra) => {
|
||||
console.log("Received ping from Guest UI");
|
||||
};
|
||||
//#endregion AppBridge_onping_handleRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Handle size change notifications from the Guest UI.
|
||||
*/
|
||||
function AppBridge_onsizechange_handleResize(bridge: AppBridge) {
|
||||
//#region AppBridge_onsizechange_handleResize
|
||||
bridge.onsizechange = ({ width, height }) => {
|
||||
if (width != null) {
|
||||
iframe.style.width = `${width}px`;
|
||||
}
|
||||
if (height != null) {
|
||||
iframe.style.height = `${height}px`;
|
||||
}
|
||||
};
|
||||
//#endregion AppBridge_onsizechange_handleResize
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Handle display mode requests from the Guest UI.
|
||||
*/
|
||||
function AppBridge_onrequestdisplaymode_handleRequest(bridge: AppBridge) {
|
||||
//#region AppBridge_onrequestdisplaymode_handleRequest
|
||||
type McpUiDisplayMode = "inline" | "fullscreen" | "pip";
|
||||
let currentDisplayMode: McpUiDisplayMode = "inline";
|
||||
|
||||
bridge.onrequestdisplaymode = async ({ mode }, extra) => {
|
||||
const availableModes = hostContext.availableDisplayModes ?? ["inline"];
|
||||
if (availableModes.includes(mode)) {
|
||||
currentDisplayMode = mode;
|
||||
return { mode };
|
||||
}
|
||||
// Return current mode if requested mode not available
|
||||
return { mode: currentDisplayMode };
|
||||
};
|
||||
//#endregion AppBridge_onrequestdisplaymode_handleRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Handle logging messages from the Guest UI.
|
||||
*/
|
||||
function AppBridge_onloggingmessage_handleLog(bridge: AppBridge) {
|
||||
//#region AppBridge_onloggingmessage_handleLog
|
||||
bridge.onloggingmessage = ({ level, logger, data }) => {
|
||||
const prefix = logger ? `[${logger}]` : "[Guest UI]";
|
||||
console[level === "error" ? "error" : "log"](
|
||||
`${prefix} ${level.toUpperCase()}:`,
|
||||
data,
|
||||
);
|
||||
};
|
||||
//#endregion AppBridge_onloggingmessage_handleLog
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Gracefully tear down the Guest UI before unmounting.
|
||||
*/
|
||||
async function AppBridge_teardownResource_gracefulShutdown(bridge: AppBridge) {
|
||||
//#region AppBridge_teardownResource_gracefulShutdown
|
||||
try {
|
||||
await bridge.teardownResource({});
|
||||
// Guest UI is ready, safe to unmount iframe
|
||||
iframe.remove();
|
||||
} catch (error) {
|
||||
console.error("Teardown failed:", error);
|
||||
}
|
||||
//#endregion AppBridge_teardownResource_gracefulShutdown
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Update theme when user toggles dark mode.
|
||||
*/
|
||||
function AppBridge_setHostContext_updateTheme(bridge: AppBridge) {
|
||||
//#region AppBridge_setHostContext_updateTheme
|
||||
bridge.setHostContext({ theme: "dark" });
|
||||
//#endregion AppBridge_setHostContext_updateTheme
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Update multiple context fields at once.
|
||||
*/
|
||||
function AppBridge_setHostContext_updateMultiple(bridge: AppBridge) {
|
||||
//#region AppBridge_setHostContext_updateMultiple
|
||||
bridge.setHostContext({
|
||||
theme: "dark",
|
||||
containerDimensions: { maxHeight: 600, width: 800 },
|
||||
});
|
||||
//#endregion AppBridge_setHostContext_updateMultiple
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Send tool input after initialization.
|
||||
*/
|
||||
function AppBridge_sendToolInput_afterInit(bridge: AppBridge) {
|
||||
//#region AppBridge_sendToolInput_afterInit
|
||||
bridge.oninitialized = () => {
|
||||
bridge.sendToolInput({
|
||||
arguments: { location: "New York", units: "metric" },
|
||||
});
|
||||
};
|
||||
//#endregion AppBridge_sendToolInput_afterInit
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Stream partial arguments as they arrive.
|
||||
*/
|
||||
function AppBridge_sendToolInputPartial_streaming(bridge: AppBridge) {
|
||||
//#region AppBridge_sendToolInputPartial_streaming
|
||||
// As streaming progresses...
|
||||
bridge.sendToolInputPartial({ arguments: { loc: "N" } });
|
||||
bridge.sendToolInputPartial({ arguments: { location: "New" } });
|
||||
bridge.sendToolInputPartial({ arguments: { location: "New York" } });
|
||||
|
||||
// When complete, send final input
|
||||
bridge.sendToolInput({
|
||||
arguments: { location: "New York", units: "metric" },
|
||||
});
|
||||
//#endregion AppBridge_sendToolInputPartial_streaming
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Send tool result after execution.
|
||||
*/
|
||||
async function AppBridge_sendToolResult_afterExecution(
|
||||
bridge: AppBridge,
|
||||
mcpClientParam: Client,
|
||||
args: Record<string, unknown>,
|
||||
) {
|
||||
//#region AppBridge_sendToolResult_afterExecution
|
||||
// import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
||||
|
||||
const result = await mcpClient.request(
|
||||
{ method: "tools/call", params: { name: "get_weather", arguments: args } },
|
||||
CallToolResultSchema,
|
||||
);
|
||||
bridge.sendToolResult(result);
|
||||
//#endregion AppBridge_sendToolResult_afterExecution
|
||||
|
||||
// Make the parameter used to satisfy linting
|
||||
void mcpClientParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: User-initiated cancellation.
|
||||
*/
|
||||
function AppBridge_sendToolCancelled_userInitiated(bridge: AppBridge) {
|
||||
//#region AppBridge_sendToolCancelled_userInitiated
|
||||
// User clicked "Cancel" button
|
||||
bridge.sendToolCancelled({ reason: "User cancelled the operation" });
|
||||
//#endregion AppBridge_sendToolCancelled_userInitiated
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: System-level cancellation.
|
||||
*/
|
||||
function AppBridge_sendToolCancelled_systemLevel(bridge: AppBridge) {
|
||||
//#region AppBridge_sendToolCancelled_systemLevel
|
||||
// Sampling error or timeout
|
||||
bridge.sendToolCancelled({ reason: "Request timeout after 30 seconds" });
|
||||
|
||||
// Classifier intervention
|
||||
bridge.sendToolCancelled({ reason: "Content policy violation detected" });
|
||||
//#endregion AppBridge_sendToolCancelled_systemLevel
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Connect with MCP client for automatic forwarding.
|
||||
*/
|
||||
async function AppBridge_connect_withMcpClient(
|
||||
mcpClientParam: Client,
|
||||
hostInfo: { name: string; version: string },
|
||||
capabilities: { openLinks: {}; serverTools: {}; logging: {} },
|
||||
toolArgs: Record<string, unknown>,
|
||||
) {
|
||||
//#region AppBridge_connect_withMcpClient
|
||||
const bridge = new AppBridge(mcpClient, hostInfo, capabilities);
|
||||
const transport = new PostMessageTransport(
|
||||
iframe.contentWindow!,
|
||||
iframe.contentWindow!,
|
||||
);
|
||||
|
||||
bridge.oninitialized = () => {
|
||||
console.log("Guest UI ready");
|
||||
bridge.sendToolInput({ arguments: toolArgs });
|
||||
};
|
||||
|
||||
await bridge.connect(transport);
|
||||
//#endregion AppBridge_connect_withMcpClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Connect without MCP client using manual handlers.
|
||||
*/
|
||||
async function AppBridge_connect_withoutMcpClient(
|
||||
hostInfo: { name: string; version: string },
|
||||
capabilities: { openLinks: {}; serverTools: {}; logging: {} },
|
||||
transport: Transport,
|
||||
) {
|
||||
//#region AppBridge_connect_withoutMcpClient
|
||||
const bridge = new AppBridge(null, hostInfo, capabilities);
|
||||
|
||||
// Register handlers manually
|
||||
bridge.oncalltool = async (params, extra) => {
|
||||
// Custom tool call handling
|
||||
return { content: [] };
|
||||
};
|
||||
|
||||
await bridge.connect(transport);
|
||||
//#endregion AppBridge_connect_withoutMcpClient
|
||||
}
|
||||
Loading…
Reference in a new issue