Skip to content

Commit ad24eab

Browse files
authored
Merge pull request #63 from mapbox/mcp-sdk-and-stability-fixes
Addressing CVE-2026-0621 and stability fixes
2 parents be69a25 + d66c90a commit ad24eab

File tree

13 files changed

+958
-398
lines changed

13 files changed

+958
-398
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
## 0.4.7
2+
3+
### Security
4+
5+
- **CVE-2026-0621**: Updated `@modelcontextprotocol/sdk` to 1.25.3 to fix ReDoS vulnerability in UriTemplate regex patterns
6+
7+
### Bug Fixes
8+
9+
- Migrated from deprecated `server.resource()` to `server.registerResource()` API in BaseResource
10+
- Fixed TypeScript implicit `any` type error in BaseTool registerTool callback
11+
12+
### Dependencies
13+
14+
- Updated `@modelcontextprotocol/sdk` from 1.17.5 to 1.25.3
15+
116
## 0.4.6
217

318
### Features Added

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"dxt_version": "0.1",
33
"name": "@mapbox/mcp-devkit-server",
44
"display_name": "Mapbox MCP DevKit Server",
5-
"version": "0.4.6",
5+
"version": "0.4.7",
66
"description": "Mapbox MCP devkit server",
77
"author": {
88
"name": "Mapbox, Inc."

package-lock.json

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

package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@mapbox/mcp-devkit-server",
3-
"version": "0.4.6",
3+
"version": "0.4.7",
44
"description": "Mapbox MCP devkit server",
55
"mcpName": "io.github.mapbox/mcp-devkit-server",
66
"main": "./dist/commonjs/index.js",
@@ -10,6 +10,7 @@
1010
"mapbox-mcp-devkit": "dist/esm/index.js"
1111
},
1212
"scripts": {
13+
"postinstall": "patch-package || true",
1314
"build": "npm run prepare && tshy && npm run generate-version && node scripts/add-shebang.cjs",
1415
"format": "prettier --check \"./src/**/*.{ts,tsx,js,json,md}\" \"./test/**/*.{ts,tsx,js,json,md}\"",
1516
"format:fix": "prettier --write \"./src/**/*.{ts,tsx,js,json,md}\" \"./test/**/*.{ts,tsx,js,json,md}\"",
@@ -40,14 +41,15 @@
4041
"node": ">=22"
4142
},
4243
"files": [
43-
"dist"
44+
"dist",
45+
"patches"
4446
],
4547
"keywords": [
4648
"mcp"
4749
],
4850
"dependencies": {
4951
"@mcp-ui/server": "^5.13.1",
50-
"@modelcontextprotocol/sdk": "^1.17.5",
52+
"@modelcontextprotocol/sdk": "^1.25.3",
5153
"@opentelemetry/api": "^1.9.0",
5254
"@opentelemetry/auto-instrumentations-node": "^0.56.0",
5355
"@opentelemetry/exporter-trace-otlp-http": "^0.56.0",
@@ -79,7 +81,8 @@
7981
"tshy": "^3.0.2",
8082
"typescript": "^5.8.3",
8183
"typescript-eslint": "^8.42.0",
82-
"vitest": "^3.2.4"
84+
"vitest": "^3.2.4",
85+
"patch-package": "^8.0.1"
8386
},
8487
"prettier": {
8588
"singleQuote": true,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
diff --git a/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp.js b/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp.js
2+
index e10bb3d..2e99bee 100644
3+
--- a/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp.js
4+
+++ b/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp.js
5+
@@ -197,15 +197,20 @@ class McpServer {
6+
return;
7+
}
8+
if (!result.structuredContent) {
9+
- throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Output validation error: Tool ${toolName} has an output schema but no structured content was provided`);
10+
+ // Log warning but don't throw - allow tools to omit structured content
11+
+ console.warn(`[MCP SDK Patch] Output validation warning: Tool ${toolName} has an output schema but no structured content was provided`);
12+
}
13+
- // if the tool has an output schema, validate structured content
14+
- const outputObj = (0, zod_compat_js_1.normalizeObjectSchema)(tool.outputSchema);
15+
- const parseResult = await (0, zod_compat_js_1.safeParseAsync)(outputObj, result.structuredContent);
16+
- if (!parseResult.success) {
17+
- const error = 'error' in parseResult ? parseResult.error : 'Unknown error';
18+
- const errorMessage = (0, zod_compat_js_1.getParseErrorMessage)(error);
19+
- throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage}`);
20+
+ else {
21+
+ // if the tool has an output schema, validate structured content
22+
+ const outputObj = (0, zod_compat_js_1.normalizeObjectSchema)(tool.outputSchema);
23+
+ const parseResult = await (0, zod_compat_js_1.safeParseAsync)(outputObj, result.structuredContent);
24+
+ if (!parseResult.success) {
25+
+ const error = 'error' in parseResult ? parseResult.error : 'Unknown error';
26+
+ const errorMessage = (0, zod_compat_js_1.getParseErrorMessage)(error);
27+
+ // Log warning but don't throw - allow schema mismatches
28+
+ console.warn(`[MCP SDK Patch] Output validation warning: Invalid structured content for tool ${toolName}: ${errorMessage}`);
29+
+ // Keep the structuredContent despite validation failure
30+
+ }
31+
}
32+
}
33+
/**
34+
diff --git a/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js b/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
35+
index 23639ce..7b8a325 100644
36+
--- a/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
37+
+++ b/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
38+
@@ -194,15 +194,20 @@ export class McpServer {
39+
return;
40+
}
41+
if (!result.structuredContent) {
42+
- throw new McpError(ErrorCode.InvalidParams, `Output validation error: Tool ${toolName} has an output schema but no structured content was provided`);
43+
+ // Log warning but don't throw - allow tools to omit structured content
44+
+ console.warn(`[MCP SDK Patch] Output validation warning: Tool ${toolName} has an output schema but no structured content was provided`);
45+
}
46+
- // if the tool has an output schema, validate structured content
47+
- const outputObj = normalizeObjectSchema(tool.outputSchema);
48+
- const parseResult = await safeParseAsync(outputObj, result.structuredContent);
49+
- if (!parseResult.success) {
50+
- const error = 'error' in parseResult ? parseResult.error : 'Unknown error';
51+
- const errorMessage = getParseErrorMessage(error);
52+
- throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage}`);
53+
+ else {
54+
+ // if the tool has an output schema, validate structured content
55+
+ const outputObj = normalizeObjectSchema(tool.outputSchema);
56+
+ const parseResult = await safeParseAsync(outputObj, result.structuredContent);
57+
+ if (!parseResult.success) {
58+
+ const error = 'error' in parseResult ? parseResult.error : 'Unknown error';
59+
+ const errorMessage = getParseErrorMessage(error);
60+
+ // Log warning but don't throw - allow schema mismatches
61+
+ console.warn(`[MCP SDK Patch] Output validation warning: Invalid structured content for tool ${toolName}: ${errorMessage}`);
62+
+ // Keep the structuredContent despite validation failure
63+
+ }
64+
}
65+
}
66+
/**

patches/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# MCP SDK Patches
2+
3+
This directory contains patches for npm dependencies that are automatically applied after `npm install`.
4+
5+
## @modelcontextprotocol/sdk
6+
7+
**File:** `@modelcontextprotocol+sdk+1.21.1.patch`
8+
9+
**Purpose:** Makes MCP output schema validation non-fatal to improve resilience and user experience.
10+
11+
**Problem:** The MCP SDK enforces strict output schema validation, which causes the entire tool call to fail when there are minor schema mismatches. This creates unnecessary risk and poor user experience - most MCP clients can gracefully handle responses that don't perfectly match the declared schema.
12+
13+
**Solution:** This patch modifies the MCP SDK to log warnings instead of throwing errors when output validation fails. This provides a better balance between:
14+
15+
- **Schema documentation** - Output schemas still serve as documentation for expected response structure
16+
- **Resilience** - Tools continue working even with minor variations in response format
17+
- **Observability** - Validation issues are logged for monitoring and debugging
18+
- **User experience** - Clients receive the actual data instead of an error
19+
20+
**Benefits:**
21+
22+
- Prevents tool failures due to minor schema variations
23+
- Allows graceful degradation when APIs evolve or return edge cases
24+
- Maintains backward compatibility while improving reliability
25+
- Clients that need strict validation can implement it themselves
26+
27+
**Changes:**
28+
29+
- Converts validation errors to console warnings with `[MCP SDK Patch]` prefix
30+
- Allows tools to return structured content even when it doesn't match the schema exactly
31+
- Preserves all existing functionality while removing unnecessary strictness
32+
33+
**Maintenance:**
34+
35+
- This patch is automatically applied after `npm install` via the `postinstall` script
36+
- If the MCP SDK is updated, you may need to recreate this patch:
37+
1. Remove the old patch file
38+
2. Make the same modifications to the new SDK version
39+
3. Run `npx patch-package @modelcontextprotocol/sdk`
40+
41+
**Philosophy:** This patch follows the robustness principle: "Be conservative in what you send, be liberal in what you accept." Output schemas remain valuable for documentation and tooling, but shouldn't cause failures when the real-world data varies slightly from the ideal schema.

server.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
{
2-
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
2+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
33
"name": "io.github.mapbox/mcp-devkit-server",
44
"description": "Provides AI assistants with direct access to Mapbox developer APIs and documentation.",
55
"repository": {
66
"url": "https://github.com/mapbox/mcp-devkit-server",
77
"source": "github"
88
},
9-
"version": "0.4.6",
9+
"version": "0.4.7",
1010
"packages": [
1111
{
1212
"registryType": "npm",
1313
"registryBaseUrl": "https://registry.npmjs.org",
1414
"runtimeHint": "npx",
15-
"version": "0.4.6",
15+
"version": "0.4.7",
1616
"identifier": "@mapbox/mcp-devkit-server",
1717
"transport": {
1818
"type": "stdio"

src/resources/BaseResource.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export abstract class BaseResource {
2222
* Install this resource to the MCP server
2323
*/
2424
installTo(server: McpServer): void {
25-
server.resource(
25+
server.registerResource(
2626
this.name,
2727
this.uri,
2828
{

src/tools/BaseTool.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,11 @@ export abstract class BaseTool<
9191
(this.outputSchema as unknown as z.ZodObject<any>).shape;
9292
}
9393

94-
return server.registerTool(this.name, config, (args, extra) =>
95-
this.run(args, extra)
94+
return server.registerTool(
95+
this.name,
96+
config,
97+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
98+
(args: any, extra: any) => this.run(args, extra)
9699
);
97100
}
98101

src/tools/create-style-tool/CreateStyleTool.output.schema.ts

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,35 @@
22
// Licensed under the MIT License.
33

44
import { z } from 'zod';
5+
import { BaseStylePropertiesSchema } from '../../schemas/style.js';
56

6-
// OUTPUT Schema - Simplified schema for tool responses
7-
// This schema describes the key metadata fields returned, not the entire style spec.
8-
// The full style data is included in structuredContent but uses .passthrough()
9-
// to avoid overwhelming clients with a massive schema definition.
10-
export const MapboxStyleOutputSchema = z
11-
.object({
12-
// API-specific metadata properties
13-
id: z.string().describe('Unique style identifier'),
14-
name: z.string().describe('Human-readable name for the style'),
15-
owner: z.string().describe('Username of the style owner'),
16-
created: z
17-
.string()
18-
.datetime()
19-
.describe('ISO 8601 timestamp when style was created'),
20-
modified: z
21-
.string()
22-
.datetime()
23-
.describe('ISO 8601 timestamp when style was last modified'),
24-
visibility: z
25-
.enum(['public', 'private'])
26-
.describe('Style visibility setting'),
27-
draft: z.boolean().optional().describe('Whether this is a draft version'),
7+
// OUTPUT Schema - For API responses (POST response)
8+
// Uses the same comprehensive schema as RetrieveStyleTool to ensure all
9+
// properties returned by the Mapbox API are explicitly defined.
10+
// This avoids issues with additionalProperties: false in strict validators.
11+
export const MapboxStyleOutputSchema = BaseStylePropertiesSchema.extend({
12+
name: z.string().describe('Human-readable name for the style'),
2813

29-
// Style spec version (always 8)
30-
version: z.literal(8).describe('Style specification version number')
31-
})
32-
.passthrough()
33-
.describe(
34-
'Mapbox style with metadata. Additional style properties (sources, layers, etc.) are included but not explicitly validated to keep the schema manageable.'
35-
);
14+
// API-specific properties (only present in responses)
15+
id: z.string().describe('Unique style identifier'),
16+
owner: z.string().describe('Username of the style owner'),
17+
created: z
18+
.string()
19+
.datetime()
20+
.describe('ISO 8601 timestamp when style was created'),
21+
modified: z
22+
.string()
23+
.datetime()
24+
.describe('ISO 8601 timestamp when style was last modified'),
25+
visibility: z
26+
.enum(['public', 'private'])
27+
.describe('Style visibility setting'),
28+
protected: z
29+
.boolean()
30+
.optional()
31+
.describe('Whether style is protected from modifications'),
32+
draft: z.boolean().optional().describe('Whether this is a draft version')
33+
});
3634

3735
// Type exports
3836
export type MapboxStyleOutput = z.infer<typeof MapboxStyleOutputSchema>;

0 commit comments

Comments
 (0)