Skip to content
Open
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
43 changes: 13 additions & 30 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"dotenv": "^17.2.2",
"firecrawl-fastmcp": "^1.0.3",
"typescript": "^5.9.2",
"zod": "^4.1.5"
"zod": "^4.1.5",
"zod-to-json-schema": "^3.24.6"
},
"engines": {
"node": ">=18.0.0"
Expand Down
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#!/usr/bin/env node
import dotenv from 'dotenv';
import { patchFastMCPSchemas } from './schema-patch.js';

// CRITICAL: Apply MCP SDK patches BEFORE importing FastMCP
// This patches the Server.prototype.setRequestHandler method
patchFastMCPSchemas();

import { FastMCP, type Logger } from 'firecrawl-fastmcp';
import { z } from 'zod';
import FirecrawlApp from '@mendable/firecrawl-js';
Expand Down
103 changes: 103 additions & 0 deletions src/schema-patch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* Patch for VS Code MCP JSON Schema Compatibility
*
* This module patches JSON schemas from draft 2020-12 to draft-07
* to ensure compatibility with VS Code's MCP validator.
*/

import { Server } from '@modelcontextprotocol/sdk/server/index.js';

/**
* Recursively converts JSON Schema draft 2020-12 to draft-07
* @param schema - The schema object to convert
* @returns The converted schema
*/
export function convertSchemaToDraft07(schema: any): any {
if (!schema || typeof schema !== 'object') {
return schema;
}

// Create a copy to avoid mutating the original
const converted = Array.isArray(schema) ? [...schema] : { ...schema };

// Replace the $schema version
if (converted.$schema === 'https://json-schema.org/draft/2020-12/schema') {
converted.$schema = 'http://json-schema.org/draft-07/schema#';
}

// Remove unsupported draft 2020-12 features
delete converted.$dynamicRef;
delete converted.$dynamicAnchor;
delete converted.prefixItems;
delete converted.dependentSchemas;
delete converted.dependentRequired;

// Convert $defs to definitions (draft-07 uses 'definitions')
if (converted.$defs) {
converted.definitions = converted.$defs;
delete converted.$defs;
}

// Recursively process nested schemas
for (const key in converted) {
if (typeof converted[key] === 'object' && converted[key] !== null) {
converted[key] = convertSchemaToDraft07(converted[key]);
}
}

return converted;
}

/**
* Patches the MCP SDK Server class directly by monkey-patching its setRequestHandler method
* This must be called BEFORE any Server instances are created
*/
export function patchFastMCPSchemas(_server?: any): void {
console.error('[Schema Patch] Applying VS Code compatibility patches...');

// Patch the Server prototype's setRequestHandler method
const originalSetRequestHandler = Server.prototype.setRequestHandler;
if (!originalSetRequestHandler) {
console.error('[Schema Patch] ERROR: Server.prototype.setRequestHandler not found');
return;
}

console.error('[Schema Patch] Patching Server.prototype.setRequestHandler...');

Server.prototype.setRequestHandler = function(requestSchema: any, handler: any) {
// Extract the method name from the Zod schema
const method = requestSchema?.shape?.method?.value;

console.error(`[Schema Patch] setRequestHandler called for method: ${method}`);

if (method === 'tools/list') {
console.error('[Schema Patch] ✓ Intercepting tools/list handler registration');

const wrappedHandler = async (...args: any[]) => {
console.error('[Schema Patch] tools/list handler called, converting schemas...');
const response = await handler(...args);

if (response && response.tools && Array.isArray(response.tools)) {
console.error(`[Schema Patch] Converting ${response.tools.length} tool schemas to draft-07`);

response.tools = response.tools.map((tool: any) => {
if (tool.inputSchema) {
const before = tool.inputSchema.$schema || 'unknown';
tool.inputSchema = convertSchemaToDraft07(tool.inputSchema);
console.error(`[Schema Patch] ✓ ${tool.name}: ${before} -> draft-07`);
}
return tool;
});
}

return response;
};

return originalSetRequestHandler.call(this, requestSchema, wrappedHandler);
}

return originalSetRequestHandler.call(this, requestSchema, handler);
};

console.error('[Schema Patch] Successfully patched Server.prototype.setRequestHandler');
}