update examples
This commit is contained in:
parent
adc73e4fa6
commit
b6da14cefd
3 changed files with 93 additions and 47 deletions
|
|
@ -1,4 +1,20 @@
|
||||||
import {
|
/**
|
||||||
|
* @file App that does NOT depend on Apps SDK runtime.
|
||||||
|
*
|
||||||
|
* The Raw UI example has no runtime dependency to the Apps SDK
|
||||||
|
* but still imports its types for static type safety.
|
||||||
|
* Types can be just stripped, e.g. w/ the command line:
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* npx esbuild src/ui-raw.ts --bundle --outfile=dist/ui-raw.js --minify --sourcemap --platform=browser
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* We implement a barebones JSON-RPC message sender/receiver (see `app` object below),
|
||||||
|
* but without timeouts or runtime type validation of any kind
|
||||||
|
* (for that, use the Apps SDK / see ui-vanilla.ts or ui-react.ts).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {
|
||||||
McpUiInitializeRequest,
|
McpUiInitializeRequest,
|
||||||
McpUiInitializeResult,
|
McpUiInitializeResult,
|
||||||
McpUiInitializedNotification,
|
McpUiInitializedNotification,
|
||||||
|
|
@ -11,7 +27,8 @@ import {
|
||||||
McpUiOpenLinkRequest,
|
McpUiOpenLinkRequest,
|
||||||
McpUiOpenLinkResult,
|
McpUiOpenLinkResult,
|
||||||
} from "@modelcontextprotocol/ext-apps";
|
} from "@modelcontextprotocol/ext-apps";
|
||||||
import {
|
|
||||||
|
import type {
|
||||||
CallToolRequest,
|
CallToolRequest,
|
||||||
CallToolResult,
|
CallToolResult,
|
||||||
JSONRPCMessage,
|
JSONRPCMessage,
|
||||||
|
|
@ -19,18 +36,21 @@ import {
|
||||||
} from "@modelcontextprotocol/sdk/types.js";
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
|
||||||
const app = (() => {
|
const app = (() => {
|
||||||
|
type Sendable = { method: string; params: any };
|
||||||
|
|
||||||
let nextId = 1;
|
let nextId = 1;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sendRequest({ method, params }: { method: string; params: any }) {
|
sendRequest<T extends Sendable, Result>({ method, params }: T) {
|
||||||
const id = nextId++;
|
const id = nextId++;
|
||||||
window.parent.postMessage({ jsonrpc: "2.0", id, method, params }, "*");
|
window.parent.postMessage({ jsonrpc: "2.0", id, method, params }, "*");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise<Result>((resolve, reject) => {
|
||||||
window.addEventListener("message", function listener(event) {
|
window.addEventListener("message", function listener(event) {
|
||||||
const data: JSONRPCMessage = event.data;
|
const data: JSONRPCMessage = event.data;
|
||||||
if (event.data?.id === id) {
|
if (event.data?.id === id) {
|
||||||
window.removeEventListener("message", listener);
|
window.removeEventListener("message", listener);
|
||||||
if (event.data?.result) {
|
if (event.data?.result) {
|
||||||
resolve(true);
|
resolve(event.data.result as Result);
|
||||||
} else if (event.data?.error) {
|
} else if (event.data?.error) {
|
||||||
reject(new Error(event.data.error));
|
reject(new Error(event.data.error));
|
||||||
}
|
}
|
||||||
|
|
@ -40,10 +60,13 @@ const app = (() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
sendNotification({ method, params }: { method: string; params: any }) {
|
sendNotification<T extends Sendable>({ method, params }: T) {
|
||||||
window.parent.postMessage({ jsonrpc: "2.0", method, params }, "*");
|
window.parent.postMessage({ jsonrpc: "2.0", method, params }, "*");
|
||||||
},
|
},
|
||||||
onNotification(method: string, handler: (params: any) => void) {
|
onNotification<T extends Sendable>(
|
||||||
|
method: T["method"],
|
||||||
|
handler: (params: T["params"]) => void,
|
||||||
|
) {
|
||||||
window.addEventListener("message", function listener(event) {
|
window.addEventListener("message", function listener(event) {
|
||||||
if (event.data?.method === method) {
|
if (event.data?.method === method) {
|
||||||
handler(event.data.params);
|
handler(event.data.params);
|
||||||
|
|
@ -69,35 +92,40 @@ window.addEventListener("load", async () => {
|
||||||
{ style: "color: red;" },
|
{ style: "color: red;" },
|
||||||
);
|
);
|
||||||
|
|
||||||
app.onNotification(
|
app.onNotification<McpUiToolInputNotification>(
|
||||||
"ui/notifications/tool-result" as McpUiToolResultNotification["method"],
|
"ui/notifications/tool-input",
|
||||||
async (params: McpUiToolResultNotification["params"]) => {
|
async (params) => {
|
||||||
appendText(`Tool call result: ${JSON.stringify(params)}`);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
app.onNotification(
|
|
||||||
"ui/notifications/host-context-changed" as McpUiHostContextChangedNotification["method"],
|
|
||||||
async (params: McpUiHostContextChangedNotification["params"]) => {
|
|
||||||
appendText(`Host context changed: ${JSON.stringify(params)}`);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
app.onNotification(
|
|
||||||
"ui/notifications/tool-input" as McpUiToolInputNotification["method"],
|
|
||||||
async (params: McpUiToolInputNotification["params"]) => {
|
|
||||||
appendText(`Tool call input: ${JSON.stringify(params)}`);
|
appendText(`Tool call input: ${JSON.stringify(params)}`);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
app.onNotification<McpUiToolResultNotification>(
|
||||||
|
"ui/notifications/tool-result",
|
||||||
|
async (params) => {
|
||||||
|
appendText(`Tool call result: ${JSON.stringify(params)}`);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
app.onNotification<McpUiHostContextChangedNotification>(
|
||||||
|
"ui/notifications/host-context-changed",
|
||||||
|
async (params) => {
|
||||||
|
appendText(`Host context changed: ${JSON.stringify(params)}`);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const initializeResult = (await app.sendRequest(<McpUiInitializeRequest>{
|
const initializeResult = await app.sendRequest<
|
||||||
|
McpUiInitializeRequest,
|
||||||
|
McpUiInitializeResult
|
||||||
|
>({
|
||||||
method: "ui/initialize",
|
method: "ui/initialize",
|
||||||
params: {
|
params: {
|
||||||
appCapabilities: {},
|
appCapabilities: {},
|
||||||
appInfo: { name: "My UI", version: "1.0.0" },
|
appInfo: { name: "My UI", version: "1.0.0" },
|
||||||
protocolVersion: "2025-06-18",
|
protocolVersion: "2025-06-18",
|
||||||
},
|
},
|
||||||
})) as McpUiInitializeResult;
|
});
|
||||||
|
|
||||||
app.sendNotification(<McpUiInitializedNotification>{
|
appendText(`Initialize result: ${JSON.stringify(initializeResult)}`);
|
||||||
|
|
||||||
|
app.sendNotification<McpUiInitializedNotification>({
|
||||||
method: "ui/notifications/initialized",
|
method: "ui/notifications/initialized",
|
||||||
params: {},
|
params: {},
|
||||||
});
|
});
|
||||||
|
|
@ -116,7 +144,7 @@ window.addEventListener("load", async () => {
|
||||||
(parseFloat(htmlStyle.borderTop) || 0) +
|
(parseFloat(htmlStyle.borderTop) || 0) +
|
||||||
(parseFloat(htmlStyle.borderBottom) || 0);
|
(parseFloat(htmlStyle.borderBottom) || 0);
|
||||||
|
|
||||||
app.sendNotification(<McpUiSizeChangeNotification>{
|
app.sendNotification<McpUiSizeChangeNotification>({
|
||||||
method: "ui/notifications/size-change",
|
method: "ui/notifications/size-change",
|
||||||
params: { width, height },
|
params: { width, height },
|
||||||
});
|
});
|
||||||
|
|
@ -127,13 +155,15 @@ window.addEventListener("load", async () => {
|
||||||
textContent: "Get Weather (Tool)",
|
textContent: "Get Weather (Tool)",
|
||||||
onclick: async () => {
|
onclick: async () => {
|
||||||
try {
|
try {
|
||||||
const result = (await app.sendRequest(<CallToolRequest>{
|
const result = await app.sendRequest<CallToolRequest, CallToolResult>(
|
||||||
method: "tools/call",
|
{
|
||||||
params: {
|
method: "tools/call",
|
||||||
name: "get-weather",
|
params: {
|
||||||
arguments: { location: "Tokyo" },
|
name: "get-weather",
|
||||||
|
arguments: { location: "Tokyo" },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})) as CallToolResult;
|
);
|
||||||
|
|
||||||
appendText(`Weather tool result: ${JSON.stringify(result)}`);
|
appendText(`Weather tool result: ${JSON.stringify(result)}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -147,7 +177,7 @@ window.addEventListener("load", async () => {
|
||||||
Object.assign(document.createElement("button"), {
|
Object.assign(document.createElement("button"), {
|
||||||
textContent: "Notify Cart Updated",
|
textContent: "Notify Cart Updated",
|
||||||
onclick: async () => {
|
onclick: async () => {
|
||||||
app.sendNotification(<LoggingMessageNotification>{
|
app.sendNotification<LoggingMessageNotification>({
|
||||||
method: "notifications/message",
|
method: "notifications/message",
|
||||||
params: {
|
params: {
|
||||||
level: "info",
|
level: "info",
|
||||||
|
|
@ -163,7 +193,10 @@ window.addEventListener("load", async () => {
|
||||||
textContent: "Prompt Weather in Tokyo",
|
textContent: "Prompt Weather in Tokyo",
|
||||||
onclick: async () => {
|
onclick: async () => {
|
||||||
try {
|
try {
|
||||||
const { isError } = (await app.sendRequest(<McpUiMessageRequest>{
|
const { isError } = await app.sendRequest<
|
||||||
|
McpUiMessageRequest,
|
||||||
|
McpUiMessageResult
|
||||||
|
>({
|
||||||
method: "ui/message",
|
method: "ui/message",
|
||||||
params: {
|
params: {
|
||||||
role: "user",
|
role: "user",
|
||||||
|
|
@ -174,7 +207,7 @@ window.addEventListener("load", async () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})) as McpUiMessageResult;
|
});
|
||||||
|
|
||||||
appendText(`Message result: ${isError ? "error" : "success"}`);
|
appendText(`Message result: ${isError ? "error" : "success"}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -189,12 +222,15 @@ window.addEventListener("load", async () => {
|
||||||
textContent: "Open Link to Google",
|
textContent: "Open Link to Google",
|
||||||
onclick: async () => {
|
onclick: async () => {
|
||||||
try {
|
try {
|
||||||
const { isError } = (await app.sendRequest(<McpUiOpenLinkRequest>{
|
const { isError } = await app.sendRequest<
|
||||||
|
McpUiOpenLinkRequest,
|
||||||
|
McpUiOpenLinkResult
|
||||||
|
>({
|
||||||
method: "ui/open-link",
|
method: "ui/open-link",
|
||||||
params: {
|
params: {
|
||||||
url: "https://www.google.com",
|
url: "https://www.google.com",
|
||||||
},
|
},
|
||||||
})) as McpUiOpenLinkResult;
|
});
|
||||||
appendText(`Link result: ${isError ? "error" : "success"}`);
|
appendText(`Link result: ${isError ? "error" : "success"}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
appendError(e);
|
appendError(e);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
/**
|
||||||
|
* @file App that demonstrates a few features using React + the Apps SDK.
|
||||||
|
*/
|
||||||
import { useState, useCallback } from "react";
|
import { useState, useCallback } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import {
|
import {
|
||||||
|
|
@ -13,8 +16,8 @@ import type {
|
||||||
const APP_INFO: Implementation = {
|
const APP_INFO: Implementation = {
|
||||||
name: "MCP UI React Example Client",
|
name: "MCP UI React Example Client",
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
protocolVersion: "2025-06-18",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function McpClientApp() {
|
export function McpClientApp() {
|
||||||
const [toolResults, setToolResults] = useState<CallToolResult[]>([]);
|
const [toolResults, setToolResults] = useState<CallToolResult[]>([]);
|
||||||
const [messages, setMessages] = useState<string[]>([]);
|
const [messages, setMessages] = useState<string[]>([]);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,18 @@
|
||||||
|
/**
|
||||||
|
* @file Demonstrate a few Apps SDK features.
|
||||||
|
*
|
||||||
|
* The vanilla (no React) UI uses the Apps SDK.
|
||||||
|
*
|
||||||
|
* The Apps SDK offers advantages over the Raw UI example,
|
||||||
|
* such as ability to set timeouts, strong runtime type validation
|
||||||
|
* and simpler methods for each request/response interaction.
|
||||||
|
*/
|
||||||
import {
|
import {
|
||||||
App,
|
App,
|
||||||
PostMessageTransport,
|
PostMessageTransport,
|
||||||
McpUiToolInputNotificationSchema,
|
McpUiToolInputNotificationSchema,
|
||||||
McpUiSizeChangeNotificationSchema,
|
|
||||||
McpUiToolResultNotificationSchema,
|
McpUiToolResultNotificationSchema,
|
||||||
|
McpUiHostContextChangedNotificationSchema,
|
||||||
} from "@modelcontextprotocol/ext-apps";
|
} from "@modelcontextprotocol/ext-apps";
|
||||||
|
|
||||||
window.addEventListener("load", async () => {
|
window.addEventListener("load", async () => {
|
||||||
|
|
@ -27,6 +36,14 @@ window.addEventListener("load", async () => {
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.setNotificationHandler(
|
||||||
|
McpUiToolInputNotificationSchema,
|
||||||
|
async ({ params }) => {
|
||||||
|
appendText(
|
||||||
|
`Tool call input received: ${JSON.stringify(params.arguments)}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
app.setNotificationHandler(
|
app.setNotificationHandler(
|
||||||
McpUiToolResultNotificationSchema,
|
McpUiToolResultNotificationSchema,
|
||||||
async ({ params: { content, structuredContent, isError } }) => {
|
async ({ params: { content, structuredContent, isError } }) => {
|
||||||
|
|
@ -36,19 +53,9 @@ window.addEventListener("load", async () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
app.setNotificationHandler(
|
app.setNotificationHandler(
|
||||||
McpUiSizeChangeNotificationSchema,
|
McpUiHostContextChangedNotificationSchema,
|
||||||
async ({ params: { width, height } }) => {
|
async (params) => {
|
||||||
appendText(
|
appendText(`Host context changed: ${JSON.stringify(params)}`);
|
||||||
`Size change notification received: width=${width}, height=${height}`,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
app.setNotificationHandler(
|
|
||||||
McpUiToolInputNotificationSchema,
|
|
||||||
async ({ params }) => {
|
|
||||||
appendText(
|
|
||||||
`Tool call input received: ${JSON.stringify(params.arguments)}`,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue