Skip to content

Commit 1bd33a8

Browse files
[tools] Update tools to use structuredContent with schema
1 parent c387b48 commit 1bd33a8

80 files changed

Lines changed: 2161 additions & 1224 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cspell.config.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"version": "0.2",
3+
"language": "en",
4+
"words": [
5+
"bbox",
6+
"denoise",
7+
"isochrone",
8+
"mapbox",
9+
"mmss",
10+
"tilequery"
11+
],
12+
"ignorePaths": [
13+
"node_modules",
14+
"dist"
15+
]
16+
}

package.json

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@
99
"mapbox-mcp-devkit": "dist/esm/index.js"
1010
},
1111
"scripts": {
12-
"lint": "eslint \"./src/**/*.{ts,tsx}\" \"./test/**/*.{ts,tsx}\"",
13-
"lint:fix": "eslint \"./src/**/*.{ts,tsx}\" \"./test/**/*.{ts,tsx}\" --fix",
14-
"fix-lint": "npm run lint:fix && npm run format:fix",
12+
"build": "npm run prepare && tshy && npm run generate-version && node scripts/add-shebang.cjs",
1513
"format": "prettier --check \"./src/**/*.{ts,tsx,js,json,md}\" \"./test/**/*.{ts,tsx,js,json,md}\"",
1614
"format:fix": "prettier --write \"./src/**/*.{ts,tsx,js,json,md}\" \"./test/**/*.{ts,tsx,js,json,md}\"",
17-
"prepare": "husky && node .husky/setup-hooks.js",
18-
"test": "vitest",
19-
"build": "npm run prepare && npm run sync-manifest && tshy && npm run generate-version && node scripts/build-helpers.cjs copy-json && node scripts/add-shebang.cjs",
2015
"generate-version": "node scripts/build-helpers.cjs generate-version",
16+
"inspect:build": "npm run build && npx @modelcontextprotocol/inspector -e MAPBOX_ACCESS_TOKEN=\"$MAPBOX_ACCESS_TOKEN\" node dist/esm/index.js",
17+
"inspect:dev": "npx @modelcontextprotocol/inspector -e MAPBOX_ACCESS_TOKEN=\"$MAPBOX_ACCESS_TOKEN\" npx -y tsx src/index.ts",
18+
"lint": "eslint \"./src/**/*.{ts,tsx}\" \"./test/**/*.{ts,tsx}\"",
19+
"lint:fix": "eslint \"./src/**/*.{ts,tsx}\" \"./test/**/*.{ts,tsx}\" --fix",
20+
"prepare": "husky && node .husky/setup-hooks.js",
21+
"spellcheck": "cspell \"*.md\" \"src/**/*.ts\" \"test/**/*.ts\"",
2122
"sync-manifest": "node scripts/sync-manifest-version.cjs",
22-
"dev": "tsc -p tsconfig.json --watch",
23-
"dev:inspect": "npx @modelcontextprotocol/inspector -e MAPBOX_ACCESS_TOKEN=\"$MAPBOX_ACCESS_PRIVATE_TOKEN\" npx -y tsx src/index.ts"
23+
"test": "vitest"
2424
},
2525
"lint-staged": {
2626
"*.{js,jsx,ts,tsx}": "eslint --fix",
@@ -52,6 +52,7 @@
5252
"@typescript-eslint/eslint-plugin": "^8.0.0",
5353
"@typescript-eslint/parser": "^8.0.0",
5454
"@vitest/coverage-istanbul": "^3.2.4",
55+
"cspell": "^9.2.1",
5556
"eslint": "^9.0.0",
5657
"eslint-config-prettier": "^10.1.8",
5758
"eslint-plugin-n": "^17.21.3",

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Copyright (c) Mapbox, Inc.
2+
// Licensed under the MIT License.
3+
14
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
25
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
36
import { parseToolConfigFromArgs, filterTools } from './config/toolConfig.js';

src/schemas/style.ts

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
// Copyright (c) Mapbox, Inc.
2+
// Licensed under the MIT License.
3+
4+
import { z } from 'zod';
5+
6+
// Basic types
7+
const ColorSchema = z
8+
.string()
9+
.describe('Color as hex, rgb, rgba, hsl, or hsla');
10+
const CoordinatesSchema = z.tuple([z.number(), z.number()]);
11+
12+
// Transition schema
13+
const TransitionSchema = z
14+
.object({
15+
duration: z.number().optional(),
16+
delay: z.number().optional()
17+
})
18+
.passthrough();
19+
20+
// Light schema
21+
const LightSchema = z
22+
.object({
23+
anchor: z.enum(['map', 'viewport']).optional(),
24+
position: z.tuple([z.number(), z.number(), z.number()]).optional(),
25+
color: ColorSchema.optional(),
26+
intensity: z.number().optional()
27+
})
28+
.passthrough();
29+
30+
// Lights (3D) schema
31+
const LightsSchema = z.array(
32+
z
33+
.object({
34+
id: z.string(),
35+
type: z.enum(['ambient', 'directional'])
36+
})
37+
.passthrough()
38+
);
39+
40+
// Terrain schema
41+
const TerrainSchema = z
42+
.object({
43+
source: z.string(),
44+
exaggeration: z.number().optional()
45+
})
46+
.passthrough();
47+
48+
// Source schemas
49+
const VectorSourceSchema = z
50+
.object({
51+
type: z.literal('vector'),
52+
url: z.string().optional(),
53+
tiles: z.array(z.string()).optional(),
54+
bounds: z
55+
.tuple([z.number(), z.number(), z.number(), z.number()])
56+
.optional(),
57+
scheme: z.enum(['xyz', 'tms']).optional(),
58+
minzoom: z.number().min(0).max(22).optional(),
59+
maxzoom: z.number().min(0).max(22).optional(),
60+
attribution: z.string().optional(),
61+
promoteId: z.union([z.string(), z.record(z.string())]).optional(),
62+
volatile: z.boolean().optional()
63+
})
64+
.passthrough();
65+
66+
const RasterSourceSchema = z
67+
.object({
68+
type: z.literal('raster'),
69+
url: z.string().optional(),
70+
tiles: z.array(z.string()).optional(),
71+
bounds: z
72+
.tuple([z.number(), z.number(), z.number(), z.number()])
73+
.optional(),
74+
minzoom: z.number().min(0).max(22).optional(),
75+
maxzoom: z.number().min(0).max(22).optional(),
76+
tileSize: z.number().optional(),
77+
scheme: z.enum(['xyz', 'tms']).optional(),
78+
attribution: z.string().optional(),
79+
volatile: z.boolean().optional()
80+
})
81+
.passthrough();
82+
83+
const RasterDemSourceSchema = z
84+
.object({
85+
type: z.literal('raster-dem'),
86+
url: z.string().optional(),
87+
tiles: z.array(z.string()).optional(),
88+
bounds: z
89+
.tuple([z.number(), z.number(), z.number(), z.number()])
90+
.optional(),
91+
minzoom: z.number().min(0).max(22).optional(),
92+
maxzoom: z.number().min(0).max(22).optional(),
93+
tileSize: z.number().optional(),
94+
attribution: z.string().optional(),
95+
encoding: z.enum(['terrarium', 'mapbox']).optional(),
96+
volatile: z.boolean().optional()
97+
})
98+
.passthrough();
99+
100+
const GeoJSONSourceSchema = z
101+
.object({
102+
type: z.literal('geojson'),
103+
data: z.union([z.string(), z.any()]), // URL or inline GeoJSON
104+
maxzoom: z.number().min(0).max(24).optional(),
105+
attribution: z.string().optional(),
106+
buffer: z.number().min(0).max(512).optional(),
107+
tolerance: z.number().optional(),
108+
cluster: z.boolean().optional(),
109+
clusterRadius: z.number().optional(),
110+
clusterMaxZoom: z.number().optional(),
111+
clusterProperties: z.record(z.any()).optional(),
112+
lineMetrics: z.boolean().optional(),
113+
generateId: z.boolean().optional(),
114+
promoteId: z.union([z.string(), z.record(z.string())]).optional()
115+
})
116+
.passthrough();
117+
118+
const ImageSourceSchema = z
119+
.object({
120+
type: z.literal('image'),
121+
url: z.string(),
122+
coordinates: z.tuple([
123+
CoordinatesSchema,
124+
CoordinatesSchema,
125+
CoordinatesSchema,
126+
CoordinatesSchema
127+
])
128+
})
129+
.passthrough();
130+
131+
const VideoSourceSchema = z
132+
.object({
133+
type: z.literal('video'),
134+
urls: z.array(z.string()),
135+
coordinates: z.tuple([
136+
CoordinatesSchema,
137+
CoordinatesSchema,
138+
CoordinatesSchema,
139+
CoordinatesSchema
140+
])
141+
})
142+
.passthrough();
143+
144+
const SourceSchema = z.union([
145+
VectorSourceSchema,
146+
RasterSourceSchema,
147+
RasterDemSourceSchema,
148+
GeoJSONSourceSchema,
149+
ImageSourceSchema,
150+
VideoSourceSchema
151+
]);
152+
153+
// Layer schema (simplified - full schema would be very extensive)
154+
const LayerSchema = z
155+
.object({
156+
id: z.string().describe('Unique layer name'),
157+
type: z.enum([
158+
'fill',
159+
'line',
160+
'symbol',
161+
'circle',
162+
'heatmap',
163+
'fill-extrusion',
164+
'raster',
165+
'hillshade',
166+
'background',
167+
'sky',
168+
'slot',
169+
'clip',
170+
'model',
171+
'raster-particle',
172+
'building'
173+
]),
174+
source: z
175+
.string()
176+
.optional()
177+
.describe('Source name (not required for background/sky/slot)'),
178+
'source-layer': z
179+
.string()
180+
.optional()
181+
.describe('Layer from vector tile source'),
182+
minzoom: z.number().min(0).max(24).optional(),
183+
maxzoom: z.number().min(0).max(24).optional(),
184+
filter: z.any().optional().describe('Expression for filtering features'),
185+
layout: z.record(z.any()).optional(),
186+
paint: z.record(z.any()).optional(),
187+
metadata: z.record(z.any()).optional(),
188+
slot: z.string().optional().describe('Slot this layer is assigned to')
189+
})
190+
.passthrough();
191+
192+
// Style import schema
193+
const StyleImportSchema = z
194+
.object({
195+
id: z.string(),
196+
url: z.string(),
197+
config: z.record(z.any()).optional()
198+
})
199+
.passthrough();
200+
201+
// Base Style properties (shared between input and output)
202+
export const BaseStylePropertiesSchema = z.object({
203+
// Required Style Spec properties
204+
version: z
205+
.literal(8)
206+
.describe('Style specification version number. Must be 8'),
207+
sources: z.record(SourceSchema).describe('Data source specifications'),
208+
layers: z.array(LayerSchema).describe('Layers in draw order'),
209+
210+
// Optional Style Spec properties
211+
metadata: z
212+
.record(z.any())
213+
.optional()
214+
.describe('Arbitrary properties for tracking'),
215+
center: CoordinatesSchema.optional().describe(
216+
'Default map center [longitude, latitude]'
217+
),
218+
zoom: z.number().optional().describe('Default zoom level'),
219+
bearing: z.number().optional().describe('Default bearing in degrees'),
220+
pitch: z.number().optional().describe('Default pitch in degrees'),
221+
sprite: z
222+
.string()
223+
.optional()
224+
.describe('Base URL for sprite image and metadata'),
225+
glyphs: z.string().optional().describe('URL template for glyph sets'),
226+
light: LightSchema.optional().describe(
227+
'Global light source (deprecated, use lights)'
228+
),
229+
lights: LightsSchema.optional().describe('Array of 3D light sources'),
230+
terrain: TerrainSchema.optional().describe('Global terrain elevation'),
231+
fog: z.record(z.any()).optional().describe('Fog properties'),
232+
projection: z.record(z.any()).optional().describe('Map projection'),
233+
transition: TransitionSchema.optional().describe('Default transition timing'),
234+
imports: z.array(StyleImportSchema).optional().describe('Imported styles')
235+
});
236+
237+
export type MapboxSource = z.infer<typeof SourceSchema>;
238+
export type MapboxLayer = z.infer<typeof LayerSchema>;

0 commit comments

Comments
 (0)