-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathPreviewStyleTool.ts
More file actions
109 lines (95 loc) · 3.04 KB
/
Copy pathPreviewStyleTool.ts
File metadata and controls
109 lines (95 loc) · 3.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { randomUUID } from 'node:crypto';
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
import { createUIResource } from '@mcp-ui/server';
import { BaseTool } from '../BaseTool.js';
import { MapboxApiBasedTool } from '../MapboxApiBasedTool.js';
import {
PreviewStyleSchema,
PreviewStyleInput
} from './PreviewStyleTool.input.schema.js';
import { getUserNameFromToken } from '../../utils/jwtUtils.js';
export class PreviewStyleTool extends BaseTool<typeof PreviewStyleSchema> {
readonly name = 'preview_style_tool';
readonly description =
'Generate preview URL for a Mapbox style using an existing public token';
readonly annotations = {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
title: 'Preview Mapbox Style Tool'
};
readonly meta = {
ui: {
resourceUri: 'ui://mapbox/preview-style/index.html',
csp: {
connectDomains: ['https://*.mapbox.com'],
resourceDomains: ['https://*.mapbox.com'],
frameDomains: ['https://*.mapbox.com']
}
}
};
constructor() {
super({ inputSchema: PreviewStyleSchema });
}
protected async execute(input: PreviewStyleInput): Promise<CallToolResult> {
let userName: string;
try {
userName = getUserNameFromToken(input.accessToken);
} catch (error) {
return {
isError: true,
content: [
{
type: 'text',
text: error instanceof Error ? error.message : String(error)
}
]
};
}
// Use the user-provided public token
const publicToken = input.accessToken;
// Build URL for the embeddable HTML endpoint
const params = new URLSearchParams();
params.append('access_token', publicToken);
params.append('fresh', 'true'); // Ensure secure access
if (input.title !== undefined) {
params.append('title', input.title.toString());
}
if (input.zoomwheel !== undefined) {
params.append('zoomwheel', input.zoomwheel.toString());
}
// Build hash fragment for map view parameters
const hashParams: string[] = [];
const hashFragment =
hashParams.length > 0 ? `#${hashParams.join('/')}` : '';
const url = `${MapboxApiBasedTool.mapboxApiEndpoint}styles/v1/${encodeURIComponent(userName)}/${encodeURIComponent(input.styleId)}.html?${params.toString()}${hashFragment}`;
// Build content array with URL
const content: CallToolResult['content'] = [
{
type: 'text',
text: url
}
];
// Add MCP-UI resource (for legacy MCP-UI clients)
const uiResource = createUIResource({
uri: `ui://mapbox/preview-style/${encodeURIComponent(userName)}/${encodeURIComponent(input.styleId)}`,
content: {
type: 'externalUrl',
iframeUrl: url
},
encoding: 'text',
uiMetadata: {
'preferred-frame-size': ['1000px', '700px']
}
});
content.push(uiResource);
return {
content,
isError: false,
_meta: {
viewUUID: randomUUID()
}
};
}
}