mcpengine/infra/factory-tools/scripts/fix-unknown-tool-error.mjs
Jake Shore f3c4cd817b Add all MCP servers + factory infra to MCPEngine — 2026-02-06
=== NEW SERVERS ADDED (7) ===
- servers/closebot — 119 tools, 14 modules, 4,656 lines TS (Stage 7)
- servers/google-console — Google Search Console MCP (Stage 7)
- servers/meta-ads — Meta/Facebook Ads MCP (Stage 8)
- servers/twilio — Twilio communications MCP (Stage 8)
- servers/competitor-research — Competitive intel MCP (Stage 6)
- servers/n8n-apps — n8n workflow MCP apps (Stage 6)
- servers/reonomy — Commercial real estate MCP (Stage 1)

=== FACTORY INFRASTRUCTURE ADDED ===
- infra/factory-tools — mcp-jest, mcp-validator, mcp-add, MCP Inspector
  - 60 test configs, 702 auto-generated test cases
  - All 30 servers score 100/100 protocol compliance
- infra/command-center — Pipeline state, operator playbook, dashboard config
- infra/factory-reviews — Automated eval reports

=== DOCS ADDED ===
- docs/MCP-FACTORY.md — Factory overview
- docs/reports/ — 5 pipeline evaluation reports
- docs/research/ — Browser MCP research

=== RULES ESTABLISHED ===
- CONTRIBUTING.md — All MCP work MUST go in this repo
- README.md — Full inventory of 37 servers + infra docs
- .gitignore — Updated for Python venvs

TOTAL: 37 MCP servers + full factory pipeline in one repo.
This is now the single source of truth for all MCP work.
2026-02-06 06:32:29 -05:00

126 lines
4.5 KiB
JavaScript

#!/usr/bin/env node
/**
* MCP Factory — Fix Unknown Tool Error
* Patches all 30 servers to properly throw McpError for unknown tools
* instead of catching and returning isError:true (which MCP spec treats as success).
*
* The fix:
* 1. Import McpError and ErrorCode from the SDK
* 2. Check tool name against known tools before calling handler
* 3. Throw McpError(ErrorCode.MethodNotFound) for unknown tools
*/
import { readFileSync, writeFileSync, readdirSync, existsSync } from 'fs';
import { execSync } from 'child_process';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const FACTORY_ROOT = resolve(__dirname, '..');
const registry = JSON.parse(readFileSync(resolve(FACTORY_ROOT, 'server-registry.json'), 'utf-8'));
const SERVERS_ROOT = resolve(FACTORY_ROOT, registry.servers_root);
let fixed = 0;
let skipped = 0;
let errors = 0;
for (const [name] of Object.entries(registry.servers)) {
const srcPath = resolve(SERVERS_ROOT, name, 'src/index.ts');
if (!existsSync(srcPath)) {
console.log(`⚠️ ${name}: No src/index.ts`);
skipped++;
continue;
}
let src = readFileSync(srcPath, 'utf-8');
// Check if already fixed
if (src.includes('McpError')) {
console.log(`⏭️ ${name}: Already has McpError import`);
skipped++;
continue;
}
try {
// Step 1: Add McpError and ErrorCode to imports
// Find the import from types.js
const typesImportMatch = src.match(/(import\s*\{[^}]*\}\s*from\s*"@modelcontextprotocol\/sdk\/types\.js";)/);
if (typesImportMatch) {
const oldImport = typesImportMatch[1];
// Extract existing imports
const existingImports = oldImport.match(/\{([^}]+)\}/)[1].trim();
const newImport = oldImport.replace(
`{${existingImports}}`,
`{${existingImports}, McpError, ErrorCode}`
);
src = src.replace(oldImport, newImport);
}
// Step 2: Add tool name validation before the try/catch in CallToolRequestSchema handler
// Pattern: Find the handler and add a check
const toolNames = [...src.matchAll(/name:\s*"([^"]+)"/g)].map(m => m[1]);
// Filter to only tool names (in the tools array, not other name fields)
const validToolNames = toolNames.filter(n => !['text', 'object'].includes(n));
// Find the CallToolRequestSchema handler and add validation
const handlerPattern = /server\.setRequestHandler\(CallToolRequestSchema,\s*async\s*\(request\)\s*=>\s*\{\s*const\s*\{\s*name,\s*arguments:\s*args\s*\}\s*=\s*request\.params;\s*\n\s*try\s*\{/;
if (handlerPattern.test(src)) {
src = src.replace(
handlerPattern,
`server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
// Validate tool exists (MCP spec requires proper error for unknown tools)
const knownTools = tools.map(t => t.name);
if (!knownTools.includes(name)) {
throw new McpError(ErrorCode.MethodNotFound, \`Unknown tool: \${name}\`);
}
try {`
);
} else {
// Try a more flexible pattern
const altPattern = /server\.setRequestHandler\(CallToolRequestSchema,\s*async\s*\(request\)\s*=>\s*\{/;
if (altPattern.test(src)) {
// Check if there's already a tool validation
const handlerBlock = src.substring(src.search(altPattern));
if (handlerBlock.includes('const { name') && !handlerBlock.includes('knownTools')) {
// Insert after the destructuring
src = src.replace(
/const\s*\{\s*name,\s*arguments:\s*args\s*\}\s*=\s*request\.params;\s*\n/,
`const { name, arguments: args } = request.params;
// Validate tool exists (MCP spec requires proper error for unknown tools)
const knownTools = tools.map(t => t.name);
if (!knownTools.includes(name)) {
throw new McpError(ErrorCode.MethodNotFound, \`Unknown tool: \${name}\`);
}
`
);
}
}
}
writeFileSync(srcPath, src);
console.log(`${name}: Patched`);
fixed++;
// Rebuild
try {
execSync('npm run build', { cwd: resolve(SERVERS_ROOT, name), timeout: 15000, stdio: 'pipe' });
console.log(` 🔨 ${name}: Rebuilt`);
} catch (buildErr) {
console.log(` ⚠️ ${name}: Build warning (check manually)`);
}
} catch (err) {
console.log(`${name}: ${err.message}`);
errors++;
}
}
console.log('\n' + '═'.repeat(60));
console.log(`Fixed: ${fixed} | Skipped: ${skipped} | Errors: ${errors}`);