vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
507 lines (506 loc) • 19.5 kB
JavaScript
import axios, { AxiosError, AxiosHeaders } from 'axios';
import { vi } from 'vitest';
import * as dotenv from 'dotenv';
dotenv.config();
const openRouterBaseUrl = process.env.OPENROUTER_BASE_URL || 'https://openrouter.ai/api/v1';
function detectOperationType(requestData) {
const messages = requestData?.messages || [];
const systemMessage = messages.find((m) => m.role === 'system')?.content || '';
const userMessage = messages.find((m) => m.role === 'user')?.content || '';
const cacheKey = `${systemMessage}|${userMessage}`;
if (operationTypeCache.has(cacheKey)) {
return operationTypeCache.get(cacheKey);
}
let operationType = 'intent_recognition';
if (systemMessage.includes('natural language processing system') ||
systemMessage.includes('recognizing user intents') ||
systemMessage.includes('intent recognition') ||
userMessage.includes('recognize intent') ||
userMessage.includes('intent recognition')) {
operationType = 'intent_recognition';
}
else if (systemMessage.includes('atomic task detection') ||
systemMessage.includes('atomic task analyzer') ||
systemMessage.includes('determine if a given task is atomic') ||
systemMessage.includes('RDD (Recursive Decomposition and Decision-making)') ||
userMessage.includes('isAtomic') ||
userMessage.includes('atomic task analysis')) {
operationType = 'atomic_detection';
}
else if (systemMessage.includes('task decomposition specialist') ||
systemMessage.includes('break down complex tasks') ||
systemMessage.includes('decomposing complex tasks') ||
systemMessage.includes('decomposition') ||
systemMessage.includes('split') ||
userMessage.includes('decompose') ||
userMessage.includes('break down')) {
operationType = 'task_decomposition';
}
operationTypeCache.set(cacheKey, operationType);
return operationType;
}
function formatResponseForOperation(content, operationType) {
if (typeof content === 'string') {
return content;
}
if (typeof content === 'object' && Object.keys(content).length > 0) {
switch (operationType) {
case 'intent_recognition':
if (content.intent && typeof content.confidence === 'number') {
return JSON.stringify(content);
}
break;
case 'atomic_detection':
if (typeof content.isAtomic === 'boolean') {
return JSON.stringify(content);
}
break;
case 'task_decomposition':
if (content.tasks || content.subTasks) {
return JSON.stringify(content);
}
break;
}
}
if (prebuiltResponses.has(operationType)) {
const prebuilt = prebuiltResponses.get(operationType);
if (typeof content === 'object' && Object.keys(content).length > 0) {
return JSON.stringify({ ...prebuilt, ...content });
}
return JSON.stringify(prebuilt);
}
return JSON.stringify(content || {});
}
const mockResponseQueues = new Map();
let currentTestId = null;
const operationTypeCache = new Map();
const prebuiltResponses = new Map();
prebuiltResponses.set('intent_recognition', {
intent: 'create_task',
confidence: 0.85,
parameters: { task_title: 'implement user authentication', type: 'development' },
context: { temporal: 'immediate', urgency: 'normal' },
alternatives: []
});
prebuiltResponses.set('task_decomposition', {
tasks: [{
title: 'Default Task',
description: 'Default decomposed task',
estimatedHours: 0.1,
acceptanceCriteria: ['Task should be completed'],
priority: 'medium'
}]
});
prebuiltResponses.set('atomic_detection', {
isAtomic: true,
confidence: 0.98,
reasoning: 'Task is atomic and focused',
estimatedHours: 0.1,
complexityFactors: [],
recommendations: []
});
export function setTestId(testId) {
currentTestId = testId;
if (!mockResponseQueues.has(testId)) {
mockResponseQueues.set(testId, []);
}
}
export function queueMockResponses(responses) {
if (!currentTestId) {
throw new Error('Test ID must be set before queueing mock responses. Call setTestId() first.');
}
mockResponseQueues.set(currentTestId, [...responses]);
if (responses.length > 0) {
mockOpenRouterResponse(responses[0]);
}
}
export function clearMockQueue() {
if (currentTestId) {
mockResponseQueues.set(currentTestId, []);
}
}
export function clearAllMockQueues() {
mockResponseQueues.clear();
currentTestId = null;
}
export function clearPerformanceCaches() {
operationTypeCache.clear();
}
export function getPerformanceStats() {
return {
cacheSize: operationTypeCache.size,
};
}
export const MockTemplates = {
intentRecognition: (intent = 'create_task', confidence = 0.85) => ({
responseContent: {
intent,
confidence,
parameters: {
task_title: 'test task',
type: 'development'
},
context: {
temporal: 'immediate',
urgency: 'normal'
},
alternatives: []
},
model: /google\/gemini-2\.5-flash-preview/,
operationType: 'intent_recognition'
}),
atomicDetection: (isAtomic = true, confidence = 0.9) => ({
responseContent: {
isAtomic,
confidence,
reasoning: isAtomic ? 'Task is atomic and focused' : 'Task can be decomposed further',
estimatedHours: isAtomic ? 0.1 : 2.0,
complexityFactors: isAtomic ? [] : ['Multiple components', 'Complex logic'],
recommendations: []
},
model: /google\/gemini-2\.5-flash-preview/,
operationType: 'atomic_detection'
}),
taskDecomposition: (subtaskCount = 3) => ({
responseContent: {
subtasks: Array(subtaskCount).fill(null).map((_, i) => ({
id: `subtask-${i + 1}`,
title: `Subtask ${i + 1}`,
description: `Description for subtask ${i + 1}`,
estimatedHours: 0.1,
priority: 'medium',
acceptanceCriteria: [`Criteria ${i + 1}`],
tags: ['test']
})),
reasoning: 'Task decomposed into atomic subtasks',
confidence: 0.9
},
model: /google\/gemini-2\.5-flash-preview/,
operationType: 'task_decomposition'
}),
error: (errorMessage = 'Mock API Error') => ({
shouldError: true,
errorMessage,
statusCode: 500,
model: /google\/gemini-2\.5-flash-preview/
})
};
export class MockQueueBuilder {
queue = [];
addIntentRecognitions(count, intent = 'create_task') {
for (let i = 0; i < count; i++) {
this.queue.push(MockTemplates.intentRecognition(intent, 0.8 + (i * 0.02)));
}
return this;
}
addAtomicDetections(count, isAtomic = true) {
for (let i = 0; i < count; i++) {
this.queue.push(MockTemplates.atomicDetection(isAtomic, 0.9 + (i * 0.01)));
}
return this;
}
addTaskDecompositions(count, subtaskCount = 3) {
for (let i = 0; i < count; i++) {
this.queue.push(MockTemplates.taskDecomposition(subtaskCount));
}
return this;
}
addErrors(count, errorMessage) {
for (let i = 0; i < count; i++) {
this.queue.push(MockTemplates.error(errorMessage));
}
return this;
}
build() {
return [...this.queue];
}
queueResponses() {
queueMockResponses(this.build());
}
clear() {
this.queue = [];
return this;
}
}
export const performFormatAwareLlmCallWithCentralizedConfig = vi.fn().mockImplementation(async (prompt, systemPrompt, logicalTaskName, expectedFormat = 'text', expectedSchema, _temperature = 0.1) => {
const detectedOperationType = detectOperationType({
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: prompt }
]
});
if (currentTestId && mockResponseQueues.has(currentTestId)) {
const testQueue = mockResponseQueues.get(currentTestId);
if (testQueue.length > 0) {
const mockOptions = testQueue.shift();
const responseContent = mockOptions.responseContent || {};
return formatResponseForOperation(responseContent, detectedOperationType);
}
}
let mockResponse;
switch (detectedOperationType) {
case 'intent_recognition':
mockResponse = {
intent: 'create_task',
confidence: 0.85,
parameters: { task_title: 'mock task', type: 'development' },
context: { temporal: 'immediate', urgency: 'normal' },
alternatives: []
};
break;
case 'atomic_detection':
mockResponse = {
isAtomic: true,
confidence: 0.95,
reasoning: 'Mock atomic analysis for centralized config',
estimatedHours: 0.5,
complexityFactors: [],
recommendations: []
};
break;
case 'task_decomposition':
mockResponse = {
tasks: [{
title: 'Mock decomposed task',
description: 'Mock task from centralized config',
type: 'development',
priority: 'medium',
estimatedHours: 1,
acceptanceCriteria: ['Mock acceptance criterion'],
tags: ['mock', 'centralized']
}]
};
break;
default:
mockResponse = {
result: 'Mock LLM response from centralized config',
confidence: 0.9,
reasoning: 'Mock reasoning for centralized config test'
};
}
switch (expectedFormat) {
case 'json':
return JSON.stringify(mockResponse);
case 'yaml':
return Object.entries(mockResponse)
.map(([key, value]) => `${key}: ${typeof value === 'object' ? JSON.stringify(value) : value}`)
.join('\n');
case 'markdown':
return `# Mock Response\n\n${JSON.stringify(mockResponse, null, 2)}`;
case 'text':
default:
return typeof mockResponse === 'string' ? mockResponse : JSON.stringify(mockResponse);
}
});
export class PerformanceTestUtils {
static createRobustQueue(primaryResponses, fallbackCount = 20) {
const fallbackResponses = Array(fallbackCount).fill(null).map(() => MockTemplates.atomicDetection(true, 0.95));
return [...primaryResponses, ...fallbackResponses];
}
static setupEnhancedMocks(testId, responses) {
setTestId(testId);
queueMockResponses(this.createRobustQueue(responses));
}
static createConcurrentMocks(operationType, count) {
const builder = new MockQueueBuilder();
switch (operationType) {
case 'intent_recognition':
builder.addIntentRecognitions(count);
break;
case 'atomic_detection':
builder.addAtomicDetections(count);
break;
case 'task_decomposition':
builder.addTaskDecompositions(count);
break;
default:
builder
.addIntentRecognitions(Math.ceil(count / 3))
.addAtomicDetections(Math.ceil(count / 3))
.addTaskDecompositions(Math.floor(count / 3));
}
return builder.build();
}
static async measureMockPerformance(testName, testFn) {
const startTime = Date.now();
const startStats = getPerformanceStats();
try {
const result = await testFn();
const endTime = Date.now();
const endStats = getPerformanceStats();
const mockPerformance = {
duration: endTime - startTime,
cacheStats: {
start: startStats,
end: endStats,
growth: endStats.cacheSize - startStats.cacheSize
}
};
if (mockPerformance.duration > 2000) {
console.warn(`⚠️ Test "${testName}" took ${mockPerformance.duration}ms - consider optimizing mocks`);
}
return { ...result, mockPerformance };
}
catch (error) {
const endTime = Date.now();
const endStats = getPerformanceStats();
const mockPerformance = {
duration: endTime - startTime,
cacheStats: {
start: startStats,
end: endStats,
growth: endStats.cacheSize - startStats.cacheSize
}
};
console.error(`❌ Test "${testName}" failed after ${mockPerformance.duration}ms`);
throw error;
}
}
}
function createOperationAwareFallback(operation, originalOptions) {
const fallbackOptions = { ...originalOptions };
switch (operation) {
case 'intent_recognition':
fallbackOptions.responseContent = {
intent: 'create_task',
confidence: 0.85,
parameters: {
task_title: 'fallback task',
type: 'development'
},
context: {
temporal: 'immediate',
urgency: 'normal'
},
alternatives: []
};
break;
case 'atomic_detection':
fallbackOptions.responseContent = {
isAtomic: true,
confidence: 0.95,
reasoning: 'Fallback atomic detection - task is considered atomic',
estimatedHours: 0.08,
complexityFactors: [],
recommendations: []
};
break;
case 'task_decomposition':
fallbackOptions.responseContent = {
tasks: [
{
title: 'Fallback Task',
description: 'Fallback task created when queue exhausted',
estimatedHours: 0.08,
acceptanceCriteria: ['Task should be completed'],
priority: 'medium',
tags: ['fallback']
}
]
};
break;
default:
fallbackOptions.responseContent = {
intent: 'create_task',
confidence: 0.75,
parameters: {},
context: {},
alternatives: []
};
}
return fallbackOptions;
}
export function mockOpenRouterResponse(options) {
const { model, matchUrl = `${openRouterBaseUrl}/chat/completions` } = options;
const axiosPostSpy = vi.spyOn(axios, 'post');
axiosPostSpy.mockImplementation(async (url, data, config) => {
const urlMatches = (typeof matchUrl === 'string' && url === matchUrl) ||
(matchUrl instanceof RegExp && matchUrl.test(url));
if (!urlMatches) {
console.error(`Unexpected axios.post call to URL: ${url}. Mock configured for ${matchUrl}`);
throw new Error(`Unexpected axios.post call to URL: ${url}. Mock configured for ${matchUrl}`);
}
const requestModel = data?.model;
let modelMatches = true;
if (model && requestModel) {
modelMatches = (typeof model === 'string' && requestModel === model) ||
(model instanceof RegExp && model.test(requestModel));
}
if (model && !modelMatches) {
console.error(`Unexpected axios.post call for model: ${requestModel}. Mock configured for ${model}`);
throw new Error(`Unexpected axios.post call for model: ${requestModel}. Mock configured for ${model}`);
}
let detectedOperationType;
if (options.operationType && options.operationType !== 'auto') {
detectedOperationType = options.operationType;
}
else {
detectedOperationType = detectOperationType(data);
}
let currentOptions = options;
if (currentTestId && mockResponseQueues.has(currentTestId)) {
const testQueue = mockResponseQueues.get(currentTestId);
if (testQueue.length > 0) {
currentOptions = testQueue.shift();
}
else {
currentOptions = createOperationAwareFallback(detectedOperationType, options);
}
}
if (currentOptions.shouldError) {
const currentErrorMessage = currentOptions.errorMessage || 'Mock API Error';
const currentStatusCode = currentOptions.statusCode || 500;
const errorConfig = config || {};
const internalErrorConfig = {
...errorConfig,
headers: new AxiosHeaders(),
};
const error = new AxiosError(currentErrorMessage, currentStatusCode.toString(), internalErrorConfig, data, {
data: { error: { message: currentErrorMessage, type: 'mock_error' } },
status: currentStatusCode,
statusText: 'Mock Error',
headers: {},
config: internalErrorConfig,
});
return Promise.reject(error);
}
if (currentOptions.operationType && currentOptions.operationType !== 'auto') {
detectedOperationType = currentOptions.operationType;
}
const messageContent = currentOptions.responseContent
? formatResponseForOperation(currentOptions.responseContent, detectedOperationType)
: formatResponseForOperation({}, detectedOperationType);
const currentStatusCode = currentOptions.statusCode || 200;
const mockResponseData = {
id: `chatcmpl-mock-${Date.now()}`,
object: 'chat.completion',
created: Math.floor(Date.now() / 1000),
model: requestModel || 'mock-model',
choices: [
{
index: 0,
message: {
role: 'assistant',
content: messageContent,
},
finish_reason: 'stop',
},
],
usage: {
prompt_tokens: 50,
completion_tokens: 50,
total_tokens: 100,
},
};
return Promise.resolve({
data: mockResponseData,
status: currentStatusCode,
statusText: 'OK',
headers: { 'content-type': 'application/json' },
config: {
...(config || {}),
headers: new AxiosHeaders(),
},
});
});
}