Skip to content

Commit 5561c9b

Browse files
committed
Merge main into swift-sdk
2 parents 3c0aec9 + 3e95d64 commit 5561c9b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+5343
-668
lines changed

.gitattributes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1+
# Force LF line endings for all text files (for consistent prettier formatting)
2+
* text=auto eol=lf
3+
14
package-lock.json linguist-generated=true
5+
src/generated/** linguist-generated=true

.github/workflows/ci.yml

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,43 @@ permissions:
1111

1212
jobs:
1313
build:
14-
runs-on: ubuntu-latest
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
include:
18+
- os: ubuntu-latest
19+
name: Linux x64
20+
- os: ubuntu-24.04-arm
21+
name: Linux ARM64
22+
- os: windows-latest
23+
name: Windows x64
24+
- os: windows-11-arm
25+
name: Windows ARM64
26+
- os: macos-latest
27+
name: macOS ARM64
28+
29+
name: Build (${{ matrix.name }})
30+
runs-on: ${{ matrix.os }}
31+
1532
steps:
1633
- uses: actions/checkout@v4
1734

1835
- name: Verify no private URLs in package-lock.json
36+
shell: bash
1937
run: '! grep -E "\"resolved\": \"https?://" package-lock.json | grep -v registry.npmjs.org'
2038

21-
- uses: oven-sh/setup-bun@v2
22-
with:
23-
bun-version: latest
24-
2539
- uses: actions/setup-node@v4
2640
with:
2741
node-version: "20"
2842

2943
- run: npm install
3044

31-
- run: npm run build:all
45+
- run: npm run build
46+
47+
- run: npm run examples:build
3248

3349
- name: Verify generated schemas are up-to-date
50+
shell: bash
3451
run: |
3552
npm run generate:schemas
3653
git diff --exit-code src/generated/ || (echo "Generated schemas are out of date. Run 'npm run generate:schemas' and commit." && exit 1)
@@ -101,3 +118,67 @@ jobs:
101118
name: test-results
102119
path: test-results/
103120
retention-days: 7
121+
122+
# Test build in Windows WSL (Ubuntu)
123+
build-wsl:
124+
name: Build (Windows WSL)
125+
runs-on: windows-latest
126+
127+
steps:
128+
- uses: actions/checkout@v4
129+
130+
- uses: Vampire/setup-wsl@v5
131+
with:
132+
distribution: Ubuntu-24.04
133+
134+
- name: Install Node.js in WSL
135+
shell: wsl-bash {0}
136+
run: |
137+
sudo apt-get update
138+
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
139+
sudo apt-get install -y nodejs
140+
141+
- name: Build and test in WSL
142+
shell: wsl-bash {0}
143+
run: |
144+
npm install
145+
npm run build
146+
npm run examples:build
147+
npm test
148+
npm run prettier
149+
150+
# Test that the package can be installed from git (triggers prepare script)
151+
test-git-install:
152+
strategy:
153+
fail-fast: false
154+
matrix:
155+
include:
156+
- os: ubuntu-latest
157+
name: Linux x64
158+
- os: ubuntu-24.04-arm
159+
name: Linux ARM64
160+
- os: windows-latest
161+
name: Windows x64
162+
- os: windows-11-arm
163+
name: Windows ARM64
164+
- os: macos-latest
165+
name: macOS ARM64
166+
167+
name: Test git install (${{ matrix.name }})
168+
runs-on: ${{ matrix.os }}
169+
170+
steps:
171+
- uses: actions/setup-node@v4
172+
with:
173+
node-version: "20"
174+
175+
- name: Create test project and install from git
176+
shell: bash
177+
run: |
178+
mkdir test-project
179+
cd test-project
180+
npm init -y
181+
# Install from the PR branch
182+
npm install "git+https://github.com/${{ github.repository }}#${{ github.head_ref || github.ref_name }}"
183+
# Verify the package is usable (ESM import)
184+
node --input-type=module -e "import { App } from '@modelcontextprotocol/ext-apps'; console.log('Import successful:', typeof App)"

.husky/pre-commit

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# Verify no private registry URLs in package-lock.json
2+
if grep -E '"resolved": "https?://' package-lock.json | grep -v registry.npmjs.org > /dev/null; then
3+
echo "ERROR: package-lock.json contains non-npmjs.org URLs"
4+
echo "Run: docker run --rm -i -v \$PWD:/src -w /src node:latest npm i --registry=https://registry.npmjs.org/"
5+
exit 1
6+
fi
7+
18
npm run build:all
29
npm run prettier:fix
310

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ The [`examples/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/exa
5555

5656
To run all examples together:
5757

58-
```
58+
```bash
5959
npm install
6060
npm run examples:start
6161
```

build.bun.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,20 @@ function buildJs(entrypoint: string, opts: Record<string, any> = {}) {
2323
}
2424

2525
await Promise.all([
26-
buildJs("src/app.ts", { outdir: "dist/src" }),
26+
buildJs("src/app.ts", {
27+
outdir: "dist/src",
28+
external: ["@modelcontextprotocol/sdk"],
29+
}),
2730
buildJs("src/app-bridge.ts", {
2831
outdir: "dist/src",
2932
external: ["@modelcontextprotocol/sdk"],
3033
}),
3134
buildJs("src/react/index.tsx", {
3235
outdir: "dist/src/react",
33-
external: ["react", "react-dom"],
36+
external: ["react", "react-dom", "@modelcontextprotocol/sdk"],
37+
}),
38+
buildJs("src/server/index.ts", {
39+
outdir: "dist/src/server",
40+
external: ["@modelcontextprotocol/sdk"],
3441
}),
3542
]);

docs/quickstart.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ Create `server.ts`:
9797
```typescript
9898
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9999
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
100-
import { RESOURCE_URI_META_KEY } from "@modelcontextprotocol/ext-apps";
100+
import {
101+
RESOURCE_MIME_TYPE,
102+
type McpUiToolMeta,
103+
} from "@modelcontextprotocol/ext-apps";
101104
import cors from "cors";
102105
import express from "express";
103106
import fs from "node:fs/promises";
@@ -119,7 +122,7 @@ server.registerTool(
119122
description: "Returns the current server time.",
120123
inputSchema: {},
121124
outputSchema: { time: z.string() },
122-
_meta: { [RESOURCE_URI_META_KEY]: resourceUri }, // Links tool to UI
125+
_meta: { ui: { resourceUri } as McpUiToolMeta }, // Links tool to UI
123126
},
124127
async () => {
125128
const time = new Date().toISOString();

examples/basic-host/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<meta name="color-scheme" content="light dark">
67
<title>MCP Apps Host</title>
78
<link rel="stylesheet" href="/src/global.css">
89
</head>

examples/basic-host/package.json

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
"version": "1.0.0",
55
"type": "module",
66
"scripts": {
7-
"build": "concurrently 'INPUT=index.html vite build' 'INPUT=sandbox.html vite build'",
8-
"watch": "concurrently 'INPUT=index.html vite build --watch' 'INPUT=sandbox.html vite build --watch'",
7+
"build": "concurrently \"cross-env INPUT=index.html vite build\" \"cross-env INPUT=sandbox.html vite build\"",
8+
"watch": "concurrently \"cross-env INPUT=index.html vite build --watch\" \"cross-env INPUT=sandbox.html vite build --watch\"",
99
"serve": "bun serve.ts",
10-
"start": "NODE_ENV=development npm run build && npm run serve",
11-
"dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve'"
10+
"start": "cross-env NODE_ENV=development npm run build && npm run serve",
11+
"dev": "cross-env NODE_ENV=development concurrently \"npm run watch\" \"npm run serve\""
1212
},
1313
"dependencies": {
1414
"@modelcontextprotocol/ext-apps": "../..",
15-
"@modelcontextprotocol/sdk": "^1.22.0",
15+
"@modelcontextprotocol/sdk": "^1.24.0",
1616
"react": "^19.2.0",
1717
"react-dom": "^19.2.0",
1818
"zod": "^4.1.13"
@@ -23,7 +23,6 @@
2323
"@types/react": "^19.2.2",
2424
"@types/react-dom": "^19.2.2",
2525
"@vitejs/plugin-react": "^4.3.4",
26-
"bun": "^1.3.2",
2726
"concurrently": "^9.2.1",
2827
"cors": "^2.8.5",
2928
"express": "^5.1.0",

examples/basic-host/sandbox.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<html>
33
<head>
44
<meta charset="utf-8" />
5+
<meta name="color-scheme" content="light dark">
56
<!-- CSP is set by serve.ts HTTP header - no meta tag needed here
67
The inner iframe's CSP is dynamically injected based on resource metadata -->
78
<title>MCP-UI Proxy</title>

examples/basic-host/src/implementation.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY, type McpUiSandboxProxyReadyNotification, AppBridge, PostMessageTransport } from "@modelcontextprotocol/ext-apps/app-bridge";
1+
import { RESOURCE_MIME_TYPE, getToolUiResourceUri, type McpUiSandboxProxyReadyNotification, AppBridge, PostMessageTransport } from "@modelcontextprotocol/ext-apps/app-bridge";
22
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
33
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
44
import type { CallToolResult, Tool } from "@modelcontextprotocol/sdk/types.js";
@@ -77,7 +77,7 @@ export function callTool(
7777

7878
const toolCallInfo: ToolCallInfo = { serverInfo, tool, input, resultPromise };
7979

80-
const uiResourceUri = getUiResourceUri(tool);
80+
const uiResourceUri = getToolUiResourceUri(tool);
8181
if (uiResourceUri) {
8282
toolCallInfo.appResourcePromise = getUiResource(serverInfo, uiResourceUri);
8383
}
@@ -86,16 +86,6 @@ export function callTool(
8686
}
8787

8888

89-
function getUiResourceUri(tool: Tool): string | undefined {
90-
const uri = tool._meta?.[RESOURCE_URI_META_KEY];
91-
if (typeof uri === "string" && uri.startsWith("ui://")) {
92-
return uri;
93-
} else if (uri !== undefined) {
94-
throw new Error(`Invalid UI resource URI: ${JSON.stringify(uri)}`);
95-
}
96-
}
97-
98-
9989
async function getUiResource(serverInfo: ServerInfo, uri: string): Promise<UiResourceData> {
10090
log.info("Reading UI resource:", uri);
10191
const resource = await serverInfo.client.readResource({ uri });

0 commit comments

Comments
 (0)