Skip to content

Commit b6da14c

Browse files
committed
update examples
1 parent adc73e4 commit b6da14c

File tree

3 files changed

+93
-47
lines changed

3 files changed

+93
-47
lines changed

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

Lines changed: 70 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
1-
import {
1+
/**
2+
* @file App that does NOT depend on Apps SDK runtime.
3+
*
4+
* The Raw UI example has no runtime dependency to the Apps SDK
5+
* but still imports its types for static type safety.
6+
* Types can be just stripped, e.g. w/ the command line:
7+
*
8+
* <code>
9+
* npx esbuild src/ui-raw.ts --bundle --outfile=dist/ui-raw.js --minify --sourcemap --platform=browser
10+
* </code>
11+
*
12+
* We implement a barebones JSON-RPC message sender/receiver (see `app` object below),
13+
* but without timeouts or runtime type validation of any kind
14+
* (for that, use the Apps SDK / see ui-vanilla.ts or ui-react.ts).
15+
*/
16+
17+
import type {
218
McpUiInitializeRequest,
319
McpUiInitializeResult,
420
McpUiInitializedNotification,
@@ -11,26 +27,30 @@ import {
1127
McpUiOpenLinkRequest,
1228
McpUiOpenLinkResult,
1329
} from "@modelcontextprotocol/ext-apps";
14-
import {
30+
31+
import type {
1532
CallToolRequest,
1633
CallToolResult,
1734
JSONRPCMessage,
1835
LoggingMessageNotification,
1936
} from "@modelcontextprotocol/sdk/types.js";
2037

2138
const app = (() => {
39+
type Sendable = { method: string; params: any };
40+
2241
let nextId = 1;
42+
2343
return {
24-
sendRequest({ method, params }: { method: string; params: any }) {
44+
sendRequest<T extends Sendable, Result>({ method, params }: T) {
2545
const id = nextId++;
2646
window.parent.postMessage({ jsonrpc: "2.0", id, method, params }, "*");
27-
return new Promise((resolve, reject) => {
47+
return new Promise<Result>((resolve, reject) => {
2848
window.addEventListener("message", function listener(event) {
2949
const data: JSONRPCMessage = event.data;
3050
if (event.data?.id === id) {
3151
window.removeEventListener("message", listener);
3252
if (event.data?.result) {
33-
resolve(true);
53+
resolve(event.data.result as Result);
3454
} else if (event.data?.error) {
3555
reject(new Error(event.data.error));
3656
}
@@ -40,10 +60,13 @@ const app = (() => {
4060
});
4161
});
4262
},
43-
sendNotification({ method, params }: { method: string; params: any }) {
63+
sendNotification<T extends Sendable>({ method, params }: T) {
4464
window.parent.postMessage({ jsonrpc: "2.0", method, params }, "*");
4565
},
46-
onNotification(method: string, handler: (params: any) => void) {
66+
onNotification<T extends Sendable>(
67+
method: T["method"],
68+
handler: (params: T["params"]) => void,
69+
) {
4770
window.addEventListener("message", function listener(event) {
4871
if (event.data?.method === method) {
4972
handler(event.data.params);
@@ -69,35 +92,40 @@ window.addEventListener("load", async () => {
6992
{ style: "color: red;" },
7093
);
7194

72-
app.onNotification(
73-
"ui/notifications/tool-result" as McpUiToolResultNotification["method"],
74-
async (params: McpUiToolResultNotification["params"]) => {
75-
appendText(`Tool call result: ${JSON.stringify(params)}`);
95+
app.onNotification<McpUiToolInputNotification>(
96+
"ui/notifications/tool-input",
97+
async (params) => {
98+
appendText(`Tool call input: ${JSON.stringify(params)}`);
7699
},
77100
);
78-
app.onNotification(
79-
"ui/notifications/host-context-changed" as McpUiHostContextChangedNotification["method"],
80-
async (params: McpUiHostContextChangedNotification["params"]) => {
81-
appendText(`Host context changed: ${JSON.stringify(params)}`);
101+
app.onNotification<McpUiToolResultNotification>(
102+
"ui/notifications/tool-result",
103+
async (params) => {
104+
appendText(`Tool call result: ${JSON.stringify(params)}`);
82105
},
83106
);
84-
app.onNotification(
85-
"ui/notifications/tool-input" as McpUiToolInputNotification["method"],
86-
async (params: McpUiToolInputNotification["params"]) => {
87-
appendText(`Tool call input: ${JSON.stringify(params)}`);
107+
app.onNotification<McpUiHostContextChangedNotification>(
108+
"ui/notifications/host-context-changed",
109+
async (params) => {
110+
appendText(`Host context changed: ${JSON.stringify(params)}`);
88111
},
89112
);
90113

91-
const initializeResult = (await app.sendRequest(<McpUiInitializeRequest>{
114+
const initializeResult = await app.sendRequest<
115+
McpUiInitializeRequest,
116+
McpUiInitializeResult
117+
>({
92118
method: "ui/initialize",
93119
params: {
94120
appCapabilities: {},
95121
appInfo: { name: "My UI", version: "1.0.0" },
96122
protocolVersion: "2025-06-18",
97123
},
98-
})) as McpUiInitializeResult;
124+
});
125+
126+
appendText(`Initialize result: ${JSON.stringify(initializeResult)}`);
99127

100-
app.sendNotification(<McpUiInitializedNotification>{
128+
app.sendNotification<McpUiInitializedNotification>({
101129
method: "ui/notifications/initialized",
102130
params: {},
103131
});
@@ -116,7 +144,7 @@ window.addEventListener("load", async () => {
116144
(parseFloat(htmlStyle.borderTop) || 0) +
117145
(parseFloat(htmlStyle.borderBottom) || 0);
118146

119-
app.sendNotification(<McpUiSizeChangeNotification>{
147+
app.sendNotification<McpUiSizeChangeNotification>({
120148
method: "ui/notifications/size-change",
121149
params: { width, height },
122150
});
@@ -127,13 +155,15 @@ window.addEventListener("load", async () => {
127155
textContent: "Get Weather (Tool)",
128156
onclick: async () => {
129157
try {
130-
const result = (await app.sendRequest(<CallToolRequest>{
131-
method: "tools/call",
132-
params: {
133-
name: "get-weather",
134-
arguments: { location: "Tokyo" },
158+
const result = await app.sendRequest<CallToolRequest, CallToolResult>(
159+
{
160+
method: "tools/call",
161+
params: {
162+
name: "get-weather",
163+
arguments: { location: "Tokyo" },
164+
},
135165
},
136-
})) as CallToolResult;
166+
);
137167

138168
appendText(`Weather tool result: ${JSON.stringify(result)}`);
139169
} catch (e) {
@@ -147,7 +177,7 @@ window.addEventListener("load", async () => {
147177
Object.assign(document.createElement("button"), {
148178
textContent: "Notify Cart Updated",
149179
onclick: async () => {
150-
app.sendNotification(<LoggingMessageNotification>{
180+
app.sendNotification<LoggingMessageNotification>({
151181
method: "notifications/message",
152182
params: {
153183
level: "info",
@@ -163,7 +193,10 @@ window.addEventListener("load", async () => {
163193
textContent: "Prompt Weather in Tokyo",
164194
onclick: async () => {
165195
try {
166-
const { isError } = (await app.sendRequest(<McpUiMessageRequest>{
196+
const { isError } = await app.sendRequest<
197+
McpUiMessageRequest,
198+
McpUiMessageResult
199+
>({
167200
method: "ui/message",
168201
params: {
169202
role: "user",
@@ -174,7 +207,7 @@ window.addEventListener("load", async () => {
174207
},
175208
],
176209
},
177-
})) as McpUiMessageResult;
210+
});
178211

179212
appendText(`Message result: ${isError ? "error" : "success"}`);
180213
} catch (e) {
@@ -189,12 +222,15 @@ window.addEventListener("load", async () => {
189222
textContent: "Open Link to Google",
190223
onclick: async () => {
191224
try {
192-
const { isError } = (await app.sendRequest(<McpUiOpenLinkRequest>{
225+
const { isError } = await app.sendRequest<
226+
McpUiOpenLinkRequest,
227+
McpUiOpenLinkResult
228+
>({
193229
method: "ui/open-link",
194230
params: {
195231
url: "https://www.google.com",
196232
},
197-
})) as McpUiOpenLinkResult;
233+
});
198234
appendText(`Link result: ${isError ? "error" : "success"}`);
199235
} catch (e) {
200236
appendError(e);

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/**
2+
* @file App that demonstrates a few features using React + the Apps SDK.
3+
*/
14
import { useState, useCallback } from "react";
25
import { createRoot } from "react-dom/client";
36
import {
@@ -13,8 +16,8 @@ import type {
1316
const APP_INFO: Implementation = {
1417
name: "MCP UI React Example Client",
1518
version: "1.0.0",
16-
protocolVersion: "2025-06-18",
1719
};
20+
1821
export function McpClientApp() {
1922
const [toolResults, setToolResults] = useState<CallToolResult[]>([]);
2023
const [messages, setMessages] = useState<string[]>([]);

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

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1+
/**
2+
* @file Demonstrate a few Apps SDK features.
3+
*
4+
* The vanilla (no React) UI uses the Apps SDK.
5+
*
6+
* The Apps SDK offers advantages over the Raw UI example,
7+
* such as ability to set timeouts, strong runtime type validation
8+
* and simpler methods for each request/response interaction.
9+
*/
110
import {
211
App,
312
PostMessageTransport,
413
McpUiToolInputNotificationSchema,
5-
McpUiSizeChangeNotificationSchema,
614
McpUiToolResultNotificationSchema,
15+
McpUiHostContextChangedNotificationSchema,
716
} from "@modelcontextprotocol/ext-apps";
817

918
window.addEventListener("load", async () => {
@@ -28,27 +37,25 @@ window.addEventListener("load", async () => {
2837
});
2938

3039
app.setNotificationHandler(
31-
McpUiToolResultNotificationSchema,
32-
async ({ params: { content, structuredContent, isError } }) => {
40+
McpUiToolInputNotificationSchema,
41+
async ({ params }) => {
3342
appendText(
34-
`Tool call result received: isError=${isError}, content=${content}, structuredContent=${JSON.stringify(structuredContent)}`,
43+
`Tool call input received: ${JSON.stringify(params.arguments)}`,
3544
);
3645
},
3746
);
3847
app.setNotificationHandler(
39-
McpUiSizeChangeNotificationSchema,
40-
async ({ params: { width, height } }) => {
48+
McpUiToolResultNotificationSchema,
49+
async ({ params: { content, structuredContent, isError } }) => {
4150
appendText(
42-
`Size change notification received: width=${width}, height=${height}`,
51+
`Tool call result received: isError=${isError}, content=${content}, structuredContent=${JSON.stringify(structuredContent)}`,
4352
);
4453
},
4554
);
4655
app.setNotificationHandler(
47-
McpUiToolInputNotificationSchema,
48-
async ({ params }) => {
49-
appendText(
50-
`Tool call input received: ${JSON.stringify(params.arguments)}`,
51-
);
56+
McpUiHostContextChangedNotificationSchema,
57+
async (params) => {
58+
appendText(`Host context changed: ${JSON.stringify(params)}`);
5259
},
5360
);
5461

0 commit comments

Comments
 (0)