diff --git a/src/resources/resourceRegistry.ts b/src/resources/resourceRegistry.ts index 84cdac7..e94e0a3 100644 --- a/src/resources/resourceRegistry.ts +++ b/src/resources/resourceRegistry.ts @@ -3,12 +3,15 @@ // INSERT NEW RESOURCE IMPORT HERE import { CategoryListResource } from './category-list/CategoryListResource.js'; +import { StaticMapUIResource } from './ui-apps/StaticMapUIResource.js'; import { httpRequest } from '../utils/httpPipeline.js'; // Central registry of all resources export const ALL_RESOURCES = [ // INSERT NEW RESOURCE INSTANCE HERE - new CategoryListResource({ httpRequest }) + new CategoryListResource({ httpRequest }), + // MCP Apps UI resources (ui:// scheme) + new StaticMapUIResource() ] as const; export type ResourceInstance = (typeof ALL_RESOURCES)[number]; diff --git a/src/resources/ui-apps/StaticMapUIResource.ts b/src/resources/ui-apps/StaticMapUIResource.ts new file mode 100644 index 0000000..0109e92 --- /dev/null +++ b/src/resources/ui-apps/StaticMapUIResource.ts @@ -0,0 +1,114 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; +import { BaseResource } from '../BaseResource.js'; + +/** + * Serves UI App HTML for Static Map Preview + * Implements MCP Apps pattern with ui:// scheme + */ +export class StaticMapUIResource extends BaseResource { + readonly name = 'Static Map Preview UI'; + readonly uri = 'ui://mapbox/static-map/*'; + readonly description = + 'Interactive UI for previewing static map images (MCP Apps)'; + readonly mimeType = 'text/html'; + + public async read(uri: string): Promise { + // Parse URI to extract style and position + // Format: ui://mapbox/static-map/{style}/{lng},{lat},{zoom} + const parsedUri = new URL(uri); + const pathParts = parsedUri.pathname.split('/').filter((p) => p); + const style = pathParts[2]; + const position = pathParts[3]; + + if (!style || !position) { + return { + contents: [ + { + uri: uri, + mimeType: 'text/plain', + text: 'Error: Invalid URI format. Expected ui://mapbox/static-map/{style}/{lng},{lat},{zoom}' + } + ] + }; + } + + // Generate HTML with embedded iframe for static map + const html = ` + + + + + Static Map Preview + + + +
Loading static map preview...
+ + + + +`; + + return { + contents: [ + { + uri: uri, + mimeType: 'text/html', + text: html + } + ] + }; + } +} diff --git a/src/tools/static-map-image-tool/StaticMapImageTool.ts b/src/tools/static-map-image-tool/StaticMapImageTool.ts index 7a9123c..6802a82 100644 --- a/src/tools/static-map-image-tool/StaticMapImageTool.ts +++ b/src/tools/static-map-image-tool/StaticMapImageTool.ts @@ -166,9 +166,22 @@ export class StaticMapImageTool extends MapboxApiBasedTool< content.push(uiResource); } - return { + // Add MCP Apps metadata (new pattern for broader client compatibility) + const result: CallToolResult = { content, isError: false }; + + // Add ui:// resource URI for MCP Apps pattern + // This works alongside MCP-UI for backward compatibility + if (isMcpUiEnabled()) { + result._meta = { + ui: { + resourceUri: `ui://mapbox/static-map/${input.style}/${lng},${lat},${input.zoom}` + } + }; + } + + return result; } }