Commit graph

708 commits

Author SHA1 Message Date
Olivier Chafik
de8048cb6d
feat(qr-server): use Python SDK meta parameter for resource CSP metadata (#303)
* update qr-server w/ python sdk fix

* feat(qr-server): use Python SDK meta parameter for resource CSP metadata

- Update qr-server to use the new meta parameter on @mcp.resource decorator
  instead of manually overriding the read_resource handler
- Add ui.resourceUri to tool meta alongside legacy ui/resourceUri for forward compatibility
- Simplify server.py by removing the custom _read_resource_with_meta handler
- Add qr-server to grid screenshot generation tests
- Update grid-cell.png screenshot

This leverages the Python SDK's native support for resource metadata,
eliminating the need for low-level handler manipulation.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-19 19:11:42 +00:00
Anton Pidkuiko
73ab853dc5
Merge pull request #297 from modelcontextprotocol/feat/shadertoy-streaming-visibility
feat(shadertoy): add streaming partial input, visibility optimization, host styling
2026-01-19 18:36:43 +00:00
Anton Pidkuiko
f71155ac7c
Merge pull request #299 from modelcontextprotocol/feat/threejs-perf-styling
feat(threejs): add visibility optimization, host styling, transparent background
2026-01-19 18:36:33 +00:00
Anton Pidkuiko
a9d8e40fe6
Merge pull request #305 from modelcontextprotocol/fix/spec-permissions-type
fix(spec): align permissions type with SDK implementation
2026-01-19 18:26:05 +00:00
Anton Pidkuiko MacBook
8c87715628
fix(spec): align permissions type with SDK implementation
The spec document incorrectly defined permission fields (camera, microphone,
geolocation, clipboardWrite) as boolean types, but the SDK implementation
uses empty object types ({}).

This caused validation errors for MCP servers (like Hex) that followed
the spec and sent boolean values like `clipboardWrite: true`, when the
Zod schema expects `clipboardWrite: {}`.

The empty object design was intentional to allow future extensibility
(e.g., adding options like `{ required: true }`). This commit aligns
the spec documentation with the actual TypeScript types in spec.types.ts.

Fixes validation error:
`{ code: 'invalid_type', expected: 'object', received: 'boolean',
   path: ['permissions', 'clipboardWrite'] }`
2026-01-19 17:26:13 +00:00
Olivier Chafik
fcd243a6f3
fix(e2e): make server-time element fill width for consistent mask (#283)
* fix(e2e): make server-time element fill width for consistent mask

- Add flex layout to .action p to make server-time stretch
- Add #server-time flex: 1 rule across all basic-server variants
- Update golden screenshots with full-width masked time field

This ensures the Playwright mask covers a consistent width regardless
of the timestamp content, fixing screenshot comparison flakiness.

* fix(e2e): wait for select to be enabled in screenshot generator

Match the logic in servers.spec.ts - wait for select to be enabled
(servers connected) instead of just visible.

* chore: trigger CI re-run
2026-01-19 17:01:55 +00:00
Jonathan Hefner
90dad1a86b Add "Migrate from OpenAI App" skill to Claude Code plugin
Adds a new skill (`plugins/mcp-apps/skills/migrate-oai-app/SKILL.md`)
that guides developers through migrating OpenAI Apps SDK applications
to the MCP Apps SDK. Includes API mapping tables, CSP configuration
guidance, and a "Before Finishing" checklist.

Also fixes the `registerAppResource()` JSDoc example in `src/server/index.ts`
to show CSP configuration in the correct location (on the content item,
not the resource config).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 07:59:41 -06:00
Anton Pidkuiko MacBook
d60224f363
feat(threejs): add visibility optimization, host styling, transparent background
- Add visibility-based animation pause (IntersectionObserver + wrapped rAF)
- Zero CPU usage when widget scrolls out of view
- Apply host style variables via useHostStyles
- Support transparent WebGL background (alpha: true)
- Use light-dark() for theme-aware fallback colors
- Tool no longer echoes code back in result
- Document performance features in README
2026-01-17 23:19:34 +00:00
Anton Pidkuiko MacBook
f1a00f9e15
feat(shadertoy): add streaming partial input, visibility optimization, host styling
Shadertoy server improvements:
- Add ontoolinputpartial handler to show code preview during generation
- Add IntersectionObserver to pause/play shader based on visibility
- Apply host style variables (theme, colors, fonts)
- Add border-radius with fullscreen mode support
- Use gradient background and ghost text color for code preview

Skill documentation updates:
- Add streaming partial input section with use cases
- Add visibility-based resource management pattern
- Add fullscreen mode API and CSS patterns
- Expand host styling with CSS variable usage examples
2026-01-17 22:29:57 +00:00
Matthew Wang
d27acc42b1 Add and helper functions 2026-01-16 21:20:10 -08:00
Jonathan Hefner
7ce5504f9f Add SSE transport fallback to basic-host
Implement the conventional MCP pattern for transport backward compatibility:
try `StreamableHTTPClientTransport` first (modern), then fall back to
`SSEClientTransport` (deprecated but needed for older servers).

This follows the approach documented in the MCP TypeScript SDK's
`streamableHttpWithSseFallbackClient.ts` example.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 10:23:07 -06:00
Olivier Chafik
ac1130cda4
style: format JSON in READMEs with prettier 2026-01-16 15:14:48 +00:00
Olivier Chafik
ed51361511
docs: add combined MCP client config to top-level README
Add a collapsible details section with configuration for all example
servers in the Examples section of the main README.
2026-01-16 14:31:42 +00:00
Olivier Chafik
94c5dff983
docs(qr-server): clarify that repo clone is required 2026-01-16 14:03:46 +00:00
Olivier Chafik
cce8d5c7a3
docs: add MCP client configuration to example READMEs
Add stdio transport configuration snippets near the top of each example
README to help users quickly set up the servers in their MCP clients.

- Node.js examples use npx with the published npm packages
- Python qr-server uses uv with local path
- Removed Claude Desktop-specific references from qr-server
2026-01-16 14:00:57 +00:00
Olivier Chafik
8c3b1daf7e
chore: bump version to 0.4.1 (#282) v0.4.1 2026-01-15 16:11:53 +00:00
Anton Pidkuiko
be8d725974
Merge pull request #272 from modelcontextprotocol/uv-qr-server
feat(qr-server): migrate to uv with PEP 723 inline dependencies
2026-01-15 14:29:33 +00:00
Olivier Chafik
0936d52d0a
feat(shadertoy): show fullscreen button only on widget hover, add min-height (#281) 2026-01-15 14:15:17 +00:00
Olivier Chafik
b78cd74c1b
feat(shadertoy): add fullscreen support + ditch insets for max visual effect (#278)
* feat(shadertoy): add fullscreen support

Add fullscreen toggle button that appears when the host supports fullscreen
display mode. Uses the same pattern as pdf-server:
- Check availableDisplayModes in host context
- Show/hide button based on capability
- Toggle between inline and fullscreen via requestDisplayMode()
- Update UI state on host context changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(shadertoy): button visibility after exiting fullscreen + Escape key

- Only update button visibility when availableDisplayModes is present
  in context (partial updates don't include all fields)
- Add Escape key handler to exit fullscreen mode

🤖 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>
2026-01-15 13:01:55 +00:00
Olivier Chafik
89a684546b
fix(examples): resolve DIST_DIR path for npm package execution (#277)
* fix(examples): resolve DIST_DIR path for npm package execution

When servers are compiled to dist/server.js and published to npm,
`import.meta.dirname` already points to dist/, so joining with "dist"
results in dist/dist/ which doesn't exist.

This fix detects whether we're running from TypeScript source or
compiled JavaScript and adjusts the path accordingly:
- From source (server.ts): use dist/ subdirectory
- From compiled (dist/server.js): use current directory

Fixes ENOENT errors like:
"ENOENT: no such file or directory, open '.../dist/dist/mcp-app.html'"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(pdf-server): mark pdfjs-dist as external in bundle

The pdf.worker.mjs file wasn't being found when running from npm
because bun bundled pdfjs-dist but didn't include the worker file.

By marking pdfjs-dist as external, it uses the installed package
from node_modules at runtime where the worker is available.

🤖 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>
2026-01-15 12:23:13 +00:00
Olivier Chafik
847427aeef
pdf-server: fix scrolling gap in inline mode (#280)
* map-server: fix async/await in ontoolresult, remove unused getViewStorageKey

* pdf-server: fix scrolling gap in inline mode

- Set html/body background to match .main background color
  This hides any gap between .main and iframe border
- Change .main from height:100% to min-height:100%
  Ensures .main fills container properly
2026-01-15 12:22:58 +00:00
Olivier Chafik
b94982cc35
fix[examples/transcript]: fix transitional deltas (#275)
* fix transcript deltas

* Update mcp-app.css
2026-01-15 12:06:02 +00:00
Anton Pidkuiko MacBook
489d28b6da
fix: increase webServer timeout for uv dependency download
uv needs extra time on first run to download Python dependencies
2026-01-15 11:53:27 +00:00
Copilot
c73d36343b
fix: change unknown source message log from error to debug level (#239)
* Initial plan

* Change console.error to console.debug for unknown message sources

Co-authored-by: liady <7003853+liady@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: liady <7003853+liady@users.noreply.github.com>
2026-01-15 11:51:27 +00:00
Anton Pidkuiko
dbc7a41550
Merge pull request #274 from matteo8p/use-tool-type-from-official-sdk
Use `Tool` as the input type in `getToolUiResourceUri`
2026-01-15 11:45:46 +00:00
Anton Pidkuiko MacBook
656a0292ae
feat: add QR Code to README gallery + rename to QR Code Server
- Add grid-cell.png screenshot for README gallery
- Rename 'QR Server' to 'QR Code Server' everywhere
- Add QR Code (Python) entry to examples table in README
2026-01-15 11:36:21 +00:00
Jerome
65a62a9fae
feat: compile example servers to JavaScript for npm consumption (#259)
* feat: compile example servers to JavaScript for npm consumption

Add TypeScript compilation for server code so the example packages can be
imported and used by consumers without requiring TypeScript compilation.

Changes:
- Add tsconfig.server.json to each example for compiling server code
- Update package.json exports to point to compiled dist/server/server.js
- Add types field pointing to dist/server/server.d.ts
- Update build scripts to include server compilation step
- Fix .ts import extensions to .js in customer-segmentation-server

This enables importing the createServer function from published packages:
  import { createServer } from '@modelcontextprotocol/server-basic-react';

* fix: add type assertion for Nominatim API response

* chore: regenerate schemas

* refactor: split server examples into library and binary entry points

- server.ts: exports createServer() only (library entry point)
- main.ts: entry point with main() that runs the server (binary)
- package.json: adds bin field for npx support, uses inline bun build
- tsconfig.server.json: emitDeclarationOnly, outputs to dist/

This allows:
1. Importing the server as a library: import { createServer } from '@modelcontextprotocol/server-basic-react'
2. Running with npx: npx @modelcontextprotocol/server-basic-react [--stdio]

Added migration script at scripts/migrate-server-examples.ts

* refactor: migrate remaining server examples to library/binary structure

Apply the same refactoring to all 13 remaining server examples:
- scenario-modeler-server, shadertoy-server, map-server
- cohort-heatmap-server, customer-segmentation-server, video-resource-server
- system-monitor-server, budget-allocator-server, sheet-music-server
- threejs-server, integration-server, transcript-server, wiki-explorer-server

Each now has:
- server.ts: exports createServer() (library)
- main.ts: runs the server (binary with shebang)
- bin field in package.json for npx support

* chore: remove unused server-utils.ts from video-resource-server/src

* update

* fix(build): make Windows-compatible by removing chmod and fixing bun externals

- Remove 'chmod +x dist/index.js' which doesn't exist on Windows
- Add '--external ./server.js' to prevent bun from treating the import as a second entry point

* fix(build): use .ts import for bun compatibility

- Change import from './server.js' to './server.ts' in main.ts files
- Remove --external flag since bun should inline everything
- Fixes Windows build where bun couldn't resolve .js to .ts properly

* fix(build): use --external for Windows bun compatibility

Bun on Windows incorrectly treats imported .ts files as entry points.
Use --external './server.js' to work around this, creating a runtime
dependency between index.js and server.js.

* style: fix prettier formatting in pdf-server/main.ts

* fix(build): remove quotes from --external for Windows cmd.exe compatibility

* fix(build): use glob pattern for --external to work on all platforms

The literal path --external ./server.js was not matching the import.
Using glob pattern --external "./server*" correctly externalizes the server module.

* fix(build): use */server.js pattern for --external

The "./server*" pattern was not matching on Windows.
Using "*/server.js" matches any path ending in /server.js which works cross-platform.

* fix(build): use *server.js pattern without slash for Windows compatibility

* update package-lock

* chore: regenerate package-lock.json for cross-platform compatibility

* fix(build): use ./server.js instead of glob pattern for Windows compatibility

* fix(build): use double quotes for banner to fix Windows cmd.exe compatibility

---------

Co-authored-by: Olivier Chafik <ochafik@anthropic.com>
2026-01-14 23:46:17 +00:00
Olivier Chafik
75cbf8612c
feat(basic-host): URL params, fullscreen mode, unified layout (#266)
* basic-host: handle containerDimensions

* feat(basic-host): Unified tool call layout with shared collapsible panels

* snapshots

* format
2026-01-14 22:09:41 +00:00
Olivier Chafik
75ecd62384
fix(examples): improve updateModelContext with structured YAML frontmatter (#271)
* map-server: fix async/await in ontoolresult, remove unused getViewStorageKey

* fix(map-server): improve updateModelContext and fix initialization

- Use structured YAML frontmatter format for model context (like pdf-server)
- Add tool name, altitude, and cleaner data format
- Fix initialization deadlock: app.connect() was inside ontoolresult handler
  which could never fire until connected
- Fix view state persistence: widgetUUID now properly extracted from
  ontoolresult and used to restore persisted camera position
- Server now includes widgetUUID in tool result _meta for widget identification

* fix(pdf-server): use widgetUUID for page persistence

- Add widgetUUID to tool result _meta for widget identification
- Use widgetUUID instead of generated key (pdfUrl+toolId) for localStorage
- Enables reliable page restoration when revisiting PDF widgets

* fix(transcript-server): use structured YAML frontmatter for model context

- Add tool name, listening status, and unsent entry count in frontmatter
- Consistent format with pdf-server and map-server

* update names

* drop bogus altitude

* Update mcp-app.ts
2026-01-14 22:08:09 +00:00
Matthew Wang
5bb0c6b4c7 Use as the input type in 2026-01-14 13:07:08 -08:00
Anton Pidkuiko MacBook
2ee15420a0
fix: add default text value for QR code generation
Without a default, the test would fail since text is required.
Uses modelcontextprotocol.io as the default URL.
2026-01-14 20:41:19 +00:00
Anton Pidkuiko MacBook
0927445025
feat(qr-server): migrate to uv with PEP 723 inline dependencies
- Replace requirements.txt with inline script dependencies in server.py
- Add package.json with npm scripts for run-all.ts integration
- Update CI workflow to install uv via astral-sh/setup-uv@v5
- Update Docker commands to install uv for e2e tests
- Add Playwright golden screenshot for qr-server
- Update README with uv installation and usage instructions
2026-01-14 20:27:35 +00:00
Olivier Chafik
db1b48552b
examples[pdf]: limit context length (#270)
* 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.

* cap length of context update in pdf-server

* format
2026-01-14 15:26:25 +00:00
Olivier Chafik
96daa46a1d
examples: add PDF viewer w/ chunked data loading, full-screen, model context updates, private tool (#267)
* 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.
2026-01-14 14:25:24 +00:00
Jonathan Hefner
63b05b4fdd
Merge pull request #252 from jonathanhefner/update-examples-for-skill
Improve consistency and organization of examples for AI learning
2026-01-13 20:55:40 -06:00
Jonathan Hefner
4140565b77 Improve consistency and organization of examples for AI learning
- Add `.gitignore` files to all 19 examples (`node_modules/`, `dist/`)
- Replace custom `log` objects with standard `console` methods
- Add `ontoolcancelled` handler to all `basic-server-*` examples
- Add host styling integration (`applyDocumentTheme`, `applyHostFonts`,
  `applyHostStyleVariables`) to non-React `basic-server-*` examples
- Add section headers for better code organization in feature examples
- Update READMEs with Build System section explaining single-file
  bundling

These changes make examples more consistent and easier to adapt.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 20:48:02 -06:00
Jonathan Hefner
6166242136
Add Claude Code plugin with "Create MCP App" skill (#256)
Add a Claude Code plugin (`plugins/mcp-apps/`) for distribution via the
plugin marketplace. The plugin provides the "Create MCP App" skill that
guides the agent through building MCP Apps with interactive UIs.

The skill includes:
- Instructions to clone SDK repo for reference templates
- Framework selection guidance (React, Vanilla JS, Vue, Svelte, etc.)
- Tool visibility configuration (`_meta.ui.visibility`)
- Host styling integration patterns
- Testing workflow using `basic-host`
- Common mistakes to avoid

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 00:50:18 +00:00
Ido Salomon
969b905c10
disambiguate resource in csp sample (#257)
* disambiguate resource in csp sample

Clarified comment on CSP extraction from resource metadata.

* Update specification/draft/apps.mdx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-14 00:49:40 +00:00
adam jones
aa6775485d
chore: update licensing to Apache 2.0 for new contributions (#251) 2026-01-14 00:42:13 +00:00
Jonathan Hefner
d48e35d88a
Improve JSDoc comments with cross-references and clarifications (#254)
Key improvements:
- Add `{@link}` cross-references throughout the codebase to make API
  documentation navigable and help AI models understand relationships
- Use `{@link module!Export}` syntax for cross-module references
- Add backticks for inline code formatting (`ResizeObserver`, `App`, etc)
- Fix `PostMessageTransport` examples to show required two-argument form
- Clarify `onteardown` callback return type requirement
- Document React hooks cleanup behavior and Strict Mode considerations
- Add tool visibility example for `registerAppTool`
- Add CSP example for `registerAppResource`
- Fix notification direction documentation in `spec.types.ts`

TypeDoc config migrated from `typedoc.json` to `typedoc.config.mjs` to:
- Support custom `@description` block tag
- Add `intentionallyNotExported` for internal types
- Include `src/server/index.ts` entry point

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 00:41:17 +00:00
Jonathan Hefner
978d8e6fff
Merge pull request #263 from jonathanhefner/oai-migration-doc-server-side-section
Add "Server-Side" section to OpenAI migration guide
2026-01-13 17:49:28 -06:00
dependabot[bot]
631090f41e
build(deps): bump hono in the npm_and_yarn group across 1 directory (#268)
Bumps the npm_and_yarn group with 1 update in the / directory: [hono](https://github.com/honojs/hono).


Updates `hono` from 4.11.3 to 4.11.4
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](https://github.com/honojs/hono/compare/v4.11.3...v4.11.4)

---
updated-dependencies:
- dependency-name: hono
  dependency-version: 4.11.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 23:47:06 +00:00
Jonathan Hefner
b231396d2b Add "Server-Side" section to OpenAI migration guide
Add "Server-Side" migration documentation covering:
- Tool metadata mapping (`_meta["openai/..."]` → `_meta.ui.*`)
- Resource metadata (CSP, permissions, domain, border preferences)
- Resource MIME type (`text/html+skybridge` → `text/html;profile=mcp-app`)
- Before/after code examples using `registerAppTool()` and
  `registerAppResource()`
- Key differences summary including not-yet-implemented features

Reorganize existing client-side content under updated heading structure.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 15:18:05 -06:00
Olivier Chafik
152b953ae6
docs: how to migrate from openai apps (#260)
* Create migrate_from_openai_apps.md

* Update AGENTS.md

* Update docs/migrate_from_openai_apps.md

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* address PR review comments

- Remove AGENTS.md reference to migration guide
- Add note that OpenAITransport is not yet available (PR #172)
- Remove unnecessary note about return type ordering

---------

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2026-01-13 15:23:26 +00:00
Olivier Chafik
574a768e09
feat: add example gallery with auto-generated screenshots (#253)
* 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 \![Screenshot](screenshot.png) 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
2026-01-13 15:12:18 +00:00
Olivier Chafik
8fa2961ffc
basic-host: filter app-only tools, prioritize UI tools (#262)
* basic-host: filter non-ui tools

* basic-host: filter app-only tools, prioritize UI tools

- Filter out tools with visibility: ['app'] from host tool list
- Sort tools to show UI-enabled tools first, then alphabetically
- Add local isToolVisibleToModel helper in basic-host
2026-01-13 15:05:12 +00:00
Olivier Chafik
bd892b6ce4
revert to document.write again; cesiumjs doesn't like srcdoc, period. (#258)
* revert to document.write again; cesiumjs doesn't like srcdoc, period.

* Update server.ts
2026-01-13 02:32:07 +00:00
Olivier Chafik
231f23fd72
chore: release 0.4.0 (#249) v0.4.0
* chore: release 0.4.0

* chore: add new examples to npm publish workflow

Add map-server, shadertoy-server, and transcript-server to the
publish-examples matrix.
2026-01-12 22:40:24 +00:00
Olivier Chafik
ba550f1aeb
fix: follow-up to PR #234 (remove accidental file, revert from document.write to equiv. srcdoc) (#248)
* chore: remove tamper-detection.ts (not meant to be submitted)

Follow-up to PR #234. This file was accidentally included.

* revert to srcdoc

Follow-up to PR #234 per domfarolino's review feedback.

* regen

* regen
2026-01-12 22:19:31 +00:00
Olivier Chafik
9a9ecf27d4
feat(examples): add --watch to bun serve commands for auto-restart, and have npm start hit dev mode of all examples (#246)
* feat(examples): add --watch to bun serve commands for auto-restart on file changes

* Update package.json

* regen
2026-01-12 22:17:12 +00:00