diff --git a/package-lock.json b/package-lock.json index f1d7e203e..003b83682 100644 --- a/package-lock.json +++ b/package-lock.json @@ -476,7 +476,7 @@ "version": "0.4.1", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.4.0", + "@modelcontextprotocol/ext-apps": "^0.4.1", "@modelcontextprotocol/sdk": "^1.24.0", "zod": "^4.1.13" }, @@ -625,12 +625,18 @@ }, "examples/qr-server": { "name": "@modelcontextprotocol/server-qr", - "version": "1.0.0" + "version": "1.0.0", + "dependencies": { + "@modelcontextprotocol/ext-apps": "^0.4.1" + } }, "examples/say-server": { "name": "@modelcontextprotocol/server-say", "version": "0.4.1", - "license": "MIT" + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/ext-apps": "^0.4.1" + } }, "examples/scenario-modeler-server": { "name": "@modelcontextprotocol/server-scenario-modeler", @@ -993,7 +999,6 @@ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -3106,6 +3111,19 @@ "node": ">= 8.0.0" } }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.56.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.56.0.tgz", @@ -3514,7 +3532,6 @@ "integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", @@ -3721,7 +3738,6 @@ "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -3746,7 +3762,6 @@ "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -4149,7 +4164,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4259,6 +4273,19 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -4475,7 +4502,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5191,7 +5217,6 @@ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -5676,7 +5701,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -6090,6 +6114,16 @@ "he": "bin/he" } }, + "node_modules/hono": { + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.5.tgz", + "integrity": "sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/html-entities": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", @@ -6790,6 +6824,19 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -7137,13 +7184,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -7238,7 +7285,6 @@ "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.2.tgz", "integrity": "sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -7334,7 +7380,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7374,6 +7419,19 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7433,7 +7491,6 @@ "integrity": "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -7555,7 +7612,6 @@ "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -7846,7 +7902,6 @@ "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.10.tgz", "integrity": "sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", @@ -8025,7 +8080,6 @@ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.48.0.tgz", "integrity": "sha512-+NUe82VoFP1RQViZI/esojx70eazGF4u0O/9ucqZ4rPcOZD+n5EVp17uYsqwdzjUjZyTpGKunHbDziW6AIAVkQ==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -8329,20 +8383,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tinypool": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", @@ -9096,9 +9136,8 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9192,7 +9231,6 @@ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -9345,20 +9383,6 @@ "vite": "5.x || 6.x || 7.x" } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/vitefu": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", @@ -9452,25 +9476,11 @@ } } }, - "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/vue": { "version": "3.5.27", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.27.tgz", "integrity": "sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.27", "@vue/compiler-sfc": "3.5.27", @@ -9643,7 +9653,6 @@ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "dev": true, "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -9694,7 +9703,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index 25d84e639..cfd383bc2 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -1457,13 +1457,13 @@ Future versions may add additional settings: #### Server Behavior -Servers SHOULD check client (host would-be) capabilities before registering UI-enabled tools: +Servers SHOULD check client capabilities before registering UI-enabled tools. The SDK provides the `getUiCapability` helper for this: ```typescript -const hasUISupport = - clientCapabilities?.extensions?.["io.modelcontextprotocol/ui"]?.mimeTypes?.includes("text/html;profile=mcp-app"); +import { getUiCapability, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; -if (hasUISupport) { +const uiCap = getUiCapability(clientCapabilities); +if (uiCap?.mimeTypes?.includes(RESOURCE_MIME_TYPE)) { // Register tools with UI templates server.registerTool("get_weather", { description: "Get weather with interactive dashboard", diff --git a/src/generated/schema.json b/src/generated/schema.json index 2637bc0c6..a89dcba85 100644 --- a/src/generated/schema.json +++ b/src/generated/schema.json @@ -28,6 +28,20 @@ }, "additionalProperties": false }, + "McpUiClientCapabilities": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "mimeTypes": { + "description": "Array of supported MIME types for UI resources.\nMust include `\"text/html;profile=mcp-app\"` for MCP Apps support.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, "McpUiDisplayMode": { "$schema": "https://json-schema.org/draft/2020-12/schema", "anyOf": [ diff --git a/src/generated/schema.test.ts b/src/generated/schema.test.ts index 727c28d29..95ec2f216 100644 --- a/src/generated/schema.test.ts +++ b/src/generated/schema.test.ts @@ -119,6 +119,10 @@ export type McpUiToolMetaSchemaInferredType = z.infer< typeof generated.McpUiToolMetaSchema >; +export type McpUiClientCapabilitiesSchemaInferredType = z.infer< + typeof generated.McpUiClientCapabilitiesSchema +>; + export type McpUiMessageRequestSchemaInferredType = z.infer< typeof generated.McpUiMessageRequestSchema >; @@ -277,6 +281,12 @@ expectType( ); expectType({} as McpUiToolMetaSchemaInferredType); expectType({} as spec.McpUiToolMeta); +expectType( + {} as McpUiClientCapabilitiesSchemaInferredType, +); +expectType( + {} as spec.McpUiClientCapabilities, +); expectType( {} as McpUiMessageRequestSchemaInferredType, ); diff --git a/src/generated/schema.ts b/src/generated/schema.ts index 3bf7b9767..edbd46520 100644 --- a/src/generated/schema.ts +++ b/src/generated/schema.ts @@ -562,6 +562,26 @@ export const McpUiToolMetaSchema = z.object({ ), }); +/** + * @description MCP Apps capability settings advertised by clients to servers. + * + * Clients advertise these capabilities via the `extensions` field in their + * capabilities during MCP initialization. Servers can check for MCP Apps + * support using {@link server-helpers!getUiCapability}. + */ +export const McpUiClientCapabilitiesSchema = z.object({ + /** + * @description Array of supported MIME types for UI resources. + * Must include `"text/html;profile=mcp-app"` for MCP Apps support. + */ + mimeTypes: z + .array(z.string()) + .optional() + .describe( + 'Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.', + ), +}); + /** * @description Request to send a message to the host's chat interface. * @see {@link app!App.sendMessage `App.sendMessage`} for the method that sends this request diff --git a/src/server/index.test.ts b/src/server/index.test.ts index 67291d442..1853cc05a 100644 --- a/src/server/index.test.ts +++ b/src/server/index.test.ts @@ -4,6 +4,8 @@ import { registerAppResource, RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE, + getUiCapability, + EXTENSION_ID, } from "./index"; import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; @@ -318,3 +320,39 @@ describe("registerAppResource", () => { expect(result).toEqual(expectedResult); }); }); + +describe("getUiCapability", () => { + const MIME_TYPE = "text/html;profile=mcp-app"; + + it("should return undefined for null/undefined capabilities", () => { + expect(getUiCapability(null)).toBeUndefined(); + expect(getUiCapability(undefined)).toBeUndefined(); + }); + + it("should return undefined for empty capabilities", () => { + expect(getUiCapability({})).toBeUndefined(); + }); + + it("should return capability from extensions field", () => { + const caps = { + extensions: { + [EXTENSION_ID]: { + mimeTypes: [MIME_TYPE], + }, + }, + }; + const result = getUiCapability(caps); + expect(result).toEqual({ mimeTypes: [MIME_TYPE] }); + }); + + it("should return undefined when extension ID is missing", () => { + const caps = { + extensions: { + "some-other-extension": { + mimeTypes: [MIME_TYPE], + }, + }, + }; + expect(getUiCapability(caps)).toBeUndefined(); + }); +}); diff --git a/src/server/index.ts b/src/server/index.ts index 56308d845..6864b9ca3 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -36,6 +36,7 @@ import { RESOURCE_MIME_TYPE, McpUiResourceMeta, McpUiToolMeta, + McpUiClientCapabilities, } from "../app.js"; import type { BaseToolCallback, @@ -49,7 +50,10 @@ import type { AnySchema, ZodRawShapeCompat, } from "@modelcontextprotocol/sdk/server/zod-compat.js"; -import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js"; +import type { + ClientCapabilities, + ToolAnnotations, +} from "@modelcontextprotocol/sdk/types.js"; // Re-exports for convenience export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE }; @@ -313,3 +317,58 @@ export function registerAppResource( readCallback, ); } + +/** + * Extension identifier for MCP Apps capability negotiation. + * + * Used as the key in `extensions` to advertise MCP Apps support. + */ +export const EXTENSION_ID = "io.modelcontextprotocol/ui"; + +/** + * Get MCP Apps capability settings from client capabilities. + * + * This helper retrieves the capability object from the `extensions` field + * where MCP Apps advertises its support. + * + * Note: The `clientCapabilities` parameter extends the SDK's `ClientCapabilities` + * type with an `extensions` field (pending SEP-1724). Once `extensions` is added + * to the SDK, this can use `ClientCapabilities` directly. + * + * @param clientCapabilities - The client capabilities from the initialize response + * @returns The MCP Apps capability settings, or `undefined` if not supported + * + * @example Check for MCP Apps support in server initialization + * ```typescript + * import { getUiCapability, RESOURCE_MIME_TYPE, registerAppTool } from "@modelcontextprotocol/ext-apps/server"; + * + * server.oninitialized = ({ clientCapabilities }) => { + * const uiCap = getUiCapability(clientCapabilities); + * if (uiCap?.mimeTypes?.includes(RESOURCE_MIME_TYPE)) { + * registerAppTool(server, "weather", { + * description: "Get weather with interactive dashboard", + * _meta: { ui: { resourceUri: "ui://weather/dashboard" } }, + * }, weatherHandler); + * } else { + * // Register text-only fallback + * server.registerTool("weather", { + * description: "Get weather as text", + * }, textWeatherHandler); + * } + * }; + * ``` + */ +export function getUiCapability( + clientCapabilities: + | (ClientCapabilities & { extensions?: Record }) + | null + | undefined, +): McpUiClientCapabilities | undefined { + if (!clientCapabilities) { + return undefined; + } + + return clientCapabilities.extensions?.[EXTENSION_ID] as + | McpUiClientCapabilities + | undefined; +} diff --git a/src/spec.types.ts b/src/spec.types.ts index af0f202c3..fc587eb6c 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -672,3 +672,18 @@ export const INITIALIZED_METHOD: McpUiInitializedNotification["method"] = "ui/notifications/initialized"; export const REQUEST_DISPLAY_MODE_METHOD: McpUiRequestDisplayModeRequest["method"] = "ui/request-display-mode"; + +/** + * @description MCP Apps capability settings advertised by clients to servers. + * + * Clients advertise these capabilities via the `extensions` field in their + * capabilities during MCP initialization. Servers can check for MCP Apps + * support using {@link server-helpers!getUiCapability}. + */ +export interface McpUiClientCapabilities { + /** + * @description Array of supported MIME types for UI resources. + * Must include `"text/html;profile=mcp-app"` for MCP Apps support. + */ + mimeTypes?: string[]; +} diff --git a/src/types.ts b/src/types.ts index 77563dc85..a4770fdaf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -61,6 +61,7 @@ export { type McpUiRequestDisplayModeResult, type McpUiToolVisibility, type McpUiToolMeta, + type McpUiClientCapabilities, } from "./spec.types.js"; // Import types needed for protocol type unions (not re-exported, just used internally)