Skip to content

Commit 249fcc6

Browse files
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

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
## Unreleased
22

3+
### Features Added
4+
5+
- **MCP Apps support for preview_style_tool, style_comparison_tool, geojson_preview_tool** (#62)
6+
- All three panels now render inline with **Mapbox GL JS** — no inner iframes, works in Claude Desktop regardless of `frame-src` CSP restrictions
7+
- **GeoJSON Preview**: renders GeoJSON (points, lines, polygons) on a GL map with auto-fit bounds. Auto-generates a short-lived scoped `pk.*` token on the customer's Mapbox account via the Token API (scopes: `styles:tiles`, `styles:read`, `fonts:read`); cached for 1 hour
8+
- **Style Preview**: renders the user's style directly via `mapbox://styles/...`; shows the human-readable style name as a pill overlay (from `map.getStyle().name`)
9+
- **Style Comparison**: two synced GL maps with a draggable reveal slider using `mapbox-gl-compare`; shows both style names as pills; respects initial map position from tool result hash fragment
10+
- Full MCP Apps handshake: `ui/initialize` → response → `ui/notifications/initialized`; errors silently ignored for hosts that don't implement the handshake
11+
- Added `↗ Open in browser` button (`ui/open-link`) to all three panels as fallback
12+
- Fullscreen toggle on all panels; `map.resize()` called on display mode change
13+
- Compatible with Claude Desktop, VS Code, and Goose
14+
315
### Configuration
416

517
- **Hosted MCP Server**: Added remote entry in `server.json` for the hosted DevKit MCP server at `https://mcp-devkit.mapbox.com/mcp` using streamable-http transport
618

19+
### Dependencies
20+
21+
- Updated `@modelcontextprotocol/sdk` from 1.25.3 to 1.26.0
22+
- Updated patch file for SDK 1.26.0
23+
724
### Documentation
825

926
- **PR Guidelines**: Added CHANGELOG requirement to CLAUDE.md (#67)

0 commit comments

Comments
 (0)