Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ https://github.com/user-attachments/assets/8b1b8ef2-9fba-4951-bc9a-beaed4f6aff6
- [Creating New Tools](#creating-new-tools)
- [Environment Variables](#environment-variables)
- [VERBOSE_ERRORS](#verbose_errors)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)

## Quick Start
Expand Down Expand Up @@ -558,6 +559,16 @@ Set `VERBOSE_ERRORS=true` to get detailed error messages from the MCP server. Th

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.

## Troubleshooting

**Issue:** Tools fail with authentication errors

**Solution:** Check that your `MAPBOX_ACCESS_TOKEN` has the required scopes for the tool you're using. See the token scopes section above.

**Issue:** Large GeoJSON files cause slow performance

**Solution:** The GeoJSON preview tool may be slow with very large files. Consider simplifying geometries or using smaller datasets for preview purposes.

## Contributing

We welcome contributions to the Mapbox Development MCP Server! Please review our standards and guidelines before contributing:
Expand Down
35 changes: 11 additions & 24 deletions src/tools/create-style-tool/CreateStyleTool.input.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,17 @@

import { z } from 'zod';

// INPUT Schema - Simplified schema for creating styles
// Only defines the essential required fields. Additional Mapbox Style Specification
// properties (sources, layers, sprite, glyphs, etc.) are allowed via .passthrough()
// INPUT Schema - Accepts a complete Mapbox Style Specification as a generic object
// This avoids complex schemas with .passthrough() that break some MCP clients (Cursor + OpenAI)
// Full spec: https://docs.mapbox.com/mapbox-gl-js/style-spec/
export const MapboxStyleInputSchema = z
.object({
name: z.string().describe('Human-readable name for the style (REQUIRED)'),
version: z
.literal(8)
.describe('Style specification version number. Must be 8'),
// Note: The Mapbox API requires at minimum 'version', 'name', 'sources', and 'layers'.
// We only validate 'name' and 'version' here. Other fields like sources, layers, sprite,
// glyphs, center, zoom, etc. are passed through without explicit validation to avoid
// overwhelming clients with the full 18+ field schema.
sources: z
.record(z.any())
.optional()
.describe('Data source specifications'),
layers: z.array(z.any()).optional().describe('Layers in draw order')
})
.passthrough()
.describe(
'Mapbox style input. Accepts standard Mapbox Style Specification properties. Only key fields are validated; additional properties (center, zoom, bearing, pitch, sprite, glyphs, metadata, etc.) are accepted but not explicitly defined to keep schema manageable.'
);
export const CreateStyleInputSchema = z.object({
name: z.string().describe('Human-readable name for the style'),
style: z
.record(z.any())
.describe(
'Complete Mapbox Style Specification object. Must include: version (8), sources, layers. Optional: sprite, glyphs, center, zoom, bearing, pitch, metadata, etc. See https://docs.mapbox.com/mapbox-gl-js/style-spec/'
)
});

// Type exports
export type MapboxStyleInput = z.infer<typeof MapboxStyleInputSchema>;
export type CreateStyleInput = z.infer<typeof CreateStyleInputSchema>;
18 changes: 12 additions & 6 deletions src/tools/create-style-tool/CreateStyleTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import { getUserNameFromToken } from '../../utils/jwtUtils.js';
import { filterExpandedMapboxStyles } from '../../utils/styleUtils.js';
import { MapboxApiBasedTool } from '../MapboxApiBasedTool.js';
import {
MapboxStyleInputSchema,
MapboxStyleInput
CreateStyleInputSchema,
CreateStyleInput
} from './CreateStyleTool.input.schema.js';
import {
MapboxStyleOutput,
MapboxStyleOutputSchema
} from './CreateStyleTool.output.schema.js';

export class CreateStyleTool extends MapboxApiBasedTool<
typeof MapboxStyleInputSchema,
typeof CreateStyleInputSchema,
typeof MapboxStyleOutputSchema
> {
name = 'create_style_tool';
Expand All @@ -31,25 +31,31 @@ export class CreateStyleTool extends MapboxApiBasedTool<

constructor(params: { httpRequest: HttpRequest }) {
super({
inputSchema: MapboxStyleInputSchema,
inputSchema: CreateStyleInputSchema,
outputSchema: MapboxStyleOutputSchema,
httpRequest: params.httpRequest
});
}

protected async execute(
input: MapboxStyleInput,
input: CreateStyleInput,
accessToken?: string
): Promise<CallToolResult> {
const username = getUserNameFromToken(accessToken);
const url = `${MapboxApiBasedTool.mapboxApiEndpoint}styles/v1/${username}?access_token=${accessToken}`;

// Merge name into style object for API request
const payload = {
...input.style,
name: input.name
};

const response = await this.httpRequest(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(input)
body: JSON.stringify(payload)
});

if (!response.ok) {
Expand Down
28 changes: 8 additions & 20 deletions src/tools/update-style-tool/UpdateStyleTool.input.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,17 @@

import { z } from 'zod';

// Simplified Mapbox Style Input Schema for updates
// Only defines essential fields. Additional properties are accepted via .passthrough()
export const MapboxStyleInputSchema = z
.object({
name: z.string().optional().describe('Human-readable name for the style'),
version: z.literal(8).optional().describe('Style specification version'),
sources: z
.record(z.any())
.optional()
.describe('Data source specifications'),
layers: z.array(z.any()).optional().describe('Layers in draw order')
})
.passthrough()
.describe(
'Mapbox style properties to update. Accepts standard Mapbox Style Specification properties.'
);

// INPUT Schema - Accepts a complete Mapbox Style Specification as a generic object
// This avoids complex schemas with .passthrough() that break some MCP clients (Cursor + OpenAI)
export const UpdateStyleInputSchema = z.object({
styleId: z.string().describe('Style ID to update'),
name: z.string().optional().describe('New name for the style'),
style: MapboxStyleInputSchema.optional().describe(
'Updated Mapbox style specification object'
)
style: z
.record(z.any())
.optional()
.describe(
'Complete Mapbox Style Specification object to update. Must include: version (8), sources, layers. Optional: sprite, glyphs, center, zoom, bearing, pitch, metadata, etc. See https://docs.mapbox.com/mapbox-gl-js/style-spec/'
)
});

export type UpdateStyleInput = z.infer<typeof UpdateStyleInputSchema>;
20 changes: 12 additions & 8 deletions test/tools/create-style-tool/CreateStyleTool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ describe('CreateStyleTool', () => {
});

it('should have correct input schema', async () => {
const { MapboxStyleInputSchema } = await import(
const { CreateStyleInputSchema } = await import(
'../../../src/tools/create-style-tool/CreateStyleTool.input.schema.js'
);
expect(MapboxStyleInputSchema).toBeDefined();
expect(CreateStyleInputSchema).toBeDefined();
});
});

Expand All @@ -50,9 +50,11 @@ describe('CreateStyleTool', () => {

await new CreateStyleTool({ httpRequest }).run({
name: 'Test Style',
version: 8,
sources: {},
layers: []
style: {
version: 8,
sources: {},
layers: []
}
});
assertHeadersSent(mockHttpRequest);
});
Expand All @@ -66,9 +68,11 @@ describe('CreateStyleTool', () => {

const result = await new CreateStyleTool({ httpRequest }).run({
name: 'Test Style',
version: 8,
sources: {},
layers: []
style: {
version: 8,
sources: {},
layers: []
}
});

expect(result.isError).toBe(true);
Expand Down
4 changes: 2 additions & 2 deletions test/tools/update-style-tool/UpdateStyleTool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ describe('UpdateStyleTool', () => {
});

it('should have correct input schema', async () => {
const { MapboxStyleInputSchema } = await import(
const { UpdateStyleInputSchema } = await import(
'../../../src/tools/update-style-tool/UpdateStyleTool.input.schema.js'
);
expect(MapboxStyleInputSchema).toBeDefined();
expect(UpdateStyleInputSchema).toBeDefined();
});
});

Expand Down
Loading