Skip to main content
Glama

Solid Multi-Tenant DevOps MCP Server

generate-tools.js•8.21 kB
#!/usr/bin/env node /** * Auto-generate MCP tools from Solid backend API routes * * This script: * 1. Fetches all 623+ routes from backend /api/v1/_debug/routes * 2. Categorizes them by API prefix * 3. Generates MCP tool definitions automatically * 4. Outputs complete tool list for the MCP server */ import fetch from 'node-fetch'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:8090'; // Method to parameter schema mapping const METHOD_PARAMS = { GET: { requiresBody: false }, POST: { requiresBody: true }, PUT: { requiresBody: true }, PATCH: { requiresBody: true }, DELETE: { requiresBody: false }, }; // Extract path parameters from route function extractPathParams(path) { const matches = path.match(/\{([^}]+)\}/g); if (!matches) return []; return matches.map(m => m.slice(1, -1)); } // Generate tool name from route path function generateToolName(path, method) { // Remove /api/v1/ prefix let cleaned = path.replace(/^\/api\/v1\//, ''); // Replace path params with descriptive names cleaned = cleaned.replace(/\{([^}]+)\}/g, 'by_$1'); // Replace slashes with dots cleaned = cleaned.replace(/\//g, '.'); // Prefix with method for non-GET if (method !== 'GET') { cleaned = `${method.toLowerCase()}_${cleaned}`; } return cleaned; } // Generate description from route function generateDescription(path, method) { const parts = path.split('/').filter(p => p && !p.startsWith('{')); const resource = parts[parts.length - 1] || parts[parts.length - 2] || 'resource'; const actions = { GET: 'Get', POST: 'Create', PUT: 'Update', PATCH: 'Update', DELETE: 'Delete', }; return `${actions[method] || 'Access'} ${resource.replace(/_/g, ' ')}`; } // Generate input schema from path params function generateInputSchema(path, method) { const pathParams = extractPathParams(path); const properties = {}; const required = []; // Add path parameters pathParams.forEach(param => { properties[param] = { type: param.includes('id') ? 'number' : 'string', description: `The ${param.replace(/_/g, ' ')}`, }; required.push(param); }); // Add common query params for GET if (method === 'GET') { if (path.includes('list') || path.match(/\/[^{]+$/)) { properties.limit = { type: 'number', description: 'Maximum number of results to return', default: 50, }; properties.offset = { type: 'number', description: 'Offset for pagination', default: 0, }; } if (path.includes('search')) { properties.query = { type: 'string', description: 'Search query string', }; } // Add company_id filter for most endpoints if (!path.includes('superadmin') && !path.includes('auth')) { properties.company_id = { type: 'number', description: 'Filter by company/tenant ID', }; } } // Add body for POST/PUT/PATCH if (METHOD_PARAMS[method]?.requiresBody) { properties.body = { type: 'object', description: 'Request body data', }; } return { type: 'object', properties, required: required.length > 0 ? required : undefined, }; } // Categorize route by API prefix function categorizeRoute(path) { const categories = { '/api/v1/superadmin': 'SuperAdmin - Platform Management', '/api/v1/agents': 'AI Agents', '/api/v1/ada': 'ADA Orchestrator', '/api/v1/crm': 'CRM System', '/api/v1/products': 'Product Management', '/api/v1/inventory': 'Inventory Management', '/api/v1/stock': 'Stock Management', '/api/v1/locations': 'Warehouse Locations', '/api/v1/variants': 'Product Variants', '/api/v1/merchant': 'Merchant Configuration', '/api/v1/performance': 'Performance Monitoring', '/api/v1/tokens': 'Token Usage & Billing', '/api/v1/payments': 'Payments', '/api/v1/payment-links': 'Payment Links', '/api/v1/pos': 'Point of Sale', '/api/v1/processors': 'Payment Processors', '/api/v1/billing': 'Billing & Subscriptions', '/api/v1/orders': 'Order Management', '/api/v1/cart': 'Shopping Cart', '/api/v1/fulfillment': 'Order Fulfillment', '/api/v1/shipping': 'Shipping', '/api/v1/analytics': 'Analytics', '/api/v1/dashboard': 'Dashboard', '/api/v1/monitoring': 'System Monitoring', '/api/v1/security': 'Security', '/api/v1/domains': 'Domain Management', '/api/v1/cms': 'Content Management', '/api/v1/social': 'Social Media', '/api/v1/email': 'Email Communication', '/api/v1/chat': 'AI Chat', '/api/v1/webhooks': 'Webhooks', '/api/v1/onboarding': 'Onboarding', '/api/v1/sandbox': 'Sandbox Environment', '/api/v1/mcp': 'MCP Tools', '/api/v1/console': 'Console', '/api/v1/llm-providers': 'LLM Providers', '/api/v1/auth': 'Authentication', }; for (const [prefix, category] of Object.entries(categories)) { if (path.startsWith(prefix)) { return category; } } return 'Other'; } // Main generator async function generateTools() { console.log('šŸ” Fetching routes from backend...'); try { const response = await fetch(`${BACKEND_URL}/api/v1/_debug/routes`); const data = await response.json(); console.log(`āœ… Found ${data.count} routes`); const tools = []; const categories = {}; // Process each route data.routes.forEach(route => { // Skip internal/debug routes if (route.path.includes('/_debug') || route.path.includes('/_health')) { return; } route.methods.forEach(method => { if (method === 'HEAD' || method === 'OPTIONS') return; const toolName = generateToolName(route.path, method); const description = generateDescription(route.path, method); const category = categorizeRoute(route.path); const inputSchema = generateInputSchema(route.path, method); const tool = { name: toolName, description: description, category: category, endpoint: route.path, method: method, inputSchema: inputSchema, }; tools.push(tool); // Track by category if (!categories[category]) { categories[category] = []; } categories[category].push(tool); }); }); console.log(`\nšŸ“Š Generated ${tools.length} MCP tools across ${Object.keys(categories).length} categories:`); Object.entries(categories).forEach(([cat, tools]) => { console.log(` ${cat}: ${tools.length} tools`); }); // Write tools to file const outputPath = path.join(__dirname, '..', 'generated-tools.json'); fs.writeFileSync(outputPath, JSON.stringify({ tools, categories }, null, 2)); console.log(`\nāœ… Tools written to: ${outputPath}`); // Generate summary const summary = { total_tools: tools.length, total_categories: Object.keys(categories).length, categories: Object.entries(categories).map(([name, tools]) => ({ name, tool_count: tools.length, sample_tools: tools.slice(0, 3).map(t => t.name), })), }; const summaryPath = path.join(__dirname, '..', 'tools-summary.json'); fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2)); console.log(`āœ… Summary written to: ${summaryPath}`); return { tools, categories, summary }; } catch (error) { console.error('āŒ Error generating tools:', error); throw error; } } // Run if called directly if (import.meta.url === `file://${process.argv[1]}`) { generateTools() .then(({ summary }) => { console.log('\nšŸŽ‰ Tool generation complete!'); console.log(`\nNext steps:`); console.log(`1. Review generated-tools.json`); console.log(`2. Run: node scripts/build-mcp-server.js`); console.log(`3. Test with: npm start`); }) .catch(err => { console.error('Fatal error:', err); process.exit(1); }); } export { generateTools };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Adam-Camp-King/solid-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server