Files
vibe-kanban/scripts/mcp_test.js
Gabriel Gordon-Hall 59fd0cfa1b chore: remove checked-in sqlitedb (#16)
* chore: remove unused sqlite db

* ignore sqlite db
2025-06-30 18:31:06 +01:00

374 lines
14 KiB
JavaScript

const { spawn } = require('child_process');
console.error('🔄 Starting MCP server for comprehensive endpoint testing...');
// Test configuration
let currentStepIndex = 0;
let messageId = 1;
let testData = {
projectId: null,
taskId: null,
createdProjectId: null,
taskTitle: "Test Task from MCP Script",
updatedTaskTitle: "Updated Test Task Title",
secondTaskTitle: "Second Test Task",
renamedTaskTitle: "Renamed Second Task",
};
const testSequence = [
'initialize',
'initialized_notification',
'list_tools',
'list_projects',
'create_project',
'list_tasks', // empty
'create_task',
'get_task',
'list_tasks', // with task
'set_task_status',
'list_tasks', // filtered
'complete_task',
'list_tasks', // completed
'create_task', // second task
'update_task', // legacy
'update_task_title',
'update_task_description',
'list_tasks', // after updates
'delete_task_by_title',
'list_tasks', // final
'summary'
];
const stepHandlers = {
initialize: {
description: 'Initialize MCP connection',
action: () => {
console.log('📤 Sending initialize request...');
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "test", "version": "1.0.0"}}}\n`);
},
responseHandler: () => {
executeNextStep();
}
},
initialized_notification: {
description: 'Send initialized notification',
action: () => {
console.log('📤 Sending initialized notification...');
mcpProcess.stdin.write('{"jsonrpc": "2.0", "method": "notifications/initialized"}\n');
// Notifications don't have responses, auto-advance
setTimeout(() => executeNextStep(), 200);
},
responseHandler: null
},
list_tools: {
description: 'List available MCP tools',
action: () => {
console.log('📤 Sending tools/list...');
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/list", "params": {}}\n`);
},
responseHandler: () => {
executeNextStep();
}
},
list_projects: {
description: 'List all projects',
action: () => {
console.log('📤 Sending list_projects...');
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/call", "params": {"name": "list_projects", "arguments": {}}}\n`);
},
responseHandler: (response) => {
try {
const parsedResponse = JSON.parse(response);
if (parsedResponse.result?.content) {
const projectsResponse = JSON.parse(parsedResponse.result.content[0].text);
if (projectsResponse.success && projectsResponse.projects.length > 0) {
testData.projectId = projectsResponse.projects[0].id;
console.log(`💾 Found existing project: ${testData.projectId}`);
}
}
} catch (e) {
console.error('⚠️ Could not parse projects response');
}
executeNextStep();
}
},
create_project: {
description: 'Create a new test project',
action: () => {
console.log('📤 Sending create_project...');
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/call", "params": {"name": "create_project", "arguments": {"name": "Test Project from MCP", "git_repo_path": "/tmp/test-project", "use_existing_repo": false, "setup_script": "echo \\"Setup complete\\"", "dev_script": "echo \\"Dev server started\\""}}}\n`);
},
responseHandler: (response) => {
try {
const parsedResponse = JSON.parse(response);
if (parsedResponse.result?.content) {
const createProjectResponse = JSON.parse(parsedResponse.result.content[0].text);
if (createProjectResponse.success && createProjectResponse.project_id) {
testData.createdProjectId = createProjectResponse.project_id;
console.log(`💾 Created project: ${testData.createdProjectId}`);
}
}
} catch (e) {
console.error('⚠️ Could not parse create project response');
}
executeNextStep();
}
},
list_tasks: {
description: 'List tasks in project',
action: () => {
const projectToUse = testData.createdProjectId || testData.projectId;
const context = getListTasksContext();
console.log(`📤 Sending list_tasks (${context})...`);
let args = { project_id: projectToUse };
// Add context-specific filters
if (context === 'filtered') {
args.status = 'in-progress';
} else if (context === 'completed') {
args.status = 'done';
} else if (context === 'empty') {
args.include_execution_status = true;
}
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/call", "params": {"name": "list_tasks", "arguments": ${JSON.stringify(args)}}}\n`);
},
responseHandler: () => {
executeNextStep();
}
},
create_task: {
description: 'Create a new task',
action: () => {
const projectToUse = testData.createdProjectId || testData.projectId;
const isSecondTask = getCreateTaskContext() === 'second';
const title = isSecondTask ? testData.secondTaskTitle : testData.taskTitle;
const description = isSecondTask ?
"This is a second task for testing updates" :
"This task was created during endpoint testing";
console.log(`📤 Sending create_task (${isSecondTask ? 'second task' : 'first task'})...`);
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/call", "params": {"name": "create_task", "arguments": {"project_id": "${projectToUse}", "title": "${title}", "description": "${description}"}}}\n`);
},
responseHandler: (response) => {
try {
const parsedResponse = JSON.parse(response);
if (parsedResponse.result?.content) {
const createTaskResponse = JSON.parse(parsedResponse.result.content[0].text);
if (createTaskResponse.success && createTaskResponse.task_id) {
testData.taskId = createTaskResponse.task_id;
console.log(`💾 Created task: ${testData.taskId}`);
}
}
} catch (e) {
console.error('⚠️ Could not parse create task response');
}
executeNextStep();
}
},
get_task: {
description: 'Get task details by ID',
action: () => {
const projectToUse = testData.createdProjectId || testData.projectId;
console.log('📤 Sending get_task...');
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/call", "params": {"name": "get_task", "arguments": {"project_id": "${projectToUse}", "task_id": "${testData.taskId}"}}}\n`);
},
responseHandler: () => {
executeNextStep();
}
},
set_task_status: {
description: 'Set task status (agent-friendly)',
action: () => {
const projectToUse = testData.createdProjectId || testData.projectId;
console.log('📤 Sending set_task_status (agent-friendly)...');
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/call", "params": {"name": "set_task_status", "arguments": {"project_id": "${projectToUse}", "task_title": "${testData.taskTitle}", "status": "in-progress"}}}\n`);
},
responseHandler: () => {
executeNextStep();
}
},
complete_task: {
description: 'Complete task (agent-friendly)',
action: () => {
const projectToUse = testData.createdProjectId || testData.projectId;
console.log('📤 Sending complete_task (agent-friendly)...');
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/call", "params": {"name": "complete_task", "arguments": {"project_id": "${projectToUse}", "task_title": "${testData.taskTitle}"}}}\n`);
},
responseHandler: () => {
executeNextStep();
}
},
update_task: {
description: 'Update task (legacy UUID method)',
action: () => {
const projectToUse = testData.createdProjectId || testData.projectId;
console.log('📤 Sending update_task (legacy method)...');
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/call", "params": {"name": "update_task", "arguments": {"project_id": "${projectToUse}", "task_id": "${testData.taskId}", "title": "${testData.updatedTaskTitle}", "description": "Updated description via legacy method", "status": "in-review"}}}\n`);
},
responseHandler: () => {
executeNextStep();
}
},
update_task_title: {
description: 'Update task title (agent-friendly)',
action: () => {
const projectToUse = testData.createdProjectId || testData.projectId;
console.log('📤 Sending update_task_title (agent-friendly)...');
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/call", "params": {"name": "update_task_title", "arguments": {"project_id": "${projectToUse}", "current_title": "${testData.secondTaskTitle}", "new_title": "${testData.renamedTaskTitle}"}}}\n`);
},
responseHandler: () => {
executeNextStep();
}
},
update_task_description: {
description: 'Update task description (agent-friendly)',
action: () => {
const projectToUse = testData.createdProjectId || testData.projectId;
console.log('📤 Sending update_task_description (agent-friendly)...');
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/call", "params": {"name": "update_task_description", "arguments": {"project_id": "${projectToUse}", "task_title": "${testData.renamedTaskTitle}", "description": "This description was updated using the agent-friendly endpoint"}}}\n`);
},
responseHandler: () => {
executeNextStep();
}
},
delete_task_by_title: {
description: 'Delete task by title (agent-friendly)',
action: () => {
const projectToUse = testData.createdProjectId || testData.projectId;
console.log('📤 Sending delete_task_by_title (agent-friendly)...');
mcpProcess.stdin.write(`{"jsonrpc": "2.0", "id": ${messageId++}, "method": "tools/call", "params": {"name": "delete_task_by_title", "arguments": {"project_id": "${projectToUse}", "task_title": "${testData.renamedTaskTitle}"}}}\n`);
},
responseHandler: () => {
executeNextStep();
}
},
summary: {
description: 'Test completion summary',
action: () => {
console.log('✅ All endpoint tests completed successfully!');
console.log('');
console.log('📊 Test Summary:');
console.log(` - Project ID used: ${testData.projectId || 'N/A'}`);
console.log(` - Created project: ${testData.createdProjectId || 'N/A'}`);
console.log(` - Task ID tested: ${testData.taskId || 'N/A'}`);
console.log(` - Task title: ${testData.taskTitle}`);
console.log('');
console.log('🎯 Agent-Friendly Endpoints Tested:');
console.log(' ✅ set_task_status - Change task status by title');
console.log(' ✅ complete_task - Mark task done by title');
console.log(' ✅ update_task_title - Change task title');
console.log(' ✅ update_task_description - Update task description');
console.log(' ✅ delete_task_by_title - Delete task by title');
console.log('');
console.log('🔧 Legacy Endpoints Tested:');
console.log(' ✅ update_task - Update task by ID (more complex)');
console.log(' ✅ get_task - Get task details by ID');
console.log('');
console.log('🎉 All MCP endpoints are working correctly!');
console.log('💡 Agents should prefer the title-based endpoints for easier usage');
setTimeout(() => mcpProcess.kill(), 500);
},
responseHandler: null
}
};
// Helper functions to determine context
function getListTasksContext() {
const prevSteps = testSequence.slice(0, currentStepIndex);
if (prevSteps[prevSteps.length - 1] === 'create_project') return 'empty';
if (prevSteps[prevSteps.length - 1] === 'set_task_status') return 'filtered';
if (prevSteps[prevSteps.length - 1] === 'complete_task') return 'completed';
if (prevSteps[prevSteps.length - 1] === 'update_task_description') return 'after updates';
if (prevSteps[prevSteps.length - 1] === 'delete_task_by_title') return 'final';
return 'with task';
}
function getCreateTaskContext() {
const prevSteps = testSequence.slice(0, currentStepIndex);
const createTaskCount = prevSteps.filter(step => step === 'create_task').length;
return createTaskCount > 0 ? 'second' : 'first';
}
// Execute current step
function executeCurrentStep() {
if (currentStepIndex >= testSequence.length) {
console.log('⚠️ All steps completed');
return;
}
const stepName = testSequence[currentStepIndex];
const stepHandler = stepHandlers[stepName];
if (!stepHandler) {
console.error(`❌ Unknown step: ${stepName}`);
return;
}
console.log(`🔄 Step ${currentStepIndex + 1}/${testSequence.length}: ${stepHandler.description}`);
setTimeout(() => {
stepHandler.action();
}, 100);
}
// Move to next step
function executeNextStep() {
currentStepIndex++;
executeCurrentStep();
}
// Start MCP process
const mcpProcess = spawn('vibe-kanban', ["--mcp"], {
stdio: ['pipe', 'pipe', 'inherit'],
});
mcpProcess.stdout.on('data', (data) => {
const response = data.toString().trim();
const currentStepName = testSequence[currentStepIndex];
const stepHandler = stepHandlers[currentStepName];
console.log(`📥 MCP Response (${currentStepName}):`);
console.log(response);
if (stepHandler?.responseHandler) {
stepHandler.responseHandler(response);
}
});
mcpProcess.on('exit', (code) => {
console.error(`🔴 MCP server exited with code: ${code}`);
process.exit(0);
});
mcpProcess.on('error', (error) => {
console.error('❌ MCP server error:', error);
process.exit(1);
});
// Start the sequence
setTimeout(() => {
executeCurrentStep();
}, 500);
// Safety timeout
setTimeout(() => {
console.error('⏰ Test timeout - killing process');
mcpProcess.kill();
}, 45000);