mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-12 02:37:45 +08:00
feat: Add CCW MCP server and tools integration
- Introduced `ccw-mcp` command for running CCW tools as an MCP server. - Updated `package.json` to include new MCP dependencies and scripts. - Enhanced CLI with new options for `codex_lens` tool. - Implemented MCP server logic to expose CCW tools via Model Context Protocol. - Added new tools and updated existing ones for better functionality and documentation. - Created quick start and full documentation for MCP server usage. - Added tests for MCP server functionality to ensure reliability.
This commit is contained in:
159
ccw/tests/mcp-server.test.js
Normal file
159
ccw/tests/mcp-server.test.js
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* Basic MCP server tests
|
||||
* Tests the MCP server functionality with mock requests
|
||||
*/
|
||||
|
||||
import { describe, it, before, after } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { spawn } from 'node:child_process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
describe('MCP Server', () => {
|
||||
let serverProcess;
|
||||
|
||||
before(async () => {
|
||||
// Start the MCP server
|
||||
const serverPath = join(__dirname, '../bin/ccw-mcp.js');
|
||||
serverProcess = spawn('node', [serverPath], {
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
});
|
||||
|
||||
// Wait for server to start
|
||||
await new Promise((resolve) => {
|
||||
serverProcess.stderr.once('data', (data) => {
|
||||
const message = data.toString();
|
||||
if (message.includes('started')) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (serverProcess) {
|
||||
serverProcess.kill();
|
||||
}
|
||||
});
|
||||
|
||||
it('should respond to tools/list request', async () => {
|
||||
const request = {
|
||||
jsonrpc: '2.0',
|
||||
id: 1,
|
||||
method: 'tools/list',
|
||||
params: {}
|
||||
};
|
||||
|
||||
// Send request
|
||||
serverProcess.stdin.write(JSON.stringify(request) + '\n');
|
||||
|
||||
// Wait for response
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
reject(new Error('Request timeout'));
|
||||
}, 5000);
|
||||
|
||||
serverProcess.stdout.once('data', (data) => {
|
||||
clearTimeout(timeout);
|
||||
try {
|
||||
const response = JSON.parse(data.toString());
|
||||
resolve(response);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
assert.equal(response.jsonrpc, '2.0');
|
||||
assert.equal(response.id, 1);
|
||||
assert(response.result);
|
||||
assert(Array.isArray(response.result.tools));
|
||||
assert(response.result.tools.length > 0);
|
||||
|
||||
// Check that essential tools are present
|
||||
const toolNames = response.result.tools.map(t => t.name);
|
||||
assert(toolNames.includes('edit_file'));
|
||||
assert(toolNames.includes('write_file'));
|
||||
assert(toolNames.includes('codex_lens'));
|
||||
});
|
||||
|
||||
it('should respond to tools/call request', async () => {
|
||||
const request = {
|
||||
jsonrpc: '2.0',
|
||||
id: 2,
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'get_modules_by_depth',
|
||||
arguments: {}
|
||||
}
|
||||
};
|
||||
|
||||
// Send request
|
||||
serverProcess.stdin.write(JSON.stringify(request) + '\n');
|
||||
|
||||
// Wait for response
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
reject(new Error('Request timeout'));
|
||||
}, 5000);
|
||||
|
||||
serverProcess.stdout.once('data', (data) => {
|
||||
clearTimeout(timeout);
|
||||
try {
|
||||
const response = JSON.parse(data.toString());
|
||||
resolve(response);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
assert.equal(response.jsonrpc, '2.0');
|
||||
assert.equal(response.id, 2);
|
||||
assert(response.result);
|
||||
assert(Array.isArray(response.result.content));
|
||||
assert(response.result.content.length > 0);
|
||||
assert.equal(response.result.content[0].type, 'text');
|
||||
});
|
||||
|
||||
it('should handle invalid tool name gracefully', async () => {
|
||||
const request = {
|
||||
jsonrpc: '2.0',
|
||||
id: 3,
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'nonexistent_tool',
|
||||
arguments: {}
|
||||
}
|
||||
};
|
||||
|
||||
// Send request
|
||||
serverProcess.stdin.write(JSON.stringify(request) + '\n');
|
||||
|
||||
// Wait for response
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
reject(new Error('Request timeout'));
|
||||
}, 5000);
|
||||
|
||||
serverProcess.stdout.once('data', (data) => {
|
||||
clearTimeout(timeout);
|
||||
try {
|
||||
const response = JSON.parse(data.toString());
|
||||
resolve(response);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
assert.equal(response.jsonrpc, '2.0');
|
||||
assert.equal(response.id, 3);
|
||||
assert(response.result);
|
||||
assert.equal(response.result.isError, true);
|
||||
assert(response.result.content[0].text.includes('not found'));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user