* chore: bump ext-apps to 1.1.2
Changes since 1.1.1:
- feat: add ui/download-file method for host-mediated file downloads (#475)
- pdf-server: robust path validation + folder/file root support (#497)
- ci: auto-fix prettier formatting in pre-commit hook and CI (#498)
* fix: sync local SDK build into node_modules for examples
npm workspaces hoists example dependencies to the root node_modules,
but installs the published registry copy of @modelcontextprotocol/ext-apps
instead of linking to the local source. This causes type-check failures
when examples use features not yet published to npm.
Add scripts/link-self.mjs which copies the freshly-built dist/ and
package.json into the hoisted node_modules copy after each SDK build,
ensuring examples always type-check against the latest local types.
See: https://github.com/npm/feedback/discussions/774
* Revert "fix: sync local SDK build into node_modules for examples"
This reverts commit e3387e8434f3def030f5a223a70632092c929119.
* pdf-server: support folders in cli + files in roots
* fix(pdf-server): use path.relative for robust ancestor dir matching
- Replace string prefix matching (startsWith) with path.relative-based
isAncestorDir() which correctly handles trailing slashes, path
normalization, and prevents both .. traversal and prefix attacks.
- Use path.resolve consistently when adding paths to allowedLocalFiles
and allowedLocalDirs (main.ts was missing this).
- Use resolved path for exact file match check in validateUrl.
- Improve error message formatting for easier debugging.
- Add tests for trailing slashes, grandparent dirs, and isAncestorDir.
* chore(pdf-server): remove JSONL interaction logging
Remove the withLogging transport wrapper, logInteraction helper,
InteractionLogger type, and all log parameter threading that were
added for debugging.
* fix(pdf-server): support computer:// URL scheme for local files
Some clients (e.g. Claude Code) use computer:// instead of file://
for local file references. Handle both schemes in isFileUrl and
fileUrlToPath.
* fix(pdf-server): add debug logging for rejected files, ignore .jsonl logs
* fix(pdf-server): accept bare file paths in addition to file:// URLs
Clients like Claude Desktop may pass raw absolute paths (e.g.
/Users/.../file.pdf) instead of file:// URLs. Handle these in
validateUrl and readPdfRange.
* debug(pdf-server): add hex char diagnostics for path mismatch
* fix(pdf-server): resolve symlinks to handle sandbox path remapping
Use fs.realpathSync to resolve symlinks/bind mounts when comparing
file paths against allowed directories. This fixes the path namespace
mismatch where Claude Desktop sends container paths (e.g. /sessions/...)
while MCP roots use host paths (e.g. /Users/...).
Both the file path and directory paths are resolved through symlinks
before comparison, so either side can be a symlink.
Also stores realpath of roots in allowedLocalDirs/Files so matching
works in both directions.
Removes verbose hex diagnostics (no longer needed).
* fix(pdf-server): decode percent-encoded bare paths (e.g. %20 for spaces)
Clients may send bare file paths with percent-encoded characters
(e.g., Application%20Support). Apply decodeURIComponent to bare paths
before resolving, matching the behavior already used for file:// URLs.
* fix(pdf-server): add full diagnostics to client-facing error message
Include url, resolved path, realpath, allowedFiles, and per-directory
path.relative results in the error returned to the client, so we can
diagnose why isAncestorDir fails when paths look identical.
* debug(pdf-server): add hex dump to diagnose invisible char differences
path.relative returns 10 levels of .. despite paths looking identical,
meaning they differ at the byte level. Add hex dump of first 30 chars
of both paths to catch invisible Unicode or encoding differences.
* fix(pdf-server): use inode comparison for bind-mount path matching
The cowork VM remaps paths via bind mounts: the tool input uses
VM-internal paths (/sessions/...) while MCP roots use host paths
(/Users/...). Since path.relative can't match across bind mounts,
compare directory inodes instead (dev+ino from fs.statSync).
Walks up the file's parent directories checking each against allowed
dirs by inode, which works regardless of path namespace differences.
* fix(pdf-server): resolve VM-internal paths via directory basename matching
When the MCP server runs on the host but receives unrewritten VM paths
(e.g., /sessions/name/mnt/uploads/file.pdf), the tool call url argument
isn't rewritten by the cowork VM proxy because it doesn't know which
arguments are file paths.
Fix: when path validation fails, try to match the file by finding the
directory basename (e.g., 'uploads') in the VM path and looking for
the relative path suffix under each allowed directory on the host.
Also returns the resolved host path so readPdfRange reads the correct
file on the host filesystem.
* revert(pdf-server): remove resolveVmPath hack
The VM path mismatch (VM-internal paths not rewritten in tool call
arguments) should be fixed in the cowork VM proxy layer, not worked
around in the MCP server.
Removes:
- resolveVmPath() function
- resolvedPath field from validateUrl return type
- effectiveUrl remapping in tool handlers
- VM path resolution test
* nit
Bug fixes / improvements since 1.1.0:
- fix: add audio/video support for basic-host (#460)
- fix(pdf-server): remove page-loading overlay (#493)
- pdf-server: remove domain allowlist, require HTTPS only (#487)
- Set audioSession.type to playback in sheet music app (#489)
- Add MCPB packaging and Claude Code plugin for pdf-server (#491)
Use the Audio Session API to declare audio as media playback so the
platform handles it correctly: background audio, lock screen controls,
Dynamic Island integration, and bypassing the iOS silent switch.
* Add MCPB packaging and Claude Code plugin for pdf-server
Two distribution paths for the pdf-server:
1. **MCPB bundle** (Claude Desktop MCP Directory):
- manifest.json: v0.3 manifest with tools, compatibility, privacy
- .mcpbignore: excludes source/tests from bundle
- icon.png: 64x64 PDF document icon
2. **Claude Code plugin** (plugin/):
- .mcp.json: declares local stdio server via npx
- commands: /pdf-research:read, /pdf-research:summarize
- skills: pdf-reading skill for document navigation
- Tested end-to-end: MCP server connects, all tools available
The plugin uses a local stdio MCP server pattern — the first known
instance in a knowledge-work plugin (all existing plugins use remote
HTTP connectors).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Run prettier:fix to satisfy CI formatting checks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the hardcoded list of allowed remote origins with a simple
HTTPS protocol check. Any HTTPS URL is now accepted for remote PDFs.
Local file allowlisting remains unchanged.
* fix(pdf-server): remove page-loading overlay
The overlay had multiple issues:
- Hardcoded white background broke dark mode
- Could get stuck visible, breaking sizing/layout
- Added little value since previous page stays visible during render
and the 150ms delay meant most renders completed before it showed
* prettier:fix
* chore: bump ext-apps to 1.1.0
Features:
- registerAppResource now returns the registered resource (#370)
- Accept UIResourceMeta in both resources/list and resources/read (#410)
- callServerTool throws helpful error when called with a string (#449)
- Add double-connect guard to prevent protocol message handling errors (#450)
Bug fixes:
- Only ignore messages that lack jsonrpc 2.0 in message-transport (#448)
- Align basic-host dark mode styles (#438)
Spec:
- Change Host <> Sandbox communication protocol to SHOULD (#435)
- Clarify UIResourceMeta in both list and read responses (#410)
* fix(debug-server): add missing npm package config (main, bin, types, exports)
Align debug-server package.json with all other server examples:
- main: dist/server.js (was server.ts)
- types: dist/server.d.ts
- bin: mcp-server-debug -> dist/index.js
- exports with types
- files: only dist (remove source server.ts)
* lazy PDF loading w/ PDFDataRangeTransport
* lint
* feat: lazy PDF loading via range transport + eager background preloading
Replace full-file download with PDFDataRangeTransport so PDF.js fetches
byte ranges on demand. First page renders immediately; remaining pages
load in background for search.
**Range transport (server + client)**
- server.ts: add read_pdf_bytes tool returning base64 chunks with offset
- mcp-app.ts: AppRangeTransport feeds PDF.js via fetchRange/fetchChunk
with parallel sub-requests for large ranges, Promise-based dedup of
concurrent requests, and a persistent range cache
**Background preloader**
- Sequential page 1→N text extraction, pauses during interactive
navigation (preloadPaused flag set in goToPage, cleared in renderPage)
- Debounced live search refresh (500ms) as pages load
- Search UI shows '(loading…)' suffix until all pages processed
**Loading indicators**
- SVG pie in toolbar: fills as pages load, fades on completion,
turns red on errors with tooltip listing failed pages
- Debounced page-loading overlay (150ms) for uncached page navigation
**Cleanup**
- Remove old full-file chunked download and progress bar
- Remove extractAllPageText (replaced by preloader)
- Remove verbose debug logs (Coverage, RangeReq, cache hit)
Add padding: 0 !important to .main.fullscreen so that
safe area insets applied via inline styles are overridden,
making the PDF viewer truly fullscreen edge-to-edge.
Also add .build directories to .prettierignore to fix
pre-commit hook failures from Swift build artifacts.
When the client advertises roots capability, the server queries roots/list
on initialization and subscribes to roots/list_changed notifications.
Any file:// roots pointing to existing directories are added to an
allowedLocalDirs set, enabling access to all PDFs under those directories
without needing to enumerate them via CLI args.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: revert unrelated changes from bad rebase
Restores files that were accidentally modified alongside the MCP roots
feature: README, pdf-server UI (search feature), threejs example,
SDK guards (app.ts, app-bridge.ts), typedoc theme, media assets,
and e2e snapshots.
Replace custom {filename, content, mimeType, encoding} params with
standard MCP resource types: EmbeddedResource for inline content and
ResourceLink for host-resolved references.
New params shape:
params: { contents: (EmbeddedResource | ResourceLink)[] }
This reuses existing MCP primitives instead of inventing new ones,
and enables hosts to show resource 'bubbles' or resolve ResourceLink
URIs via resources/read on the MCP server.
Add a new ui/download-file JSON-RPC request that allows MCP App views to
request file downloads through the host. Since MCP Apps run in sandboxed
iframes where direct downloads are blocked (allow-downloads not set),
this provides a host-mediated mechanism for file exports.
Spec changes (draft):
- Add ui/download-file request to Requests (View → Host) section
- Add downloadFile capability to HostCapabilities
- Document host behavior requirements (confirmation dialog, filename
sanitization, base64 decoding, size limits)
SDK changes:
- Add McpUiDownloadFileRequest/Result interfaces to spec.types.ts
- Add downloadFile() method to App class
- Add ondownloadfile setter to AppBridge class
- Add DOWNLOAD_FILE_METHOD constant
- Generate Zod schemas
Example:
- Add download demo to integration-server (exports JSON with server time)
- Capability check: only shows download button when host advertises
downloadFile capability
Based on developer feedback requesting file export capabilities for
visualization tools, document editors, and data analysis apps.
* Add PDF search feature to pdf-server
Adds full-text search functionality to the PDF viewer:
- Search button in toolbar (Ctrl/Cmd+F to open)
- Search bar with input, prev/next navigation, match count
- Text extraction and caching across all pages
- Highlight overlays using DOM Range API
- Case-insensitive search across entire document
- Keyboard shortcuts: Ctrl+F (open), Enter (next), Shift+Enter (prev), Escape (close)
Changes:
- mcp-app.html: Add search button, search bar, highlight layer
- src/mcp-app.css: Add search/highlight styles and PDF.js text layer rules
- src/mcp-app.ts: Add search state, functions, event listeners
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* update screenshots
* Add fullscreen button to threejs-server
* Fix prettier formatting
* Move search bar below toolbar, right-aligned
* Make search bar compact and right-aligned
* Float search bar over page content
* Ctrl/Cmd+F toggles search; pass through to browser when empty
* Hide search nav controls until query is entered
* Skip scroll when search highlight is already visible
* Fix search bar right-edge alignment with container border
* Prevent browser find when our search is open with text
* Cmd+F when search input is focused passes through to browser find
* Fix prettier formatting in README
* Remove unrelated formatting and threejs changes from PR
* prettier
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Users frequently call callServerTool("tool_name", args) instead of the
correct callServerTool({ name: "tool_name", arguments: args }), resulting
in a silent/confusing failure. Detect this and throw immediately with
a message that shows the correct call shape.
Also fixes the incorrect example in examples/pdf-server/README.md that
demonstrated this same wrong usage pattern.
Fixes#386https://claude.ai/code/session_01GqAyN4Ux7svWoU2HqsSLZF
Co-authored-by: Claude <noreply@anthropic.com>
* Clarify spec and SDK: accept UIResourceMeta in both resources/list and resources/read
The spec defines UIResource with _meta.ui, implicitly as an extension of the Resource type (resources/list), but examples and practice place _meta.ui on TextResourceContents (resources/read). The registerAppResource helper accepts _meta.ui in its config (which ends up in resources/list), but examples only return said _meta.ui from resources/read — and current hosts might be just ignoring it.
This change:
- Adds a 'Metadata Location' subsection to the draft spec explicitly stating that _meta.ui is valid on both the resource listing entry AND the content item, with content-item taking precedence.
- Updates the spec example to show _meta on the resource declaration (resources/list).
- Updates the CSP construction example to reference both locations.
- Clarifies McpUiAppResourceConfig JSDoc: _meta here appears in resources/list and serves as a listing-level fallback for when content items omit _meta.ui.
- Fixes the map-server example comment that incorrectly stated _meta must only be on the content item.
* Add McpUiReadResourceResult type for UI metadata in resources/read
* basic-host: support UIResourceMeta fallback from resources/list
---------
Co-authored-by: Claude <noreply@anthropic.com>
Single quotes in `concurrently` commands work on Unix shells but not on
Windows Command Shell (`cmd.exe`), causing the `INPUT` environment
variable to not be set and npm scripts to fail with errors like:
'cross-env' is not recognized as an internal or external command
Error: INPUT environment variable is not set
Replace single quotes with escaped double quotes (`\"`) in npm scripts
in all example `package.json` files.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(pdf-server): handle HTTP 200 fallback in remote range requests
When a remote server ignores the Range header and returns HTTP 200
with the full body, readPdfRange previously passed the entire file
(potentially 10MB+) through as a single chunk. This bypassed the
512KB limit because:
1. The error check `!response.ok && status !== 206` short-circuits
on 200 (ok is true), so the full body is read via arrayBuffer()
2. No Content-Range header on a 200 response leaves totalBytes at 0
3. hasMore becomes `offset + fullSize < 0` = false, so the client
stops after one oversized message
Fix: detect HTTP 200, cache the full body in memory (to avoid
re-downloading on every subsequent chunk request), then slice to the
requested range. The 512KB per-message limit is now enforced for all
remote URLs regardless of Range request support.
* Add session-based PDF caching with timeout cleanup and size limits
- Add dual timeout strategy for cache cleanup:
- 10s inactivity timeout (resets on each access)
- 60s max lifetime (absolute timeout from creation)
- Add 50MB max size limit with both Content-Length and actual size checks
- Add unit tests for caching behavior including:
- Cache on HTTP 200 response (no range support)
- No cache on HTTP 206 response (range supported)
- Slice cached data for subsequent range requests
- Reject PDFs exceeding size limits
- Export getCacheSize() and clearCache() for testing
* refactor(pdf-server): make PDF cache session-local via factory pattern
- Replace module-level global cache with createPdfCache() factory
- Each server instance now gets its own isolated cache
- Export PdfCache interface for type-safe usage
- Update tests to use per-test cache instances
- Add test verifying cache isolation between sessions
---------
Co-authored-by: Claude <noreply@anthropic.com>
Update `mcp-app.css`, `mcp-app.module.css`, and Svelte/Vue component
styles across all `basic-server-*` examples (and `integration-server`)
to use CSS variables instead of hardcoded values:
- Remove component-local color variables (`--color-primary`, etc.)
- Add derived spacing variables to `global.css` based on `--spacing-unit`
- Replace hardcoded spacing with `--spacing-xs/sm/md/lg` variables
- Add app-defined accent color variables (`--color-accent`,
`--color-text-on-accent`) to `global.css` for brand customization
- Use app accent colors for button styling
- Update button hover to use `color-mix()` for theme-adaptive lightening
- Change focus-visible to use `--color-ring-primary` outline
This makes the templates more instructive for users and AI coding agents
building new MCP Apps by showing how to consume host-provided variables
semantically, define app-specific accent colors, and create adaptive
styling that integrates with host themes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update `global.css` in all `basic-server-*` examples (and `integration-server`
for consistency) to better serve as templates for AI coding agents scaffolding
new MCP Apps.
Changes:
- Use CSS custom properties (`--font-sans`, `--color-text-primary`, etc.) with
sensible fallbacks, enabling host design system integration
- Add `color-scheme: light dark` and `light-dark()` fallbacks for theme support
- Include comprehensive typography: h1-h6, body text, `code`/`pre`/`kbd`, `b`/`strong`
- Move `#server-time` styles from global CSS to component stylesheets (classes
instead of IDs for better CSS practices)
Updates E2E test selectors in `DYNAMIC_MASKS` to match the new class-based
selectors.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The two-tool architecture previously had both tools returning identical
`SystemStats` data. This refactors them to properly separate concerns:
- `get-system-info` (Model-visible): Returns static system configuration
(hostname, platform, CPU model/count, total memory) sent once via
`ontoolresult`
- `poll-system-stats` (App-only): Returns dynamic metrics (per-core CPU
timing, memory usage, uptime) polled every 2 seconds
Moves `formatBytes()` and `formatUptime()` utilities to the client since
the server now returns raw values for client-side formatting.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The tool input JSON examples for Gradient and Kaleidoscope had literal
newlines in the string values, making them invalid JSON. Changed to use
`\n` escape sequences to match the Interactive Julia Set example.
Fixes regression from 574a768.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Apply the same pattern as map-server: capture the PDF canvas as a
scaled-down screenshot and include it in the model context update
when the host supports image content.
- Check host capability via getHostCapabilities().updateModelContext.image
- Scale canvas to max 768px dimension to reduce token usage
- Convert to base64 PNG and include with text content
* fix(examples): use npm reference for ext-apps dependency
Replace local path '../..' with '^1.0.0' npm reference in all example
packages. The local path was accidentally published to npm, causing
npm install to hang when installing server-* packages.
Bump versions to 1.0.1 for all public packages to fix the published
versions.
Fixes npm install hanging issue in dependent projects like
example-remote-server.
Note: npm workspaces automatically resolves the dependency to the
local package during development, so no overrides are needed.
* fix: update zod to 4.x in transcript-server and fix update-lock:docker script
- Update transcript-server to use zod ^4.1.13 for compatibility
- Fix update-lock:docker script to properly clean workspace node_modules
- Regenerate package-lock.json with clean npmjs.org URLs
The main branch of the MCP Python SDK has removed `FastMCP` (likely
moving it to the standalone `fastmcp` package), causing import errors.
Pin to mcp>=1.26.0 which is the latest release that still includes
`mcp.server.fastmcp`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix(qr-server): use stable mcp SDK from PyPI
- Replace git-based mcp dependency with mcp>=1.26.0 from PyPI for stability
- Remove TransportSecuritySettings that was blocking browser connections
- Keep simple FastMCP initialization like other Python examples
Fixes ModuleNotFoundError and 403 Forbidden errors when connecting from basic-host
* fix(qr-server): use stable mcp SDK from PyPI
- Replace git-based mcp dependency with mcp>=1.26.0 from PyPI
- Add stateless_http=True for proper HTTP transport handling
- Remove TransportSecuritySettings that blocked browser connections
* chore: add stable spec release 2026-01-26
- Use git mv to copy specification from draft to specification/2026-01-26/apps.mdx (preserves history)
- Update status from 'Draft' to 'Stable (2026-01-26)'
- Add specification version table to README
- Update Resources section with links to both stable and draft specs
* chore: bump version to 1.0.0
- Update main package version from 0.4.2 to 1.0.0
- Update all example package versions to 1.0.0
- Regenerate package-lock.json
* prettier
* chore: align protocol version with spec release 2026-01-26
- Add 'Protocol Version: 2026-01-26' to spec header
- Update LATEST_PROTOCOL_VERSION from '2025-11-21' to '2026-01-26'
- Update spec examples to use '2026-01-26' protocol version
- Add '2025-11-21' to SUPPORTED_PROTOCOL_VERSIONS for backwards compatibility
* chore: update spec references to stable 2026-01-26
- Update typedoc.config.mjs navigation link
- Update docs/overview.md spec links (10 occurrences)
- Update plugins/mcp-apps/skills/create-mcp-app/SKILL.md spec link
- Update src/spec.types.ts @see links
- Update AGENTS.md spec path
- Regenerate src/generated/schema.ts with updated links
* chore: simplify versioning - first stable release
- Remove backwards compatibility version (2025-11-21) - this is the first stable release
- Remove redundant 'Protocol Version' header - spec version IS the protocol version
* chore: add history reference comment to stable spec
* docs: add local development MCP client configuration to READMEs
Add instructions for configuring MCP clients to use locally-cloned
and modified servers. This allows developers to test local changes
with MCP clients like Claude Desktop or VS Code.
- Top-level README: Added 'Local Development' section after npx config
- All TypeScript examples: Added 'Local Development' subsection with
bash one-liner that builds and runs the server
- Python examples (qr-server, say-server): Added local path config
* docs: include all servers in local development config
* docs: use bash wrapper for Python server configs to enable tilde expansion
* Adding a simple (bad) pdf icon for directory (#330)
* chore: bump version to 0.4.2
- Update package version from 0.4.1 to 0.4.2
- Pin @types/node to 22.10.0 for dev compatibility
- Override seroval to ^1.4.1 to fix security vulnerabilities
- Use '*' for workspace SDK dependency (works with unpublished versions)
* fix(e2e): update Map Server display name to CesiumJS Map Server (#324)
* fix(test): update map-server test to expect 'CesiumJS Map Server'
The server name was renamed but the E2E test config was not updated.
* fix: change in another file
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Olivier Chafik <ochafik@anthropic.com>
* feat(server): add getUiCapability helper for capability negotiation (#313)
* feat(server): add hasUiSupport/getUiCapability for experimental+extensions
Support double-tagging for MCP Apps capability negotiation:
- Check both experimental and extensions fields in client capabilities
- Add hasUiSupport() to easily check if client supports MCP Apps
- Add getUiCapability() to retrieve the capability settings
- Update spec to document both capability locations
This enables forward compatibility as MCP transitions from experimental
to the extensions field (SEP-1724).
Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 0
Claude-Permission-Prompts: 0
Claude-Escapes: 0
* fix: update @types/node version in package-lock.json
Replace invalid 22.19.5/22.19.6/22.19.7 versions with valid 22.19.3
* docs(spec): clarify hosts SHOULD double-tag, servers SHOULD check both
- Hosts SHOULD advertise capabilities in both experimental and extensions
- Servers SHOULD check both locations, preferring extensions when present
- Updated example to show double-tagging pattern
* chore: remove double-tagging recommendation, keep hasUiSupport helper
Reverts the spec changes that recommended clients double-tag capabilities
in both experimental and extensions fields. The helper functions remain
as they're useful for checking capability in either location.
* refactor(server): simplify to only getUiCapability, remove hasUiSupport
- Remove hasUiSupport function (use getUiCapability directly)
- Remove double-tagging logic (only check extensions field)
- Add ClientCapabilitiesWithExtensions type using SDK's ClientCapabilities
- Update spec to use getUiCapability
- Simplify tests
* fix: update @see link to getUiCapability
* refactor(server): inline ClientCapabilitiesWithExtensions type in getUiCapability
* refactor: move McpUiClientCapabilities to spec.types.ts
* fix: update playwright docker image to v1.58.0
* fix: use -i instead of -it for docker commands (no TTY needed)
* fix: Python server dependency resolution and Docker host validation (#333)
* fix(say-server): add --index flag to uv run for PyPI resolution
The pocket-tts dependency wasn't resolving without explicit --index flag
when invoked via npm scripts.
* fix(qr-server): allow Docker bridge IP in transport security
Add 172.17.0.1:* to allowed_hosts to fix 421 Misdirected Request errors
when the server is accessed from Docker containers.
* feat(basic-host): Add theme toggle and MCP style variables (#336)
* feat(basic-host): add theme toggle and MCP style variables
Add theme support to basic-host example:
- theme.ts: Global theme manager with light/dark toggle
- host-styles.ts: Full set of MCP CSS variables using light-dark()
- ThemeToggle component with sun/moon icons
- Theme-aware CSS using CSS custom properties
- Pass theme + styles to apps via hostContext
- Notify apps when theme changes via sendHostContextChange
Apps now receive:
- hostContext.theme: 'light' | 'dark'
- hostContext.styles.variables: All 60+ MCP CSS variables
- onhostcontextchanged notifications when theme changes
* test: update e2e goldens for theme changes
* [MCP Apps] Add appCapabilities.availableDisplayModes documentation to spec (#331)
* Add appCapabilities.availableDisplayModes documentation to spec
Document the availableDisplayModes field in McpUiAppCapabilities that allows
apps to declare which display modes they support. This enables hosts to
compute the intersection of host and app capabilities and only offer
appropriate display mode controls.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Address PR review feedback
- Change "Guest UI" to "View" terminology
- Add "View" prefix to availableDisplayModes heading for disambiguation
- Remove intersection logic - host only checks its own supported modes
- Remove assumption that apps must support inline mode
- Simplify host behavior to honor/reject based on its own capabilities
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add appCapabilities.availableDisplayModes to types
- Add `availableDisplayModes` field to `McpUiAppCapabilities` so apps can
declare which display modes they support
- Change `HostContext.availableDisplayModes` type from `string[]` to
`McpUiDisplayMode[]` for type safety
- Regenerate schemas
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Address Olivier's feedback - consolidate display mode docs
- Remove separate "App Capabilities" section for availableDisplayModes
- Add display mode requirements to existing ui/request-display-mode section:
- Host MUST NOT switch View to mode not in appCapabilities.availableDisplayModes
- Host MAY decline requests from Views that didn't declare modes
- View MUST declare supported modes during initialization
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Restore McpUiAppCapabilities docs and fix display mode clause
- Restore McpUiAppCapabilities interface documentation with availableDisplayModes
- Restore "View MUST check if the requested mode is in availableDisplayModes
from host context" clause that was accidentally removed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Create unified Display Modes section
Consolidate all display mode documentation into a single section:
- Display mode types (inline, fullscreen, pip)
- Declaring support from View (appCapabilities.availableDisplayModes)
- Declaring support from Host (HostContext.availableDisplayModes)
- Requesting changes (ui/request-display-mode)
- Notifying changes (ui/notifications/host-context-changed)
- All MUST/SHOULD requirements for View and Host behavior
The ui/request-display-mode in Requests section now references
the Display Modes section for behavior details.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Remove mode coercion clause from display modes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Regenerate schemas with correct version
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(say-server): add light/dark theme support (#338)
- Add importmap entry for main SDK exports (applyDocumentTheme, etc.)
- Import and apply host theme and styles on context changes
- Apply initial host context theme/styles when app connects
- Update CSS to use [data-theme="dark"] selector for host-provided themes
- Keep @media prefers-color-scheme as fallback for standalone mode
- Add color-scheme meta tag for native element theming
* fix: followup app -> view terminology (#339)
* chore: husky env fix, version pins, update-lock:docker script (#341)
* fix(husky): load Node.js env for GUI apps + add update-lock:docker script
Cherry-picked non-e2e changes from ochafik/fix-e2e-flaky-tests:
- Load nvm/fnm/volta/homebrew paths in pre-commit for GitHub Desktop
- Add update-lock:docker script to regenerate lockfile with public registry
* pin package versions for dev compatibility
- solid-js: 1.9.10
- caniuse-lite: 1.0.30001763
- cheerio: 1.1.2
- electron-to-chromium: 1.5.267
- @playwright/test: 1.57.0
- playwright: 1.57.0
- playwright-core: 1.57.0
* regen
* fix: pin @types/node to existing version and regenerate lockfile
- Fix @types/node 20.19.28 -> 20.19.27 (typo, version didn't exist)
- Regenerate package-lock.json to resolve versions available on both
public npm and Anthropic artifactory registries
* fix(basic-server-solid): bump solid-js 1.9.10 -> 1.9.11 to fix seroval vulnerabilities
* fix: pin seroval to 1.4.1 for dev compatibility
* Refactor server startup functions
Rename `startServer` to `startStreamableHTTPServer` and add a new
`startStdioServer` helper to make the transport type explicit. Remove
the `ServerOptions` interface in favor of reading `PORT` directly from
the environment inside the function (defaulting to `3001`).
Also update Python examples to default to port `3001` for consistency.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Support full-file inclusion in synced code snippets
Previously, the script only supported extracting regions from `.ts/.tsx`
files using the `#regionName` syntax. Now it also supports including
entire files without a region specifier, enabling sync of any file type
(JSON, YAML, shell scripts, etc.) into documentation.
Changes:
- Make `#regionName` optional in the `source=""` attribute
- Accept any fence language (not just `ts`/`tsx`)
- Simplify cache to flat map with composite keys
- Add validation error for region extraction on non-TypeScript files
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Rewrite Quickstart guide with type-checked code examples
Rewrites the Quickstart tutorial to provide a more focused, step-by-step
introduction to building MCP Apps. The guide now uses synced code fences
that pull from `examples/quickstart/`, ensuring all code shown in the
documentation is type-checked and tested.
Key improvements:
- Clearer project setup instructions with explicit commands
- Streamlined explanations of the tool + resource registration pattern
- Added screenshot showing the completed app
- E2E test coverage validates the example works end-to-end
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add MCP Apps Overview document
Adds `docs/overview.md` covering the core concepts: architecture,
lifecycle, UI resources, bidirectional communication, theming, display
modes, and security. Includes mermaid flowchart and sequence diagrams.
Also adds the `@boneskull/typedoc-plugin-mermaid` TypeDoc plugin for
mermaid diagram support. However, the plugin handles HTML entities from
TypeDoc incorrectly, so `scripts/typedoc-plugin-fix-mermaid-entities.mjs`
decodes HTML entities before the plugin processes them.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Agent Skills installation guide
Add `docs/agent-skills.md` with installation instructions for multiple AI
coding agents including Claude Code, VS Code/Copilot, Gemini CLI, Cline,
and Goose. The guide provides three installation methods: Claude Code
plugin, Vercel Skills CLI (`npx skills add`), and manual installation.
Update `README.md` to link to the new guide and reframe the plugin
section around the broader "Agent Skills" concept.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Polish OpenAI migration reference for readability
Add framing prose to the migration guide to improve navigation:
- Expanded intro describing document purpose and how to use it
- Added note about features marked "Not yet implemented"
- Added brief transition sentences before Server-Side and Client-Side sections
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix mermaid diagram arrowheads not rendering
The `@boneskull/typedoc-plugin-mermaid` creates both dark and light theme
variants of each diagram, each with identical marker IDs (e.g., `#arrowhead`).
When mermaid renders SVGs at runtime, lines reference `url(#arrowhead)`, but
the browser resolves this to the first match in document order — the hidden
dark-theme SVG — causing arrows to be invisible.
The fix removes dark-theme `<div class="mermaid dark">` elements at build time,
leaving only one SVG per diagram with unique marker IDs. A CSS filter
(`filter: invert(1) hue-rotate(180deg)`) handles dark mode styling instead.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(docs): improve OpenAI migration guide accuracy (#349)
- Add missing setOpenInAppUrl API to 'Not Yet in MCP Apps' section
- Clarify Event Handling table: specify exact OpenAI properties
(toolInput/toolOutput) instead of vague 'window.openai.*'
- Improve notes to distinguish sync property reads vs callbacks
* Add Testing MCP Apps guide
Document how to test MCP Apps using `basic-host` and MCP Apps-compatible
hosts like Claude.ai and VS Code Copilot. Cover debugging features for
`basic-host`, and using `cloudflared` to expose local servers for remote
hosts.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix `onhostcontextchanged` handler invocation in examples
`App.onhostcontextchanged` is a setter-only property with no getter.
TypeScript doesn't prevent reading from setter-only properties, so
`app.onhostcontextchanged?.(ctx)` compiles but silently returns
`undefined` at runtime.
Extract the handler into a named `handleHostContextChanged()` function
and call it directly after `connect()`, matching the pattern used in
`basic-server-vanillajs` and other examples.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Expand Agent Skills guide
Add intro explaining what Agent Skills are and link to the two skill
files (`create-mcp-app`, `migrate-oai-app`). Reorganize headings and add
"Invoke a Skill" and "Test Your App" sections. Update `README.md` link
text and add tip to quickstart suggesting agent skills as an alternative.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(e2e): improve test stability and developer experience (#337)
* fix(e2e): add masks for dynamic content and increase timeouts for slow servers
- Add HOST_MASKS for Tool Input/Result panels with dynamic timestamps
- Add say-server to SLOW_SERVERS (10s) for TTS model download
- Add say-server to EXTRA_WAIT_MS (30s) in generate-grid-screenshots
* feat: add update-lock:docker script to regenerate lockfile with public registry
* pin package versions for dev compatibility
* feat(basic-host): add ?theme=hide query param to hide theme toggle in screenshots
- Add hideThemeToggle param to getQueryParams()
- Conditionally render ThemeToggle based on ?theme=hide
- Update e2e tests to use ?theme=hide for consistent screenshots
* update screenshots
* update screenshots
* fix(husky): load Node.js environment for GUI apps like GitHub Desktop
* fix(e2e): increase map-server wait time to 15s for tile loading
* fix(e2e): increase waitForAppLoad timeout to 30s for nested iframe loading
* ci: add workflow to update e2e snapshots
* ci: trigger snapshot update on push to this branch
* chore: update e2e snapshots [skip ci]
* ci: trigger CI after snapshot update
* style: fix prettier formatting in update-snapshots workflow
* fix: pin seroval, seroval-plugins, and solid-js for dev compatibility
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* Improve README structure and clarity
Add "Why MCP Apps?" section to explain the motivation upfront. Reorganize
the "How It Works" section to lead with tool definition. Rename "Overview"
to "Using the SDK" and add "For" prefix to audience headings.
Restructure the Examples section with clearer subsections for running
examples with `basic-host` vs MCP clients using stdio transport. Move the
`qr` server note outside the collapsed details block.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Codex and Goose to documentation
Add Codex to the list of agents supporting skills in `agent-skills.md`.
Add Goose to the list of supported clients in `testing-mcp-apps.md` and
rename "VS Code Copilot" to "VS Code (Insiders)" for accuracy.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: regenerate lockfile with public registry, use ^0.4.2 for ext-apps deps
* fix: update quickstart example to use workspace reference
* chore: remove internal registry references from lockfile
* feat(ci): add /update-snapshots comment trigger for PRs
- Comment '/update-snapshots' on any PR to update E2E snapshots
- Adds rocket reaction to acknowledge the command
- Comments back with result (updated or no changes needed)
- Removes hardcoded branch trigger
- Keeps workflow_dispatch for manual runs
* docs: document /update-snapshots PR comment trigger
* fix: pin @hono/node-server to 1.19.7 for dev compatibility
SDK 1.25.3 requires @hono/node-server@^1.19.9 which doesn't exist
on the public npm registry (latest is 1.19.7).
* fix: pin @modelcontextprotocol/sdk to 1.25.2
SDK 1.25.3 has broken dependency on @hono/node-server@^1.19.9
which doesn't exist on public npm registry.
* fix: restore flexible SDK peerDep range (^1.24.0)
devDependencies stays pinned to 1.25.2 for our builds,
but peerDependencies should allow users flexibility.
* fix: pin @types/node to avoid unpublished versions
- Pin quickstart @types/node to 22.19.5 (latest public 22.x)
- Add @types/node override to root package.json
- Regenerate lockfile with public registry only
---------
Co-authored-by: Tobin South <tobin.south@gmail.com>
Co-authored-by: Liad Yosef <liady@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: martinalong <martinalong10@gmail.com>
Co-authored-by: Ido Salomon <idosalomon@gmail.com>
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* feat(map-server): add screenshot to updateModelContext and remove rounded corners
- Remove border-radius and overflow: hidden from cesiumContainer CSS
- Capture map canvas as base64 PNG and include in updateModelContext
- Only include image if host supports image content blocks
* feat(map-server): reduce screenshot size for fewer tokens
- Scale down to max 256px (longest dimension)
- Use PNG for better quality with map graphics
* prettier
* fix(e2e): add masks for dynamic content and increase timeouts for slow servers
- Add HOST_MASKS for Tool Input/Result panels with dynamic timestamps
- Add say-server to SLOW_SERVERS (10s) for TTS model download
- Add say-server to EXTRA_WAIT_MS (30s) in generate-grid-screenshots
* feat: add update-lock:docker script to regenerate lockfile with public registry
* pin package versions for dev compatibility
* feat(basic-host): add ?theme=hide query param to hide theme toggle in screenshots
- Add hideThemeToggle param to getQueryParams()
- Conditionally render ThemeToggle based on ?theme=hide
- Update e2e tests to use ?theme=hide for consistent screenshots
* update screenshots
* update screenshots
* fix(husky): load Node.js environment for GUI apps like GitHub Desktop
* fix(e2e): increase map-server wait time to 15s for tile loading
* fix(e2e): increase waitForAppLoad timeout to 30s for nested iframe loading
* ci: add workflow to update e2e snapshots
* ci: trigger snapshot update on push to this branch
* chore: update e2e snapshots [skip ci]
* ci: trigger CI after snapshot update
* style: fix prettier formatting in update-snapshots workflow
* fix: pin seroval, seroval-plugins, and solid-js for artifactory compatibility
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
`App.onhostcontextchanged` is a setter-only property with no getter.
TypeScript doesn't prevent reading from setter-only properties, so
`app.onhostcontextchanged?.(ctx)` compiles but silently returns
`undefined` at runtime.
Extract the handler into a named `handleHostContextChanged()` function
and call it directly after `connect()`, matching the pattern used in
`basic-server-vanillajs` and other examples.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Rewrites the Quickstart tutorial to provide a more focused, step-by-step
introduction to building MCP Apps. The guide now uses synced code fences
that pull from `examples/quickstart/`, ensuring all code shown in the
documentation is type-checked and tested.
Key improvements:
- Clearer project setup instructions with explicit commands
- Streamlined explanations of the tool + resource registration pattern
- Added screenshot showing the completed app
- E2E test coverage validates the example works end-to-end
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Rename `startServer` to `startStreamableHTTPServer` and add a new
`startStdioServer` helper to make the transport type explicit. Remove
the `ServerOptions` interface in favor of reading `PORT` directly from
the environment inside the function (defaulting to `3001`).
Also update Python examples to default to port `3001` for consistency.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add importmap entry for main SDK exports (applyDocumentTheme, etc.)
- Import and apply host theme and styles on context changes
- Apply initial host context theme/styles when app connects
- Update CSS to use [data-theme="dark"] selector for host-provided themes
- Keep @media prefers-color-scheme as fallback for standalone mode
- Add color-scheme meta tag for native element theming
* feat(basic-host): add theme toggle and MCP style variables
Add theme support to basic-host example:
- theme.ts: Global theme manager with light/dark toggle
- host-styles.ts: Full set of MCP CSS variables using light-dark()
- ThemeToggle component with sun/moon icons
- Theme-aware CSS using CSS custom properties
- Pass theme + styles to apps via hostContext
- Notify apps when theme changes via sendHostContextChange
Apps now receive:
- hostContext.theme: 'light' | 'dark'
- hostContext.styles.variables: All 60+ MCP CSS variables
- onhostcontextchanged notifications when theme changes
* test: update e2e goldens for theme changes
* fix(say-server): add --index flag to uv run for PyPI resolution
The pocket-tts dependency wasn't resolving without explicit --index flag
when invoked via npm scripts.
* fix(qr-server): allow Docker bridge IP in transport security
Add 172.17.0.1:* to allowed_hosts to fix 421 Misdirected Request errors
when the server is accessed from Docker containers.
* feat(examples): add debug-server example
A comprehensive debug/development server for testing MCP Apps SDK features:
- Vanilla TypeScript UI with collapsible panels
- Message visualization (sent, received, errors)
- Server tool calling interface
- Multiple tool endpoints for testing
* refactor(debug-server): convert to React
- Replace vanilla TypeScript with React components
- Add comprehensive Host Styles panel showing all CSS variables grouped by category
- Use React hooks (useApp, useHostStyles, useAutoResize)
- Cleaner state management with useReducer for event log
- Collapsible sections for better organization
- CSS modules for scoped styling
* fix(debug-server): correct server name in main.ts
* Revert "refactor(debug-server): convert to React"
This reverts commit 5d22d2daa9a98ba412892447202ef10fbdb62f5b.
* fix(debug-server): update default values for multipleBlocks and includeMeta
* fix(debug-server): correct serve script and server name
- serve script now runs main.ts instead of server.ts
- Updated server name to 'Debug Server'
- Fixed npx command in comment
* fix: increase maxHeight to 6000px and add tools capability to debug app
- basic-host: Increase maxHeight from 600 to 6000px
- debug-server: Add tools capability to enable oncalltool/onlisttools handlers
* fix(debug-server): use consistent callback names for event tracking
Event types now match callback names (ontoolinput, ontoolresult, etc.)
so the Callback Status panel shows correct counts and payloads.
* feat(debug-server): add Update (Image) button for model context
* feat(debug-server): add expandable event log entries
- Click on any event log row to expand/collapse
- Arrow indicator rotates when expanded
- Full JSON payload shown indented in pre block
- Max height with scroll for large payloads
* style(debug-server): format long line in fullPayload function
* test(debug-server): add to E2E tests and screenshot specs
* chore: regenerate package-lock.json for cross-platform compatibility
* update dep
* fix(map-server): simplify location update message format
* fix(say-server): pass host to streamable_http_app for non-localhost deployments
* refactor(pdf-server): simplify to stateless range-query architecture
- Remove caching/indexing in favor of direct HTTP Range requests
- Use HEAD requests to get file size (no full download)
- Use Range requests to proxy byte chunks (no caching)
- Local files: fs.read with offset/length
- Remote files: HTTP Range header
- Zero memory footprint (no PDF caching)
- Truly stateless (no session management needed)
- Single server.ts (~350 lines) + main.ts CLI entry point
- Instant startup (no preloading)
- Security: whitelist local files, allowed origins
Removed files:
- src/pdf-indexer.ts
- src/pdf-loader.ts
- src/types.ts
* feat(pdf-server): add dark mode support
- Add CSS variables with light-dark() function for automatic theme switching
- Sync theme with host via applyDocumentTheme()
- Apply host CSS variables via applyHostStyleVariables()
* feat(pdf-server): add list_pdfs tool back
Lists local files and allowed remote origins.
Also moves CHUNK_SIZE to module level constant.
* feat(pdf-server): improve tool descriptions and increase context limit
- display_pdf: list all allowed domains in description
- Increase MAX_MODEL_CONTEXT_LENGTH to 15000
- Move CHUNK_SIZE to module level constant
* chore(say-server): remove deployment docs from repo
Keep server.py fix for non-localhost deployments.
Deployment docs kept locally but not in repo.
* refactor(pdf-server): simplify updateModelContext format
- Add widget ID for multi-widget disambiguation
- Use concise header: 'PDF viewer (id) | title | Current Page: X/Y'
- Add 'Page content:' prefix before text
- Keep selection markers (<pdf-selection>...</pdf-selection>)
* refactor: rename widgetUUID to viewUUID for consistency
Aligns with main's standardization on 'view' terminology (PR #325).
Also dynamically generate allowed domains in display_pdf description.
* viewUUID
* fix(map-server): fix syntax error in location update message
* fix(pdf-server): restore package.json, improve server docs
- Restore --external pdfjs-dist in build
- Rewrite server.ts header for newcomers (list tools, not implementation details)
- Minimize main.ts diff
* chore(pdf-server): remove unused addAllowedOrigin/addAllowedLocalFile
* docs(pdf-server): update README for new architecture
* docs(pdf-server): restore full README, add theming section, remove get_pdf_info
* rm
* fix(pdf-server): restore createMcpExpressApp and error handling
- Use createMcpExpressApp for DNS rebinding protection
- Restore error callback in app.listen()
* fix(say-server): pass host to streamable_http_app for non-localhost deployments
* chore(say-server): remove deployment docs from repo
* widgetId->tool id
* fix(map-server): add null check for center in location update
chore(pdf-server): dedupe and sort allowed origins
* fix(map-server): use transparent background for rounded corners
* prettier