This document covers how the Mapbox MCP Server delivers interactive map previews in compatible clients.
- Overview
- Supported Tools
- Compatible Clients
- How It Works
- Configuration
- Technical Details
- Examples
- Troubleshooting
The Mapbox MCP Server actively invests in MCP Apps as its primary interactive preview protocol. MCP Apps serves self-contained HTML app panels directly inside the chat, and is supported by Claude Desktop, VS Code with GitHub Copilot, and Claude Code.
The server also maintains support for MCP-UI for backwards compatibility with clients like Goose. MCP-UI is not being removed, but new interactive preview development is focused on MCP Apps.
All clients — regardless of protocol support — receive the base64-encoded map image as standard output.
- Rich interactive previews: MCP Apps clients get a full HTML map panel with a Fullscreen toggle
- Full backwards compatibility: Clients without MCP Apps or MCP-UI support still receive the base64 image
- Progressive enhancement: Every client gets a usable result; supporting clients get a richer experience
This tool always returns:
- The Mapbox Static Images API URL (text, required first for MCP Apps to render)
- Base64-encoded map image (for all clients without interactive preview support)
- MCP-UI UIResource (only when MCP-UI is enabled, for Goose and other MCP-UI clients)
MCP Apps clients (Claude Desktop, VS Code, Claude Code): Render the interactive HTML panel served by StaticMapUIResource, with click-to-zoom and a Fullscreen toggle.
MCP-UI clients (Goose): Render the embedded iframe resource.
Standard clients: Display the base64 image inline.
| Client | MCP Apps | MCP-UI |
|---|---|---|
| Claude Desktop | ✅ | |
| VS Code with GitHub Copilot | ✅ | |
| Claude Code | ✅ | |
| Goose | ✅ | ✅ |
| Cursor IDE | ||
| Other clients |
Note: Client compatibility may change as adoption of both protocols grows. Check your client's documentation for the latest support status.
All clients in the table receive the base64 image regardless of protocol support.
┌─────────────────┐
│ MCP Client │
│ (e.g., Goose) │
└────────┬────────┘
│
│ MCP Protocol
│
┌────────▼────────────────────┐
│ Mapbox MCP Server │
│ ┌────────────────────────┐ │
│ │ static_map_image_tool │ │
│ └────────┬───────────────┘ │
│ │ │
│ ├─► Text description│
│ │ (always) │
│ ├─► Base64 image │
│ │ (always) │
│ └─► UIResource │
│ (if enabled) │
└─────────────────────────────┘
The static_map_image_tool returns a response with multiple content items, following the progressive enhancement pattern:
{
content: [
{
// The Mapbox Static Images API URL — MCP Apps reads this to render the map
type: 'text',
text: 'https://api.mapbox.com/styles/v1/mapbox/streets-v12/static/...'
},
{
// Base64-encoded image — for all clients without interactive preview support
type: 'image',
data: '<base64-encoded-image>',
mimeType: 'image/png'
},
{
// MCP-UI resource for interactive iframes — only present when MCP-UI is enabled
type: 'resource',
resource: {
uri: 'ui://mapbox/static-map/...',
mimeType: 'text/html',
text: '...',
uiMetadata: {
'preferred-frame-size': ['800px', '600px']
}
}
}
];
}MCP Apps clients (Claude Desktop, VS Code, Claude Code) render the interactive HTML panel served via StaticMapUIResource. The URL in content[0] is used by the panel to fetch and display the map image.
MCP-UI clients (Goose) render the iframe resource for an inline preview.
Standard clients (Cursor, etc.) render the base64 image from content[1].
MCP-UI is enabled by default. You can disable it using either an environment variable or command-line flag.
export ENABLE_MCP_UI=false
npm run buildOr in your client configuration:
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"mapbox": {
"command": "npx",
"args": ["-y", "@mapbox/mcp-server"],
"env": {
"MAPBOX_ACCESS_TOKEN": "your_token_here",
"ENABLE_MCP_UI": "false"
}
}
}
}VS Code (settings.json):
{
"mcp": {
"servers": {
"MapboxServer": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@mapbox/mcp-server"],
"env": {
"MAPBOX_ACCESS_TOKEN": "your_token",
"ENABLE_MCP_UI": "false"
}
}
}
}
}npx @mapbox/mcp-server --disable-mcp-uiOr in your client configuration:
Claude Desktop:
{
"mcpServers": {
"mapbox": {
"command": "npx",
"args": ["-y", "@mapbox/mcp-server", "--disable-mcp-ui"],
"env": {
"MAPBOX_ACCESS_TOKEN": "your_token_here"
}
}
}
}If both the environment variable and command-line flag are specified, the environment variable takes precedence.
The Mapbox MCP Server supports two interactive preview protocols:
- MCP Apps (
@modelcontextprotocol/ext-apps) — serves a self-contained HTML app viaStaticMapUIResourcewithRESOURCE_MIME_TYPE(text/html;profile=mcp-app). Supported by Claude Desktop, VS Code, and Claude Code. - MCP-UI (
@mcp-ui/server) — createsUIResourceobjects with embedded iframe URLs. Supported by Goose.
Key Files:
src/config/toolConfig.ts- Configuration logic for MCP-UI (isMcpUiEnabled()helper)src/tools/static-map-image-tool/StaticMapImageTool.ts- Fetch + base64 encoding, URL as first content item, conditional UIResourcesrc/resources/ui-apps/StaticMapUIResource.ts- MCP Apps HTML panel implementationsrc/tools/BaseTool.ts-meta.uiproperty for MCP Apps CSP configuration
Code Example:
import { createUIResource } from '@mcp-ui/server';
import { isMcpUiEnabled } from '../../config/toolConfig.js';
// content[0] MUST be the URL — MCP Apps UI finds it via content.find(c => c.type === 'text')
const content: CallToolResult['content'] = [
{
type: 'text',
text: url // Mapbox Static Images API URL
},
{
type: 'image',
data: base64Data,
mimeType
}
];
// Conditionally add MCP-UI resource if enabled (for Goose and other MCP-UI clients)
if (isMcpUiEnabled()) {
const uiResource = createUIResource({
uri: `ui://mapbox/static-map/${input.style}/${lng},${lat},${input.zoom}`,
content: {
type: 'externalUrl',
iframeUrl: url
},
encoding: 'text',
uiMetadata: {
'preferred-frame-size': [`${width}px`, `${height}px`]
}
});
content.push(uiResource);
}
return { content, isError: false };The UIResource includes:
- uri: Unique identifier for the resource (e.g.,
ui://mapbox/static-map/...) - content.type: Set to
'externalUrl'to indicate an iframe - content.iframeUrl: The actual URL to embed (Mapbox Static Images API URL)
- encoding: Set to
'text' - uiMetadata: Optional metadata including preferred dimensions
-
Install Goose (follow Goose installation instructions)
-
Configure the Mapbox MCP Server in Goose's configuration
-
Use natural language to request a map:
"Show me a map of downtown San Francisco centered at 37.7749, -122.4194" -
Goose will display an inline interactive map rather than just a static image
Create a map showing the route from Golden Gate Bridge to Fisherman's Wharf
with markers at both locations
MCP Apps clients (Claude Desktop, VS Code, Claude Code):
- Displays interactive HTML map panel with Fullscreen toggle
MCP-UI clients (Goose):
- Displays interactive embedded map via iframe
Standard clients (Cursor, etc.):
- Displays base64-encoded static PNG image
All clients provide the same map information; the experience differs based on client capabilities.
Solution: Check that:
- Your MCP client supports MCP-UI (see Compatible Clients)
- MCP-UI is enabled (check environment variable and command-line flags)
- Your client is properly configured to render MCP-UI resources
Solution: Check the server logs or test with a known MCP-UI compatible client like Goose. When MCP-UI is enabled, the static_map_image_tool will return 3 content items (URL text + image + UIResource) instead of 2 (URL text + image).
Solution: Yes! If you're developing custom tools for this server:
- Add dependency:
@mcp-ui/server - Import
createUIResourceandisMcpUiEnabled() - Conditionally add UIResource to your tool's response
- Follow the pattern in
StaticMapImageTool.ts
Solution: No. Disabling MCP-UI only removes the iframe URLs from responses. All tools continue to function normally, returning their standard output (text, images, JSON, etc.).
- MCP-UI Specification - Official MCP-UI documentation
- Goose Documentation - MCP-UI compatible AI agent
- Mapbox Static Images API - API used for map rendering
For questions or issues related to MCP-UI support, please open an issue on GitHub.