Task API Server
by milkosten
- src
#!/usr/bin/env node
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import * as path from 'path';
import { spawn } from 'child_process';
async function runTests() {
console.log('Starting MCP Task Manager API Tests');
// Start server in a child process
const serverProcess = spawn('node', [path.resolve('./dist/index.js')], {
stdio: ['pipe', 'pipe', 'pipe']
});
// Log server output for debugging
serverProcess.stderr.on('data', (data) => {
console.error(`Server stderr: ${data.toString().trim()}`);
});
// Declare transport outside try block so it's accessible in finally block
let transport;
let client;
try {
// Wait for server to initialize
await new Promise((resolve) => setTimeout(resolve, 2000));
console.log('Server should be running now. Setting up MCP client...');
// Create MCP client transport using the command and args
// This will spawn a new server process instead of using our existing one
transport = new StdioClientTransport({
command: 'node',
args: [path.resolve('./dist/index.js')]
});
// Initialize the MCP client
client = new Client(
{
name: "task-api-test-client",
version: "1.0.0"
},
{
capabilities: {
prompts: {},
resources: {},
tools: {}
}
}
);
// Connect to the server
await client.connect(transport);
console.log('Client connected to server');
// Get server capabilities
console.log('\nRetrieving server capabilities...');
const serverInfo = client.getServerVersion();
const capabilities = client.getServerCapabilities();
if (serverInfo) {
console.log('Server info:');
console.log(`Server name: ${serverInfo.name}`);
console.log(`Server version: ${serverInfo.version}`);
console.log('✅ Server info retrieved successfully');
} else {
console.log('❌ Failed to retrieve server info');
}
if (capabilities) {
console.log('Server capabilities:');
console.log(`Available tools: ${capabilities.tools ? 'Yes' : 'No'}`);
console.log(`Available resources: ${capabilities.resources ? 'Yes' : 'No'}`);
console.log('✅ Server capabilities retrieved successfully');
} else {
console.log('❌ Failed to retrieve server capabilities');
}
// Test the listTasks tool
console.log('\nTesting listTasks tool...');
try {
const listTasksResult = await client.callTool({
name: "listTasks",
arguments: {}
});
console.log('List tasks response received:');
if (Array.isArray(listTasksResult.content) && listTasksResult.content.length > 0) {
const firstContent = listTasksResult.content[0];
if (firstContent && 'text' in firstContent) {
console.log(firstContent.text);
console.log('✅ List tasks test passed');
} else {
console.log('❌ List tasks test failed - unexpected content format');
}
} else {
console.log('❌ List tasks test failed - empty content');
}
} catch (error: any) {
console.log(`❌ List tasks test failed with error: ${error.message}`);
}
// Test creating a task
console.log('\nTesting createTask tool...');
let createdTaskId: number | undefined;
try {
const createTaskResult = await client.callTool({
name: "createTask",
arguments: {
task: `Test task ${new Date().toISOString()}`,
category: "Test",
priority: "medium"
}
});
console.log('Create task response received:');
if (Array.isArray(createTaskResult.content) && createTaskResult.content.length > 0) {
const firstContent = createTaskResult.content[0];
if (firstContent && 'text' in firstContent) {
console.log(firstContent.text);
// Extract task ID if successful
const idMatch = firstContent.text.match(/ID: (\d+)/);
if (idMatch && idMatch[1]) {
createdTaskId = parseInt(idMatch[1], 10);
console.log(`✅ Create task test passed. Created task ID: ${createdTaskId}`);
} else {
console.log('❌ Create task test failed - could not extract task ID');
}
} else {
console.log('❌ Create task test failed - unexpected content format');
}
} else {
console.log('❌ Create task test failed - empty content');
}
} catch (error: any) {
console.log(`❌ Create task test failed with error: ${error.message}`);
}
// If we successfully created a task, test updating it with various field combinations
if (createdTaskId) {
// Test 1: Update task description
console.log('\nTesting updateTask - description change...');
try {
const newDescription = `Updated description ${new Date().toISOString()}`;
const updateDescResult = await client.callTool({
name: "updateTask",
arguments: {
taskId: createdTaskId,
task: newDescription
}
});
console.log('Update description response received:');
if (Array.isArray(updateDescResult.content) && updateDescResult.content.length > 0) {
const firstContent = updateDescResult.content[0];
if (firstContent && 'text' in firstContent) {
console.log(firstContent.text);
if (firstContent.text.includes('updated successfully')) {
console.log('✅ Update description test passed');
} else {
console.log('❌ Update description test failed - response does not indicate success');
}
} else {
console.log('❌ Update description test failed - unexpected content format');
}
} else {
console.log('❌ Update description test failed - empty content');
}
} catch (error: any) {
console.log(`❌ Update description test failed with error: ${error.message}`);
}
// Test 2: Update task status
console.log('\nTesting updateTask - status change...');
try {
const updateStatusResult = await client.callTool({
name: "updateTask",
arguments: {
taskId: createdTaskId,
status: "started"
}
});
console.log('Update status response received:');
if (Array.isArray(updateStatusResult.content) && updateStatusResult.content.length > 0) {
const firstContent = updateStatusResult.content[0];
if (firstContent && 'text' in firstContent) {
console.log(firstContent.text);
if (firstContent.text.includes('updated successfully')) {
console.log('✅ Update status test passed');
} else {
console.log('❌ Update status test failed - response does not indicate success');
}
// Verify the status was actually updated in the response
if (updateStatusResult.content.length > 1) {
const secondContent = updateStatusResult.content[1];
if (secondContent && 'text' in secondContent) {
const responseJson = JSON.parse(secondContent.text);
if (responseJson.status === "started") {
console.log('✅ Status verification passed - status is "started"');
} else {
console.log(`❌ Status verification failed - expected "started" but got "${responseJson.status}"`);
}
}
}
} else {
console.log('❌ Update status test failed - unexpected content format');
}
} else {
console.log('❌ Update status test failed - empty content');
}
} catch (error: any) {
console.log(`❌ Update status test failed with error: ${error.message}`);
}
// Test 3: Update task category
console.log('\nTesting updateTask - category change...');
try {
const newCategory = `Category-${Date.now().toString().slice(-5)}`;
const updateCategoryResult = await client.callTool({
name: "updateTask",
arguments: {
taskId: createdTaskId,
category: newCategory
}
});
console.log('Update category response received:');
if (Array.isArray(updateCategoryResult.content) && updateCategoryResult.content.length > 0) {
const firstContent = updateCategoryResult.content[0];
if (firstContent && 'text' in firstContent) {
console.log(firstContent.text);
if (firstContent.text.includes('updated successfully')) {
console.log('✅ Update category test passed');
} else {
console.log('❌ Update category test failed - response does not indicate success');
}
// Verify the category was actually updated in the response
if (updateCategoryResult.content.length > 1) {
const secondContent = updateCategoryResult.content[1];
if (secondContent && 'text' in secondContent) {
const responseJson = JSON.parse(secondContent.text);
if (responseJson.category === newCategory) {
console.log(`✅ Category verification passed - category is "${newCategory}"`);
} else {
console.log(`❌ Category verification failed - expected "${newCategory}" but got "${responseJson.category}"`);
}
}
}
} else {
console.log('❌ Update category test failed - unexpected content format');
}
} else {
console.log('❌ Update category test failed - empty content');
}
} catch (error: any) {
console.log(`❌ Update category test failed with error: ${error.message}`);
}
// Test 4: Update task priority
console.log('\nTesting updateTask - priority change...');
try {
const updatePriorityResult = await client.callTool({
name: "updateTask",
arguments: {
taskId: createdTaskId,
priority: "high"
}
});
console.log('Update priority response received:');
if (Array.isArray(updatePriorityResult.content) && updatePriorityResult.content.length > 0) {
const firstContent = updatePriorityResult.content[0];
if (firstContent && 'text' in firstContent) {
console.log(firstContent.text);
if (firstContent.text.includes('updated successfully')) {
console.log('✅ Update priority test passed');
} else {
console.log('❌ Update priority test failed - response does not indicate success');
}
// Verify the priority was actually updated in the response
if (updatePriorityResult.content.length > 1) {
const secondContent = updatePriorityResult.content[1];
if (secondContent && 'text' in secondContent) {
const responseJson = JSON.parse(secondContent.text);
if (responseJson.priority === "high") {
console.log('✅ Priority verification passed - priority is "high"');
} else {
console.log(`❌ Priority verification failed - expected "high" but got "${responseJson.priority}"`);
}
}
}
} else {
console.log('❌ Update priority test failed - unexpected content format');
}
} else {
console.log('❌ Update priority test failed - empty content');
}
} catch (error: any) {
console.log(`❌ Update priority test failed with error: ${error.message}`);
}
// Test 5: Update multiple fields at once
console.log('\nTesting updateTask - multiple fields at once...');
try {
const finalDesc = `Final description ${new Date().toISOString()}`;
const finalCategory = `Final-Category-${Date.now().toString().slice(-5)}`;
const updateMultipleResult = await client.callTool({
name: "updateTask",
arguments: {
taskId: createdTaskId,
task: finalDesc,
category: finalCategory,
priority: "medium",
status: "done"
}
});
console.log('Update multiple fields response received:');
if (Array.isArray(updateMultipleResult.content) && updateMultipleResult.content.length > 0) {
const firstContent = updateMultipleResult.content[0];
if (firstContent && 'text' in firstContent) {
console.log(firstContent.text);
if (firstContent.text.includes('updated successfully')) {
console.log('✅ Update multiple fields test passed');
} else {
console.log('❌ Update multiple fields test failed - response does not indicate success');
}
// Verify all fields were actually updated in the response
if (updateMultipleResult.content.length > 1) {
const secondContent = updateMultipleResult.content[1];
if (secondContent && 'text' in secondContent) {
const responseJson = JSON.parse(secondContent.text);
let verificationsPassed = true;
if (responseJson.task !== finalDesc) {
console.log(`❌ Description verification failed - expected "${finalDesc}" but got "${responseJson.task}"`);
verificationsPassed = false;
}
if (responseJson.category !== finalCategory) {
console.log(`❌ Category verification failed - expected "${finalCategory}" but got "${responseJson.category}"`);
verificationsPassed = false;
}
if (responseJson.priority !== "medium") {
console.log(`❌ Priority verification failed - expected "medium" but got "${responseJson.priority}"`);
verificationsPassed = false;
}
if (responseJson.status !== "done") {
console.log(`❌ Status verification failed - expected "done" but got "${responseJson.status}"`);
verificationsPassed = false;
}
if (verificationsPassed) {
console.log('✅ All field verifications passed');
}
}
}
} else {
console.log('❌ Update multiple fields test failed - unexpected content format');
}
} else {
console.log('❌ Update multiple fields test failed - empty content');
}
} catch (error: any) {
console.log(`❌ Update multiple fields test failed with error: ${error.message}`);
}
// Finally, test deleting the task
console.log('\nTesting deleteTask tool...');
try {
const deleteTaskResult = await client.callTool({
name: "deleteTask",
arguments: {
taskId: createdTaskId
}
});
console.log('Delete task response received:');
if (Array.isArray(deleteTaskResult.content) && deleteTaskResult.content.length > 0) {
const firstContent = deleteTaskResult.content[0];
if (firstContent && 'text' in firstContent) {
console.log(firstContent.text);
if (firstContent.text.includes('deleted successfully') ||
firstContent.text.includes('successfully deleted')) {
console.log('✅ Delete task test passed');
} else {
console.log('❌ Delete task test failed - response does not indicate success');
}
} else {
console.log('❌ Delete task test failed - unexpected content format');
}
} else {
console.log('❌ Delete task test failed - empty content');
}
} catch (error: any) {
console.log(`❌ Delete task test failed with error: ${error.message}`);
}
}
// Test accessing resources
console.log('\nTesting resources...');
try {
const resourcesList = await client.listResources();
if (resourcesList && 'resources' in resourcesList) {
const resources = resourcesList.resources;
console.log(`Available resources: ${resources.map(r => r.name).join(', ')}`);
if (resources.length > 0) {
console.log('✅ List resources test passed');
// Try to read a resource if any are available
const resourceURI = `tasks://${resources[0].name}`;
try {
const resourceResult = await client.readResource({ uri: resourceURI });
if (resourceResult && 'contents' in resourceResult) {
console.log(`Resource ${resources[0].name} retrieved successfully`);
console.log('✅ Read resource test passed');
} else {
console.log('❌ Read resource test failed - unexpected result format');
}
} catch (error: any) {
console.log(`❌ Read resource test failed with error: ${error.message}`);
}
} else {
console.log('ℹ️ No resources available to test');
}
} else {
console.log('❌ List resources test failed - unexpected result format');
}
} catch (error: any) {
console.log(`❌ List resources test failed with error: ${error.message}`);
}
console.log('\nTests completed');
} catch (error: any) {
console.error('Test execution error:', error);
} finally {
// Clean up - kill the server process
console.log('Terminating test server');
serverProcess.kill();
// Make sure to close the client which will terminate the second server process
if (client) {
try {
await client.close();
console.log('Test client closed');
} catch (closeError) {
console.error('Error closing client:', closeError);
}
}
// Force exit after a short delay to ensure all processes are terminated
setTimeout(() => {
console.log('Exiting test process');
process.exit(0);
}, 500);
}
}
// Run the tests
runTests().catch(console.error);