index.ts•19.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);
});
}