Skip to content

Commit e846426

Browse files
[mcp-ui] Adding MCP-UI support to those that support URLs
1 parent 7201837 commit e846426

File tree

11 files changed

+311
-35
lines changed

11 files changed

+311
-35
lines changed

CLAUDE.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,29 @@ Each tool execution span includes:
130130
- Tools can be enabled/disabled at startup (see `TOOL_CONFIGURATION.md`)
131131
- Example: `node dist/esm/index.js --enable-tools list_styles_tool,create_style_tool`
132132

133+
### MCP-UI Support (Enabled by Default)
134+
135+
MCP-UI allows tools that return URLs to also provide interactive iframe resources. **Enabled by default** and fully backwards compatible.
136+
137+
**Supported tools:**
138+
139+
- `preview_style_tool` - Embeds style previews
140+
- `geojson_preview_tool` - Embeds GeoJSON visualizations
141+
- `style_comparison_tool` - Embeds style comparisons
142+
143+
**How it works:**
144+
145+
- Tools return both text URL and UIResource
146+
- Clients without MCP-UI support (e.g., Claude Desktop) ignore UIResource
147+
- Clients with MCP-UI support (e.g., Goose) render iframes
148+
149+
**Disable if needed:**
150+
151+
- Environment variable: `ENABLE_MCP_UI=false`
152+
- Command-line flag: `--disable-mcp-ui`
153+
154+
**Note:** You rarely need to disable this. See [mcpui.dev](https://mcpui.dev) for compatible clients.
155+
133156
## Mapbox Token Scopes
134157

135158
- Each tool requires specific Mapbox token scopes (see `README.md` for details)

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,42 @@ Set `VERBOSE_ERRORS=true` to get detailed error messages from the MCP server. Th
671671

672672
By default, the server returns generic error messages. With verbose errors enabled, you'll receive the actual error details, which can help diagnose API connection issues, invalid parameters, or other problems.
673673

674+
#### ENABLE_MCP_UI
675+
676+
**MCP-UI Support (Enabled by Default)**
677+
678+
MCP-UI allows tools that return URLs to also return interactive iframe resources that can be embedded directly in supporting MCP clients. **This is enabled by default** and is fully backwards compatible with all MCP clients.
679+
680+
**Supported Tools:**
681+
682+
- `preview_style_tool` - Embeds Mapbox style previews
683+
- `geojson_preview_tool` - Embeds geojson.io visualizations
684+
- `style_comparison_tool` - Embeds side-by-side style comparisons
685+
686+
**How it Works:**
687+
688+
- Tools return **both** a text URL (always works) and a UIResource for iframe embedding
689+
- Clients that don't support MCP-UI (like Claude Desktop) simply ignore the UIResource and use the text URL
690+
- Clients that support MCP-UI (like Goose) can render the iframe for a richer experience
691+
692+
**Disabling MCP-UI (Optional):**
693+
694+
If you want to disable MCP-UI support:
695+
696+
Via environment variable:
697+
698+
```bash
699+
export ENABLE_MCP_UI=false
700+
```
701+
702+
Or via command-line flag:
703+
704+
```bash
705+
node dist/esm/index.js --disable-mcp-ui
706+
```
707+
708+
**Note:** You typically don't need to disable this. The implementation is fully backwards compatible and doesn't affect clients that don't support MCP-UI. See [mcpui.dev](https://mcpui.dev) for compatible clients.
709+
674710
## Troubleshooting
675711

676712
**Issue:** Tools fail with authentication errors

package-lock.json

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"mcp"
4747
],
4848
"dependencies": {
49+
"@mcp-ui/server": "^5.13.1",
4950
"@modelcontextprotocol/sdk": "^1.17.5",
5051
"@opentelemetry/api": "^1.9.0",
5152
"@opentelemetry/auto-instrumentations-node": "^0.56.0",

src/config/toolConfig.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@ import { ToolInstance } from '../tools/toolRegistry.js';
33
export interface ToolConfig {
44
enabledTools?: string[];
55
disabledTools?: string[];
6+
enableMcpUi?: boolean;
67
}
78

89
export function parseToolConfigFromArgs(): ToolConfig {
910
const args = process.argv.slice(2);
1011
const config: ToolConfig = {};
1112

13+
// Check environment variable first (takes precedence)
14+
if (process.env.ENABLE_MCP_UI !== undefined) {
15+
config.enableMcpUi = process.env.ENABLE_MCP_UI === 'true';
16+
}
17+
1218
for (let i = 0; i < args.length; i++) {
1319
const arg = args[i];
1420

@@ -22,9 +28,19 @@ export function parseToolConfigFromArgs(): ToolConfig {
2228
if (value) {
2329
config.disabledTools = value.split(',').map((t) => t.trim());
2430
}
31+
} else if (arg === '--disable-mcp-ui') {
32+
// Command-line flag can disable it if env var not set
33+
if (config.enableMcpUi === undefined) {
34+
config.enableMcpUi = false;
35+
}
2536
}
2637
}
2738

39+
// Default to true if not set (enabled by default)
40+
if (config.enableMcpUi === undefined) {
41+
config.enableMcpUi = true;
42+
}
43+
2844
return config;
2945
}
3046

@@ -53,3 +69,27 @@ export function filterTools(
5369

5470
return filteredTools;
5571
}
72+
73+
/**
74+
* Check if MCP-UI support is enabled.
75+
* MCP-UI is enabled by default and can be explicitly disabled via:
76+
* - Environment variable: ENABLE_MCP_UI=false
77+
* - Command-line flag: --disable-mcp-ui
78+
*
79+
* @returns true if MCP-UI is enabled (default), false if explicitly disabled
80+
*/
81+
export function isMcpUiEnabled(): boolean {
82+
// Check environment variable first (takes precedence)
83+
if (process.env.ENABLE_MCP_UI === 'false') {
84+
return false;
85+
}
86+
87+
// Check command-line arguments
88+
const args = process.argv.slice(2);
89+
if (args.includes('--disable-mcp-ui')) {
90+
return false;
91+
}
92+
93+
// Default to enabled
94+
return true;
95+
}

src/tools/geojson-preview-tool/GeojsonPreviewTool.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
// Licensed under the MIT License.
33

44
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
5+
import { createUIResource } from '@mcp-ui/server';
56
import { GeoJSON } from 'geojson';
67
import { BaseTool } from '../BaseTool.js';
78
import {
89
GeojsonPreviewSchema,
910
GeojsonPreviewInput
1011
} from './GeojsonPreviewTool.input.schema.js';
12+
import { isMcpUiEnabled } from '../../config/toolConfig.js';
1113

1214
export class GeojsonPreviewTool extends BaseTool<typeof GeojsonPreviewSchema> {
1315
name = 'geojson_preview_tool';
@@ -72,14 +74,30 @@ export class GeojsonPreviewTool extends BaseTool<typeof GeojsonPreviewSchema> {
7274
const encodedGeoJSON = encodeURIComponent(geojsonString);
7375
const geojsonIOUrl = `https://geojson.io/#data=data:application/json,${encodedGeoJSON}`;
7476

77+
// Build content array with URL
78+
const content: CallToolResult['content'] = [
79+
{
80+
type: 'text',
81+
text: geojsonIOUrl
82+
}
83+
];
84+
85+
// Conditionally add MCP-UI resource if enabled
86+
if (isMcpUiEnabled()) {
87+
const uiResource = createUIResource({
88+
uri: `ui://mapbox/geojson-preview/${Date.now()}`,
89+
content: {
90+
type: 'externalUrl',
91+
iframeUrl: geojsonIOUrl
92+
},
93+
encoding: 'text'
94+
});
95+
content.push(uiResource);
96+
}
97+
7598
return {
7699
isError: false,
77-
content: [
78-
{
79-
type: 'text',
80-
text: geojsonIOUrl
81-
}
82-
]
100+
content
83101
};
84102
} catch (error) {
85103
const errorMessage =

src/tools/preview-style-tool/PreviewStyleTool.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2+
import { createUIResource } from '@mcp-ui/server';
23
import { BaseTool } from '../BaseTool.js';
34
import { MapboxApiBasedTool } from '../MapboxApiBasedTool.js';
45
import {
56
PreviewStyleSchema,
67
PreviewStyleInput
78
} from './PreviewStyleTool.input.schema.js';
89
import { getUserNameFromToken } from '../../utils/jwtUtils.js';
10+
import { isMcpUiEnabled } from '../../config/toolConfig.js';
911

1012
export class PreviewStyleTool extends BaseTool<typeof PreviewStyleSchema> {
1113
readonly name = 'preview_style_tool';
@@ -63,13 +65,29 @@ export class PreviewStyleTool extends BaseTool<typeof PreviewStyleSchema> {
6365

6466
const url = `${MapboxApiBasedTool.mapboxApiEndpoint}styles/v1/${userName}/${input.styleId}.html?${params.toString()}${hashFragment}`;
6567

68+
// Build content array with URL
69+
const content: CallToolResult['content'] = [
70+
{
71+
type: 'text',
72+
text: url
73+
}
74+
];
75+
76+
// Conditionally add MCP-UI resource if enabled
77+
if (isMcpUiEnabled()) {
78+
const uiResource = createUIResource({
79+
uri: `ui://mapbox/preview-style/${userName}/${input.styleId}`,
80+
content: {
81+
type: 'externalUrl',
82+
iframeUrl: url
83+
},
84+
encoding: 'text'
85+
});
86+
content.push(uiResource);
87+
}
88+
6689
return {
67-
content: [
68-
{
69-
type: 'text',
70-
text: url
71-
}
72-
],
90+
content,
7391
isError: false
7492
};
7593
}

src/tools/style-comparison-tool/StyleComparisonTool.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
// Licensed under the MIT License.
33

44
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
5+
import { createUIResource } from '@mcp-ui/server';
56
import { BaseTool } from '../BaseTool.js';
67
import {
78
StyleComparisonSchema,
89
StyleComparisonInput
910
} from './StyleComparisonTool.schema.js';
1011
import { getUserNameFromToken } from '../../utils/jwtUtils.js';
12+
import { isMcpUiEnabled } from '../../config/toolConfig.js';
1113

1214
export class StyleComparisonTool extends BaseTool<
1315
typeof StyleComparisonSchema
@@ -99,13 +101,29 @@ export class StyleComparisonTool extends BaseTool<
99101
url += `#${input.zoom}/${input.latitude}/${input.longitude}`;
100102
}
101103

104+
// Build content array with URL
105+
const content: CallToolResult['content'] = [
106+
{
107+
type: 'text',
108+
text: url
109+
}
110+
];
111+
112+
// Conditionally add MCP-UI resource if enabled
113+
if (isMcpUiEnabled()) {
114+
const uiResource = createUIResource({
115+
uri: `ui://mapbox/style-comparison/${beforeStyleId}/${afterStyleId}`,
116+
content: {
117+
type: 'externalUrl',
118+
iframeUrl: url
119+
},
120+
encoding: 'text'
121+
});
122+
content.push(uiResource);
123+
}
124+
102125
return {
103-
content: [
104-
{
105-
type: 'text',
106-
text: url
107-
}
108-
],
126+
content,
109127
isError: false
110128
};
111129
}

0 commit comments

Comments
 (0)