Skip to content

Commit 39d84a9

Browse files
authored
Merge pull request #13 from modelcontextprotocol/ochafik/easier-notification-handlers
sdk: add notification handlers (App.ontoolinput, .ontoolresult, AppBridge.onsizechange...)
2 parents 535dae6 + ac38630 commit 39d84a9

File tree

6 files changed

+89
-45
lines changed

6 files changed

+89
-45
lines changed

examples/simple-server/src/ui-raw.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* <code>
99
* npx esbuild src/ui-raw.ts --bundle --outfile=dist/ui-raw.js --minify --sourcemap --platform=browser
1010
* </code>
11-
*
11+
*
1212
* We implement a barebones JSON-RPC message sender/receiver (see `app` object below),
1313
* but without timeouts or runtime type validation of any kind
1414
* (for that, use the Apps SDK / see ui-vanilla.ts or ui-react.ts).

examples/simple-server/src/ui-react.tsx

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,9 @@ export function McpClientApp() {
2626
appInfo: APP_INFO,
2727
capabilities: {},
2828
onAppCreated: (app) => {
29-
app.setNotificationHandler(
30-
McpUiToolResultNotificationSchema,
31-
async (notification) => {
32-
setToolResults((prev) => [...prev, notification.params]);
33-
},
34-
);
35-
app.setNotificationHandler(
36-
McpUiSizeChangeNotificationSchema,
37-
async (notification) => {
38-
document.body.style.width = `${notification.params.width}px`;
39-
document.body.style.height = `${notification.params.height}px`;
40-
},
41-
);
29+
app.ontoolresult = async (params) => {
30+
setToolResults((prev) => [...prev, params]);
31+
};
4232
},
4333
});
4434

examples/simple-server/src/ui-vanilla.ts

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,28 +36,17 @@ window.addEventListener("load", async () => {
3636
version: "1.0.0",
3737
});
3838

39-
app.setNotificationHandler(
40-
McpUiToolInputNotificationSchema,
41-
async ({ params }) => {
42-
appendText(
43-
`Tool call input received: ${JSON.stringify(params.arguments)}`,
44-
);
45-
},
46-
);
47-
app.setNotificationHandler(
48-
McpUiToolResultNotificationSchema,
49-
async ({ params: { content, structuredContent, isError } }) => {
50-
appendText(
51-
`Tool call result received: isError=${isError}, content=${content}, structuredContent=${JSON.stringify(structuredContent)}`,
52-
);
53-
},
54-
);
55-
app.setNotificationHandler(
56-
McpUiHostContextChangedNotificationSchema,
57-
async (params) => {
58-
appendText(`Host context changed: ${JSON.stringify(params)}`);
59-
},
60-
);
39+
app.ontoolinput = (params) => {
40+
appendText(`Tool call input received: ${JSON.stringify(params.arguments)}`);
41+
};
42+
app.ontoolresult = ({ content, structuredContent, isError }) => {
43+
appendText(
44+
`Tool call result received: isError=${isError}, content=${content}, structuredContent=${JSON.stringify(structuredContent)}`,
45+
);
46+
};
47+
app.onhostcontextchanged = (params) => {
48+
appendText(`Host context changed: ${JSON.stringify(params)}`);
49+
};
6150

6251
document.body.addEventListener("resize", () => {
6352
app.sendSizeChange({

src/app-bridge.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ListResourceTemplatesRequestSchema,
1414
ListResourceTemplatesResultSchema,
1515
Notification,
16+
PingRequest,
1617
PingRequestSchema,
1718
PromptListChangedNotificationSchema,
1819
ResourceListChangedNotificationSchema,
@@ -34,12 +35,16 @@ import {
3435
LATEST_PROTOCOL_VERSION,
3536
McpUiAppCapabilities,
3637
McpUiHostCapabilities,
38+
McpUiInitializedNotification,
3739
McpUiInitializedNotificationSchema,
3840
McpUiInitializeRequest,
3941
McpUiInitializeRequestSchema,
4042
McpUiInitializeResult,
4143
McpUiResourceTeardownRequest,
4244
McpUiResourceTeardownResultSchema,
45+
McpUiSandboxProxyReadyNotification,
46+
McpUiSandboxProxyReadyNotificationSchema,
47+
McpUiSizeChangeNotificationSchema,
4348
} from "./types";
4449
export * from "./types";
4550
export { PostMessageTransport } from "./message-transport";
@@ -48,9 +53,11 @@ type HostOptions = ProtocolOptions;
4853

4954
export const SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION];
5055

51-
export class AppBridge extends Protocol<Request, Notification, Result> {
52-
oninitialized?: () => void;
56+
type RequestExtra = Parameters<
57+
Parameters<AppBridge["setRequestHandler"]>[1]
58+
>[1];
5359

60+
export class AppBridge extends Protocol<Request, Notification, Result> {
5461
private _appCapabilities?: McpUiAppCapabilities;
5562

5663
constructor(
@@ -64,16 +71,37 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
6471
this.setRequestHandler(McpUiInitializeRequestSchema, (request) =>
6572
this._oninitialize(request),
6673
);
67-
this.setNotificationHandler(McpUiInitializedNotificationSchema, () =>
68-
this.oninitialized?.(),
69-
);
7074

71-
this.setRequestHandler(PingRequestSchema, (request) => {
72-
console.log("Received ping:", request.params);
75+
this.setRequestHandler(PingRequestSchema, (request, extra) => {
76+
this.onping?.(request.params, extra);
7377
return {};
7478
});
7579
}
7680

81+
onping?: (params: PingRequest["params"], extra: RequestExtra) => void;
82+
83+
set onsizechange(
84+
callback: (params: McpUiSizeChangeNotification["params"]) => void,
85+
) {
86+
this.setNotificationHandler(McpUiSizeChangeNotificationSchema, (n) =>
87+
callback(n.params),
88+
);
89+
}
90+
set onsandboxready(
91+
callback: (params: McpUiSandboxProxyReadyNotification["params"]) => void,
92+
) {
93+
this.setNotificationHandler(McpUiSandboxProxyReadyNotificationSchema, (n) =>
94+
callback(n.params),
95+
);
96+
}
97+
set oninitialized(
98+
callback: (params: McpUiInitializedNotification["params"]) => void,
99+
) {
100+
this.setNotificationHandler(McpUiInitializedNotificationSchema, (n) =>
101+
callback(n.params),
102+
);
103+
}
104+
77105
assertCapabilityForMethod(method: Request["method"]): void {
78106
// TODO
79107
}

src/app.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
LATEST_PROTOCOL_VERSION,
2020
McpUiAppCapabilities,
2121
McpUiHostCapabilities,
22+
McpUiHostContextChangedNotification,
23+
McpUiHostContextChangedNotificationSchema,
2224
McpUiInitializedNotification,
2325
McpUiInitializeRequest,
2426
McpUiInitializeResultSchema,
@@ -27,6 +29,12 @@ import {
2729
McpUiOpenLinkRequest,
2830
McpUiOpenLinkResultSchema,
2931
McpUiSizeChangeNotification,
32+
McpUiToolInputNotification,
33+
McpUiToolInputNotificationSchema,
34+
McpUiToolInputPartialNotification,
35+
McpUiToolInputPartialNotificationSchema,
36+
McpUiToolResultNotification,
37+
McpUiToolResultNotificationSchema,
3038
} from "./types";
3139
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
3240

@@ -54,6 +62,36 @@ export class App extends Protocol<Request, Notification, Result> {
5462
});
5563
}
5664

65+
set ontoolinput(
66+
callback: (params: McpUiToolInputNotification["params"]) => void,
67+
) {
68+
this.setNotificationHandler(McpUiToolInputNotificationSchema, (n) =>
69+
callback(n.params),
70+
);
71+
}
72+
set ontoolinputpartial(
73+
callback: (params: McpUiToolInputPartialNotification["params"]) => void,
74+
) {
75+
this.setNotificationHandler(McpUiToolInputPartialNotificationSchema, (n) =>
76+
callback(n.params),
77+
);
78+
}
79+
set ontoolresult(
80+
callback: (params: McpUiToolResultNotification["params"]) => void,
81+
) {
82+
this.setNotificationHandler(McpUiToolResultNotificationSchema, (n) =>
83+
callback(n.params),
84+
);
85+
}
86+
set onhostcontextchanged(
87+
callback: (params: McpUiHostContextChangedNotification["params"]) => void,
88+
) {
89+
this.setNotificationHandler(
90+
McpUiHostContextChangedNotificationSchema,
91+
(n) => callback(n.params),
92+
);
93+
}
94+
5795
assertCapabilityForMethod(method: Request["method"]): void {
5896
// TODO
5997
}

src/react/useApp.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ export interface UseAppOptions {
99
capabilities: McpUiAppCapabilities;
1010
/**
1111
* Called after client is created but before connection.
12-
* Use this to register request/notification handlers via
13-
* client.setRequestHandler() and client.setNotificationHandler().
12+
* Use this to register handlers via app.ontoolinput, app.toolresult, etc.
1413
*/
1514
onAppCreated?: (app: App) => void;
1615
}

0 commit comments

Comments
 (0)