Skip to content

Conversation

@mattpodwysocki
Copy link
Contributor

@mattpodwysocki mattpodwysocki commented Jan 28, 2026

Summary

This PR implements the MCP Apps pattern for all tools with existing MCP-UI support, maintaining backward compatibility by supporting both patterns simultaneously.

Changes

Tools with MCP Apps Support

  • preview_style_tool - Preview Mapbox styles
  • style_comparison_tool - Compare two styles side-by-side
  • geojson_preview_tool - Visualize GeoJSON data

Implementation Details

Tool Response Pattern

Each tool now returns both:

  1. Text content - The URL to display (primary content)
  2. MCP-UI resource via createUIResource() (for MCP-UI clients)
  3. MCP Apps metadata via _meta.ui.resourceUri (for MCP Apps clients)

UI Resources with MCP Apps SDK

New UI resources created using the @modelcontextprotocol/ext-apps SDK:

  • PreviewStyleUIResource - Serves interactive HTML for style preview
  • StyleComparisonUIResource - Serves interactive HTML for style comparison
  • GeojsonPreviewUIResource - Serves interactive HTML for GeoJSON visualization

Each UI resource:

  1. Imports the MCP Apps SDK from esm.sh CDN
  2. Connects to the MCP host via App.connect()
  3. Uses App.onContextUpdate() to receive the tool result
  4. Extracts the URL from text content and displays in an iframe

Example implementation:

import { App } from "https://esm.sh/@modelcontextprotocol/ext-apps@0.1.1";

const app = new App();
await app.connect();

app.onContextUpdate((context) => {
  const url = context.toolResult.content.find(c => c.type === 'text')?.text;
  if (url) iframe.src = url;
});

Client Compatibility

This graceful degradation approach supports:

  • Claude Code - Supports MCP Apps via _meta.ui.resourceUri
  • VS Code - Supports MCP Apps via _meta.ui.resourceUri
  • Existing MCP-UI clients - Continue to use createUIResource()
  • Text-only clients - Display the URL directly

Testing

All tests passing: 520 tests

Build verified successfully.

Related

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).
mattpodwysocki and others added 5 commits January 28, 2026 10:22
…ibility

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).
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).
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).
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>
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant