* fix(readme): use picture element for theme-aware logo
The #gh-light-mode-only / #gh-dark-mode-only URL fragments only work
on github.com. On npm, VS Code preview, and other markdown renderers
both img tags render, showing two stacked logos.
Switch to <picture> with prefers-color-scheme, which GitHub recommends
and which degrades gracefully to a single logo elsewhere.
* ci: add Cloudflare Pages preview for docs PRs
Deploys a per-PR preview of the TypeDoc site to Cloudflare Pages project
mcp-ext-apps-docs-preview when docs, src JSDoc, README, or typedoc config
change. Uses the shared modelcontextprotocol/actions composite action that
injects noindex headers, posts a sticky PR comment with preview URLs, and
cleans up deployments on PR close. Fork PRs are skipped (no secret access).
* fix(docs): sync README logo with TypeDoc theme switcher
The README <picture> logo uses prefers-color-scheme, which only follows
the OS setting. TypeDoc's theme switcher sets data-theme on <html>, so
manually picking Light/Dark would show the wrong logo (e.g. white logo
on light background if OS is dark but user picked Light).
Rewrite the <picture> in the generated docs into two class-tagged <img>s
via the mcpstyle plugin, and hide the inactive one via CSS keyed on both
data-theme (explicit selection) and prefers-color-scheme (OS mode). The
README source is unchanged so GitHub/npm still get <picture>.
Also: prettier normalized mcp-theme.css (tabs to spaces, quote style).
Update all documentation links from `modelcontextprotocol.github.io/ext-apps`
to the new custom domain across README.md, docs/, and example READMEs.
Add `cname` to the `peaceiris/actions-gh-pages` deploy action in
`.github/workflows/docs.yml` so the CNAME file persists across deploys.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the agent skills section to the top of the README so readers
immediately see how to install and start building. Keep Claude Code
install commands front and center with a brief pointer to the agent
skills guide for other agents. Specification section moves below
Examples.
* 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>
* 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
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 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>
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>
This enables users to install the `mcp-apps` plugin via:
/plugin marketplace add modelcontextprotocol/ext-apps
/plugin install mcp-apps@modelcontextprotocol-ext-apps
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Relative directory links like `](examples/map-server)` caused TypeDoc
warnings ("relative path is not a file") and resulted in broken links
in the generated API documentation. Converted all 20 example directory
links to full GitHub URLs while keeping image paths relative.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add say-server: streaming TTS MCP App with karaoke UI
- Streaming TTS MCP App with real-time word highlighting
- React widget with pause/resume controls
- Debounced model context updates
- Self-contained server.py for standalone uv run
Co-authored-by: Claude <noreply@anthropic.com>
* Update server.py
* fix(say-server): fix React/Zod version conflicts in widget
- Pin Zod to v3.25.1 via esm.sh deps param (fixes .custom() error)
- Add external=react,react-dom to prevent duplicate React instances
- Update React from 19.1.0 to 19.2.0
- Import React for Babel's classic JSX transform
- Consolidate imports: only use @modelcontextprotocol/ext-apps/react
- Use Babel from unpkg.com instead of esm.sh
- Simplify resource decorator with meta= parameter
- Update CSP to allow both esm.sh and unpkg.com
* fix(say-server): show play button when paused, restart when finished
* feat(say-server): add fullscreen button, remove inline scroll
- Add fullscreen toggle button (appears on hover when available)
- Remove max-height/scroll in inline mode for cleaner display
- Enable scrolling only in fullscreen mode
- Track displayMode and fullscreenAvailable state
- Handle onHostContextChanged to detect fullscreen support
* fix(say-server): fix fullscreen button visibility and prevent multiple audio streams
- Check initial host context for fullscreen availability (not just updates)
- Add audioOperationInProgressRef guard to prevent concurrent audio operations
- Prevents race conditions from rapid clicks/double-clicks
* fix(qr-server): use meta parameter instead of manual handler patching
Simplified CSP metadata handling to match say-server's approach:
- Use meta parameter on @mcp.resource decorator directly
- Removed custom _read_resource_with_meta handler and handler replacement
* fix(say-server): simplify controls to prevent multiple audio streams
- Remove onClick from text display (was conflicting with double-click)
- Always show overlay button (with pause icon when playing)
- Lower opacity (0.3) when playing, full on hover
- Button shows: ▶️ (play), ⏸️ (pause), 🔄 (restart)
- Double-click text to restart still works
- Eliminates click/dblclick race condition
* fix(say-server): only show pause button on hover
- Play/restart buttons visible normally
- Pause button hidden until hover (doesn't block karaoke text)
* fix(say-server): fix partial input handling and session management
- Add promise lock to initTTSQueue to prevent concurrent initialization
- Reset queueIdRef and lastTextRef in ontoolresult for next tool call
- Close existing AudioContext when starting new session
- Reset all playback state (chunks, timings, position) on new session
- Fixes: text deltas not sent when new tool call has shorter text
* fix(say-server): detect new sessions by text comparison, not queueIdRef reset
- Don't reset queueIdRef in ontoolresult - audio should keep playing
- Detect new sessions in ontoolinputpartial/ontoolinput by checking if
new text starts with lastTextRef (continuation) or not (new session)
- Only reset and create new queue when genuinely new session detected
- Fixes: pause/play was creating new streams because queueIdRef was reset
* feat(say-server): add theming, floating button, and keyboard shortcuts
Theming:
- Use CSS variables (--font-sans, --color-text-primary/secondary) with fallbacks
- Apply host fonts via applyHostFonts() on connect and context changes
- Apply host style variables via applyHostStyleVariables()
- Apply document theme via applyDocumentTheme()
Floating button:
- Button follows cursor position (below current line being spoken)
- Uses invisible cursor marker element for accurate positioning
- Shows ⏸️ when playing, ▶️ when paused, 🔄 when finished
- Low opacity when playing, full on hover
Keyboard shortcuts:
- Space: toggle play/pause
- Enter: toggle fullscreen (when available)
UI changes:
- Large overlay only shown for initial play (idle state)
- Floating button used during playback for less intrusive controls
* docs(say-server): add TODO items for future improvements
* fix(say-server): simplify to fixed play button at top right
- Remove floating cursor-following button (was too complex)
- Fixed play button at top-right corner
- Low opacity when playing, full on hover
- Keep theming and keyboard shortcuts (Space/Enter)
- Add say-server to E2E tests
* test(e2e): add say-server to tests but skip in CI
Say-server requires Pocket TTS model (~500MB) and GPU support,
which isn't available in the Docker CI environment. Skip it for now.
* test(e2e): add say-server and qr-server to screenshot generation
- Remove say-server from SKIP_SERVERS (widget works without TTS model)
- Add say-server masks for play buttons
- Add both say-server and qr-server to grid screenshot generator
* fix(say-server): restore working play/pause overlay, keep theming and keyboard shortcuts
Reverted to working commit 413beca and carefully reapplied:
- CSS variables for theming (--font-sans, --color-text-primary/secondary)
- Dark mode support via @media (prefers-color-scheme)
- Host theming via applyHostStyleVariables/applyHostFonts/applyDocumentTheme
- Keyboard shortcuts: Space = play/pause, Enter = fullscreen
Preserved working behavior:
- Play overlay visible when not playing, hover to show when playing
- Pause on click works correctly
- Karaoke highlighting works
* fix(say-server): minimal CSS-only theming, no JS changes from working version
Only CSS changes from working commit 413beca:
- Added CSS variables for theming foundation
- No JavaScript changes (reverted theming calls and keyboard shortcuts)
This isolates whether the issue is CSS or JS related.
* feat(say-server): add autoPlay param and Reset button
- Add autoPlay parameter (default: true) with Pydantic Field description
- Description visible in MCP tool schema: 'browsers may block autoplay'
- Add Reset button next to Fullscreen button (bottom right)
- Both buttons share controlBtn base styles, shown on hover
- Widget reads autoPlay from tool input params
* fix(say-server): update FastMCP API for new SDK version
Python SDK refactored transport params from constructor to run():
- Remove port, stateless_http from FastMCP() constructor
- Pass stateless_http=True to streamable_http_app() instead
* debug(say-server): add console logs to trace audio stream issue
Logging added to:
- togglePlayPause (all branches)
- initTTSQueue (entry and AudioContext creation)
- restartPlayback (entry)
- ontoolinputpartial/ontoolinput (entry and new session detection)
Open browser DevTools console and reproduce the issue - logs will show what's being called.
* feat(say-server): add play/pause buttons in top-left and bottom-right corners
- Top left: playPauseTopBtn for quick access
- Bottom right: playPauseBtn alongside reset and fullscreen
- Both show on hover with same styling as other control buttons
- Icons: ▶️ (play), ⏸️ (pause), 🔄 (restart when finished)
* fix(say-server): simplify UI and fix pause/resume audio duplication
- Remove central play overlay, use single toolbar in top-right
- Fix audio duplication on pause/resume (only defer to pendingChunks for initial autoplay)
- Remove double-click to restart (use toolbar restart button)
- Sync displayMode when host changes it externally
- Click on text to play/pause
* feat(say-server): add voice selection support
- Add list_voices tool to show available voices
- Add voice parameter to say tool (predefined names, HuggingFace URLs, or local paths)
- Predefined voices: alba, marius, javert, jean, cosette, eponine, azelma, fantine
- Update README with voice documentation
* docs(say-server): improve tool description for better LLM triggering
- Add English-only constraint prominently in first line and note
- List common trigger patterns (say, speak, read aloud, etc.)
- Update text parameter description to mention English
- Keep description concise for quick LLM scanning
* feat(say-server): add speak lock for multi-widget coordination
When multiple TTS widgets exist, only one plays at a time:
- Widget announces to localStorage when starting playback
- Playing widgets poll localStorage every 200ms
- If another widget starts, previous one pauses automatically
- Lock is cleared on pause/finish/teardown
Uses _meta.widgetUUID in tool result for widget identification.
* update package.json of say
* feat(say-server): add screenshots and update README gallery
- Add say-server to demo gallery with 300x300 grid-cell.png
- Fix qr-server grid-cell.png dimensions (was 672x668, now 300x300)
- Add golden snapshot for say-server E2E tests
- Add full screenshot and grid-cell thumbnail for say-server
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* style(say-server): apply prettier formatting
- Add blank lines in README for better readability
- Add trailing newline to package.json
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(say-server): add Escape key to exit fullscreen and update docs
- Add onKeyDown handler to main element for Escape key
- Update README with full list of MCP App features:
- Model context updates
- Native theming
- Fullscreen mode
- Multi-widget speak lock
- Update server.py docstring with feature documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(say-server): add queue timeout to prevent infinite polling
The TTS queue could hang forever if:
- The LLM stops generating mid-stream (no end signal)
- User cancels before tool completes
- Network interruption prevents end_tts_queue call
Changes:
- Add last_activity timestamp to track queue activity
- Add 30-second timeout (QUEUE_TIMEOUT_SECONDS)
- Poll with 5-second timeout, checking for stale queues
- Mark stale queues as "error" so widget stops polling
- Return error message in poll_tts_audio response
- Add error logging in widget polling loop
This prevents tool calls from running forever when queues are abandoned.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* debug(say-server): add logging to track queue lifecycle
Add console logging to debug why end_tts_queue might not be called:
Widget-side:
- Log queueId in ontoolinputpartial, ontoolinput, ontoolresult
- Log when new session is detected (queue reset)
- Log when initTTSQueue fails
- Log when end_tts_queue is called/not called
Server-side:
- Log when end_tts_queue is called
- Log warnings for unknown queues
- Log info for already-ended queues
This will help identify if the issue is:
- User interruption (ontoolresult never called)
- Session reset (queueId becomes null)
- Host bug (callback dropped)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* perf(say-server): add initial delay before polling
Add 150ms delay before first poll to give TTS model time to generate
the first audio chunk. This reduces unnecessary polling and server load.
Current backoff after that:
- 30ms if chunks received (keep streaming fast)
- 80ms if no chunks (wait longer for generation)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(say-server): use adaptive polling instead of fixed initial delay
Previous approach: Always wait 150ms before first poll
Problem: Even when chunks are ready, we wait unnecessarily
New approach: Adaptive polling
- Start polling immediately (no initial delay)
- 20ms backoff when receiving chunks (fast streaming)
- Exponential backoff when no chunks: 50ms → 100ms → 150ms max
- Reset backoff when chunks start flowing
This reduces latency when TTS is fast while being polite when it's slow.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(say-server): add speaker icon to server info
Uses SVG data URI for a speaker with sound waves icon.
* docs(say-server): add comprehensive third-party licensing information
- Expand License section in README with component attribution table
- Add voice collection license matrix with commercial use indicators
- Add warning about non-commercial expresso/ears voice collections
- Clarify NON-COMMERCIAL restriction in list_voices() output
- Use consistent CC-BY formatting (hyphenated)
* Update last activity to prevent timeout during active polling
* feat(say-server): add info button, SVG icons, and UI fixes
- Replace emoji play/pause buttons with neutral white SVG icons
- Add (i) info button in bottom-right with attribution popup
- Use app.openLink() API for external links in popup
- Add dynamic padding to fit popup when shown
- Make create_tts_queue async to fix AnyIO event loop error
- Add better error logging for TTS queue initialization
- Update README with openLink feature documentation
- Remove unnecessary HuggingFace login prerequisites
* docs(say-server): document multi-widget speak lock mechanism
* prettier:fix
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(pdf-server): Interactive PDF viewer example
PDF viewer with PDF.js featuring:
- Chunked binary loading with progress bar
- Text extraction for AI context
- arXiv paper support (fetch by ID)
- Page navigation with keyboard shortcuts
- Zoom controls (including Ctrl+0 reset)
- Fullscreen mode support
- Horizontal swipe for page changes (disabled when zoomed)
- Page persistence in localStorage
- Text selection via PDF.js TextLayer
- Clickable title link to source URL
- Rounded corners and subtle border styling
* chore: Add pdf-server to screenshot generation list
* refactor(pdf-server): Simplify and generalize PDF loading
- Accept any HTTP(s) URLs instead of ArXiv-only
- Use HTTP Range requests for chunked binary loading
- Remove ArXiv-specific code (arxiv.ts, metadata fetching)
- Remove CLAUDE.md index generation
- Flatten hierarchical folder structure to simple entries list
- Remove dead code: getPdfSummary, httpFileSizes
- Simplify base64 encoding using Buffer
- Simplify chunk extraction using slice()
- Consolidate DEFAULT_PDF_URL constant
The server now works with any PDF URL, not just arXiv papers.
HTTP Range requests stream chunks on-demand when supported.
* feat(pdf-server): Include title and selection in model context
- Add pdfTitle to updateModelContext structuredContent
- Include selection position (text, start, end) when text is selected
- Add debounced selectionchange listener to update context on selection
* fix(pdf-server): Restore default URL in view_pdf schema
The UI needs the default value in the schema to show it properly.
* refactor(pdf-server): Further simplifications
- Remove hard-coded test paths from main()
- Remove unused resources: pdfs://metadata/{pdfId}, pdfs://content/{pdfId}
- Remove unused metadata fields: subject, creator, producer, creationDate, modDate
- Remove unused entry fields: relativePath, estimatedTextSize
- Remove filterEntriesByFolder and folder filter from list_pdfs
- Remove redundant output schema validation (trust typed returns)
- Simplify scanDirectory and createLocalEntry signatures
Total: 1836 → 1666 lines (-170 lines, -9%)
* refactor(pdf-server): Major simplification for didactic focus
Simplified the example to focus on key MCP Apps SDK patterns:
- Chunked data through size-limited tool calls
- Model context updates (page text + selection)
- Display modes (fullscreen vs inline)
- External links (openLink)
Changes:
- Remove local file support (HTTP URLs only)
- Restrict dynamic URLs to arxiv.org for security
- Simplify types: url instead of sourcePath/sourceType
- Simplify indexer: 168 → 44 lines
- Simplify loader: 318 → 171 lines
- Simplify server: 337 → 233 lines
- Fix selection text normalization
- Rewrite README with didactic focus
Total: 1836 → 1236 lines (-33%)
* feat(pdf-server): Add file:// URL support for local files
- Local paths are converted to file:// URLs on startup
- file:// URLs must be in the initial list (strict validation)
- Dynamic URLs still restricted to arxiv.org only
- Updated README with local file examples
* fix(pdf-server): Improve selection detection with logging
- Add logging to selectionchange handler to verify it fires
- Add fallback matching without spaces (TextLayer spans may lack spaces)
- Log selection detection success/failure for debugging
The issue: PDF.js TextLayer renders text as positioned spans without
space characters between them. When selecting across spans:
- pageText has spaces (items joined with ' ')
- sel.toString() may not have spaces
- indexOf fails to match
The fix tries exact match first, then falls back to spaceless matching.
* feat(pdf-server): Format model context as markdown with front matter
Model context now looks like:
```markdown
---
url: https://arxiv.org/pdf/...
page: 5/144
---
Page text with <pdf-selection>selected text</pdf-selection> inline.
```
This is cleaner for the model to parse and includes the source URL.
* refactor(pdf-server): Extract smart truncation helpers
Added two well-designed helpers:
formatPageContent(text, maxLength, selection?)
- Centers truncation window around selection if present
- Adds <truncated-content/> markers at elision points
- Wraps selection in <pdf-selection> tags
- Allocates 60% context before, 40% after for readability
findSelectionInText(pageText, selectedText)
- Tries exact match first
- Falls back to spaceless match for TextLayer quirks
- Returns { start, end } or undefined
Example output with selection:
```
<truncated-content/>
...context before... <pdf-selection>selected text</pdf-selection> ...context after...
<truncated-content/>
```
* fix(pdf-server): Truncate inside selection tags when selection too long
When selection is too large for the budget:
<truncated-content/><pdf-selection><truncated-content/>start...end<truncated-content/></pdf-selection><truncated-content/>
This keeps the selection structure intact while showing beginning and end.
* refactor(pdf-server): Remove unused read_pdf_text, use Attention paper as default
- Remove read_pdf_text tool (viewer extracts text client-side with pdfjs)
- Remove PdfTextChunk and ReadPdfTextInput types
- Remove loadPdfTextChunk from pdf-loader
- Change default PDF to 'Attention Is All You Need' (1706.03762)
- Update README with modest language
* refactor(pdf-server): Simplify to use URL as ID, rename view_pdf to display_pdf
Major simplifications:
- Use URL directly as identifier (no hashing)
- Remove displayName - show elided URL with full URL as tooltip
- Rename view_pdf to display_pdf with better description
- Update all references from pdfId to url
- Simplify storage key and model context
The tool description now explains it displays an interactive viewer in the chat.
* feat(pdf-server): Normalize arxiv URLs to PDF format
arxiv.org/abs/... -> arxiv.org/pdf/...
Applied both at startup and when loading dynamic URLs.
* docs(pdf-server): Add prompt engineering to display_pdf description
* fix(pdf-server): Sharp rendering on retina displays
Account for devicePixelRatio when rendering canvas:
- Scale canvas dimensions by dpr
- Scale context by dpr
- Keep CSS size at logical pixels
* fix(pdf-server): Normalize arxiv URLs in read_pdf_bytes too
* add to e2e spec
* add to e2e spec
* add to e2e spec
* add to e2e spec
* regen
* chore: regenerate package-lock.json and fix hono vulnerability
* docs: add pdf-server screenshot to READMEs
* regen
* ci: add missing examples to pkg-pr-new publish
* ci: add pdf-server to npm publish examples
* Update README.md
* pdf-server: improve tool response text for better model context
* revert unrelated screenshot changes
* pdf-server: dynamically add arxiv URLs in read_pdf_bytes
Fixes 'PDF not found' error when server restarts between display_pdf
(which adds the entry) and read_pdf_bytes (which previously only looked
up existing entries). Now read_pdf_bytes mirrors display_pdf's logic
and dynamically adds arxiv URLs to the index.
* feat: add script to generate 300x300 grid-cell.png screenshots for examples
- Add generate-grid-screenshots.spec.ts Playwright test to capture screenshots
- Add npm run generate:screenshots command (Docker-based for Santa compatibility)
- Add sharp dependency for image resizing with centered-fill mode
- Generate grid-cell.png for 14 example servers (only React for basic-server-* series)
The screenshots are 300x300 PNG images cropped with centered fill mode,
suitable for use in a grid layout showcasing the examples.
* docs: add example gallery grid to README
Add a 3-column visual grid showcasing 14 example MCP Apps with:
- Thumbnail screenshots (300x300 grid-cell.png)
- Clickable links to example directories
- Hover tooltips with brief descriptions
Organized by visual impact: 3D/WebGL examples first, then
visualizations, charts, monitoring, and starter templates.
* fix: use aspect-fit instead of crop for screenshots
Change from fit: 'cover' (crops) to fit: 'contain' (letterbox)
so the full app content is visible without cropping.
* feat: improve screenshot generation
- Constrain viewport to 500px width for consistent rendering
- Wait 5 seconds for full app load (tiles, animations, data)
- Screenshot just the inner iframe (no extra whitespace)
- Save both screenshot.png (full-size) and grid-cell.png (300x300 thumbnail)
- Use contain mode to preserve aspect ratio
* fix: use cover mode with top alignment for grid thumbnails
No letterboxing - crops to fill 300x300, aligned to top so headers
and important content are always visible.
* feat: improve screenshot gallery
- Remove integration-server from gallery (it's for E2E testing only)
- Add 30s extra wait for Map and Video servers to load fully
- Improve Three.js default scene: isometric rotation + better lighting
- Update README: Starter Templates row with links to all framework variants
(React, Vue, Svelte, Preact, Solid, Vanilla JS)
- Regenerate all screenshots with updated settings
* fix: improve README gallery layout
- Fix uneven column widths by using minimal separator
- Move Starter Templates to separate 2-cell table with description
- Add screenshot.png to top of each example README (13 examples)
- Shorten tooltips for cleaner table
* revert to document.write again; cesiumjs doesn't like srcdoc, period.
* fix: map-server screenshot now renders properly
- Merged ochafik/post-fixes which reverts sandbox from srcdoc to document.write
(CesiumJS doesn't work with srcdoc iframes)
- Fixed map-server name in screenshot test ('Map Server' not 'CesiumJS Map Server')
- Simplified screenshot generation script (removed complex debugging code)
- Increased map-server wait time to 45s for reliable tile loading
- Regenerated all 13 screenshots
* fix: remove redundant screenshots from READMEs
Remove the simple \ line from READMEs that
already had nicer HTML table screenshots. Keep it only for examples
that didn't have existing screenshots.
* docs: move gallery to Examples section
- Move screenshot gallery from top of README to Examples section
- Remove redundant list of basic examples (now shown visually in gallery)
- Clean up duplicate run instructions
* fix: remove empty header row from starter templates table
* fix: remove empty header rows from both tables
* fix: add prettier-ignore to prevent table header expansion
* feat: use custom screenshot for video-resource-server
- Use better screenshot from PR #253 comment showing actual video playback
- Skip video-resource-server in screenshot generation script
- Add SKIP_SERVERS set for manually maintained screenshots
* fix: revert unrelated map-server name change
* fix: use correct server name 'CesiumJS Map Server' in screenshot test
Provides a concise 4-step overview of the MCP Apps architecture:
tool call → `ui://` resource → sandboxed iframe → bidirectional
communication via notifications.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add basic-server-vue example
Demonstrates MCP App SDK usage with Vue 3, providing framework
parity alongside the existing React and vanilla JS examples.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add basic-server-svelte example
Demonstrates MCP App SDK usage with Svelte, providing parity
with the existing basic-server-react and basic-server-vue examples.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add basic-server-preact example
Demonstrates MCP App SDK usage with Preact, providing another
lightweight framework option alongside the existing React, Vue,
and Svelte examples.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add basic-server-solid example
Demonstrates MCP App SDK usage with Solid.js, providing another
reactive framework option alongside the existing React, Vue,
Svelte, and Preact examples.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add framework GitHub links to README
Link each framework name to its official GitHub repo in basic examples
list.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Allows running `npm start` as a shorthand for `npm run examples:start`,
providing a conventional entry point to launch the example servers.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix: Windows compatibility for npm scripts and cross-platform CI
Fixes#142 - Windows PowerShell install bug
Changes:
- Replace single quotes with escaped double quotes in generate:schemas
(cmd.exe passes single quotes literally, breaking prettier glob matching)
- Simplify prettier scripts: remove redundant --ignore-path flags
(prettier auto-loads .gitignore and .prettierignore by default)
- Remove $(pwd) from prettier scripts (bash-only syntax)
- Add cross-platform CI matrix: Linux x64, Windows x64, macOS ARM64, macOS x64
- Use shell: bash for grep commands in CI (not available in Windows cmd.exe)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: remove bun npm package from all workspaces
The bun npm package tries to install platform-specific binaries which
fails on Windows CI. Since bun is installed via oven-sh/setup-bun action
in CI (and developers install it globally), we don't need the npm package.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: skip examples:build on Windows (uses Unix-only INPUT=... syntax)
The examples use `INPUT=mcp-app.html vite build` which is bash syntax
for setting environment variables. This doesn't work on Windows cmd.exe.
Since examples aren't published to npm, we can skip building them on Windows.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: force LF line endings for cross-platform prettier consistency
Git on Windows auto-converts line endings to CRLF, causing prettier to
report formatting issues. Force LF for all text files.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci: add test for git-based npm install (cross-platform)
This verifies that `npm install git+https://...` works on all platforms,
which triggers the prepare script and requires bun to be available.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci: add Linux ARM64 and Windows ARM64 to CI matrix
- Added ubuntu-24.04-arm (Linux ARM64)
- Added windows-11-arm (Windows ARM64)
Note: WSL is not available on GitHub-hosted runners (requires nested
virtualization which isn't supported).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci: disable Windows ARM64 (Bun lacks stable ARM64 binaries)
The oven-sh/setup-bun action fails with 404 when trying to download
Bun for Windows ARM64. Re-enable when Bun adds proper support.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci: add Windows WSL build test
Uses Vampire/setup-wsl action to install Ubuntu-24.04 in WSL and run
the full build/test cycle. This tests the Linux-in-Windows scenario
that was reported in issue #142.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci: fix WSL build - install unzip for Bun installer
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci: add setup-bun to publish.yml workflow
The pkg-pr-new publish workflow was missing bun setup, causing
"bun: not found" errors during npm ci (which triggers prepare script).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci: enable Windows ARM64 with x64-baseline Bun (via emulation)
Windows ARM64 can run x64 binaries via emulation. Use bun-download-url
to force x64-baseline build since native ARM64 Bun isn't available yet.
Reference: https://github.com/oven-sh/bun/issues/9824🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci: remove macOS x64 (macos-13 runner retired)
The macos-13 based runner images are now retired.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: add Bun installation instructions per platform
Documents that Bun is required for development, with platform-specific
install commands in collapsible sections.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: note Bun requirement in README examples section
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: add bun as optional dependency (no global install required)
- Add @oven/bun-* platform packages as optionalDependencies
- Create postinstall script to set up bun binary from optional deps
- Handle Windows ARM64 by downloading x64-baseline for emulation
- Remove setup-bun from CI workflows (npm install handles it now)
- Remove manual Bun install instructions from docs
This alternative approach eliminates the need for global Bun installation
or setup-bun action in CI. npm install automatically installs the
platform-appropriate bun binary.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: add debug logging to setup-bun script
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: force stdout flush in setup-bun for CI debugging
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: add more debugging to setup-bun
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: remove accidental test artifacts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: run setup-bun in prepare script (before build)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: use cross-env for Windows-compatible example builds
- Add cross-env to devDependencies
- Update all example package.json scripts to use cross-env for INPUT and NODE_ENV
- Enable examples:build on Windows in CI (no longer Unix-only)
- Add examples:build to WSL CI job
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: remove unused imports in setup-bun.mjs
Address github-code-quality bot comments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: use run-bun.mjs wrapper for prepare hook compatibility
During npm install's prepare hook, node_modules/.bin is not in PATH,
so calling 'bun' directly fails. The new run-bun.mjs wrapper finds
bun at node_modules/.bin/bun and runs it with the correct path.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci: restore setup-bun action for build jobs
- Add setup-bun action to build and test-git-install jobs
- Disable Windows ARM64 (Bun doesn't support this platform)
- Add bun installation to WSL build
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: remove bun devDep, add libc detection for Linux ARM64
- Remove 'bun' from devDependencies (its postinstall fails on Windows ARM64)
- Add libc detection (glibc vs musl) to setup-bun.mjs
- Only try compatible binaries based on detected libc
- Re-enable Windows ARM64 in CI (now uses x64 emulation fallback)
- Remove setup-bun action from CI (no longer needed)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci: simplify WSL build - let setup-bun.mjs handle bun installation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>