Skip to main content
Glama
index.ts19.3 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, CallToolResult, Tool, } from '@modelcontextprotocol/sdk/types.js'; import { ACIApiService, ACIConfig } from './services/aciApi.js'; import { loadConfig, loadMCPServerConfig, isToolEnabled, getEnabledTools, MCPServerConfig } from './utils/config.js'; let aciApi: ACIApiService; let serverConfig: MCPServerConfig; // Initialize the server const server = new Server( { name: 'aci-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, }, }, ); // Tool definitions with full ACI management capabilities const ALL_TOOL_DEFINITIONS: Tool[] = [ // Tenant Management { name: 'list_tenants', description: 'List all tenants in the ACI fabric', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_tenant', description: 'Get details of a specific tenant', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Tenant name' }, }, required: ['name'], }, }, { name: 'create_tenant', description: 'Create a new tenant', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Tenant name' }, description: { type: 'string', description: 'Tenant description' }, }, required: ['name'], }, }, { name: 'delete_tenant', description: 'Delete a tenant', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Tenant name' }, }, required: ['name'], }, }, { name: 'get_tenant_health', description: 'Get health status for a specific tenant', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Tenant name' }, }, required: ['name'], }, }, // Application Profile Management { name: 'list_application_profiles', description: 'List application profiles in a tenant', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, }, required: ['tenant'], }, }, { name: 'get_application_profile', description: 'Get details of a specific application profile', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'Application profile name' }, }, required: ['tenant', 'name'], }, }, { name: 'create_application_profile', description: 'Create a new application profile', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'Application profile name' }, description: { type: 'string', description: 'Application profile description' }, }, required: ['tenant', 'name'], }, }, { name: 'delete_application_profile', description: 'Delete an application profile', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'Application profile name' }, }, required: ['tenant', 'name'], }, }, // Endpoint Group Management { name: 'list_endpoint_groups', description: 'List endpoint groups in an application profile', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, applicationProfile: { type: 'string', description: 'Application profile name' }, }, required: ['tenant', 'applicationProfile'], }, }, { name: 'get_endpoint_group', description: 'Get details of a specific endpoint group', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, applicationProfile: { type: 'string', description: 'Application profile name' }, name: { type: 'string', description: 'Endpoint group name' }, }, required: ['tenant', 'applicationProfile', 'name'], }, }, { name: 'create_endpoint_group', description: 'Create a new endpoint group', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, applicationProfile: { type: 'string', description: 'Application profile name' }, name: { type: 'string', description: 'Endpoint group name' }, bridgeDomain: { type: 'string', description: 'Bridge domain name' }, }, required: ['tenant', 'applicationProfile', 'name', 'bridgeDomain'], }, }, { name: 'delete_endpoint_group', description: 'Delete an endpoint group', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, applicationProfile: { type: 'string', description: 'Application profile name' }, name: { type: 'string', description: 'Endpoint group name' }, }, required: ['tenant', 'applicationProfile', 'name'], }, }, // Bridge Domain Management { name: 'list_bridge_domains', description: 'List bridge domains in a tenant', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, }, required: ['tenant'], }, }, { name: 'get_bridge_domain', description: 'Get details of a specific bridge domain', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'Bridge domain name' }, }, required: ['tenant', 'name'], }, }, { name: 'create_bridge_domain', description: 'Create a new bridge domain', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'Bridge domain name' }, vrf: { type: 'string', description: 'VRF name' }, subnet: { type: 'string', description: 'Subnet (optional)' }, }, required: ['tenant', 'name', 'vrf'], }, }, { name: 'delete_bridge_domain', description: 'Delete a bridge domain', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'Bridge domain name' }, }, required: ['tenant', 'name'], }, }, // VRF Management { name: 'list_vrfs', description: 'List VRFs in a tenant', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, }, required: ['tenant'], }, }, { name: 'get_vrf', description: 'Get details of a specific VRF', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'VRF name' }, }, required: ['tenant', 'name'], }, }, { name: 'create_vrf', description: 'Create a new VRF', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'VRF name' }, }, required: ['tenant', 'name'], }, }, { name: 'delete_vrf', description: 'Delete a VRF', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'VRF name' }, }, required: ['tenant', 'name'], }, }, // Contract Management { name: 'list_contracts', description: 'List contracts in a tenant', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, }, required: ['tenant'], }, }, { name: 'get_contract', description: 'Get details of a specific contract', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'Contract name' }, }, required: ['tenant', 'name'], }, }, { name: 'create_contract', description: 'Create a new contract with subject and filter', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'Contract name' }, subject: { type: 'string', description: 'Subject name' }, filter: { type: 'string', description: 'Filter name' }, }, required: ['tenant', 'name', 'subject', 'filter'], }, }, { name: 'delete_contract', description: 'Delete a contract', inputSchema: { type: 'object', properties: { tenant: { type: 'string', description: 'Tenant name' }, name: { type: 'string', description: 'Contract name' }, }, required: ['tenant', 'name'], }, }, // Monitoring & Health { name: 'get_fabric_health', description: 'Get overall fabric health status', inputSchema: { type: 'object', properties: {}, }, }, { name: 'list_faults', description: 'List faults in the fabric', inputSchema: { type: 'object', properties: { severity: { type: 'string', description: 'Filter by severity (critical, major, minor, warning, info)', enum: ['critical', 'major', 'minor', 'warning', 'info'] }, }, }, }, { name: 'get_fault_summary', description: 'Get a summary of faults by severity', inputSchema: { type: 'object', properties: {}, }, }, { name: 'list_nodes', description: 'List all fabric nodes', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_node_health', description: 'Get health status for a specific node', inputSchema: { type: 'object', properties: { nodeId: { type: 'string', description: 'Node ID' }, }, required: ['nodeId'], }, }, { name: 'get_system_info', description: 'Get system information from the APIC', inputSchema: { type: 'object', properties: {}, }, }, ]; // Handler for listing tools server.setRequestHandler(ListToolsRequestSchema, async () => { const enabledToolNames = getEnabledTools(serverConfig); const enabledTools = ALL_TOOL_DEFINITIONS.filter(tool => enabledToolNames.includes(tool.name) ); return { tools: enabledTools, }; }); // Handler for calling tools server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; if (!isToolEnabled(name, serverConfig)) { throw new Error(`Tool ${name} is not enabled`); } try { const result = await handleToolCall(name, args || {}); return result; } catch (error: any) { throw new Error(`Tool ${name} failed: ${error.message}`); } }); async function handleToolCall(toolName: string, args: any): Promise<CallToolResult> { switch (toolName) { // Tenant Management case 'list_tenants': const tenants = await aciApi.listTenants(); return { content: [ { type: 'text', text: `Found ${tenants.imdata?.length || 0} tenants:\n${JSON.stringify(tenants, null, 2)}` } ] }; case 'get_tenant': const tenant = await aciApi.getTenant(args.name); return { content: [ { type: 'text', text: tenant ? JSON.stringify(tenant, null, 2) : `Tenant ${args.name} not found` } ] }; case 'create_tenant': const newTenant = await aciApi.createTenant(args.name, args.description); return { content: [ { type: 'text', text: `Tenant ${args.name} created successfully:\n${JSON.stringify(newTenant, null, 2)}` } ] }; case 'delete_tenant': await aciApi.deleteTenant(args.name); return { content: [ { type: 'text', text: `Tenant ${args.name} deleted successfully` } ] }; // Application Profile Management case 'list_application_profiles': const aps = await aciApi.listApplicationProfiles(args.tenant); return { content: [ { type: 'text', text: `Found ${aps.imdata?.length || 0} application profiles in tenant ${args.tenant}:\n${JSON.stringify(aps, null, 2)}` } ] }; case 'create_application_profile': const newAp = await aciApi.createApplicationProfile(args.tenant, args.name, args.description); return { content: [ { type: 'text', text: `Application profile ${args.name} created in tenant ${args.tenant}:\n${JSON.stringify(newAp, null, 2)}` } ] }; // Endpoint Group Management case 'list_endpoint_groups': const epgs = await aciApi.listEndpointGroups(args.tenant, args.applicationProfile); return { content: [ { type: 'text', text: `Found ${epgs.imdata?.length || 0} endpoint groups in ${args.tenant}/${args.applicationProfile}:\n${JSON.stringify(epgs, null, 2)}` } ] }; case 'create_endpoint_group': const newEpg = await aciApi.createEndpointGroup(args.tenant, args.applicationProfile, args.name, args.bridgeDomain); return { content: [ { type: 'text', text: `Endpoint group ${args.name} created:\n${JSON.stringify(newEpg, null, 2)}` } ] }; // Bridge Domain Management case 'list_bridge_domains': const bds = await aciApi.listBridgeDomains(args.tenant); return { content: [ { type: 'text', text: `Found ${bds.imdata?.length || 0} bridge domains in tenant ${args.tenant}:\n${JSON.stringify(bds, null, 2)}` } ] }; case 'create_bridge_domain': const newBd = await aciApi.createBridgeDomain(args.tenant, args.name, args.vrf); return { content: [ { type: 'text', text: `Bridge domain ${args.name} created:\n${JSON.stringify(newBd, null, 2)}` } ] }; // VRF Management case 'list_vrfs': const vrfs = await aciApi.listVRFs(args.tenant); return { content: [ { type: 'text', text: `Found ${vrfs.imdata?.length || 0} VRFs in tenant ${args.tenant}:\n${JSON.stringify(vrfs, null, 2)}` } ] }; case 'create_vrf': const newVrf = await aciApi.createVRF(args.tenant, args.name); return { content: [ { type: 'text', text: `VRF ${args.name} created:\n${JSON.stringify(newVrf, null, 2)}` } ] }; // Contract Management case 'list_contracts': const contracts = await aciApi.listContracts(args.tenant); return { content: [ { type: 'text', text: `Found ${contracts.imdata?.length || 0} contracts in tenant ${args.tenant}:\n${JSON.stringify(contracts, null, 2)}` } ] }; case 'create_contract': const newContract = await aciApi.createContract(args.tenant, args.name); return { content: [ { type: 'text', text: `Contract ${args.name} created:\n${JSON.stringify(newContract, null, 2)}` } ] }; // Health and Monitoring case 'get_fabric_health': const health = await aciApi.getFabricHealth(); return { content: [ { type: 'text', text: `Fabric health status:\n${JSON.stringify(health, null, 2)}` } ] }; case 'list_faults': const faults = await aciApi.listFaults(args.severity); return { content: [ { type: 'text', text: `Found ${faults.imdata?.length || 0} faults${args.severity ? ` with severity ${args.severity}` : ''}:\n${JSON.stringify(faults, null, 2)}` } ] }; case 'get_fault_summary': const allFaults = await aciApi.listFaults(); const summary = allFaults.imdata?.reduce((acc: any, fault: any) => { const severity = fault.faultInst?.attributes?.severity || 'unknown'; acc[severity] = (acc[severity] || 0) + 1; return acc; }, {}) || {}; return { content: [ { type: 'text', text: `Fault summary:\n${JSON.stringify(summary, null, 2)}\n\nTotal faults: ${allFaults.imdata?.length || 0}` } ] }; case 'list_nodes': const nodes = await aciApi.listNodes(); return { content: [ { type: 'text', text: `Found ${nodes.imdata?.length || 0} fabric nodes:\n${JSON.stringify(nodes, null, 2)}` } ] }; case 'get_system_info': const sysInfo = await aciApi.getSystemInfo(); return { content: [ { type: 'text', text: `System information:\n${JSON.stringify(sysInfo, null, 2)}` } ] }; default: throw new Error(`Unknown tool: ${toolName}`); } } // Initialize and start server async function main() { try { // Load configurations const aciConfig: ACIConfig = loadConfig(); serverConfig = loadMCPServerConfig(); // Initialize ACI API aciApi = new ACIApiService(aciConfig); // Test connection await aciApi.authenticate(); console.error('Successfully connected to ACI fabric'); console.error(`Server configured with ${getEnabledTools(serverConfig).length} tools`); // Start server const transport = new StdioServerTransport(); await server.connect(transport); console.error('ACI MCP Server running on stdio'); } catch (error: any) { console.error('Failed to start ACI MCP server:', error.message); process.exit(1); } } // Handle graceful shutdown process.on('SIGINT', async () => { try { await aciApi?.logout(); } catch (error) { console.error('Error during logout:', error); } process.exit(0); }); process.on('SIGTERM', async () => { try { await aciApi?.logout(); } catch (error) { console.error('Error during logout:', error); } process.exit(0); }); if (import.meta.url === `file://${process.argv[1]}`) { main().catch((error) => { console.error('Unhandled error:', error); process.exit(1); }); }

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/jim-coyne/ACI_MCP'

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