test-interactive.jsā¢6.57 kB
/**
* Interactive test script for MCP filesystem server
* Run: node test-interactive.js
*
* Available commands:
* list - List tools
* dirs - List allowed directories
* ls [path] - List directory (default: .)
* read <path> [start_line] - Read file
* search <pattern> - Search code
* find <pattern> - Find files
* info <path> - Get file info
* exit - Exit
*/
import { spawn } from 'child_process';
import readline from 'readline';
// Start the server
const serverPath = process.argv[2] || process.cwd();
console.log(`š Starting MCP server with allowed directory: ${serverPath}\n`);
const server = spawn('node', ['dist/index.js', serverPath]);
let messageId = 1;
let waitingForResponse = false;
// Handle server output
server.stdout.on('data', (data) => {
const lines = data.toString().trim().split('\n');
for (const line of lines) {
try {
const json = JSON.parse(line);
console.log('\nš„ RESPONSE:');
console.log(JSON.stringify(json, null, 2));
} catch {
console.log('\nš„ RAW:', line);
}
}
waitingForResponse = false;
rl.prompt();
});
server.stderr.on('data', (data) => {
console.log('š§', data.toString().trim());
});
server.on('close', (code) => {
console.log(`\nā Server exited with code ${code}`);
process.exit(code);
});
// Send a request
function sendRequest(method, params = {}) {
const request = {
jsonrpc: '2.0',
id: messageId++,
method: method,
params: params
};
server.stdin.write(JSON.stringify(request) + '\n');
waitingForResponse = true;
}
// Setup interactive CLI
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: '\n> '
});
console.log('=== Interactive MCP Filesystem Test ===');
console.log('Type "help" for available commands\n');
// Initialize the server first
setTimeout(() => {
sendRequest('initialize', {
protocolVersion: '2024-11-05',
capabilities: {},
clientInfo: { name: 'test-client', version: '1.0.0' }
});
setTimeout(() => {
console.log('\nā
Server initialized! Type commands below:');
rl.prompt();
}, 500);
}, 500);
rl.on('line', (line) => {
const input = line.trim();
if (!input) {
rl.prompt();
return;
}
const parts = input.split(/\s+/);
const cmd = parts[0].toLowerCase();
switch (cmd) {
case 'help':
console.log('\nAvailable commands:');
console.log(' list - List available tools');
console.log(' dirs - List allowed directories');
console.log(' ls [path] - List directory (default: .)');
console.log(' read <path> [line] - Read file (optionally from line)');
console.log(' search <pattern> - Search code with pattern');
console.log(' find <pattern> - Find files by name pattern');
console.log(' info <path> - Get file info');
console.log(' exit - Exit');
rl.prompt();
break;
case 'list':
console.log('\nš¤ Requesting tools list...');
sendRequest('tools/list');
break;
case 'dirs':
console.log('\nš¤ Requesting allowed directories...');
sendRequest('tools/call', {
name: 'list_allowed_directories',
arguments: {}
});
break;
case 'ls':
const lsPath = parts[1] || '.';
console.log(`\nš¤ Listing directory: ${lsPath}`);
sendRequest('tools/call', {
name: 'list_directory',
arguments: { path: lsPath }
});
break;
case 'read':
if (!parts[1]) {
console.log('ā Usage: read <path> [start_line]');
rl.prompt();
break;
}
const readPath = parts[1];
const startLine = parts[2] ? parseInt(parts[2]) : 0;
console.log(`\nš¤ Reading file: ${readPath} (from line ${startLine})`);
sendRequest('tools/call', {
name: 'read_file',
arguments: { path: readPath, start_line: startLine }
});
break;
case 'search':
if (!parts[1]) {
console.log('ā Usage: search [-s] <pattern>');
console.log(' Note: Case-insensitive by default (LLM-friendly)');
console.log(' Examples: search class.*ChatRequest (matches any case)');
console.log(' search enum.*llmproviders (finds LLmProviders)');
console.log(' search -s PublicClass (case-sensitive)');
rl.prompt();
break;
}
// Check for -i flag (case-insensitive is default, but explicit -i is still supported)
let caseInsensitive = true; // Default to true for LLM-friendly behavior
let searchParts = parts.slice(1);
if (searchParts[0] === '-s') {
// -s for case-sensitive (opposite of default)
caseInsensitive = false;
searchParts = searchParts.slice(1);
}
// Remove surrounding quotes if present
let pattern = searchParts.join(' ').replace(/^["']|["']$/g, '');
console.log(`\nš¤ Searching for: ${pattern}${caseInsensitive ? ' (case-insensitive)' : ' (case-sensitive)'}`);
sendRequest('tools/call', {
name: 'search_code',
arguments: {
pattern: pattern,
contextLines: 2,
caseInsensitive: caseInsensitive
}
});
break;
case 'find':
if (!parts[1]) {
console.log('ā Usage: find <pattern>');
rl.prompt();
break;
}
const findPattern = parts[1];
console.log(`\nš¤ Finding files: ${findPattern}`);
sendRequest('tools/call', {
name: 'find_files',
arguments: { pattern: findPattern }
});
break;
case 'info':
if (!parts[1]) {
console.log('ā Usage: info <path>');
rl.prompt();
break;
}
const infoPath = parts[1];
console.log(`\nš¤ Getting file info: ${infoPath}`);
sendRequest('tools/call', {
name: 'get_file_info',
arguments: { path: infoPath }
});
break;
case 'exit':
case 'quit':
console.log('\nš Goodbye!');
server.kill();
process.exit(0);
break;
default:
console.log(`ā Unknown command: ${cmd}. Type "help" for available commands.`);
rl.prompt();
}
});
rl.on('close', () => {
server.kill();
process.exit(0);
});
// Handle Ctrl+C
process.on('SIGINT', () => {
console.log('\n\nš Shutting down...');
server.kill();
process.exit(0);
});