163 lines
7.4 KiB
JavaScript
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`);
|