/** * @file App that demonstrates a few features using MCP Apps SDK + Solid. */ import { App, applyDocumentTheme, applyHostFonts, applyHostStyleVariables, type McpUiHostContext, } from "@modelcontextprotocol/ext-apps"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { createEffect, createSignal, onMount, Show } from "solid-js"; import { render } from "solid-js/web"; import styles from "./mcp-app.module.css"; function extractTime(callToolResult: CallToolResult): string { const { text } = callToolResult.content?.find((c) => c.type === "text")!; return text; } function GetTimeApp() { const [app, setApp] = createSignal(null); const [error, setError] = createSignal(null); const [toolResult, setToolResult] = createSignal(null); const [hostContext, setHostContext] = createSignal(); // Apply host styles reactively when hostContext changes createEffect(() => { const ctx = hostContext(); if (ctx?.theme) { applyDocumentTheme(ctx.theme); } if (ctx?.styles?.variables) { applyHostStyleVariables(ctx.styles.variables); } if (ctx?.styles?.css?.fonts) { applyHostFonts(ctx.styles.css.fonts); } }); onMount(async () => { const instance = new App({ name: "Get Time App", version: "1.0.0" }); instance.ontoolinput = async (input) => { console.info("Received tool call input:", input); }; instance.ontoolresult = async (result) => { console.info("Received tool call result:", result); setToolResult(result); }; instance.ontoolcancelled = (params) => { console.info("Tool call cancelled:", params.reason); }; instance.onerror = console.error; instance.onhostcontextchanged = (params) => { setHostContext((prev) => ({ ...prev, ...params })); }; try { await instance.connect(); setApp(instance); setHostContext(instance.getHostContext()); } catch (e) { setError(e as Error); } }); return ( ERROR: {error()!.message}}> Connecting...}> ); } interface GetTimeAppInnerProps { app: App; toolResult: CallToolResult | null; hostContext?: McpUiHostContext; } function GetTimeAppInner(props: GetTimeAppInnerProps) { const [serverTime, setServerTime] = createSignal("Loading..."); const [messageText, setMessageText] = createSignal("This is message text."); const [logText, setLogText] = createSignal("This is log text."); const [linkUrl, setLinkUrl] = createSignal("https://modelcontextprotocol.io/"); // Update serverTime when toolResult changes createEffect(() => { if (props.toolResult) { setServerTime(extractTime(props.toolResult)); } }); async function handleGetTime() { try { console.info("Calling get-time tool..."); const result = await props.app.callServerTool({ name: "get-time", arguments: {} }); console.info("get-time result:", result); setServerTime(extractTime(result)); } catch (e) { console.error(e); setServerTime("[ERROR]"); } } async function handleSendMessage() { const signal = AbortSignal.timeout(5000); try { console.info("Sending message text to Host:", messageText()); const { isError } = await props.app.sendMessage( { role: "user", content: [{ type: "text", text: messageText() }] }, { signal }, ); console.info("Message", isError ? "rejected" : "accepted"); } catch (e) { console.error("Message send error:", signal.aborted ? "timed out" : e); } } async function handleSendLog() { console.info("Sending log text to Host:", logText()); await props.app.sendLog({ level: "info", data: logText() }); } async function handleOpenLink() { console.info("Sending open link request to Host:", linkUrl()); const { isError } = await props.app.openLink({ url: linkUrl() }); console.info("Open link request", isError ? "rejected" : "accepted"); } return (

Watch activity in the DevTools console!

Server Time: {serverTime()}