diff --git a/RELEASES.md b/RELEASES.md new file mode 100644 index 00000000..7b80eaf1 --- /dev/null +++ b/RELEASES.md @@ -0,0 +1,64 @@ +# Release Notes + +## 0.3.0 + +### Breaking Changes + +- **`viewport` replaced with `containerDimensions`** — The `viewport` property in host context has been replaced with `containerDimensions`, which provides a clearer distinction between fixed dimensions and maximum constraints. The new type allows specifying either `height` or `maxHeight`, and either `width` or `maxWidth` by @martinalong in [#153](https://github.com/modelcontextprotocol/ext-apps/pull/153) + +### New Examples + +- **Video resource server** — Demonstrates video resource handling with proper mimeType declarations by @antonpk1 in [#175](https://github.com/modelcontextprotocol/ext-apps/pull/175) +- **Sheet music server** — Interactive sheet music notation example by @jonathanhefner in [#196](https://github.com/modelcontextprotocol/ext-apps/pull/196) + +### Developer Experience + +- **`npm start` alias** — Added `npm start` as alias for `npm run examples:start` by @jonathanhefner in [#183](https://github.com/modelcontextprotocol/ext-apps/pull/183) +- **Examples cleanup** — Improved consistency across example servers by @jonathanhefner in [#182](https://github.com/modelcontextprotocol/ext-apps/pull/182) +- **Documentation fixes** — Fixed tsc command in docs to use tsconfig.json by @blackgirlbytes in [#188](https://github.com/modelcontextprotocol/ext-apps/pull/188) + +### Bug Fixes + +- **Move prettier to dev dependency** — Fixed incorrect dependency classification by @niclim in [#179](https://github.com/modelcontextprotocol/ext-apps/pull/179) +- **Fix build errors in examples** — Resolved build issues across example servers by @jonathanhefner in [#180](https://github.com/modelcontextprotocol/ext-apps/pull/180) + +**Full Changelog**: https://github.com/modelcontextprotocol/ext-apps/compare/v0.2.2...v0.3.0 + +--- + +## 0.2.2 + +Changes from 0.1.x to 0.2.2. + +### Highlights + +- **Server helpers** — New `registerAppTool()` and `registerAppResource()` simplify server setup with proper type safety. `connect()` now defaults to `PostMessageTransport(window.parent)`, enabling simpler initialization with just `await app.connect()` by @ochafik in [#165](https://github.com/modelcontextprotocol/ext-apps/pull/165) +- **Tool visibility control** — New `visibility` array field controls whether tools are visible to the agent, apps, or both. Restructured `_meta` format from flat `"ui/resourceUri"` to nested `_meta.ui.resourceUri` by @jonathanhefner in [#131](https://github.com/modelcontextprotocol/ext-apps/pull/131) +- **Host-provided theming** — Apps receive 36 standardized CSS variables for colors, typography, and spacing via `styles.variables`, enabling visual consistency with the host by @martinalong in [#127](https://github.com/modelcontextprotocol/ext-apps/pull/127) +- **Display mode requests** — Apps can request display mode changes (e.g., fullscreen) via `requestDisplayMode`, with hosts able to accept or reject by @martinalong in [#152](https://github.com/modelcontextprotocol/ext-apps/pull/152) +- **Custom fonts support** — Apps can receive custom fonts via `styles.css.fonts` and apply them using `applyHostFonts()` or `useHostFonts()` helpers by @martinalong in [#159](https://github.com/modelcontextprotocol/ext-apps/pull/159) + +### API Changes + +- **MCP SDK as peer dependency** — Consumers control their SDK version, reducing duplication by @ochafik in [#168](https://github.com/modelcontextprotocol/ext-apps/pull/168) +- **React as peer dependency** — Supports React 17, 18, and 19 by @ochafik in [#164](https://github.com/modelcontextprotocol/ext-apps/pull/164) +- **Renamed request methods** — Removed `send` prefix: `openLink()` and `teardownResource()`. Deprecated aliases maintained by @ochafik in [#161](https://github.com/modelcontextprotocol/ext-apps/pull/161) +- **Optional Client in AppBridge** — Enables custom forwarding scenarios without direct MCP client access by @ochafik in [#146](https://github.com/modelcontextprotocol/ext-apps/pull/146) +- **Zod v3 and v4 support** — Bring your own Zod by @alpic-ai in [#49](https://github.com/modelcontextprotocol/ext-apps/pull/49) +- **Zod schemas now version-agnostic** — Generated schemas work with both Zod 3.25+ and v4, using `z.object().passthrough()` instead of v4-only `z.looseObject()` by @ochafik in [#178](https://github.com/modelcontextprotocol/ext-apps/pull/178) + +### Platform & DX + +- **Windows compatibility** — Bun as optional dependency with automatic setup, `cross-env` for examples. Just run `npm install` by @ochafik in [#145](https://github.com/modelcontextprotocol/ext-apps/pull/145) +- **Widened `@oven/bun-*` version range** — Lowered the minimum from `^1.3.4` to `^1.2.21`, helping contributors whose registries may not have the latest packages by @ochafik in [#176](https://github.com/modelcontextprotocol/ext-apps/pull/176) +- **SSE transport support** — All examples now support both stdio and HTTP transports with SSE endpoints for older clients by @ochafik in [#136](https://github.com/modelcontextprotocol/ext-apps/pull/136) +- **Playwright E2E tests** — Screenshot golden testing across all 9 example servers with parallel execution (~28s) by @ochafik in [#115](https://github.com/modelcontextprotocol/ext-apps/pull/115) +- **hostContext exposure** — Access `hostContext` directly from the `App` class by @ochafik in [#139](https://github.com/modelcontextprotocol/ext-apps/pull/139) + +### Bug Fixes + +- **Responsive UIs** — Fixed narrow viewport handling for mobile and sidebar experiences by @ochafik in [#135](https://github.com/modelcontextprotocol/ext-apps/pull/135) +- **Sandbox notification fix** — Fixed `sandbox-ready` notification name to match implementation by @ochafik in [#160](https://github.com/modelcontextprotocol/ext-apps/pull/160) +- **Fixed non-UI tool registration** — Use `server.registerTool` for tools without UI and fix missing imports across examples by @ochafik in [#173](https://github.com/modelcontextprotocol/ext-apps/pull/173) + +**Full Changelog**: https://github.com/modelcontextprotocol/ext-apps/compare/v0.1.1...v0.2.2 diff --git a/examples/basic-server-preact/package.json b/examples/basic-server-preact/package.json index 47fdc033..3b237059 100644 --- a/examples/basic-server-preact/package.json +++ b/examples/basic-server-preact/package.json @@ -24,7 +24,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "preact": "^10.0.0", "zod": "^4.1.13" diff --git a/examples/basic-server-react/package.json b/examples/basic-server-react/package.json index 09185554..d859013d 100644 --- a/examples/basic-server-react/package.json +++ b/examples/basic-server-react/package.json @@ -24,7 +24,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/basic-server-solid/package.json b/examples/basic-server-solid/package.json index f943ee17..bf5b40db 100644 --- a/examples/basic-server-solid/package.json +++ b/examples/basic-server-solid/package.json @@ -24,7 +24,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "solid-js": "^1.9.0", "zod": "^4.1.13" diff --git a/examples/basic-server-svelte/package.json b/examples/basic-server-svelte/package.json index c1b165e9..7269f79c 100644 --- a/examples/basic-server-svelte/package.json +++ b/examples/basic-server-svelte/package.json @@ -24,7 +24,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "svelte": "^5.0.0", "zod": "^4.1.13" diff --git a/examples/basic-server-vanillajs/package.json b/examples/basic-server-vanillajs/package.json index aba360fc..99d38d86 100644 --- a/examples/basic-server-vanillajs/package.json +++ b/examples/basic-server-vanillajs/package.json @@ -24,7 +24,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "zod": "^4.1.13" }, diff --git a/examples/basic-server-vue/package.json b/examples/basic-server-vue/package.json index 6e8ae8c6..ac836b5f 100644 --- a/examples/basic-server-vue/package.json +++ b/examples/basic-server-vue/package.json @@ -24,7 +24,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "vue": "^3.5.0", "zod": "^4.1.13" diff --git a/examples/budget-allocator-server/package.json b/examples/budget-allocator-server/package.json index b353042c..4aee9568 100644 --- a/examples/budget-allocator-server/package.json +++ b/examples/budget-allocator-server/package.json @@ -27,7 +27,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "chart.js": "^4.4.0", "zod": "^4.1.13" diff --git a/examples/cohort-heatmap-server/package.json b/examples/cohort-heatmap-server/package.json index ebe5dfdf..6d456d62 100644 --- a/examples/cohort-heatmap-server/package.json +++ b/examples/cohort-heatmap-server/package.json @@ -27,7 +27,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/customer-segmentation-server/package.json b/examples/customer-segmentation-server/package.json index ec9551ba..b5694c7e 100644 --- a/examples/customer-segmentation-server/package.json +++ b/examples/customer-segmentation-server/package.json @@ -27,7 +27,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "chart.js": "^4.4.0", "zod": "^4.1.13" diff --git a/examples/scenario-modeler-server/package.json b/examples/scenario-modeler-server/package.json index d13875c9..aa6f3da7 100644 --- a/examples/scenario-modeler-server/package.json +++ b/examples/scenario-modeler-server/package.json @@ -27,7 +27,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "chart.js": "^4.4.0", "react": "^19.2.0", diff --git a/examples/sheet-music-server/package.json b/examples/sheet-music-server/package.json index 81008c58..bd1cefb9 100644 --- a/examples/sheet-music-server/package.json +++ b/examples/sheet-music-server/package.json @@ -24,7 +24,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "abcjs": "^6.4.4", "zod": "^4.1.13" diff --git a/examples/system-monitor-server/package.json b/examples/system-monitor-server/package.json index d8198d17..573c63a0 100644 --- a/examples/system-monitor-server/package.json +++ b/examples/system-monitor-server/package.json @@ -27,7 +27,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "chart.js": "^4.4.0", "systeminformation": "^5.27.11", diff --git a/examples/threejs-server/package.json b/examples/threejs-server/package.json index 8145b8e2..909c77d0 100644 --- a/examples/threejs-server/package.json +++ b/examples/threejs-server/package.json @@ -27,7 +27,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/video-resource-server/package.json b/examples/video-resource-server/package.json index 1de4caca..c765cef2 100644 --- a/examples/video-resource-server/package.json +++ b/examples/video-resource-server/package.json @@ -24,7 +24,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "zod": "^4.1.13" }, diff --git a/examples/video-resource-server/src/server-utils.ts b/examples/video-resource-server/src/server-utils.ts new file mode 100644 index 00000000..40524237 --- /dev/null +++ b/examples/video-resource-server/src/server-utils.ts @@ -0,0 +1,110 @@ +/** + * Shared utilities for running MCP servers with various transports. + */ + +import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import cors from "cors"; +import type { Request, Response } from "express"; + +/** + * Starts an MCP server using the appropriate transport based on command-line arguments. + * + * If `--stdio` is passed, uses stdio transport. Otherwise, uses Streamable HTTP transport. + * + * @param createServer - Factory function that creates a new McpServer instance. + */ +export async function startServer( + createServer: () => McpServer, +): Promise { + try { + if (process.argv.includes("--stdio")) { + await startStdioServer(createServer); + } else { + await startStreamableHttpServer(createServer); + } + } catch (e) { + console.error(e); + process.exit(1); + } +} + +/** + * Starts an MCP server with stdio transport. + * + * @param createServer - Factory function that creates a new McpServer instance. + */ +export async function startStdioServer( + createServer: () => McpServer, +): Promise { + await createServer().connect(new StdioServerTransport()); +} + +/** + * Starts an MCP server with Streamable HTTP transport in stateless mode. + * + * Each request creates a fresh server and transport instance, which are + * closed when the response ends (no session tracking). + * + * The server listens on the port specified by the PORT environment variable, + * defaulting to 3001 if not set. + * + * @param createServer - Factory function that creates a new McpServer instance per request. + */ +export async function startStreamableHttpServer( + createServer: () => McpServer, +): Promise { + const port = parseInt(process.env.PORT ?? "3001", 10); + + // Express app - bind to all interfaces for development/testing + const expressApp = createMcpExpressApp({ host: "0.0.0.0" }); + expressApp.use(cors()); + + expressApp.all("/mcp", async (req: Request, res: Response) => { + // Create fresh server and transport for each request (stateless mode) + const server = createServer(); + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + }); + + // Clean up when response ends + res.on("close", () => { + transport.close().catch(() => {}); + server.close().catch(() => {}); + }); + + try { + await server.connect(transport); + await transport.handleRequest(req, res, req.body); + } catch (error) { + console.error("MCP error:", error); + if (!res.headersSent) { + res.status(500).json({ + jsonrpc: "2.0", + error: { code: -32603, message: "Internal server error" }, + id: null, + }); + } + } + }); + + const { promise, resolve, reject } = Promise.withResolvers(); + + const httpServer = expressApp.listen(port, (err?: Error) => { + if (err) return reject(err); + console.log(`Server listening on http://localhost:${port}/mcp`); + resolve(); + }); + + const shutdown = () => { + console.log("\nShutting down..."); + httpServer.close(() => process.exit(0)); + }; + + process.on("SIGINT", shutdown); + process.on("SIGTERM", shutdown); + + return promise; +} diff --git a/examples/wiki-explorer-server/package.json b/examples/wiki-explorer-server/package.json index 47fba403..1511dd5e 100644 --- a/examples/wiki-explorer-server/package.json +++ b/examples/wiki-explorer-server/package.json @@ -27,7 +27,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "cheerio": "^1.0.0", "zod": "^4.1.13" diff --git a/package-lock.json b/package-lock.json index ad85c004..078cf710 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@modelcontextprotocol/ext-apps", - "version": "0.2.2", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@modelcontextprotocol/ext-apps", - "version": "0.2.2", + "version": "0.3.0", "hasInstallScript": true, "license": "MIT", "workspaces": [ @@ -116,7 +116,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "preact": "^10.0.0", "zod": "^4.1.13" @@ -157,7 +157,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -201,7 +201,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "solid-js": "^1.9.0", "zod": "^4.1.13" @@ -242,7 +242,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "svelte": "^5.0.0", "zod": "^4.1.13" @@ -283,7 +283,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "zod": "^4.1.13" }, @@ -322,7 +322,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "vue": "^3.5.0", "zod": "^4.1.13" @@ -363,7 +363,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "chart.js": "^4.4.0", "zod": "^4.1.13" @@ -403,7 +403,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -447,7 +447,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "chart.js": "^4.4.0", "zod": "^4.1.13" @@ -528,7 +528,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "chart.js": "^4.4.0", "react": "^19.2.0", @@ -573,7 +573,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "abcjs": "^6.4.4", "zod": "^4.1.13" @@ -613,7 +613,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "chart.js": "^4.4.0", "systeminformation": "^5.27.11", @@ -654,7 +654,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -700,7 +700,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "zod": "^4.1.13" }, @@ -739,7 +739,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^0.2.2", + "@modelcontextprotocol/ext-apps": "^0.3.0", "@modelcontextprotocol/sdk": "^1.24.0", "cheerio": "^1.0.0", "zod": "^4.1.13" diff --git a/package.json b/package.json index 7ac95a70..2291a5f5 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "url": "https://github.com/modelcontextprotocol/ext-apps" }, "homepage": "https://github.com/modelcontextprotocol/ext-apps", - "version": "0.2.2", + "version": "0.3.0", "license": "MIT", "description": "MCP Apps SDK — Enable MCP servers to display interactive user interfaces in conversational clients.", "type": "module",