Skip to main content
Glama

Codebase MCP Server

by Ravenight13
k6_workflow_load.js11.5 kB
/** * k6 Load Test Scenario for Workflow-MCP Server * * Purpose: Validate that workflow-mcp handles 50 concurrent clients without * crashing or becoming unresponsive under sustained load. * * Constitutional Compliance: * - SC-006: 50 concurrent clients handled without crash * - SC-007: 99.9% uptime during extended load testing * - Principle IV: Performance guarantees (project switching <50ms p95, entity queries <100ms p95) * * Test Scenario: * - Ramp-up: 0 -> 10 users (2 min) -> 50 users (5 min) * - Sustained: 50 users for 10 minutes * - Ramp-down: 50 -> 0 users (2 min) * - Total duration: 19 minutes * * Workload Mix: * - 40% project switching operations * - 40% entity queries with JSONB filters * - 20% work item queries * * Success Criteria: * - Error rate < 1% (99% success rate) * - No server crashes or unresponsiveness * - Project switching: p95 < 100ms (under load, slight degradation acceptable) * - Entity queries: p95 < 200ms (under load, slight degradation acceptable) * * Usage: * k6 run tests/load/k6_workflow_load.js * * Requirements: * - workflow-mcp server running on http://localhost:8010 * - Multiple projects and entities pre-populated in database * - k6 version 0.45+ installed */ import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate, Trend } from 'k6/metrics'; // Custom metrics for detailed analysis const errorRate = new Rate('workflow_errors'); const projectSwitchLatency = new Trend('workflow_project_switch_duration', true); const entityQueryLatency = new Trend('workflow_entity_query_duration', true); const workItemQueryLatency = new Trend('workflow_work_item_query_duration', true); // Load test configuration export const options = { stages: [ { duration: '2m', target: 10 }, // Ramp-up to 10 users over 2 minutes { duration: '5m', target: 50 }, // Ramp-up to 50 users over 5 minutes { duration: '10m', target: 50 }, // Sustained load at 50 concurrent users { duration: '2m', target: 0 }, // Ramp-down to 0 users over 2 minutes ], // Performance thresholds (test fails if any threshold is violated) thresholds: { 'http_req_duration': ['p(95)<500'], // Overall p95 latency < 500ms 'http_req_failed': ['rate<0.01'], // Error rate < 1% 'workflow_errors': ['rate<0.01'], // Custom error tracking < 1% 'workflow_project_switch_duration': ['p(95)<100'], // Project switch p95 < 100ms 'workflow_entity_query_duration': ['p(95)<200'], // Entity query p95 < 200ms (under load) 'http_reqs': ['rate>0'], // Ensure requests are being made }, // Additional configuration noConnectionReuse: false, // Reuse connections (more realistic) userAgent: 'k6-load-test/1.0 (workflow-mcp)', // Summary configuration summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(90)', 'p(95)', 'p(99)'], }; // Test data: simulated project IDs and entity types for testing let projectIds = []; let entityTypes = ['vendor', 'commission_record', 'dealer', 'product']; /** * Main test scenario executed by each virtual user */ export default function () { // If project IDs not initialized, use setup data if (projectIds.length === 0 && __ENV.PROJECT_IDS) { projectIds = JSON.parse(__ENV.PROJECT_IDS); } // Workload mix: 40% project switching, 40% entity queries, 20% work item queries const operation = Math.random(); if (operation < 0.4) { // Project switching operation (40% of requests) performProjectSwitch(); } else if (operation < 0.8) { // Entity query operation (40% of requests) performEntityQuery(); } else { // Work item query operation (20% of requests) performWorkItemQuery(); } // Realistic user think time: 1-3 seconds between requests sleep(1 + Math.random() * 2); } /** * Perform project switching operation */ function performProjectSwitch() { // Select random project or use default if none available const projectId = projectIds.length > 0 ? projectIds[Math.floor(Math.random() * projectIds.length)] : null; const url = 'http://localhost:8010/switch_project'; const payload = JSON.stringify({ project_id: projectId, }); const params = { headers: { 'Content-Type': 'application/json', }, tags: { name: 'workflow_project_switch', }, }; const response = http.post(url, payload, params); // Track custom metrics projectSwitchLatency.add(response.timings.duration); errorRate.add(response.status !== 200); // Validate response check(response, { 'project switch status is 200': (r) => r.status === 200, 'project switch has success': (r) => { try { const body = JSON.parse(r.body); return body.success === true || body.project_id !== undefined; } catch (e) { return false; } }, 'project switch time acceptable': (r) => r.timings.duration < 100, }); } /** * Perform entity query operation with JSONB filters */ function performEntityQuery() { // Select random entity type const entityType = entityTypes[Math.floor(Math.random() * entityTypes.length)]; const url = 'http://localhost:8010/query_entities'; const payload = JSON.stringify({ entity_type: entityType, filter: null, // No filter for baseline query performance tags: null, include_deleted: false, }); const params = { headers: { 'Content-Type': 'application/json', }, tags: { name: 'workflow_entity_query', }, }; const response = http.post(url, payload, params); // Track custom metrics entityQueryLatency.add(response.timings.duration); errorRate.add(response.status !== 200); // Validate response check(response, { 'entity query status is 200': (r) => r.status === 200, 'entity query returns results': (r) => { try { const body = JSON.parse(r.body); return Array.isArray(body) || body.entities !== undefined; } catch (e) { return false; } }, 'entity query time acceptable': (r) => r.timings.duration < 200, }); } /** * Perform work item query operation */ function performWorkItemQuery() { const url = 'http://localhost:8010/query_work_items'; const payload = JSON.stringify({ item_type: null, status: 'active', parent_id: null, include_descendants: false, }); const params = { headers: { 'Content-Type': 'application/json', }, tags: { name: 'workflow_work_item_query', }, }; const response = http.post(url, payload, params); // Track custom metrics workItemQueryLatency.add(response.timings.duration); errorRate.add(response.status !== 200); // Validate response check(response, { 'work item query status is 200': (r) => r.status === 200, 'work item query returns results': (r) => { try { const body = JSON.parse(r.body); return body.work_items !== undefined || Array.isArray(body); } catch (e) { return false; } }, 'work item query time acceptable': (r) => r.timings.duration < 200, }); } /** * Setup function executed once before load test starts */ export function setup() { console.log('Starting workflow-mcp load test...'); console.log('Target: 50 concurrent users for 10 minutes'); console.log('Workload: 40% project switch, 40% entity query, 20% work item query'); console.log('Thresholds: error rate <1%, project switch p95<100ms, entity query p95<200ms'); // Health check to ensure server is running const healthUrl = 'http://localhost:8010/health'; const healthResponse = http.get(healthUrl); if (healthResponse.status !== 200) { throw new Error(`Server health check failed: ${healthResponse.status}`); } console.log('Server health check passed.'); // Try to get list of projects for realistic testing let discoveredProjects = []; try { const projectsUrl = 'http://localhost:8010/list_projects'; const projectsResponse = http.post(projectsUrl, JSON.stringify({ limit: 10 }), { headers: { 'Content-Type': 'application/json' }, }); if (projectsResponse.status === 200) { const body = JSON.parse(projectsResponse.body); discoveredProjects = body.projects?.map(p => p.id) || []; console.log(`Discovered ${discoveredProjects.length} projects for testing`); } } catch (e) { console.log('Could not discover projects, will use default workspace'); } console.log('Starting load test...'); return { startTime: new Date().toISOString(), projectIds: discoveredProjects, }; } /** * Teardown function executed once after load test completes */ export function teardown(data) { console.log('Load test completed.'); console.log(`Started at: ${data.startTime}`); console.log(`Completed at: ${new Date().toISOString()}`); // Final health check to ensure server is still responsive const healthUrl = 'http://localhost:8010/health'; const healthResponse = http.get(healthUrl); if (healthResponse.status === 200) { console.log('✓ Server still healthy after load test'); } else { console.warn('⚠ Server health degraded after load test'); } } /** * Custom summary handler for detailed results output */ export function handleSummary(data) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const resultsFile = `tests/load/results/workflow_load_results_${timestamp}.json`; // Generate summary report const summary = { test_name: 'workflow-mcp load test', timestamp: timestamp, duration_seconds: data.state.testRunDurationMs / 1000, metrics: { requests_total: data.metrics.http_reqs.values.count, requests_per_second: data.metrics.http_reqs.values.rate, error_rate_percent: data.metrics.http_req_failed.values.rate * 100, latency: { p50: data.metrics.http_req_duration.values['p(50)'], p95: data.metrics.http_req_duration.values['p(95)'], p99: data.metrics.http_req_duration.values['p(99)'], avg: data.metrics.http_req_duration.values.avg, max: data.metrics.http_req_duration.values.max, }, custom_metrics: { project_switch_p95: data.metrics.workflow_project_switch_duration?.values['p(95)'] || null, entity_query_p95: data.metrics.workflow_entity_query_duration?.values['p(95)'] || null, work_item_query_p95: data.metrics.workflow_work_item_query_duration?.values['p(95)'] || null, custom_error_rate_percent: data.metrics.workflow_errors?.values.rate * 100 || null, }, }, thresholds_passed: data.metrics.http_req_failed.thresholds['rate<0.01'].ok && data.metrics.workflow_project_switch_duration.thresholds['p(95)<100'].ok && data.metrics.workflow_entity_query_duration.thresholds['p(95)<200'].ok, success_criteria: { concurrent_clients_handled: true, // If test completes, clients were handled error_rate_under_1_percent: data.metrics.http_req_failed.values.rate < 0.01, uptime_99_9_percent: data.metrics.http_req_failed.values.rate < 0.001, // SC-007 project_switch_performance: data.metrics.workflow_project_switch_duration?.values['p(95)'] < 100, entity_query_performance: data.metrics.workflow_entity_query_duration?.values['p(95)'] < 200, }, }; return { 'stdout': JSON.stringify(summary, null, 2), [resultsFile]: JSON.stringify(summary, null, 2), }; }

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/Ravenight13/codebase-mcp'

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