Commit 249fcc6
MCP Apps: render panels inline with Mapbox GL JS (no inner iframes) (#62)
* Add MCP Apps support alongside MCP-UI
Implement the MCP Apps pattern for all tools with MCP-UI support, maintaining backward compatibility by supporting both patterns simultaneously.
Changes:
- Add _meta.ui.resourceUri to tool responses for MCP Apps pattern
- Create UI resources (ui:// scheme) for each tool:
- PreviewStyleUIResource for preview_style_tool
- StyleComparisonUIResource for style_comparison_tool
- GeojsonPreviewUIResource for geojson_preview_tool
- Register UI resources in resourceRegistry.ts
The MCP Apps pattern enables broader client compatibility (Claude Code, VS Code) while preserving existing MCP-UI functionality. Tools now return both:
1. MCP-UI resource content via createUIResource()
2. MCP Apps metadata via _meta.ui.resourceUri
This graceful degradation allows clients to use whichever pattern they support.
Related: https://blog.modelcontextprotocol.io/posts/2026-01-26-mcp-apps/
All tests passing (520 tests).
* Add descriptive text content to MCP-UI tools for better client compatibility
Similar to mcp-server PR #104, enhance text content in tool responses to follow progressive enhancement pattern for better MCP spec compliance.
Changes:
- preview_style_tool: Add descriptive metadata (style ID, options, URL)
- style_comparison_tool: Add comparison metadata (before/after styles, view position, URL)
- geojson_preview_tool: Add GeoJSON metadata (type, feature count, geometry types, URL)
- Update tests to verify new descriptive text format
This ensures all MCP clients (including text-only clients) can display meaningful information, not just URLs. The text content includes:
- Success confirmation
- Key metadata about the operation
- The preview/comparison URL
Text content appears as first element (progressive enhancement):
1. Text description (for all clients)
2. MCP-UI resource (for MCP-UI clients)
3. MCP Apps metadata (for MCP Apps clients)
Related: mcp-server#104
All tests passing (520 tests).
* Revert text description enhancements - keep URLs simple
The enhanced text descriptions were not appropriate for these tools.
Unlike static_map_image_tool (PR #104) which returns an IMAGE,
these tools return URLs that should be opened by the user.
The URL itself is the primary actionable content, and adding
metadata made it less clear. LLMs can understand and use URLs
directly.
Keeping text content simple:
- preview_style_tool: Returns URL only
- style_comparison_tool: Returns URL only
- geojson_preview_tool: Returns URL only
All tests passing (520 tests).
* Implement MCP Apps SDK integration for UI resources
Properly integrate @modelcontextprotocol/ext-apps SDK in all UI resources:
- PreviewStyleUIResource: Use App.onContextUpdate() to receive tool result
- StyleComparisonUIResource: Extract URL from tool result and display in iframe
- GeojsonPreviewUIResource: Connect to host and render preview dynamically
Implementation follows MCP Apps quickstart pattern:
1. Import App SDK from esm.sh CDN
2. Connect to MCP host
3. Use onContextUpdate() to receive tool result containing the URL
4. Extract text content and set as iframe src
The tool result structure is:
{
content: [
{ type: 'text', text: 'https://...' }, // URL to display
{ type: 'resource', ... } // MCP-UI resource (if enabled)
]
}
All tests passing (520 tests).
* Add MCP Apps support for visualization tools
Implements MCP Apps pattern (2026-01-26 spec) for three visualization tools:
- geojson_preview_tool - Displays GeoJSON on maps
- preview_style_tool - Previews Mapbox styles
- style_comparison_tool - Compares two styles side-by-side
Key changes:
- Added @modelcontextprotocol/ext-apps dependency
- Register UI resources with registerAppResource() using RESOURCE_MIME_TYPE
- Include CSP metadata in resource contents to allow external domains
- Add meta.ui.resourceUri to tool schemas for MCP Apps integration
- Maintain backward compatibility by returning both URL and MCP-UI resource
Host compatibility:
- Goose: Full support (all tools work with CSP configuration)
- Claude Desktop & VS Code: Partial (CSP not yet implemented, iframes blocked)
CSP configuration allows https://*.mapbox.com domains for:
- connectDomains (fetch/XHR)
- resourceDomains (images, tiles)
- frameDomains (embedded maps)
Tests updated to reflect dual-content return (URL + MCP-UI resource).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Switch GeoJSON preview to light basemap for better visibility
Changes mapbox/streets-v12 to mapbox/light-v11 for cleaner GeoJSON visualization.
The @2x retina flag was already in place for high-resolution displays.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Enhance GeoJSON preview UI with click-to-zoom and better layout
- Add click-to-zoom functionality (toggle fit-to-window vs full size)
- Add hover hint showing zoom instructions at bottom
- Improve layout with dark theme and centered image
- Suggest larger default window size (1200x900)
- Add smooth hover effects and transitions
- Matches enhancements made to mcp-server static_map_image_tool
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Replace Mapbox Static Images with geojson.io/next for GeoJSON preview
Replace static PNG image previews with interactive geojson.io/next iframe embeds:
- Update GeojsonPreviewTool to generate geojson.io/next URLs (both text and UI resource)
- Replace GeojsonPreviewUIResource image viewer with iframe embed
- Remove unused generateStaticImageUrl() method
- Update CSP configuration to allow geojson.io frame domains
- Update tests and snapshots to reflect new URL format
Benefits:
- Interactive map editor instead of static image
- No Mapbox access token required for previews
- No URL length limitations
- Better user experience with zoom, pan, and editing capabilities
All 536 tests passing.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Fix geojson.io/next URL format: use query params instead of hash
Change from hash-based (#data=) to query parameter-based (?data=) URL format
to match geojson.io/next API requirements.
Key changes:
- Update URL format from #data= to ?data= in GeojsonPreviewTool
- Add comment explaining the difference
- Update all tests to expect query parameter format
- Update test snapshots
Analysis:
- Reviewed geojson.io/next source code in ~/git/geojson.io/next/app/components/url_api.tsx
- Confirmed it uses useSearchParams() for query params, not hash params
- Tested with actual geojson.io/next and verified it works
All 536 tests passing.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Add explicit subpath exports for tools, resources, prompts, and utils (#66)
* Add explicit subpath exports for tools, resources, prompts, and utils
Implements explicit, well-documented subpath exports similar to mcp-server
to allow users to import specific components without the full server.
## Changes
### Package Exports (via tshy)
- `@mapbox/mcp-devkit-server/tools` - Tool classes and pre-configured instances
- `@mapbox/mcp-devkit-server/resources` - Resource classes and instances
- `@mapbox/mcp-devkit-server/prompts` - Prompt classes and instances
- `@mapbox/mcp-devkit-server/utils` - HTTP pipeline utilities
All exports support both ESM and CommonJS via tshy dual builds.
### New Files
- `src/tools/index.ts` - Barrel export for 25 devkit tools
- `src/resources/index.ts` - Barrel export for 8 resources
- `src/prompts/index.ts` - Barrel export for 7 prompts
- `src/utils/index.ts` - Barrel export for HTTP utilities
### Updated Files
- `package.json` - Added tshy exports config
### Usage Examples
Simple - pre-configured instances:
\`\`\`typescript
import { listStyles, createStyle, previewStyle } from '@mapbox/mcp-devkit-server/tools';
\`\`\`
Advanced - custom tool instances:
\`\`\`typescript
import { ListStylesTool } from '@mapbox/mcp-devkit-server/tools';
import { httpRequest } from '@mapbox/mcp-devkit-server/utils';
const tool = new ListStylesTool({ httpRequest });
\`\`\`
Expert - custom HTTP pipeline:
\`\`\`typescript
import { HttpPipeline, UserAgentPolicy } from '@mapbox/mcp-devkit-server/utils';
const pipeline = new HttpPipeline();
pipeline.usePolicy(new UserAgentPolicy('MyApp/1.0'));
\`\`\`
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Add comprehensive documentation and examples for subpath exports
- Create docs/importing-tools.md with detailed guide covering:
- Simple usage: pre-configured instances
- Advanced usage: tool classes with default httpRequest
- Expert usage: custom HTTP pipeline
- Registry functions for batch operations
- Complete API reference for tools, resources, prompts, utils
- Best practices and troubleshooting
- Create examples/import-example.ts with working code examples
demonstrating all import patterns
- Create tsconfig.examples.json for type checking examples
- Update package.json scripts to include examples in:
- lint/lint:fix
- format/format:fix
- spellcheck
All examples pass linting and type checking.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* Fix MCP Apps support: remove ui/initialize, add viewUUID, fullscreen, sizing
Key fixes based on lessons from mapbox/mcp-server#109:
- Remove outdated ui/initialize handshake from all 3 UI resources (ext-apps
0.2.x pattern, not needed in 1.0.x - caused silent failures in Claude Desktop)
- Add viewUUID to tool response _meta in all 3 tools so host routes tool result
to the correct UI panel (required for Claude Desktop)
- Add fullscreen toggle button and ui/request-display-mode support to all 3
UI resources
- Add ui/notifications/host-context-changed handler to re-fit on fullscreen exit
- Add ui/notifications/size-changed (height: 700) after iframe load so panel
doesn't collapse in inline mode
- Remove debug console.log statements from all UI resource HTML
- Upgrade @modelcontextprotocol/sdk from ^1.25.3 to ^1.26.0
- Rename patch file to match new SDK version
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add Google Fonts domains to GeoJSON preview CSP resource domains
geojson.io loads fonts from fonts.gstatic.com and fonts.googleapis.com.
Adding them to resourceDomains so hosts that respect our CSP config
(VS Code, Goose) allow those fonts to load.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Re-add ui/initialize as non-blocking to fix Goose MCP Apps display
Goose requires the ui/initialize handshake before it will send
ui/notifications/tool-result to the app. Without it, the panel
renders but stays on "Loading..." indefinitely.
The call is fire-and-forget (.catch(() => {})) so Claude Desktop's
rejection is silently ignored — preserving compatibility with all hosts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Send ui/notifications/initialized after ui/initialize response
Per the MCP Apps spec, the host MUST NOT send tool-input or tool-result
until the app sends ui/notifications/initialized. Goose blocks on this
notification — without it the panel stays on "Loading..." forever.
Fix: after ui/initialize response resolves, send initialized notification.
Claude Desktop (which doesn't support the handshake) still gets errors
silently caught by .catch(() => {}).
Also fixes clientInfo field name (was incorrectly appInfo).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add debug console.log to GeoJSON preview for tracing protocol flow
Temporary diagnostic logging to trace which messages are sent/received
during the MCP Apps handshake in Goose.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Replace console.log debug with visible in-panel debug overlay
Console logs from sandboxed iframes don't appear in parent devtools.
Use a visible #debug div instead so the message flow is readable
directly in the Goose panel without switching iframe contexts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Remove type=module from UI app scripts (all three panels)
Inline module scripts may not execute in sandboxed srcdoc iframes in
Electron. Switching to classic scripts removes this risk. No module
features were being used anyway.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Clean up UI app scripts: remove debug overlay, use ES5 syntax
Removes temporary debug overlay from GeojsonPreviewUIResource.
Converts all three UI app scripts from modern ES syntax (const/let,
arrow functions, optional chaining, async/await) to plain ES5 to
maximize compatibility with sandboxed srcdoc iframes in Electron.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Always send ui/notifications/initialized regardless of ui/initialize outcome
If the host rejects ui/initialize (responds with an error), our previous
.catch(() => {}) silently swallowed the error and never sent initialized —
leaving hosts that gate tool-result on initialized blocked forever.
Fix: send ui/notifications/initialized in both success and error paths.
This covers all cases:
- Host responds successfully → send initialized → tool-result delivered
- Host rejects ui/initialize → still send initialized → tool-result delivered
- Host ignores ui/initialize (no handshake) → tool-result already delivered
independently via message listener
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Render MCP Apps panels inline with Mapbox GL JS (no inner iframes)
Rewrites all three UI App panels (geojson_preview_tool,
preview_style_tool, style_comparison_tool) to render directly with
Mapbox GL JS — eliminating inner iframes so Claude Desktop's
frame-src CSP restriction has no effect.
- GeoJSON Preview: auto-generates scoped pk.* token via Token API,
renders GeoJSON with fill/line/circle layers, auto-fits bounds
- Style Preview: parses pk.* token/username/styleId from tool result URL,
shows style name pill via map.getStyle().name after style.load
- Style Comparison: uses mapbox-gl-compare v0.4.0 for synced maps with
draggable reveal slider; shows both style name pills
- All panels: fullscreen toggle, ↗ Open in browser button (ui/open-link)
with clipboard fallback, MCP Apps handshake, ui/notifications/size-changed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Remove importing-tools docs and import example (unrelated to this PR)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Use Mapbox Standard style for GeoJSON preview base map
Switches from streets-v12 to mapbox://styles/mapbox/standard, the
current recommended default for GL JS v3. Adds slot: 'top' to all
GeoJSON layers so they render above 3D buildings and other Standard
style features.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Remove examples glob from lint scripts (no example files remain)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>1 parent 4722a2f commit 249fcc6
22 files changed
Lines changed: 2016 additions & 206 deletions
File tree
- patches
- src
- prompts
- resources
- ui-apps
- tools
- geojson-preview-tool
- preview-style-tool
- style-comparison-tool
- utils
- test/tools
- __snapshots__
- geojson-preview-tool
- preview-style-tool
- style-comparison-tool
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
3 | 15 | | |
4 | 16 | | |
5 | 17 | | |
6 | 18 | | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
7 | 24 | | |
8 | 25 | | |
9 | 26 | | |
| |||
0 commit comments