Skip to main content
Glama

Solid Multi-Tenant DevOps MCP Server

build-mcp-server.js•8.18 kB
#!/usr/bin/env node /** * Build comprehensive MCP server from generated tools * * This script: * 1. Reads generated-tools.json * 2. Creates a dynamic MCP server that exposes ALL tools * 3. Implements smart request handling for each endpoint */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); function buildServer() { console.log('šŸ”Ø Building comprehensive MCP server...'); // Read generated tools const toolsPath = path.join(__dirname, '..', 'generated-tools.json'); if (!fs.existsSync(toolsPath)) { console.error('āŒ generated-tools.json not found. Run generate-tools.js first!'); process.exit(1); } const { tools, categories } = JSON.parse(fs.readFileSync(toolsPath, 'utf-8')); console.log(`šŸ“¦ Loaded ${tools.length} tools across ${Object.keys(categories).length} categories`); // Generate MCP server code const serverCode = `#!/usr/bin/env node /** * COMPREHENSIVE SOLID MCP SERVER * * Auto-generated from ${tools.length} backend API endpoints * Generated: ${new Date().toISOString()} * * This server provides access to ALL Solid platform APIs through MCP. */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import fetch from 'node-fetch'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import { readFileSync } from 'fs'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Load environment variables const envPath = join(__dirname, '..', '.env'); const envContent = readFileSync(envPath, 'utf-8'); const env = {}; envContent.split('\\n').forEach(line => { const match = line.match(/^([^=# ]+)=(.*)$/); if (match) { env[match[1]] = match[2].trim(); } }); const BACKEND_URL = env.BACKEND_URL || 'http://localhost:8090'; // Helper function to make API calls async function makeRequest(url, options = {}) { try { const response = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', ...options.headers, }, }); const text = await response.text(); let data; try { data = JSON.parse(text); } catch { data = text; } return { status: response.status, ok: response.ok, data, }; } catch (error) { return { status: 0, ok: false, error: error.message, }; } } // Tool registry - ${tools.length} tools const TOOL_REGISTRY = ${JSON.stringify(tools, null, 2)}; // Create server instance const server = new Server( { name: 'solid-comprehensive-mcp', version: '2.0.0', }, { capabilities: { tools: {}, }, } ); // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { const mcpTools = TOOL_REGISTRY.map(tool => ({ name: tool.name, description: tool.description + ' [' + tool.category + ']', inputSchema: tool.inputSchema, })); return { tools: mcpTools, }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; // Find tool in registry const tool = TOOL_REGISTRY.find(t => t.name === name); if (!tool) { return { content: [ { type: 'text', text: JSON.stringify({ error: \`Unknown tool: \${name}\`, available_tools: TOOL_REGISTRY.length, }, null, 2), }, ], isError: true, }; } try { // Build URL with path parameters let url = \`\${BACKEND_URL}\${tool.endpoint}\`; // Replace path parameters Object.entries(args).forEach(([key, value]) => { url = url.replace(\`{\${key}}\`, value); }); // Build query string for GET requests if (tool.method === 'GET') { const queryParams = new URLSearchParams(); Object.entries(args).forEach(([key, value]) => { if (!tool.endpoint.includes(\`{\${key}}\`) && value !== undefined) { queryParams.append(key, value); } }); const queryString = queryParams.toString(); if (queryString) { url += \`?\${queryString}\`; } } // Make request const options = { method: tool.method, }; // Add body for POST/PUT/PATCH if (['POST', 'PUT', 'PATCH'].includes(tool.method) && args.body) { options.body = typeof args.body === 'string' ? args.body : JSON.stringify(args.body); } const result = await makeRequest(url, options); return { content: [ { type: 'text', text: JSON.stringify({ tool: tool.name, category: tool.category, endpoint: tool.endpoint, method: tool.method, url: url, status: result.status, ok: result.ok, response: result.data, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: JSON.stringify({ error: error.message, tool: tool.name, stack: error.stack, }, null, 2), }, ], isError: true, }; } }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('Solid Comprehensive MCP Server running'); console.error(\`Total tools available: \${TOOL_REGISTRY.length}\`); console.error(\`Backend: \${BACKEND_URL}\`); } main().catch((error) => { console.error('Fatal error:', error); process.exit(1); }); `; // Write comprehensive server const serverPath = path.join(__dirname, '..', 'src', 'comprehensive-index.js'); fs.writeFileSync(serverPath, serverCode); console.log(`āœ… Comprehensive server written to: ${serverPath}`); // Update package.json scripts const packagePath = path.join(__dirname, '..', 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8')); packageJson.scripts = packageJson.scripts || {}; packageJson.scripts['start:comprehensive'] = 'node src/comprehensive-index.js'; packageJson.scripts['generate'] = 'node scripts/generate-tools.js'; packageJson.scripts['build'] = 'node scripts/build-mcp-server.js'; packageJson.scripts['rebuild'] = 'npm run generate && npm run build'; fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2)); console.log(`āœ… Updated package.json scripts`); // Generate statistics const stats = { total_tools: tools.length, by_category: Object.entries(categories).map(([name, tools]) => ({ name, count: tools.length, })).sort((a, b) => b.count - a.count), by_method: {}, }; tools.forEach(tool => { stats.by_method[tool.method] = (stats.by_method[tool.method] || 0) + 1; }); console.log(`\nšŸ“Š Statistics:`); console.log(` Total tools: ${stats.total_tools}`); console.log(` Categories: ${stats.by_category.length}`); console.log(` Methods:`, stats.by_method); console.log(`\n Top 5 categories:`); stats.by_category.slice(0, 5).forEach(cat => { console.log(` - ${cat.name}: ${cat.count} tools`); }); return { tools, categories, stats }; } // Run if called directly if (import.meta.url === `file://${process.argv[1]}`) { buildServer() .then(({ stats }) => { console.log('\nšŸŽ‰ MCP server build complete!'); console.log(`\nTo use the comprehensive server:`); console.log(`1. Test: echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | node src/comprehensive-index.js`); console.log(`2. Update Claude Desktop config to use: src/comprehensive-index.js`); console.log(`3. Enjoy access to ALL ${stats.total_tools} tools!`); }) .catch(err => { console.error('Fatal error:', err); process.exit(1); }); } export { buildServer };

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