Skip to content

Commit c09b46d

Browse files
committed
Merge remote-tracking branch 'origin/main' into ochafik/kotlin-sdk
# Conflicts: # .prettierignore # README.md # examples/basic-server-react/main.ts # examples/basic-server-vanillajs/main.ts # examples/budget-allocator-server/main.ts # examples/cohort-heatmap-server/main.ts # examples/customer-segmentation-server/src/server-utils.ts # examples/integration-server/src/server-utils.ts # examples/scenario-modeler-server/src/server-utils.ts # examples/sheet-music-server/src/server-utils.ts # examples/system-monitor-server/src/server-utils.ts # examples/threejs-server/src/server-utils.ts # examples/video-resource-server/src/server-utils.ts # examples/wiki-explorer-server/src/server-utils.ts
2 parents 7d7146f + ec0f217 commit c09b46d

375 files changed

Lines changed: 34276 additions & 5921 deletions

File tree

Some content is hidden

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

.claude-plugin/marketplace.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "mcp-apps",
3+
"description": "Plugins for MCP Apps development",
4+
"owner": {
5+
"name": "MCP Apps"
6+
},
7+
"plugins": [
8+
{
9+
"name": "mcp-apps",
10+
"source": "./plugins/mcp-apps",
11+
"description": "Skills for creating MCP Apps with the MCP Apps SDK"
12+
}
13+
]
14+
}

.github/workflows/ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ jobs:
3636
shell: bash
3737
run: '! grep -E "\"resolved\": \"https?://" package-lock.json | grep -v registry.npmjs.org'
3838

39+
- name: Verify example dependency versions
40+
shell: bash
41+
run: node scripts/check-versions.mjs
42+
3943
- uses: actions/setup-node@v4
4044
with:
4145
node-version: "20"
@@ -52,6 +56,12 @@ jobs:
5256
npm run generate:schemas
5357
git diff --exit-code src/generated/ || (echo "Generated schemas are out of date. Run 'npm run generate:schemas' and commit." && exit 1)
5458
59+
- name: Verify synced snippets are up-to-date
60+
shell: bash
61+
run: |
62+
npm run sync:snippets
63+
git diff --exit-code src/ docs/ || (echo "Synced snippets are out of date. Run 'npm run sync:snippets' and commit." && exit 1)
64+
5565
- run: npm test
5666

5767
- run: npm run prettier
@@ -69,6 +79,8 @@ jobs:
6979
with:
7080
node-version: "20"
7181

82+
- uses: astral-sh/setup-uv@v5
83+
7284
- run: npm ci
7385

7486
- name: Install Playwright browsers

.github/workflows/npm-publish.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,57 @@ jobs:
8383
- run: npm publish --provenance --access public ${{ steps.npm-tag.outputs.tag }}
8484
env:
8585
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}
86+
87+
publish-examples:
88+
runs-on: ubuntu-latest
89+
if: github.event_name == 'release'
90+
environment: Release
91+
needs: [publish]
92+
93+
permissions:
94+
contents: read
95+
id-token: write
96+
97+
strategy:
98+
fail-fast: false
99+
matrix:
100+
example:
101+
- basic-server-preact
102+
- basic-server-react
103+
- basic-server-solid
104+
- basic-server-svelte
105+
- basic-server-vanillajs
106+
- basic-server-vue
107+
- budget-allocator-server
108+
- cohort-heatmap-server
109+
- customer-segmentation-server
110+
- map-server
111+
- pdf-server
112+
- scenario-modeler-server
113+
- shadertoy-server
114+
- sheet-music-server
115+
- system-monitor-server
116+
- threejs-server
117+
- transcript-server
118+
- video-resource-server
119+
- wiki-explorer-server
120+
121+
steps:
122+
- uses: actions/checkout@v4
123+
- uses: oven-sh/setup-bun@v2
124+
with:
125+
bun-version: latest
126+
- uses: actions/setup-node@v4
127+
with:
128+
node-version: "22"
129+
cache: npm
130+
registry-url: "https://registry.npmjs.org"
131+
- run: npm ci
132+
133+
- name: Build example
134+
run: npm run build --workspace examples/${{ matrix.example }}
135+
136+
- name: Publish example
137+
run: npm publish --workspace examples/${{ matrix.example }} --provenance --access public
138+
env:
139+
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

.github/workflows/publish.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,22 @@ jobs:
2020
cache: npm
2121
- run: npm ci
2222
- run: npm run build
23-
- run: npx pkg-pr-new publish
23+
- run: npm run examples:build
24+
- run: |
25+
npx pkg-pr-new publish \
26+
. \
27+
./examples/basic-server-react \
28+
./examples/basic-server-vanillajs \
29+
./examples/budget-allocator-server \
30+
./examples/cohort-heatmap-server \
31+
./examples/customer-segmentation-server \
32+
./examples/map-server \
33+
./examples/pdf-server \
34+
./examples/scenario-modeler-server \
35+
./examples/shadertoy-server \
36+
./examples/sheet-music-server \
37+
./examples/system-monitor-server \
38+
./examples/threejs-server \
39+
./examples/transcript-server \
40+
./examples/video-resource-server \
41+
./examples/wiki-explorer-server
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: Update E2E Snapshots
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
branch:
7+
description: "Branch to update snapshots on"
8+
required: true
9+
default: "main"
10+
issue_comment:
11+
types: [created]
12+
13+
permissions:
14+
contents: write
15+
pull-requests: write
16+
17+
jobs:
18+
update-snapshots:
19+
# Run on workflow_dispatch OR when someone comments "/update-snapshots" on a PR
20+
if: >
21+
github.event_name == 'workflow_dispatch' ||
22+
(github.event.issue.pull_request && contains(github.event.comment.body, '/update-snapshots'))
23+
runs-on: ubuntu-latest
24+
steps:
25+
- name: Get PR branch
26+
if: github.event_name == 'issue_comment'
27+
id: pr
28+
uses: actions/github-script@v7
29+
with:
30+
script: |
31+
const pr = await github.rest.pulls.get({
32+
owner: context.repo.owner,
33+
repo: context.repo.repo,
34+
pull_number: context.issue.number
35+
});
36+
core.setOutput('ref', pr.data.head.ref);
37+
core.setOutput('sha', pr.data.head.sha);
38+
39+
- name: Add reaction to comment
40+
if: github.event_name == 'issue_comment'
41+
uses: actions/github-script@v7
42+
with:
43+
script: |
44+
await github.rest.reactions.createForIssueComment({
45+
owner: context.repo.owner,
46+
repo: context.repo.repo,
47+
comment_id: context.payload.comment.id,
48+
content: 'rocket'
49+
});
50+
51+
- uses: actions/checkout@v4
52+
with:
53+
ref: ${{ github.event.inputs.branch || steps.pr.outputs.ref || github.ref }}
54+
token: ${{ secrets.GITHUB_TOKEN }}
55+
56+
- uses: oven-sh/setup-bun@v2
57+
with:
58+
bun-version: latest
59+
60+
- uses: actions/setup-node@v4
61+
with:
62+
node-version: "20"
63+
64+
- uses: astral-sh/setup-uv@v5
65+
66+
- run: npm ci
67+
68+
- name: Install Playwright browsers
69+
run: npx playwright install --with-deps chromium
70+
71+
- name: Update snapshots
72+
run: npx playwright test --update-snapshots --reporter=list
73+
74+
- name: Commit updated snapshots
75+
id: commit
76+
run: |
77+
git config user.name "github-actions[bot]"
78+
git config user.email "github-actions[bot]@users.noreply.github.com"
79+
git add tests/e2e/**/*.png
80+
if git diff --staged --quiet; then
81+
echo "changed=false" >> $GITHUB_OUTPUT
82+
echo "No snapshot changes to commit"
83+
else
84+
git commit -m "chore: update e2e snapshots [skip ci]"
85+
git push
86+
echo "changed=true" >> $GITHUB_OUTPUT
87+
fi
88+
89+
- name: Comment on PR
90+
if: github.event_name == 'issue_comment'
91+
uses: actions/github-script@v7
92+
with:
93+
script: |
94+
const changed = '${{ steps.commit.outputs.changed }}' === 'true';
95+
const body = changed
96+
? '✅ Snapshots updated and pushed to this branch.'
97+
: '✅ No snapshot changes needed - all snapshots are up to date.';
98+
await github.rest.issues.createComment({
99+
owner: context.repo.owner,
100+
repo: context.repo.repo,
101+
issue_number: context.issue.number,
102+
body
103+
});

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ intermediate-findings/
1111
# Playwright
1212
playwright-report/
1313
test-results/
14+
__pycache__/
15+
*.pyc

.husky/pre-commit

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# Load Node.js environment for GUI apps (GitHub Desktop, etc.)
2+
# that don't inherit shell PATH
3+
export NVM_DIR="$HOME/.nvm"
4+
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # nvm
5+
[ -s "$HOME/.fnm/fnm" ] && eval "$(~/.fnm/fnm env)" # fnm
6+
[ -s "$HOME/.volta/bin/volta" ] && export PATH="$HOME/.volta/bin:$PATH" # volta
7+
[ -d "/opt/homebrew/bin" ] && export PATH="/opt/homebrew/bin:$PATH" # homebrew (macOS ARM)
8+
[ -d "/usr/local/bin" ] && export PATH="/usr/local/bin:$PATH" # homebrew (macOS Intel)
9+
110
# Verify no private registry URLs in package-lock.json
211
if grep -E '"resolved": "https?://' package-lock.json | grep -v registry.npmjs.org > /dev/null; then
312
echo "ERROR: package-lock.json contains non-npmjs.org URLs"

.prettierignore

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
examples/basic-host/**/*.ts
22
examples/basic-host/**/*.tsx
3-
examples/basic-server-react/**/*.ts
4-
examples/basic-server-react/**/*.tsx
5-
examples/basic-server-vanillajs/**/*.ts
6-
examples/basic-server-vanillajs/**/*.tsx
3+
examples/basic-server-*/**/*.ts
4+
examples/basic-server-*/**/*.tsx
5+
examples/quickstart/**/*.ts
6+
**/vendor/**
7+
SKILL.md
78

89
# Swift package manager build artifacts
910
sdk/swift/.build/

AGENTS.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# MCP Apps SDK
2+
3+
## Project Overview
4+
5+
MCP Apps SDK (`@modelcontextprotocol/ext-apps`) enables MCP servers to display interactive UIs in conversational clients.
6+
7+
Key abstractions:
8+
9+
- **View** - UI running in an iframe, uses `App` class with `PostMessageTransport` to communicate with host
10+
- **Host** - Chat client embedding the iframe, uses `AppBridge` class to proxy MCP requests
11+
- **Server** - MCP server that registers tools/resources with UI metadata
12+
13+
Specification (stable): `specification/2026-01-26/apps.mdx`
14+
15+
## Commands
16+
17+
```bash
18+
# Install dependencies
19+
npm install
20+
21+
# Build the SDK only (generates schemas + bundles, does not build examples)
22+
npm run build
23+
24+
# Build everything (SDK + all examples)
25+
npm run build:all
26+
27+
# Type check + build a single example
28+
npm run --workspace examples/<example-name> build
29+
30+
# Run all examples (starts server at http://localhost:8080)
31+
npm start
32+
33+
# Run E2E tests (primary testing mechanism - starts examples server automatically)
34+
npm run test:e2e
35+
36+
# Run unit tests (E2E tests have broader coverage; unit tests cover specific modules)
37+
npm test
38+
39+
# Check JSDoc comment syntax and `{@link}` references
40+
npm exec typedoc -- --treatValidationWarningsAsErrors --emit none
41+
42+
# Regenerate package-lock.json (especially on setups w/ custom npm registry)
43+
rm -fR package-lock.json node_modules && \
44+
docker run --rm -it --platform linux/amd64 -v $PWD:/src:rw -w /src node:latest npm i && \
45+
rm -fR node_modules && \
46+
npm i --cache=~/.npm-mcp-apps --registry=https://registry.npmjs.org/
47+
```
48+
49+
## Architecture
50+
51+
### SDK Entry Points
52+
53+
- `@modelcontextprotocol/ext-apps` - Main SDK for Apps (`App` class, `PostMessageTransport`)
54+
- `@modelcontextprotocol/ext-apps/react` - React hooks (`useApp`, `useHostStyleVariables`, etc.)
55+
- `@modelcontextprotocol/ext-apps/app-bridge` - SDK for hosts (`AppBridge` class)
56+
- `@modelcontextprotocol/ext-apps/server` - Server helpers (`registerAppTool`, `registerAppResource`)
57+
58+
### Key Source Files
59+
60+
- `src/app.ts` - `App` class extends MCP Protocol, handles guest initialization, tool calls, messaging
61+
- `src/app-bridge.ts` - `AppBridge` class for hosts, proxies MCP requests, sends tool input/results to guests
62+
- `src/server/index.ts` - Helpers for MCP servers to register tools/resources with UI metadata
63+
- `src/types.ts` - Protocol types re-exported from `spec.types.ts` and Zod schemas from `generated/schema.ts` (auto-generated during build)
64+
- `src/message-transport.ts` - `PostMessageTransport` for iframe communication
65+
- `src/react/` - React hooks: `useApp`, `useHostStyles`, `useAutoResize`, `useDocumentTheme`
66+
67+
### Protocol Flow
68+
69+
```
70+
View (App) <--PostMessageTransport--> Host (AppBridge) <--MCP Client--> MCP Server
71+
```
72+
73+
1. Host creates iframe with view HTML
74+
2. View creates `App` instance and calls `connect()` with `PostMessageTransport`
75+
3. View sends `ui/initialize` request, receives host capabilities and context
76+
4. Host sends `sendToolInput()` with tool arguments after initialization
77+
5. View can call server tools via `app.callServerTool()` or send messages via `app.sendMessage()`
78+
6. Host sends `sendToolResult()` when tool execution completes
79+
7. Host calls `teardownResource()` before unmounting iframe
80+
81+
## Documentation
82+
83+
JSDoc `@example` tags should pull type-checked code from companion `.examples.ts` files (e.g., `app.ts``app.examples.ts`). Use ` ```ts source="./file.examples.ts#regionName" ` fences referencing `//#region regionName` blocks; region names follow `exportedName_variant` or `ClassName_methodName_variant` pattern (e.g., `useApp_basicUsage`, `App_hostCapabilities_checkAfterConnection`). For whole-file inclusion (any file type), omit the `#regionName`. Run `npm run sync:snippets` to sync.
84+
85+
Standalone docs in `docs/` (listed in `typedoc.config.mjs` `projectDocuments`) can also have type-checked companion `.ts`/`.tsx` files using the same pattern.
86+
87+
## Full Examples
88+
89+
Uses npm workspaces. Full examples in `examples/` are separate packages:
90+
91+
- `basic-server-*` - Starter templates (vanillajs, react, vue, svelte, preact, solid). Use these as the basis for new examples.
92+
- `basic-host` - Reference host implementation
93+
- Other examples showcase specific features (charts, 3D, video, etc.)
94+
95+
## Claude Code Plugin
96+
97+
The `plugins/mcp-apps/` directory contains a Claude Code plugin distributed via the plugin marketplace. It provides the following Claude Code skills files:
98+
99+
- `plugins/mcp-apps/skills/create-mcp-app/SKILL.md` — for creating an MCP App
100+
- `plugins/mcp-apps/skills/migrate-oai-app/SKILL.md` — for migrating an app from the OpenAI Apps SDK to the MCP Apps SDK

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AGENTS.md

0 commit comments

Comments
 (0)