Skip to main content
Glama
manual-test.tsโ€ข10.8 kB
#!/usr/bin/env node /** * Manual Testing Script for ACI MCP Server * * This script provides an interactive way to test individual MCP tools * and see their responses. Perfect for manual testing and debugging. */ import readline from 'readline'; import { ACIApiService } from '../src/services/aciApi.js'; import { loadConfig } from '../src/utils/config.js'; import { MockAPICServer } from './mock-server.js'; interface ToolTest { name: string; description: string; args?: any; handler: () => Promise<any>; } class ManualTester { private aciApi: ACIApiService; private mockServer?: MockAPICServer; private rl: readline.Interface; private tools: ToolTest[]; constructor(useMockServer: boolean = true) { if (useMockServer) { this.mockServer = new MockAPICServer(8443); const config = { apicUrl: 'http://localhost:8443', username: 'admin', password: 'admin', validateCerts: false, timeout: 5000 }; this.aciApi = new ACIApiService(config); } else { const config = loadConfig(); this.aciApi = new ACIApiService(config); } this.rl = readline.createInterface({ input: process.stdin, output: process.stdout }); this.tools = this.initializeTools(); } private initializeTools(): ToolTest[] { return [ { name: 'auth', description: 'Test authentication', handler: () => this.aciApi.authenticate() }, { name: 'tenants', description: 'List all tenants', handler: () => this.aciApi.listTenants() }, { name: 'health', description: 'Get fabric health', handler: () => this.aciApi.getFabricHealth() }, { name: 'faults', description: 'List all faults', handler: () => this.aciApi.listFaults() }, { name: 'critical-faults', description: 'List critical faults only', handler: () => this.aciApi.listFaults('critical') }, { name: 'nodes', description: 'List fabric nodes', handler: () => this.aciApi.listNodes() }, { name: 'system', description: 'Get system information', handler: () => this.aciApi.getSystemInfo() }, { name: 'create-tenant', description: 'Create a test tenant', handler: () => this.createTestTenant() }, { name: 'apps', description: 'List application profiles (requires tenant)', handler: () => this.listApplicationProfiles() }, { name: 'epgs', description: 'List endpoint groups (requires tenant and app profile)', handler: () => this.listEndpointGroups() }, { name: 'bridge-domains', description: 'List bridge domains (requires tenant)', handler: () => this.listBridgeDomains() }, { name: 'vrfs', description: 'List VRFs (requires tenant)', handler: () => this.listVRFs() }, { name: 'contracts', description: 'List contracts (requires tenant)', handler: () => this.listContracts() } ]; } async start(): Promise<void> { console.log('๐Ÿงช ACI MCP Server Manual Testing Tool\n'); if (this.mockServer) { console.log('๐Ÿ“ก Starting mock APIC server...'); this.mockServer.start(); await this.sleep(1000); console.log('โœ… Mock server ready!\n'); } try { console.log('๐Ÿ” Testing authentication...'); await this.aciApi.authenticate(); console.log('โœ… Authentication successful!\n'); } catch (error) { console.log(`โŒ Authentication failed: ${error}\n`); } this.showMenu(); this.startInteractiveSession(); } private showMenu(): void { console.log('๐Ÿ“‹ Available Tests:'); console.log('=' .repeat(50)); this.tools.forEach((tool, index) => { console.log(`${index + 1}. ${tool.name.padEnd(20)} - ${tool.description}`); }); console.log('\n๐ŸŽ›๏ธ Special Commands:'); console.log('menu - Show this menu'); console.log('clear - Clear screen'); console.log('quit / exit - Exit the tool'); console.log(''); } private async startInteractiveSession(): Promise<void> { return new Promise((resolve) => { const prompt = () => { this.rl.question('๐Ÿ”ธ Enter test name or number: ', async (input) => { const trimmed = input.trim().toLowerCase(); if (trimmed === 'quit' || trimmed === 'exit') { console.log('๐Ÿ‘‹ Goodbye!'); this.cleanup(); resolve(); return; } if (trimmed === 'menu') { this.showMenu(); prompt(); return; } if (trimmed === 'clear') { console.clear(); this.showMenu(); prompt(); return; } if (trimmed === '') { prompt(); return; } await this.executeTest(trimmed); prompt(); }); }; prompt(); }); } private async executeTest(input: string): Promise<void> { let tool: ToolTest | undefined; // Try to find by number const num = parseInt(input); if (!isNaN(num) && num >= 1 && num <= this.tools.length) { tool = this.tools[num - 1]; } else { // Try to find by name tool = this.tools.find(t => t.name === input || t.name.includes(input)); } if (!tool) { console.log(`โŒ Test '${input}' not found. Type 'menu' to see available tests.\n`); return; } console.log(`\n๐Ÿ”„ Running: ${tool.name} - ${tool.description}`); console.log('โ”€'.repeat(50)); const startTime = Date.now(); try { const result = await tool.handler(); const duration = Date.now() - startTime; console.log('โœ… Success!'); console.log(`โฑ๏ธ Duration: ${duration}ms`); if (result) { console.log('๐Ÿ“„ Response:'); if (typeof result === 'string') { console.log(result); } else { console.log(JSON.stringify(result, null, 2)); } } } catch (error) { const duration = Date.now() - startTime; console.log('โŒ Failed!'); console.log(`โฑ๏ธ Duration: ${duration}ms`); console.log(`๐Ÿ’ฅ Error: ${error}`); } console.log('โ”€'.repeat(50)); console.log(''); } // Helper methods for complex tests private async createTestTenant(): Promise<any> { const tenantName = `test-tenant-${Date.now()}`; console.log(`Creating tenant: ${tenantName}`); const result = await this.aciApi.createTenant(tenantName, 'Manual test tenant'); // Schedule cleanup setTimeout(async () => { try { await this.aciApi.deleteTenant(tenantName); console.log(`๐Ÿ—‘๏ธ Auto-cleaned up tenant: ${tenantName}`); } catch (error) { console.log(`โš ๏ธ Could not clean up tenant: ${tenantName}`); } }, 10000); // Clean up after 10 seconds return result; } private async listApplicationProfiles(): Promise<any> { const tenants = await this.aciApi.listTenants(); if (!tenants.imdata || tenants.imdata.length === 0) { throw new Error('No tenants found. Create a tenant first.'); } const tenantName = tenants.imdata[0].fvTenant?.attributes?.name; if (!tenantName) { throw new Error('Invalid tenant data'); } console.log(`Using tenant: ${tenantName}`); return await this.aciApi.listApplicationProfiles(tenantName); } private async listEndpointGroups(): Promise<any> { const tenants = await this.aciApi.listTenants(); if (!tenants.imdata || tenants.imdata.length === 0) { throw new Error('No tenants found'); } const tenantName = tenants.imdata[0].fvTenant?.attributes?.name; if (!tenantName) { throw new Error('Invalid tenant data'); } const aps = await this.aciApi.listApplicationProfiles(tenantName); if (!aps.imdata || aps.imdata.length === 0) { throw new Error(`No application profiles found in tenant ${tenantName}`); } const apName = aps.imdata[0].fvAp?.attributes?.name; if (!apName) { throw new Error('Invalid application profile data'); } console.log(`Using tenant: ${tenantName}, app profile: ${apName}`); return await this.aciApi.listEndpointGroups(tenantName, apName); } private async listBridgeDomains(): Promise<any> { const tenants = await this.aciApi.listTenants(); if (!tenants.imdata || tenants.imdata.length === 0) { throw new Error('No tenants found'); } const tenantName = tenants.imdata[0].fvTenant?.attributes?.name; if (!tenantName) { throw new Error('Invalid tenant data'); } console.log(`Using tenant: ${tenantName}`); return await this.aciApi.listBridgeDomains(tenantName); } private async listVRFs(): Promise<any> { const tenants = await this.aciApi.listTenants(); if (!tenants.imdata || tenants.imdata.length === 0) { throw new Error('No tenants found'); } const tenantName = tenants.imdata[0].fvTenant?.attributes?.name; if (!tenantName) { throw new Error('Invalid tenant data'); } console.log(`Using tenant: ${tenantName}`); return await this.aciApi.listVRFs(tenantName); } private async listContracts(): Promise<any> { const tenants = await this.aciApi.listTenants(); if (!tenants.imdata || tenants.imdata.length === 0) { throw new Error('No tenants found'); } const tenantName = tenants.imdata[0].fvTenant?.attributes?.name; if (!tenantName) { throw new Error('Invalid tenant data'); } console.log(`Using tenant: ${tenantName}`); return await this.aciApi.listContracts(tenantName); } private sleep(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } private cleanup(): void { this.rl.close(); if (this.mockServer) { this.mockServer.stop(); } } } // Run if executed directly if (import.meta.url === `file://${process.argv[1]}`) { const useMock = process.argv.includes('--mock') || !process.env.ACI_APIC_URL; console.log(`๐Ÿ”ง Manual testing with ${useMock ? 'Mock APIC Server' : 'Real APIC'}`); const tester = new ManualTester(useMock); tester.start().catch((error) => { console.error('๐Ÿ’ฅ Manual tester crashed:', error); process.exit(1); }); // Handle Ctrl+C process.on('SIGINT', () => { console.log('\n\n๐Ÿ‘‹ Received SIGINT, exiting gracefully...'); process.exit(0); }); } export { ManualTester };

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