40 lines
35 KiB
Plaintext
40 lines
35 KiB
Plaintext
import{useEffect as nQ,useState as r}from"react";import{Protocol as lQ}from"@modelcontextprotocol/client";import{EmptyResultSchema as pQ}from"@modelcontextprotocol/core";var $Q="ui/resourceUri",KQ="text/html;profile=mcp-app";class j{_eventSlots=new Map;setHandler(X,Y){let Z=this._ensureEventSlot(X);if(Z.onHandler&&Y)console.warn(`[MCP Apps] on${String(X)} handler replaced. Use addEventListener("${String(X)}", …) to add multiple listeners without replacing.`);Z.onHandler=Y}getHandler(X){return this._eventSlots.get(X)?.onHandler}addEventListener(X,Y){this._ensureEventSlot(X).listeners.push(Y)}removeEventListener(X,Y){let Z=this._eventSlots.get(X);if(!Z)return;let $=Z.listeners.indexOf(Y);if($!==-1)Z.listeners.splice($,1)}dispatch(X,Y){let Z=this._eventSlots.get(X);if(!Z)return;Z.onHandler?.(Y);for(let $ of[...Z.listeners])$(Y)}_ensureEventSlot(X){let Y=this._eventSlots.get(X);if(!Y)Y={listeners:[]},this._eventSlots.set(X,Y);return Y}}class M{_methods=new Set;claim(X,Y){if(this._methods.has(X))throw Error(`Handler for "${X}" already registered (via ${Y}). Use addEventListener() to attach multiple listeners, or the on* setter for replace semantics.`);this._methods.add(X)}replace(X){this._methods.add(X)}release(X){this._methods.delete(X)}has(X){return this._methods.has(X)}}var i=new Set;function H(X,Y){let[Z,$]=Y,K=Z?.shape;if(typeof K?.method?.value!=="string")return;let J=K.method.value,G=X==="request"?"setRequestHandler":"setNotificationHandler";if(!i.has(G))i.add(G),console.warn(`[ext-apps] ${G}(Schema, handler) is deprecated; use ${G}("${J}", { params: Schema.shape.params }, handler).`);let W=$;return[J,{params:K.params},X==="request"?(L,N)=>W({method:J,params:L},JQ(N)):(L)=>W({method:J,params:L})]}function JQ(X){return{signal:X.mcpReq.signal,requestId:X.mcpReq.id,sessionId:X.sessionId,_meta:X.mcpReq._meta,sendRequest:X.mcpReq.send,sendNotification:X.mcpReq.notify,authInfo:X.http?.authInfo}}import{JSONRPCMessageSchema as zQ}from"@modelcontextprotocol/core";var T="2026-01-26",GQ="ui/open-link",WQ="ui/download-file",LQ="ui/message",NQ="ui/notifications/sandbox-proxy-ready",BQ="ui/notifications/sandbox-resource-ready",VQ="ui/notifications/size-changed",_Q="ui/notifications/tool-input",A="ui/notifications/tool-input-partial",qQ="ui/notifications/tool-result",OQ="ui/notifications/tool-cancelled",PQ="ui/notifications/host-context-changed",jQ="ui/notifications/request-teardown",DQ="ui/resource-teardown",FQ="ui/initialize",EQ="ui/notifications/initialized",RQ="ui/request-display-mode";class V{eventTarget;eventSource;messageListener;constructor(X=window.parent,Y){this.eventTarget=X;this.eventSource=Y;this.messageListener=(Z)=>{if(Y&&Z.source!==this.eventSource){console.debug("Ignoring message from unknown source",Z);return}let $=zQ.safeParse(Z.data);if($.success)console.debug("Parsed message",$.data),this.onmessage?.($.data);else if(Z.data?.jsonrpc!=="2.0")console.debug("Ignoring non-JSON-RPC message",$.error.message,Z);else console.error("Failed to parse message",$.error.message,Z),this.onerror?.(Error("Invalid JSON-RPC message received: "+$.error.message))}}async start(){window.addEventListener("message",this.messageListener)}async send(X,Y){if(X.method!==A)console.debug("Sending message",X);this.eventTarget.postMessage(X,"*")}async close(){window.removeEventListener("message",this.messageListener),this.onclose?.()}onclose;onerror;onmessage;sessionId;setProtocolVersion}import{z as Q}from"zod/v4";import{ContentBlockSchema as n,CallToolResultSchema as IQ,EmbeddedResourceSchema as UQ,ImplementationSchema as o,RequestIdSchema as wQ,ResourceLinkSchema as MQ,ToolSchema as HQ}from"@modelcontextprotocol/core";var s=Q.union([Q.literal("light"),Q.literal("dark")]).describe("Color theme preference for the host environment."),_=Q.union([Q.literal("inline"),Q.literal("fullscreen"),Q.literal("pip")]).describe("Display mode for UI presentation."),TQ=Q.union([Q.literal("--color-background-primary"),Q.literal("--color-background-secondary"),Q.literal("--color-background-tertiary"),Q.literal("--color-background-inverse"),Q.literal("--color-background-ghost"),Q.literal("--color-background-info"),Q.literal("--color-background-danger"),Q.literal("--color-background-success"),Q.literal("--color-background-warning"),Q.literal("--color-background-disabled"),Q.literal("--color-text-primary"),Q.literal("--color-text-secondary"),Q.literal("--color-text-tertiary"),Q.literal("--color-text-inverse"),Q.literal("--color-text-ghost"),Q.literal("--color-text-info"),Q.literal("--color-text-danger"),Q.literal("--color-text-success"),Q.literal("--color-text-warning"),Q.literal("--color-text-disabled"),Q.literal("--color-border-primary"),Q.literal("--color-border-secondary"),Q.literal("--color-border-tertiary"),Q.literal("--color-border-inverse"),Q.literal("--color-border-ghost"),Q.literal("--color-border-info"),Q.literal("--color-border-danger"),Q.literal("--color-border-success"),Q.literal("--color-border-warning"),Q.literal("--color-border-disabled"),Q.literal("--color-ring-primary"),Q.literal("--color-ring-secondary"),Q.literal("--color-ring-inverse"),Q.literal("--color-ring-info"),Q.literal("--color-ring-danger"),Q.literal("--color-ring-success"),Q.literal("--color-ring-warning"),Q.literal("--font-sans"),Q.literal("--font-mono"),Q.literal("--font-weight-normal"),Q.literal("--font-weight-medium"),Q.literal("--font-weight-semibold"),Q.literal("--font-weight-bold"),Q.literal("--font-text-xs-size"),Q.literal("--font-text-sm-size"),Q.literal("--font-text-md-size"),Q.literal("--font-text-lg-size"),Q.literal("--font-heading-xs-size"),Q.literal("--font-heading-sm-size"),Q.literal("--font-heading-md-size"),Q.literal("--font-heading-lg-size"),Q.literal("--font-heading-xl-size"),Q.literal("--font-heading-2xl-size"),Q.literal("--font-heading-3xl-size"),Q.literal("--font-text-xs-line-height"),Q.literal("--font-text-sm-line-height"),Q.literal("--font-text-md-line-height"),Q.literal("--font-text-lg-line-height"),Q.literal("--font-heading-xs-line-height"),Q.literal("--font-heading-sm-line-height"),Q.literal("--font-heading-md-line-height"),Q.literal("--font-heading-lg-line-height"),Q.literal("--font-heading-xl-line-height"),Q.literal("--font-heading-2xl-line-height"),Q.literal("--font-heading-3xl-line-height"),Q.literal("--border-radius-xs"),Q.literal("--border-radius-sm"),Q.literal("--border-radius-md"),Q.literal("--border-radius-lg"),Q.literal("--border-radius-xl"),Q.literal("--border-radius-full"),Q.literal("--border-width-regular"),Q.literal("--shadow-hairline"),Q.literal("--shadow-sm"),Q.literal("--shadow-md"),Q.literal("--shadow-lg")]).describe("CSS variable keys available to MCP apps for theming."),AQ=Q.record(TQ.describe(`Style variables for theming MCP apps.
|
|
|
|
Individual style keys are optional - hosts may provide any subset of these values.
|
|
Values are strings containing CSS values (colors, sizes, font stacks, etc.).
|
|
|
|
Note: This type uses \`Record<K, string | undefined>\` rather than \`Partial<Record<K, string>>\`
|
|
for compatibility with Zod schema generation. Both are functionally equivalent for validation.`),Q.union([Q.string(),Q.undefined()]).describe(`Style variables for theming MCP apps.
|
|
|
|
Individual style keys are optional - hosts may provide any subset of these values.
|
|
Values are strings containing CSS values (colors, sizes, font stacks, etc.).
|
|
|
|
Note: This type uses \`Record<K, string | undefined>\` rather than \`Partial<Record<K, string>>\`
|
|
for compatibility with Zod schema generation. Both are functionally equivalent for validation.`)).describe(`Style variables for theming MCP apps.
|
|
|
|
Individual style keys are optional - hosts may provide any subset of these values.
|
|
Values are strings containing CSS values (colors, sizes, font stacks, etc.).
|
|
|
|
Note: This type uses \`Record<K, string | undefined>\` rather than \`Partial<Record<K, string>>\`
|
|
for compatibility with Zod schema generation. Both are functionally equivalent for validation.`),kQ=Q.object({method:Q.literal("ui/open-link"),params:Q.object({url:Q.string().describe("URL to open in the host's browser")})}),S=Q.object({isError:Q.boolean().optional().describe("True if the host failed to open the URL (e.g., due to security policy).")}).passthrough(),C=Q.object({isError:Q.boolean().optional().describe("True if the download failed (e.g., user cancelled or host denied).")}).passthrough(),b=Q.object({isError:Q.boolean().optional().describe("True if the host rejected or failed to deliver the message.")}).passthrough(),SQ=Q.object({method:Q.literal("ui/notifications/sandbox-proxy-ready"),params:Q.object({})}),D=Q.object({connectDomains:Q.array(Q.string()).optional().describe(`Origins for network requests (fetch/XHR/WebSocket).
|
|
|
|
- Maps to CSP \`connect-src\` directive
|
|
- Empty or omitted → no network connections (secure default)`),resourceDomains:Q.array(Q.string()).optional().describe("Origins for static resources (images, scripts, stylesheets, fonts, media).\n\n- Maps to CSP `img-src`, `script-src`, `style-src`, `font-src`, `media-src` directives\n- Wildcard subdomains supported: `https://*.example.com`\n- Empty or omitted → no network resources (secure default)"),frameDomains:Q.array(Q.string()).optional().describe("Origins for nested iframes.\n\n- Maps to CSP `frame-src` directive\n- Empty or omitted → no nested iframes allowed (`frame-src 'none'`)"),baseUriDomains:Q.array(Q.string()).optional().describe("Allowed base URIs for the document.\n\n- Maps to CSP `base-uri` directive\n- Empty or omitted → only same origin allowed (`base-uri 'self'`)")}),F=Q.object({camera:Q.object({}).optional().describe("Request camera access.\n\nMaps to Permission Policy `camera` feature."),microphone:Q.object({}).optional().describe("Request microphone access.\n\nMaps to Permission Policy `microphone` feature."),geolocation:Q.object({}).optional().describe("Request geolocation access.\n\nMaps to Permission Policy `geolocation` feature."),clipboardWrite:Q.object({}).optional().describe("Request clipboard write access.\n\nMaps to Permission Policy `clipboard-write` feature.")}),CQ=Q.object({method:Q.literal("ui/notifications/size-changed"),params:Q.object({width:Q.number().optional().describe("New width in pixels."),height:Q.number().optional().describe("New height in pixels.")})}),x=Q.object({method:Q.literal("ui/notifications/tool-input"),params:Q.object({arguments:Q.record(Q.string(),Q.unknown().describe("Complete tool call arguments as key-value pairs.")).optional().describe("Complete tool call arguments as key-value pairs.")})}),v=Q.object({method:Q.literal("ui/notifications/tool-input-partial"),params:Q.object({arguments:Q.record(Q.string(),Q.unknown().describe("Partial tool call arguments (incomplete, may change).")).optional().describe("Partial tool call arguments (incomplete, may change).")})}),g=Q.object({method:Q.literal("ui/notifications/tool-cancelled"),params:Q.object({reason:Q.string().optional().describe('Optional reason for the cancellation (e.g., "user action", "timeout").')})}),a=Q.object({fonts:Q.string().optional()}),t=Q.object({variables:AQ.optional().describe("CSS variables for theming the app."),css:a.optional().describe("CSS blocks that apps can inject.")}),y=Q.object({method:Q.literal("ui/resource-teardown"),params:Q.object({})}),f=Q.record(Q.string(),Q.unknown()),k=Q.object({text:Q.object({}).optional().describe("Host supports text content blocks."),image:Q.object({}).optional().describe("Host supports image content blocks."),audio:Q.object({}).optional().describe("Host supports audio content blocks."),resource:Q.object({}).optional().describe("Host supports resource content blocks."),resourceLink:Q.object({}).optional().describe("Host supports resource link content blocks."),structuredContent:Q.object({}).optional().describe("Host supports structured content.")}),bQ=Q.object({method:Q.literal("ui/notifications/request-teardown"),params:Q.object({}).optional()}),e=Q.object({experimental:Q.record(Q.string(),Q.record(Q.string(),Q.any()).describe("Experimental features keyed by identifier.")).optional().describe("Experimental features keyed by identifier."),openLinks:Q.object({}).optional().describe("Host supports opening external URLs."),downloadFile:Q.object({}).optional().describe("Host supports file downloads via ui/download-file."),serverTools:Q.object({listChanged:Q.boolean().optional().describe("Host supports tools/list_changed notifications.")}).optional().describe("Host can proxy tool calls to the MCP server."),serverResources:Q.object({listChanged:Q.boolean().optional().describe("Host supports resources/list_changed notifications.")}).optional().describe("Host can proxy resource reads to the MCP server."),logging:Q.object({}).optional().describe("Host accepts log messages."),sandbox:Q.object({permissions:F.optional().describe("Permissions granted by the host (camera, microphone, geolocation)."),csp:D.optional().describe("CSP domains approved by the host.")}).optional().describe("Sandbox configuration applied by the host."),updateModelContext:k.optional().describe("Host accepts context updates (ui/update-model-context) to be included in the model's context for future turns."),message:k.optional().describe("Host supports receiving content messages (ui/message) from the view."),sampling:Q.object({tools:Q.object({}).optional().describe("Host supports tool use via `tools` and `toolChoice` parameters.")}).optional().describe("Host supports LLM sampling (sampling/createMessage) from the view.\nMirrors the MCP `ClientCapabilities.sampling` shape so hosts can pass it through.")}),QQ=Q.object({experimental:Q.record(Q.string(),Q.record(Q.string(),Q.any()).describe("Experimental features keyed by identifier.")).optional().describe("Experimental features keyed by identifier."),tools:Q.object({listChanged:Q.boolean().optional().describe("App supports tools/list_changed notifications.")}).optional().describe("App exposes MCP-style tools that the host can call."),availableDisplayModes:Q.array(_).optional().describe("Display modes the app supports.")}),xQ=Q.object({method:Q.literal("ui/notifications/initialized"),params:Q.object({}).optional()}),vQ=Q.object({csp:D.optional().describe("Content Security Policy configuration for UI resources."),permissions:F.optional().describe("Sandbox permissions requested by the UI resource."),domain:Q.string().optional().describe(`Dedicated origin for view sandbox.
|
|
|
|
Useful when views need stable, dedicated origins for OAuth callbacks, CORS policies, or API key allowlists.
|
|
|
|
**Host-dependent:** The format and validation rules for this field are determined by each host. Servers MUST consult host-specific documentation for the expected domain format. Common patterns include:
|
|
- Hash-based subdomains (e.g., \`{hash}.claudemcpcontent.com\`)
|
|
- URL-derived subdomains (e.g., \`www-example-com.oaiusercontent.com\`)
|
|
|
|
If omitted, host uses default sandbox origin (typically per-conversation).`),prefersBorder:Q.boolean().optional().describe(`Visual boundary preference - true if view prefers a visible border.
|
|
|
|
Boolean requesting whether a visible border and background is provided by the host. Specifying an explicit value for this is recommended because hosts' defaults may vary.
|
|
|
|
- \`true\`: request visible border + background
|
|
- \`false\`: request no visible border + background
|
|
- omitted: host decides border`)}),gQ=Q.object({method:Q.literal("ui/request-display-mode"),params:Q.object({mode:_.describe("The display mode being requested.")})}),u=Q.object({mode:_.describe("The display mode that was actually set. May differ from requested if not supported.")}).passthrough(),XQ=Q.union([Q.literal("model"),Q.literal("app")]).describe("Tool visibility scope - who can access the tool."),yQ=Q.object({resourceUri:Q.string().optional(),visibility:Q.array(XQ).optional().describe(`Who can access this tool. Default: ["model", "app"]
|
|
- "model": Tool visible to and callable by the agent
|
|
- "app": Tool callable by the app from this server only`),csp:Q.never().optional(),permissions:Q.never().optional()}),LX=Q.object({mimeTypes:Q.array(Q.string()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.')}),fQ=Q.object({method:Q.literal("ui/download-file"),params:Q.object({contents:Q.array(Q.union([UQ,MQ])).describe("Resource contents to download — embedded (inline data) or linked (host fetches). Uses standard MCP resource types.")})}),uQ=Q.object({method:Q.literal("ui/message"),params:Q.object({role:Q.literal("user").describe('Message role, currently only "user" is supported.'),content:Q.array(n).describe("Message content blocks (text, image, etc.).")})}),dQ=Q.object({method:Q.literal("ui/notifications/sandbox-resource-ready"),params:Q.object({html:Q.string().describe("HTML content to load into the inner iframe."),sandbox:Q.string().optional().describe("Optional override for the inner iframe's sandbox attribute."),csp:D.optional().describe("CSP configuration from resource metadata."),permissions:F.optional().describe("Sandbox permissions from resource metadata.")})}),d=Q.object({method:Q.literal("ui/notifications/tool-result"),params:IQ.describe("Standard MCP tool execution result.")}),h=Q.object({toolInfo:Q.object({id:wQ.optional().describe("JSON-RPC id of the tools/call request."),tool:HQ.describe("Tool definition including name, inputSchema, etc.")}).optional().describe("Metadata of the tool call that instantiated this App."),theme:s.optional().describe("Current color theme preference."),styles:t.optional().describe("Style configuration for theming the app."),displayMode:_.optional().describe("How the UI is currently displayed."),availableDisplayModes:Q.array(_).optional().describe("Display modes the host supports."),containerDimensions:Q.union([Q.object({height:Q.number().describe("Fixed container height in pixels.")}),Q.object({maxHeight:Q.union([Q.number(),Q.undefined()]).optional().describe("Maximum container height in pixels.")})]).and(Q.union([Q.object({width:Q.number().describe("Fixed container width in pixels.")}),Q.object({maxWidth:Q.union([Q.number(),Q.undefined()]).optional().describe("Maximum container width in pixels.")})])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
|
|
container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:Q.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:Q.string().optional().describe("User's timezone in IANA format."),userAgent:Q.string().optional().describe("Host application identifier."),platform:Q.union([Q.literal("web"),Q.literal("desktop"),Q.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:Q.object({touch:Q.boolean().optional().describe("Whether the device supports touch input."),hover:Q.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:Q.object({top:Q.number().describe("Top safe area inset in pixels."),right:Q.number().describe("Right safe area inset in pixels."),bottom:Q.number().describe("Bottom safe area inset in pixels."),left:Q.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),m=Q.object({method:Q.literal("ui/notifications/host-context-changed"),params:h.describe("Partial context update containing only changed fields.")}),hQ=Q.object({method:Q.literal("ui/update-model-context"),params:Q.object({content:Q.array(n).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:Q.record(Q.string(),Q.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})}),mQ=Q.object({method:Q.literal("ui/initialize"),params:Q.object({appInfo:o.describe("App identification (name and version)."),appCapabilities:QQ.describe("Features and capabilities this app provides."),protocolVersion:Q.string().describe("Protocol version this app supports.")})}),c=Q.object({protocolVersion:Q.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:o.describe("Host application identification and version."),hostCapabilities:e.describe("Features and capabilities provided by the host."),hostContext:h.describe("Rich context about the host environment.")}).passthrough();var cQ={target:"draft-2020-12"};async function l(X,Y){let Z=X["~standard"];if(Z.jsonSchema)return Z.jsonSchema[Y](cQ);throw Error(`Schema (vendor: ${Z.vendor}) does not implement Standard JSON Schema (~standard.jsonSchema). Use a library that does (zod v4, ArkType, Valibot) or wrap your schema accordingly.`)}async function p(X,Y,Z=""){let $=await X["~standard"].validate(Y);if($.issues){let K=$.issues.map((J)=>{let G=J.path?.map((W)=>typeof W==="object"?W.key:W).join(".");return G?`${G}: ${J.message}`:J.message}).join("; ");throw Error(Z+K)}return $.value}import{z as rQ}from"zod/v4";function E(){let X=document.documentElement.getAttribute("data-theme");if(X==="dark"||X==="light")return X;return document.documentElement.classList.contains("dark")?"dark":"light"}function R(X){let Y=document.documentElement;Y.setAttribute("data-theme",X),Y.style.colorScheme=X}function z(X,Y=document.documentElement){for(let[Z,$]of Object.entries(X))if($!==void 0)Y.style.setProperty(Z,$)}function I(X){if(document.getElementById("__mcp-host-fonts"))return;let Z=document.createElement("style");Z.id="__mcp-host-fonts",Z.textContent=X,document.head.appendChild(Z)}function iQ(X,Y){let Z={...X,...Y};if(Y.experimental!==void 0)Z.experimental={...X.experimental,...Y.experimental};if(Y.tools!==void 0)Z.tools={...X.tools,...Y.tools};return Z}class U extends lQ{_appInfo;_appCapabilities;options;_hostCapabilities;_hostInfo;_hostContext;_registeredTools={};_initializedSent=!1;_registeredEvents=new Set;_events=new j;_methods=new M;setRequestHandler=(...X)=>{let[Y,...Z]=H("request",X)??X;this._methods.claim(Y,"setRequestHandler"),super.setRequestHandler.call(this,Y,...Z)};setNotificationHandler=(...X)=>{let[Y,...Z]=H("notification",X)??X;this._methods.claim(Y,"setNotificationHandler"),super.setNotificationHandler.call(this,Y,...Z)};removeRequestHandler=(X)=>{this._methods.release(X),super.removeRequestHandler(X)};removeNotificationHandler=(X)=>{this._methods.release(X),super.removeNotificationHandler(X)};replaceRequestHandler=(X,...Y)=>{this._methods.replace(X),super.setRequestHandler.call(this,X,...Y)};_assertInitialized(X){if(this._initializedSent)return;let Y=`[ext-apps] App.${X}() called before connect() completed the ui/initialize handshake. Await app.connect() before calling this method, or move data loading to an ontoolresult handler.`;if(this.options?.strict)throw Error(Y);console.warn(`${Y}. This will throw in a future release.`)}eventSchemas={toolinput:x,toolinputpartial:v,toolresult:d,toolcancelled:g,hostcontextchanged:m};static ONE_SHOT_EVENTS=new Set(["toolinput","toolinputpartial","toolresult","toolcancelled"]);_everHadListener=new Set;_assertHandlerTiming(X){if(!U.ONE_SHOT_EVENTS.has(X)||this._everHadListener.has(X))return;if(this._everHadListener.add(X),!this._initializedSent)return;let Y=`[ext-apps] "${String(X)}" handler registered after connect() completed the ui/initialize handshake. The host may have already sent this notification. Register handlers before calling app.connect().`;if(this.options?.strict)throw Error(Y);console.warn(Y)}setEventHandler(X,Y){if(Y)this._assertHandlerTiming(X);this._ensureEventSlot(X),this._events.setHandler(X,Y)}addEventListener(X,Y){this._assertHandlerTiming(X),this._ensureEventSlot(X),this._events.addEventListener(X,Y)}removeEventListener(X,Y){this._events.removeEventListener(X,Y)}_ensureEventSlot(X){if(this._registeredEvents.has(X))return;let Y=this.eventSchemas[X];if(!Y)throw Error(`Unknown event: ${String(X)}`);this.setNotificationHandler(Y.shape.method.value,{params:Y.shape.params},(Z)=>{let $=Z;if(X==="hostcontextchanged")this._hostContext={...this._hostContext,...$};this._events.dispatch(X,$)}),this._registeredEvents.add(X)}getEventHandler(X){return this._events.getHandler(X)}warnIfRequestHandlerReplaced(X,Y,Z){if(Y&&Z)console.warn(`[MCP Apps] ${X} handler replaced. Previous handler will no longer be called.`)}constructor(X,Y={},Z={autoResize:!0}){super(Z);this._appInfo=X;this._appCapabilities=Y;this.options=Z;if(this._methods.replace("notifications/cancelled"),this._methods.replace("notifications/progress"),!Z.allowUnsafeEval)rQ.config({jitless:!0});this.replaceRequestHandler("ping",($)=>{return console.log("Received ping:",$.params),{}}),this.setEventHandler("hostcontextchanged",void 0)}buildContext(X){return X}registerAppCapabilities(X){if(this.transport)throw Error("Cannot register capabilities after transport is established");this._appCapabilities=iQ(this._appCapabilities,X)}registerTool(X,Y,Z){if(this._registeredTools[X])throw Error(`Tool ${X} is already registered`);let $=this,K=()=>{if($._initializedSent&&$._appCapabilities.tools?.listChanged)$.sendToolListChanged()},J=Y.inputSchema!==void 0,G={title:Y.title,description:Y.description,inputSchema:Y.inputSchema,outputSchema:Y.outputSchema,annotations:Y.annotations,_meta:Y._meta,enabled:!0,enable(){this.enabled=!0,K()},disable(){this.enabled=!1,K()},update(W){Object.assign(this,W),K()},remove(){if($._registeredTools[X]!==G)return;delete $._registeredTools[X],K()},handler:async(W,L)=>{if(!G.enabled)throw Error(`Tool ${X} is disabled`);let N;if(J){let q=G.inputSchema,O=q?await p(q,W??{},`Invalid input for tool ${X}: `):W??{};N=await Z(O,L)}else N=await Z(L);if(G.outputSchema&&!N.isError)N.structuredContent=await p(G.outputSchema,N.structuredContent,`Invalid output for tool ${X}: `);return N}};if(this._registeredTools[X]=G,!this._appCapabilities.tools&&!this.transport)this.registerAppCapabilities({tools:{listChanged:!0}});return this.ensureToolHandlersInitialized(),K(),G}_toolHandlersInitialized=!1;ensureToolHandlersInitialized(){if(this._toolHandlersInitialized)return;this._toolHandlersInitialized=!0,this.oncalltool=async(X,Y)=>{let Z=this._registeredTools[X.name];if(!Z)throw Error(`Tool ${X.name} not found`);return Z.handler(X.arguments,Y)},this.onlisttools=async(X,Y)=>{return{tools:await Promise.all(Object.entries(this._registeredTools).filter(([$,K])=>K.enabled).map(async([$,K])=>{let J={name:$,title:K.title,description:K.description,inputSchema:K.inputSchema?await l(K.inputSchema,"input"):{type:"object",properties:{}}};if(K.outputSchema)J.outputSchema=await l(K.outputSchema,"output");if(K.annotations)J.annotations=K.annotations;if(K._meta)J._meta=K._meta;return J}))}}}async sendToolListChanged(X={}){this._assertInitialized("sendToolListChanged"),await this.notification({method:"notifications/tools/list_changed",params:X})}getHostCapabilities(){return this._hostCapabilities}getHostVersion(){return this._hostInfo}getHostContext(){return this._hostContext}get ontoolinput(){return this.getEventHandler("toolinput")}set ontoolinput(X){this.setEventHandler("toolinput",X)}get ontoolinputpartial(){return this.getEventHandler("toolinputpartial")}set ontoolinputpartial(X){this.setEventHandler("toolinputpartial",X)}get ontoolresult(){return this.getEventHandler("toolresult")}set ontoolresult(X){this.setEventHandler("toolresult",X)}get ontoolcancelled(){return this.getEventHandler("toolcancelled")}set ontoolcancelled(X){this.setEventHandler("toolcancelled",X)}get onhostcontextchanged(){return this.getEventHandler("hostcontextchanged")}set onhostcontextchanged(X){this.setEventHandler("hostcontextchanged",X)}_onteardown;get onteardown(){return this._onteardown}set onteardown(X){this.warnIfRequestHandlerReplaced("onteardown",this._onteardown,X),this._onteardown=X,this.replaceRequestHandler("ui/resource-teardown",{params:y.shape.params,result:f},(Y,Z)=>{if(!this._onteardown)throw Error("No onteardown handler set");return this._onteardown(Y,Z)})}_oncalltool;get oncalltool(){return this._oncalltool}set oncalltool(X){this.warnIfRequestHandlerReplaced("oncalltool",this._oncalltool,X),this._oncalltool=X,this.replaceRequestHandler("tools/call",(Y,Z)=>{if(!this._oncalltool)throw Error("No oncalltool handler set");return this._oncalltool(Y.params,Z)})}_onlisttools;get onlisttools(){return this._onlisttools}set onlisttools(X){this.warnIfRequestHandlerReplaced("onlisttools",this._onlisttools,X),this._onlisttools=X,this.replaceRequestHandler("tools/list",(Y,Z)=>{if(!this._onlisttools)throw Error("No onlisttools handler set");return this._onlisttools(Y.params,Z)})}assertCapabilityForMethod(X){switch(X){case"sampling/createMessage":if(!this._hostCapabilities?.sampling)throw Error(`Host does not support sampling (required for ${X})`);break}}assertRequestHandlerCapability(X){switch(X){case"tools/call":case"tools/list":if(!this._appCapabilities.tools)throw Error(`Client does not support tool capability (required for ${X})`);return;default:return}}assertNotificationCapability(X){}async callServerTool(X,Y){if(this._assertInitialized("callServerTool"),typeof X==="string")throw Error(`callServerTool() expects an object as its first argument, but received a string ("${X}"). Did you mean: callServerTool({ name: "${X}", arguments: { ... } })?`);return await this.request({method:"tools/call",params:X},{onprogress:()=>{},resetTimeoutOnProgress:!0,...Y})}async readServerResource(X,Y){return this._assertInitialized("readServerResource"),await this.request({method:"resources/read",params:X},Y)}async listServerResources(X,Y){return this._assertInitialized("listServerResources"),await this.request({method:"resources/list",params:X},Y)}async createSamplingMessage(X,Y){return this._assertInitialized("createSamplingMessage"),await this.request({method:"sampling/createMessage",params:X},Y)}sendMessage(X,Y){return this._assertInitialized("sendMessage"),this.request({method:"ui/message",params:X},b,Y)}sendLog(X){return this.notification({method:"notifications/message",params:X})}updateModelContext(X,Y){return this._assertInitialized("updateModelContext"),this.request({method:"ui/update-model-context",params:X},pQ,Y)}openLink(X,Y){return this._assertInitialized("openLink"),this.request({method:"ui/open-link",params:X},S,Y)}sendOpenLink=this.openLink;downloadFile(X,Y){return this._assertInitialized("downloadFile"),this.request({method:"ui/download-file",params:X},C,Y)}requestTeardown(X={}){return this.notification({method:"ui/notifications/request-teardown",params:X})}requestDisplayMode(X,Y){return this._assertInitialized("requestDisplayMode"),this.request({method:"ui/request-display-mode",params:X},u,Y)}sendSizeChanged(X){return this.notification({method:"ui/notifications/size-changed",params:X})}setupSizeChangedNotifications(){let X=!1,Y=0,Z=0,$=()=>{if(X)return;X=!0,requestAnimationFrame(()=>{X=!1;let J=document.documentElement,G=J.style.height;J.style.height="max-content";let W=Math.ceil(J.getBoundingClientRect().height);J.style.height=G;let L=Math.ceil(window.innerWidth);if(L!==Y||W!==Z)Y=L,Z=W,this.sendSizeChanged({width:L,height:W})})};$();let K=new ResizeObserver($);return K.observe(document.documentElement),K.observe(document.body),()=>K.disconnect()}async connect(X=new V(window.parent,window.parent),Y){if(this.transport)throw Error("App is already connected. Call close() before connecting again.");this._initializedSent=!1;try{await super.connect(X);let Z=await this.request({method:"ui/initialize",params:{appCapabilities:this._appCapabilities,appInfo:this._appInfo,protocolVersion:T}},c,Y);if(Z===void 0)throw Error(`Server sent invalid initialize result: ${Z}`);if(this._hostCapabilities=Z.hostCapabilities,this._hostInfo=Z.hostInfo,this._hostContext=Z.hostContext,await this.notification({method:"ui/notifications/initialized"}),this._initializedSent=!0,this.options?.autoResize)this.setupSizeChangedNotifications()}catch(Z){throw this.close(),Z}}}function QY({appInfo:X,capabilities:Y,onAppCreated:Z,autoResize:$=!0,strict:K}){let[J,G]=r(null),[W,L]=r(!1),[N,q]=r(null);return nQ(()=>{let O=!0,B;async function ZQ(){try{let P=new V(window.parent,window.parent);if(B=new U(X,Y,{autoResize:$,strict:K}),Z?.(B),await B.connect(P),!O){B.close();return}G(B),L(!0),q(null)}catch(P){if(O)G(null),L(!1),q(P instanceof Error?P:Error("Failed to connect"))}}return ZQ(),()=>{O=!1,B?.close()}},[]),{app:J,isConnected:W,error:N}}import{useEffect as oQ}from"react";function KY(X,Y){oQ(()=>{if(!X)return;return X.setupSizeChangedNotifications()},[X,Y])}import{useEffect as sQ,useState as aQ}from"react";function LY(){let[X,Y]=aQ(E);return sQ(()=>{let Z=new MutationObserver(()=>{Y(E())});return Z.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme","class"],characterData:!1,childList:!1,subtree:!1}),()=>Z.disconnect()},[]),X}import{useEffect as w,useRef as YQ}from"react";function tQ(X,Y){let Z=YQ(!1);w(()=>{if(Z.current)return;if(Y?.theme)R(Y.theme);if(Y?.styles?.variables)z(Y.styles.variables);if(Y?.theme||Y?.styles?.variables)Z.current=!0},[Y]),w(()=>{if(!X)return;let $=(K)=>{if(K.theme)R(K.theme);if(K.styles?.variables)z(K.styles.variables)};return X.addEventListener("hostcontextchanged",$),()=>X.removeEventListener("hostcontextchanged",$)},[X])}function eQ(X,Y){let Z=YQ(!1);w(()=>{if(Z.current)return;if(Y?.styles?.css?.fonts)I(Y.styles.css.fonts),Z.current=!0},[Y]),w(()=>{if(!X)return;let $=(K)=>{if(K.styles?.css?.fonts)I(K.styles.css.fonts)};return X.addEventListener("hostcontextchanged",$),()=>X.removeEventListener("hostcontextchanged",$)},[X])}function _Y(X,Y){tQ(X,Y),eQ(X,Y)}export{_Y as useHostStyles,tQ as useHostStyleVariables,eQ as useHostFonts,LY as useDocumentTheme,KY as useAutoResize,QY as useApp,E as getDocumentTheme,z as applyHostStyleVariables,I as applyHostFonts,R as applyDocumentTheme,qQ as TOOL_RESULT_METHOD,A as TOOL_INPUT_PARTIAL_METHOD,_Q as TOOL_INPUT_METHOD,OQ as TOOL_CANCELLED_METHOD,VQ as SIZE_CHANGED_METHOD,BQ as SANDBOX_RESOURCE_READY_METHOD,NQ as SANDBOX_PROXY_READY_METHOD,$Q as RESOURCE_URI_META_KEY,DQ as RESOURCE_TEARDOWN_METHOD,KQ as RESOURCE_MIME_TYPE,jQ as REQUEST_TEARDOWN_METHOD,RQ as REQUEST_DISPLAY_MODE_METHOD,V as PostMessageTransport,GQ as OPEN_LINK_METHOD,hQ as McpUiUpdateModelContextRequestSchema,XQ as McpUiToolVisibilitySchema,d as McpUiToolResultNotificationSchema,yQ as McpUiToolMetaSchema,v as McpUiToolInputPartialNotificationSchema,x as McpUiToolInputNotificationSchema,g as McpUiToolCancelledNotificationSchema,s as McpUiThemeSchema,k as McpUiSupportedContentBlockModalitiesSchema,CQ as McpUiSizeChangedNotificationSchema,dQ as McpUiSandboxResourceReadyNotificationSchema,SQ as McpUiSandboxProxyReadyNotificationSchema,f as McpUiResourceTeardownResultSchema,y as McpUiResourceTeardownRequestSchema,F as McpUiResourcePermissionsSchema,vQ as McpUiResourceMetaSchema,D as McpUiResourceCspSchema,bQ as McpUiRequestTeardownNotificationSchema,u as McpUiRequestDisplayModeResultSchema,gQ as McpUiRequestDisplayModeRequestSchema,S as McpUiOpenLinkResultSchema,kQ as McpUiOpenLinkRequestSchema,b as McpUiMessageResultSchema,uQ as McpUiMessageRequestSchema,xQ as McpUiInitializedNotificationSchema,c as McpUiInitializeResultSchema,mQ as McpUiInitializeRequestSchema,t as McpUiHostStylesSchema,a as McpUiHostCssSchema,h as McpUiHostContextSchema,m as McpUiHostContextChangedNotificationSchema,e as McpUiHostCapabilitiesSchema,C as McpUiDownloadFileResultSchema,fQ as McpUiDownloadFileRequestSchema,_ as McpUiDisplayModeSchema,QQ as McpUiAppCapabilitiesSchema,LQ as MESSAGE_METHOD,T as LATEST_PROTOCOL_VERSION,FQ as INITIALIZE_METHOD,EQ as INITIALIZED_METHOD,PQ as HOST_CONTEXT_CHANGED_METHOD,j as EventDispatcher,WQ as DOWNLOAD_FILE_METHOD,U as App};
|