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>
* fix: guard against double-connect in App and AppBridge (#429)
Calling connect() twice on the same transport instance caused the MCP
SDK's Protocol class to chain onmessage handlers, so each incoming
message was processed twice. This made incoming responses get consumed
by the first processing (resolving the handler) and then trigger a
spurious "Received a response for an unknown message ID" error on the
second processing pass, since the handler was already removed from the
response map.
Add an explicit guard in both App.connect() and AppBridge.connect() that
throws if the instance is already connected. This converts the silent
message-corruption into a clear, actionable error and prevents
downstream confusion.
Adds three regression tests:
- AppBridge.connect() throws when already connected
- App.connect() throws when already connected
- Demonstrates the double-processing root cause and verifies it is fixed
Fixes#429https://claude.ai/code/session_01XU3FW1TpjBkyRbkBUH5gqR
* test: replace redundant regression test with cleaner same-transport guard check
The previous third test in "double-connect guard" never actually attempted
a double-connect — it created fresh instances and verified normal operation,
which every other test already covers. The stray bridge.onupdatemodelcontext
and bridgeTransport.close() at the top were leftover noise.
Replace it with a focused test that verifies the guard fires even when
the caller passes the *same* transport object (not just a different one),
which is the exact scenario described in #429.
https://claude.ai/code/session_01XU3FW1TpjBkyRbkBUH5gqR
---------
Co-authored-by: Claude <noreply@anthropic.com>
v0.4.0 includes the typography fixes from upstream PR
JulianWowra/typedoc-github-theme#7, so the local `customCss` override
(`scripts/typedoc-github-theme-fixes.css`) added in 0bbbfee is no
longer needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <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>
In ChatGPT, we have our own sandbox <> host communication protocol that doesn't match the names of the protocol enforced by the "MUST" in the spec.
This changes that by marking the specific messages sent back and forth as a "SHOULD".
Tighten the parse-failure handler: silently ignore postMessage frames
where event.data?.jsonrpc !== "2.0" (i.e. not JSON-RPC at all — null,
undefined, a primitive, or an object from the host environment that
doesn't carry the jsonrpc field). Messages that do carry jsonrpc: "2.0"
but are otherwise malformed still surface via onerror so genuine protocol
errors aren't swallowed.
https://claude.ai/code/session_01DaJpUtSJf4hA7tnu3nJz8s
Co-authored-by: Claude <noreply@anthropic.com>
The skill name spec requires lowercase letters, numbers, and hyphens
only. Change `name` from "Migrate from OpenAI App" to `migrate-oai-app`.
Companion to #443, which does the same for the `create-mcp-app` skill.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
As per the spec (https://agentskills.io/specification) `name` should be:
"Max 64 characters. *Lowercase letters*, numbers, and *hyphens* only. Must not start or end with a hyphen."
Backport the `.tsd-typography` CSS fixes from upstream PR
JulianWowra/typedoc-github-theme#7 so document pages render with
GitHub-accurate heading spacing, list bullets, line-height, and code
block overflow. The override file and `customCss` config can be removed
once the PR is merged and the dependency is updated.
Co-Authored-By: Claude Opus 4.6 (1M context) <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>
The term "external" is misleading for MCP Apps because developers
typically interpret it as "third-party." However, MCP App HTML runs in
a sandboxed iframe with no same-origin server, so *all* origins must be
declared—including where your own bundled JS/CSS is served from. Using
"network" instead avoids this confusion and aligns with the detailed
explanations in `patterns.md` and `spec.types.ts`.
Co-Authored-By: Claude Opus 4.5 <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>
Adds a new section to the patterns guide explaining how to configure
Content Security Policy and CORS for MCP Apps that make network requests:
- CSP via `_meta.ui.csp` controls browser-side permissions (`connectDomains`,
`resourceDomains`)
- CORS via `_meta.ui.domain` provides a stable origin for API servers to
allowlist
Includes a type-checked example showing `computeAppDomainForClaude()` helper
that hashes an MCP server URL to create a stable `*.claudemcpcontent.com`
subdomain. Also adds `@see` links to `McpUiResourceMeta` and `McpUiResourceCsp`
in `registerAppResource` JSDoc.
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>
Move the inline example from the `getUiCapability()` JSDoc to
`index.examples.ts` for type checking, following the pattern used
by other examples in the codebase.
The example now uses the correct MCP SDK API:
- `server.server.oninitialized` callback for post-initialization hook
- `server.server.getClientCapabilities()` to retrieve capabilities
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>