Add addEventListener/removeEventListener with DOM-model on* semantics (#573)
* feat(events): DOM-model on* semantics with addEventListener/removeEventListener Fixes #551 (useHostStyles broken: fonts clobber theme/CSS variables) Fixes #225 (user onhostcontextchanged and SDK hooks mutually exclusive) The on* setters now follow the DOM event model: - Replace semantics (like el.onclick) with getters - Coexist with addEventListener/removeEventListener listeners - Both channels fire on dispatch: on* handler then listeners Introduces ProtocolWithEvents base class between Protocol and App/AppBridge. Each notification event gets a slot with two independent channels (singular on* handler + addEventListener listener array). useHostStyles hooks now use addEventListener with proper cleanup, so they compose correctly with user on* handlers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix typedoc validation warnings - Export AppEventMap, AppBridgeEventMap, ProtocolWithEvents so they appear in generated docs (they're part of public addEventListener signatures) - Move AppBridgeEventMap above the AppBridge class JSDoc — it had been inserted between the doc block and the class, orphaning the AppBridge docs onto the wrong type - Drop @param callback tags from on* getter/setter pairs — typedoc attaches the doc block to the getter signature which has no params - Add MethodSchema to intentionallyNotExported (internal zod helper referenced by the now-documented eventSchemas field) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
14969c5da2
commit
0266171b92
1 changed files with 1009 additions and 201 deletions
291
src/app.ts
291
src/app.ts
|
|
@ -1,6 +1,5 @@
|
|||
import {
|
||||
type RequestOptions,
|
||||
Protocol,
|
||||
ProtocolOptions,
|
||||
} from "@modelcontextprotocol/sdk/shared/protocol.js";
|
||||
|
||||
|
|
@ -16,6 +15,7 @@ import {
|
|||
ListResourcesResultSchema,
|
||||
ListToolsRequest,
|
||||
ListToolsRequestSchema,
|
||||
ListToolsResult,
|
||||
LoggingMessageNotification,
|
||||
PingRequestSchema,
|
||||
ReadResourceRequest,
|
||||
|
|
@ -23,6 +23,8 @@ import {
|
|||
ReadResourceResultSchema,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { AppNotification, AppRequest, AppResult } from "./types";
|
||||
import { ProtocolWithEvents } from "./events";
|
||||
export { ProtocolWithEvents };
|
||||
import { PostMessageTransport } from "./message-transport";
|
||||
import {
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
|
|
@ -159,6 +161,21 @@ type RequestHandlerExtra = Parameters<
|
|||
Parameters<App["setRequestHandler"]>[1]
|
||||
>[1];
|
||||
|
||||
/**
|
||||
* Maps DOM-style event names to their notification `params` types.
|
||||
*
|
||||
* Used by {@link App `App`} (which extends {@link ProtocolWithEvents `ProtocolWithEvents`})
|
||||
* to provide type-safe `addEventListener` / `removeEventListener` and
|
||||
* singular `on*` handler support.
|
||||
*/
|
||||
export type AppEventMap = {
|
||||
toolinput: McpUiToolInputNotification["params"];
|
||||
toolinputpartial: McpUiToolInputPartialNotification["params"];
|
||||
toolresult: McpUiToolResultNotification["params"];
|
||||
toolcancelled: McpUiToolCancelledNotification["params"];
|
||||
hostcontextchanged: McpUiHostContextChangedNotification["params"];
|
||||
};
|
||||
|
||||
/**
|
||||
* Main class for MCP Apps to communicate with their host.
|
||||
*
|
||||
|
|
@ -182,24 +199,26 @@ type RequestHandlerExtra = Parameters<
|
|||
*
|
||||
* ## Inherited Methods
|
||||
*
|
||||
* As a subclass of `Protocol`, `App` inherits key methods for handling communication:
|
||||
* As a subclass of {@link ProtocolWithEvents `ProtocolWithEvents`}, `App` inherits:
|
||||
* - `setRequestHandler()` - Register handlers for requests from host
|
||||
* - `setNotificationHandler()` - Register handlers for notifications from host
|
||||
* - `addEventListener()` - Append a listener for a notification event (multi-listener)
|
||||
* - `removeEventListener()` - Remove a previously added listener
|
||||
*
|
||||
* @see `Protocol` from @modelcontextprotocol/sdk for all inherited methods
|
||||
* @see {@link ProtocolWithEvents `ProtocolWithEvents`} for the DOM-model event system
|
||||
*
|
||||
* ## Notification Setters
|
||||
* ## Notification Setters (DOM-model `on*` handlers)
|
||||
*
|
||||
* For common notifications, the `App` class provides convenient setter properties
|
||||
* that simplify handler registration:
|
||||
* For common notifications, the `App` class provides getter/setter properties
|
||||
* that follow DOM-model replace semantics (like `el.onclick`):
|
||||
* - `ontoolinput` - Complete tool arguments from host
|
||||
* - `ontoolinputpartial` - Streaming partial tool arguments
|
||||
* - `ontoolresult` - Tool execution results
|
||||
* - `ontoolcancelled` - Tool execution was cancelled by user or host
|
||||
* - `onhostcontextchanged` - Host context changes (theme, locale, etc.)
|
||||
*
|
||||
* These setters are convenience wrappers around `setNotificationHandler()`.
|
||||
* Both patterns work; use whichever fits your coding style better.
|
||||
* Assigning replaces the previous handler; assigning `undefined` clears it.
|
||||
* Use `addEventListener` to attach multiple listeners without replacing.
|
||||
*
|
||||
* @example Basic usage with PostMessageTransport
|
||||
* ```ts source="./app.examples.ts#App_basicUsage"
|
||||
|
|
@ -216,11 +235,33 @@ type RequestHandlerExtra = Parameters<
|
|||
* await app.connect();
|
||||
* ```
|
||||
*/
|
||||
export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
||||
export class App extends ProtocolWithEvents<
|
||||
AppRequest,
|
||||
AppNotification,
|
||||
AppResult,
|
||||
AppEventMap
|
||||
> {
|
||||
private _hostCapabilities?: McpUiHostCapabilities;
|
||||
private _hostInfo?: Implementation;
|
||||
private _hostContext?: McpUiHostContext;
|
||||
|
||||
protected readonly eventSchemas = {
|
||||
toolinput: McpUiToolInputNotificationSchema,
|
||||
toolinputpartial: McpUiToolInputPartialNotificationSchema,
|
||||
toolresult: McpUiToolResultNotificationSchema,
|
||||
toolcancelled: McpUiToolCancelledNotificationSchema,
|
||||
hostcontextchanged: McpUiHostContextChangedNotificationSchema,
|
||||
};
|
||||
|
||||
protected override onEventDispatch<K extends keyof AppEventMap>(
|
||||
event: K,
|
||||
params: AppEventMap[K],
|
||||
): void {
|
||||
if (event === "hostcontextchanged") {
|
||||
this._hostContext = { ...this._hostContext, ...params };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MCP App instance.
|
||||
*
|
||||
|
|
@ -249,9 +290,10 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
return {};
|
||||
});
|
||||
|
||||
// Set up default handler to update _hostContext when notifications arrive.
|
||||
// Users can override this by setting onhostcontextchanged.
|
||||
this.onhostcontextchanged = () => {};
|
||||
// Eagerly register the hostcontextchanged event slot so that
|
||||
// onEventDispatch (which merges into _hostContext) fires even if the
|
||||
// user never assigns onhostcontextchanged or calls addEventListener.
|
||||
this.setEventHandler("hostcontextchanged", undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -339,13 +381,12 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* 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.
|
||||
* Assigning replaces the previous handler; assigning `undefined` clears it.
|
||||
* Use {@link addEventListener `addEventListener`} to attach multiple listeners
|
||||
* without replacing.
|
||||
*
|
||||
* Register handlers before calling {@link connect `connect`} to avoid missing notifications.
|
||||
*
|
||||
* @param callback - Function called with the tool input params ({@link McpUiToolInputNotification.params `McpUiToolInputNotification.params`})
|
||||
*
|
||||
* @example
|
||||
* ```ts source="./app.examples.ts#App_ontoolinput_setter"
|
||||
* // Register before connecting to ensure no notifications are missed
|
||||
|
|
@ -356,15 +397,20 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* await app.connect();
|
||||
* ```
|
||||
*
|
||||
* @see {@link setNotificationHandler `setNotificationHandler`} for the underlying method
|
||||
* @deprecated Use {@link addEventListener `addEventListener("toolinput", handler)`} instead — it composes with other listeners and supports cleanup via {@link removeEventListener `removeEventListener`}.
|
||||
* @see {@link McpUiToolInputNotification `McpUiToolInputNotification`} for the notification structure
|
||||
*/
|
||||
get ontoolinput():
|
||||
| ((params: McpUiToolInputNotification["params"]) => void)
|
||||
| undefined {
|
||||
return this.getEventHandler("toolinput");
|
||||
}
|
||||
set ontoolinput(
|
||||
callback: (params: McpUiToolInputNotification["params"]) => void,
|
||||
callback:
|
||||
| ((params: McpUiToolInputNotification["params"]) => void)
|
||||
| undefined,
|
||||
) {
|
||||
this.setNotificationHandler(McpUiToolInputNotificationSchema, (n) =>
|
||||
callback(n.params),
|
||||
);
|
||||
this.setEventHandler("toolinput", callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -379,13 +425,12 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* (e.g., the last item in an array may be truncated). Use partial data only
|
||||
* for preview UI, not for critical operations.
|
||||
*
|
||||
* This setter is a convenience wrapper around `setNotificationHandler()` that
|
||||
* automatically handles the notification schema and extracts the params for you.
|
||||
* Assigning replaces the previous handler; assigning `undefined` clears it.
|
||||
* Use {@link addEventListener `addEventListener`} to attach multiple listeners
|
||||
* without replacing.
|
||||
*
|
||||
* Register handlers before calling {@link connect `connect`} to avoid missing notifications.
|
||||
*
|
||||
* @param callback - Function called with each partial tool input update ({@link McpUiToolInputPartialNotification.params `McpUiToolInputPartialNotification.params`})
|
||||
*
|
||||
* @example Progressive rendering of tool arguments
|
||||
* ```ts source="./app.examples.ts#App_ontoolinputpartial_progressiveRendering"
|
||||
* const codePreview = document.querySelector<HTMLPreElement>("#code-preview")!;
|
||||
|
|
@ -404,16 +449,21 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link setNotificationHandler `setNotificationHandler`} for the underlying method
|
||||
* @deprecated Use {@link addEventListener `addEventListener("toolinputpartial", handler)`} instead — it composes with other listeners and supports cleanup via {@link removeEventListener `removeEventListener`}.
|
||||
* @see {@link McpUiToolInputPartialNotification `McpUiToolInputPartialNotification`} for the notification structure
|
||||
* @see {@link ontoolinput `ontoolinput`} for the complete tool input handler
|
||||
*/
|
||||
get ontoolinputpartial():
|
||||
| ((params: McpUiToolInputPartialNotification["params"]) => void)
|
||||
| undefined {
|
||||
return this.getEventHandler("toolinputpartial");
|
||||
}
|
||||
set ontoolinputpartial(
|
||||
callback: (params: McpUiToolInputPartialNotification["params"]) => void,
|
||||
callback:
|
||||
| ((params: McpUiToolInputPartialNotification["params"]) => void)
|
||||
| undefined,
|
||||
) {
|
||||
this.setNotificationHandler(McpUiToolInputPartialNotificationSchema, (n) =>
|
||||
callback(n.params),
|
||||
);
|
||||
this.setEventHandler("toolinputpartial", callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -423,13 +473,12 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* 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.
|
||||
* Assigning replaces the previous handler; assigning `undefined` clears it.
|
||||
* Use {@link addEventListener `addEventListener`} to attach multiple listeners
|
||||
* without replacing.
|
||||
*
|
||||
* Register handlers before calling {@link connect `connect`} to avoid missing notifications.
|
||||
*
|
||||
* @param callback - Function called with the tool result ({@link McpUiToolResultNotification.params `McpUiToolResultNotification.params`})
|
||||
*
|
||||
* @example Display tool execution results
|
||||
* ```ts source="./app.examples.ts#App_ontoolresult_displayResults"
|
||||
* app.ontoolresult = (params) => {
|
||||
|
|
@ -441,16 +490,21 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link setNotificationHandler `setNotificationHandler`} for the underlying method
|
||||
* @deprecated Use {@link addEventListener `addEventListener("toolresult", handler)`} instead — it composes with other listeners and supports cleanup via {@link removeEventListener `removeEventListener`}.
|
||||
* @see {@link McpUiToolResultNotification `McpUiToolResultNotification`} for the notification structure
|
||||
* @see {@link ontoolinput `ontoolinput`} for the initial tool input handler
|
||||
*/
|
||||
get ontoolresult():
|
||||
| ((params: McpUiToolResultNotification["params"]) => void)
|
||||
| undefined {
|
||||
return this.getEventHandler("toolresult");
|
||||
}
|
||||
set ontoolresult(
|
||||
callback: (params: McpUiToolResultNotification["params"]) => void,
|
||||
callback:
|
||||
| ((params: McpUiToolResultNotification["params"]) => void)
|
||||
| undefined,
|
||||
) {
|
||||
this.setNotificationHandler(McpUiToolResultNotificationSchema, (n) =>
|
||||
callback(n.params),
|
||||
);
|
||||
this.setEventHandler("toolresult", callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -461,13 +515,12 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* 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.
|
||||
* Assigning replaces the previous handler; assigning `undefined` clears it.
|
||||
* Use {@link addEventListener `addEventListener`} to attach multiple listeners
|
||||
* without replacing.
|
||||
*
|
||||
* Register handlers before calling {@link connect `connect`} to avoid missing notifications.
|
||||
*
|
||||
* @param callback - Function called when tool execution is cancelled. Receives optional cancellation reason — see {@link McpUiToolCancelledNotification.params `McpUiToolCancelledNotification.params`}.
|
||||
*
|
||||
* @example Handle tool cancellation
|
||||
* ```ts source="./app.examples.ts#App_ontoolcancelled_handleCancellation"
|
||||
* app.ontoolcancelled = (params) => {
|
||||
|
|
@ -476,16 +529,21 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link setNotificationHandler `setNotificationHandler`} for the underlying method
|
||||
* @deprecated Use {@link addEventListener `addEventListener("toolcancelled", handler)`} instead — it composes with other listeners and supports cleanup via {@link removeEventListener `removeEventListener`}.
|
||||
* @see {@link McpUiToolCancelledNotification `McpUiToolCancelledNotification`} for the notification structure
|
||||
* @see {@link ontoolresult `ontoolresult`} for successful tool completion
|
||||
*/
|
||||
get ontoolcancelled():
|
||||
| ((params: McpUiToolCancelledNotification["params"]) => void)
|
||||
| undefined {
|
||||
return this.getEventHandler("toolcancelled");
|
||||
}
|
||||
set ontoolcancelled(
|
||||
callback: (params: McpUiToolCancelledNotification["params"]) => void,
|
||||
callback:
|
||||
| ((params: McpUiToolCancelledNotification["params"]) => void)
|
||||
| undefined,
|
||||
) {
|
||||
this.setNotificationHandler(McpUiToolCancelledNotificationSchema, (n) =>
|
||||
callback(n.params),
|
||||
);
|
||||
this.setEventHandler("toolcancelled", callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -496,17 +554,17 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* 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.
|
||||
* Assigning replaces the previous handler; assigning `undefined` clears it.
|
||||
* Use {@link addEventListener `addEventListener`} to attach multiple listeners
|
||||
* without replacing.
|
||||
*
|
||||
* Notification params are automatically merged into the internal host context
|
||||
* before the callback is invoked. This means {@link getHostContext `getHostContext`} will
|
||||
* return the updated values even before your callback runs.
|
||||
* via {@link onEventDispatch `onEventDispatch`} before any handler or listener
|
||||
* fires. This means {@link getHostContext `getHostContext`} will return the
|
||||
* updated values even before your callback runs.
|
||||
*
|
||||
* Register handlers before calling {@link connect `connect`} to avoid missing notifications.
|
||||
*
|
||||
* @param callback - Function called with the updated host context
|
||||
*
|
||||
* @example Respond to theme changes
|
||||
* ```ts source="./app.examples.ts#App_onhostcontextchanged_respondToTheme"
|
||||
* app.onhostcontextchanged = (ctx) => {
|
||||
|
|
@ -518,21 +576,21 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link setNotificationHandler `setNotificationHandler`} for the underlying method
|
||||
* @deprecated Use {@link addEventListener `addEventListener("hostcontextchanged", handler)`} instead — it composes with other listeners and supports cleanup via {@link removeEventListener `removeEventListener`}.
|
||||
* @see {@link McpUiHostContextChangedNotification `McpUiHostContextChangedNotification`} for the notification structure
|
||||
* @see {@link McpUiHostContext `McpUiHostContext`} for the full context structure
|
||||
*/
|
||||
get onhostcontextchanged():
|
||||
| ((params: McpUiHostContextChangedNotification["params"]) => void)
|
||||
| undefined {
|
||||
return this.getEventHandler("hostcontextchanged");
|
||||
}
|
||||
set onhostcontextchanged(
|
||||
callback: (params: McpUiHostContextChangedNotification["params"]) => void,
|
||||
callback:
|
||||
| ((params: McpUiHostContextChangedNotification["params"]) => void)
|
||||
| undefined,
|
||||
) {
|
||||
this.setNotificationHandler(
|
||||
McpUiHostContextChangedNotificationSchema,
|
||||
(n) => {
|
||||
// Merge the partial update into the stored context
|
||||
this._hostContext = { ...this._hostContext, ...n.params };
|
||||
callback(n.params);
|
||||
},
|
||||
);
|
||||
this.setEventHandler("hostcontextchanged", callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -545,8 +603,7 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* 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.
|
||||
* Assigning replaces the previous handler; assigning `undefined` clears it.
|
||||
*
|
||||
* Register handlers before calling {@link connect `connect`} to avoid missing requests.
|
||||
*
|
||||
|
|
@ -563,18 +620,31 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link setRequestHandler `setRequestHandler`} for the underlying method
|
||||
* @see {@link McpUiResourceTeardownRequest `McpUiResourceTeardownRequest`} for the request structure
|
||||
*/
|
||||
private _onteardown?: (
|
||||
params: McpUiResourceTeardownRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => McpUiResourceTeardownResult | Promise<McpUiResourceTeardownResult>;
|
||||
get onteardown() {
|
||||
return this._onteardown;
|
||||
}
|
||||
set onteardown(
|
||||
callback: (
|
||||
params: McpUiResourceTeardownRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => McpUiResourceTeardownResult | Promise<McpUiResourceTeardownResult>,
|
||||
callback:
|
||||
| ((
|
||||
params: McpUiResourceTeardownRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => McpUiResourceTeardownResult | Promise<McpUiResourceTeardownResult>)
|
||||
| undefined,
|
||||
) {
|
||||
this.setRequestHandler(
|
||||
this.warnIfRequestHandlerReplaced("onteardown", this._onteardown, callback);
|
||||
this._onteardown = callback;
|
||||
this.replaceRequestHandler(
|
||||
McpUiResourceTeardownRequestSchema,
|
||||
(request, extra) => callback(request.params, extra),
|
||||
(request, extra) => {
|
||||
if (!this._onteardown) throw new Error("No onteardown handler set");
|
||||
return this._onteardown(request.params, extra);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -587,8 +657,7 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
*
|
||||
* 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.
|
||||
* Assigning replaces the previous handler; assigning `undefined` clears it.
|
||||
*
|
||||
* Register handlers before calling {@link connect `connect`} to avoid missing requests.
|
||||
*
|
||||
|
|
@ -606,18 +675,28 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
* throw new Error(`Unknown tool: ${params.name}`);
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link setRequestHandler `setRequestHandler`} for the underlying method
|
||||
*/
|
||||
private _oncalltool?: (
|
||||
params: CallToolRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<CallToolResult>;
|
||||
get oncalltool() {
|
||||
return this._oncalltool;
|
||||
}
|
||||
set oncalltool(
|
||||
callback: (
|
||||
params: CallToolRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<CallToolResult>,
|
||||
callback:
|
||||
| ((
|
||||
params: CallToolRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<CallToolResult>)
|
||||
| undefined,
|
||||
) {
|
||||
this.setRequestHandler(CallToolRequestSchema, (request, extra) =>
|
||||
callback(request.params, extra),
|
||||
);
|
||||
this.warnIfRequestHandlerReplaced("oncalltool", this._oncalltool, callback);
|
||||
this._oncalltool = callback;
|
||||
this.replaceRequestHandler(CallToolRequestSchema, (request, extra) => {
|
||||
if (!this._oncalltool) throw new Error("No oncalltool handler set");
|
||||
return this._oncalltool(request.params, extra);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -629,36 +708,54 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
*
|
||||
* 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.
|
||||
* Assigning replaces the previous handler; assigning `undefined` clears it.
|
||||
*
|
||||
* Register handlers before calling {@link connect `connect`} to avoid missing requests.
|
||||
*
|
||||
* @param callback - Async function that returns tool names as strings (simplified
|
||||
* from full `ListToolsResult` with `Tool` objects). Registration is always
|
||||
* allowed; capability validation occurs when handlers are invoked.
|
||||
* @param callback - Async function that returns a {@link ListToolsResult `ListToolsResult`}.
|
||||
* Registration is always allowed; capability validation occurs when handlers
|
||||
* are invoked.
|
||||
*
|
||||
* @example Return available tools
|
||||
* ```ts source="./app.examples.ts#App_onlisttools_returnTools"
|
||||
* app.onlisttools = async (params, extra) => {
|
||||
* return {
|
||||
* tools: ["greet", "calculate", "format"],
|
||||
* tools: [
|
||||
* { name: "greet", inputSchema: { type: "object" as const } },
|
||||
* { name: "calculate", inputSchema: { type: "object" as const } },
|
||||
* { name: "format", inputSchema: { type: "object" as const } },
|
||||
* ],
|
||||
* };
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link setRequestHandler `setRequestHandler`} for the underlying method
|
||||
* @see {@link oncalltool `oncalltool`} for handling tool execution
|
||||
*/
|
||||
private _onlisttools?: (
|
||||
params: ListToolsRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<ListToolsResult>;
|
||||
get onlisttools() {
|
||||
return this._onlisttools;
|
||||
}
|
||||
set onlisttools(
|
||||
callback: (
|
||||
params: ListToolsRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<{ tools: string[] }>,
|
||||
callback:
|
||||
| ((
|
||||
params: ListToolsRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<ListToolsResult>)
|
||||
| undefined,
|
||||
) {
|
||||
this.setRequestHandler(ListToolsRequestSchema, (request, extra) =>
|
||||
callback(request.params, extra),
|
||||
this.warnIfRequestHandlerReplaced(
|
||||
"onlisttools",
|
||||
this._onlisttools,
|
||||
callback,
|
||||
);
|
||||
this._onlisttools = callback;
|
||||
this.replaceRequestHandler(ListToolsRequestSchema, (request, extra) => {
|
||||
if (!this._onlisttools) throw new Error("No onlisttools handler set");
|
||||
return this._onlisttools(request.params, extra);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue