163 lines
7.4 KiB
JavaScript

#!/usr/bin/env node
/**
* Automatically add _meta labels to all tools across all servers
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const serversDir = path.join(__dirname, 'mcp-servers');
// Category mapping based on tool name patterns
const getCategoryFromToolName = (toolName, serverName) => {
// Check server type first
if (serverName.includes('freshdesk') || serverName.includes('zendesk') || serverName.includes('helpscout')) {
if (toolName.includes('ticket')) return 'support';
if (toolName.includes('contact') || toolName.includes('customer')) return 'contacts';
if (toolName.includes('agent') || toolName.includes('user')) return 'team';
return 'support';
}
if (serverName.includes('servicetitan') || serverName.includes('housecall') || serverName.includes('jobber') || serverName.includes('fieldedge')) {
if (toolName.includes('job') || toolName.includes('work_order')) return 'jobs';
if (toolName.includes('customer') || toolName.includes('client')) return 'customers';
if (toolName.includes('invoice')) return 'billing';
if (toolName.includes('technician') || toolName.includes('employee')) return 'team';
if (toolName.includes('appointment') || toolName.includes('schedule')) return 'scheduling';
return 'jobs';
}
if (serverName.includes('mailchimp') || serverName.includes('constant-contact') || serverName.includes('brevo')) {
if (toolName.includes('campaign')) return 'campaigns';
if (toolName.includes('contact') || toolName.includes('subscriber')) return 'contacts';
if (toolName.includes('list')) return 'lists';
if (toolName.includes('template')) return 'templates';
return 'campaigns';
}
if (serverName.includes('pipedrive') || serverName.includes('close')) {
if (toolName.includes('deal') || toolName.includes('opportunity')) return 'deals';
if (toolName.includes('person') || toolName.includes('contact') || toolName.includes('lead')) return 'contacts';
if (toolName.includes('activity')) return 'activities';
return 'crm';
}
if (serverName.includes('trello') || serverName.includes('clickup') || serverName.includes('wrike') || serverName.includes('basecamp')) {
if (toolName.includes('board') || toolName.includes('project')) return 'projects';
if (toolName.includes('task')) return 'tasks';
if (toolName.includes('comment')) return 'collaboration';
return 'projects';
}
if (serverName.includes('gusto') || serverName.includes('rippling') || serverName.includes('bamboohr')) {
if (toolName.includes('employee')) return 'employees';
if (toolName.includes('payroll')) return 'payroll';
if (toolName.includes('benefit')) return 'benefits';
if (toolName.includes('time_off') || toolName.includes('leave')) return 'time-off';
return 'hr';
}
if (serverName.includes('toast') || serverName.includes('square') || serverName.includes('clover') || serverName.includes('lightspeed') || serverName.includes('touchbistro')) {
if (toolName.includes('order') || toolName.includes('check')) return 'orders';
if (toolName.includes('menu') || toolName.includes('item')) return 'menu';
if (toolName.includes('employee') || toolName.includes('staff')) return 'team';
if (toolName.includes('reservation')) return 'reservations';
return 'pos';
}
if (serverName.includes('calendly') || serverName.includes('acuity')) {
if (toolName.includes('event') || toolName.includes('appointment')) return 'scheduling';
if (toolName.includes('availability')) return 'availability';
if (toolName.includes('calendar')) return 'calendars';
return 'scheduling';
}
// Generic patterns
if (toolName.includes('contact') || toolName.includes('customer') || toolName.includes('client') || toolName.includes('person')) return 'contacts';
if (toolName.includes('deal') || toolName.includes('opportunity')) return 'deals';
if (toolName.includes('invoice') || toolName.includes('payment')) return 'billing';
if (toolName.includes('task')) return 'tasks';
if (toolName.includes('project')) return 'projects';
if (toolName.includes('calendar') || toolName.includes('event') || toolName.includes('appointment')) return 'calendar';
if (toolName.includes('campaign')) return 'campaigns';
if (toolName.includes('report') || toolName.includes('analytics') || toolName.includes('stats')) return 'analytics';
if (toolName.includes('employee') || toolName.includes('team') || toolName.includes('user')) return 'team';
return 'general';
};
// Access type from tool name
const getAccessType = (toolName) => {
if (toolName.startsWith('list_') || toolName.startsWith('get_') || toolName.startsWith('search_')) return 'read';
if (toolName.startsWith('create_') || toolName.startsWith('add_')) return 'write';
if (toolName.startsWith('update_') || toolName.startsWith('modify_')) return 'write';
if (toolName.startsWith('delete_') || toolName.startsWith('remove_') || toolName.startsWith('void_') || toolName.startsWith('cancel_') || toolName.startsWith('archive_')) return 'delete';
if (toolName.startsWith('send_')) return 'write';
return 'read'; // default to read
};
// Complexity from tool name
const getComplexity = (toolName) => {
if (toolName.startsWith('list_') || toolName.startsWith('get_')) return 'simple';
if (toolName.startsWith('search_')) return 'simple';
if (toolName.startsWith('create_') || toolName.startsWith('update_')) return 'simple';
return 'simple'; // Most operations are simple
};
// Get all server directories
const serverDirs = fs.readdirSync(serversDir).filter(dir => {
const fullPath = path.join(serversDir, dir);
return fs.statSync(fullPath).isDirectory() && fs.existsSync(path.join(fullPath, 'src/index.ts'));
});
console.log(`\n🔧 Fixing ${serverDirs.length} MCP servers...\n`);
let totalFixed = 0;
for (const serverName of serverDirs) {
const indexPath = path.join(serversDir, serverName, 'src/index.ts');
let content = fs.readFileSync(indexPath, 'utf-8');
let modified = false;
let fixedCount = 0;
// Find all tool definitions
const toolRegex = /{\s*name:\s*["']([^"']+)["']\s*,\s*description:\s*["']([^"']+)["']\s*,\s*inputSchema:\s*{[\s\S]*?}(\s*,?\s*)}(?=\s*,?\s*{?\s*name:|\s*\])/g;
content = content.replace(toolRegex, (match, toolName, description) => {
// Check if already has _meta
if (match.includes('_meta')) {
return match;
}
// Get labels
const category = getCategoryFromToolName(toolName, serverName);
const access = getAccessType(toolName);
const complexity = getComplexity(toolName);
// Add _meta before closing brace
const metaSection = `,\n _meta: {\n labels: {\n category: "${category}",\n access: "${access}",\n complexity: "${complexity}",\n },\n }`;
// Find the last } before the closing of the tool object
const lastBraceIndex = match.lastIndexOf('}');
const modifiedMatch = match.slice(0, lastBraceIndex) + metaSection + '\n ' + match.slice(lastBraceIndex);
modified = true;
fixedCount++;
return modifiedMatch;
});
if (modified) {
fs.writeFileSync(indexPath, content, 'utf-8');
console.log(`${serverName}: Fixed ${fixedCount} tools`);
totalFixed += fixedCount;
} else {
console.log(`⏭️ ${serverName}: No changes needed`);
}
}
console.log(`\n✨ Total tools fixed: ${totalFixed}\n`);