Coder Toolbox MCP Server
by lamemind
Verified
- src
import {Server} from "@modelcontextprotocol/sdk/server/index.js";
import {StdioServerTransport} from "@modelcontextprotocol/sdk/server/stdio.js";
import {CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError} from "@modelcontextprotocol/sdk/types.js";
import fs from 'fs/promises';
import path from 'path';
import {expandHome, normalizePath} from "./utils/paths.js";
import {handleLocateJavaClass, locateJavaClassTool} from "./functions/locateJavaClass.js";
import {createJavaClass, createJavaClassTool} from "./functions/createJavaClass.js";
import {javaCodebaseRetrieve, javaCodebaseRetrieveTool} from "./functions/javaCodebaseRetrieve.js";
import {classRewriteFullTool, rewriteClassFull} from "./functions/classRewriteFull.js";
import {classAddContent, classAddContentTool} from "./functions/classAddContent.js";
import {classReplaceContent, classReplaceContentTool} from "./functions/classReplaceContent.js";
import {classDeleteContent, classDeleteContentTool} from "./functions/classDeleteContent.js";
// Command line argument parsing
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: mcp-server <project-path> <log-directory>");
process.exit(1);
}
// Store allowed directories in normalized form
const projectPath = normalizePath(path.resolve(expandHome(args[0])));
const logDirectory = normalizePath(path.resolve(expandHome(args[1])));
// Validate that all directories exist and are accessible
await Promise.all([logDirectory].map(async (dir) => {
try {
const stats = await fs.stat(dir);
if (!stats.isDirectory()) {
console.error(`Error: ${dir} is not a directory`);
process.exit(1);
}
} catch (error) {
console.error(`Error accessing directory ${dir}:`, error);
process.exit(1);
}
}));
class TestingServer {
private server: Server;
private readonly logPath: string;
constructor() {
this.logPath = logDirectory;
this.server = new Server({
name: "java-testing-server",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
this.setupHandlers();
}
private setupHandlers(): void {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: "get_test_execution_logs",
description: "Retrieve the test execution logs. Test are meant to run continuously and log their output to a file.",
inputSchema: {
type: "object",
properties: {},
required: []
}
}, locateJavaClassTool,
createJavaClassTool,
javaCodebaseRetrieveTool,
classAddContentTool,
classReplaceContentTool,
classDeleteContentTool,
classRewriteFullTool
]
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "locate_java_class")
return handleLocateJavaClass(projectPath, request.params.arguments)
.then(result => ({content: [{type: "text", text: JSON.stringify(result)}]}));
if (request.params.name === "java_codebase_retrieve")
return javaCodebaseRetrieve(projectPath, request.params.arguments)
.then(result => ({content: [{type: "text", text: JSON.stringify(result)}]}));
if (request.params.name === "create_java_class")
return createJavaClass(projectPath, request.params.arguments)
.then(result => ({content: [{type: "text", text: JSON.stringify(result)}]}));
if (request.params.name === "class_rewrite_full")
return rewriteClassFull(projectPath, request.params.arguments)
.then(result => ({content: [{type: "text", text: result}]}));
if (request.params.name === "class_add_content")
return classAddContent(projectPath, request.params.arguments)
.then(result => ({content: [{type: "text", text: JSON.stringify(result)}]}));
if (request.params.name === "class_replace_content")
return classReplaceContent(projectPath, request.params.arguments)
.then(result => ({content: [{type: "text", text: result}]}));
if (request.params.name === "class_delete_content")
return classDeleteContent(projectPath, request.params.arguments)
.then(result => ({content: [{type: "text", text: result}]}));
if (request.params.name === "get_test_execution_logs") {
try {
const files = await fs.readdir(this.logPath);
const logFiles = files.filter(file => file.endsWith('.log'));
const MAX_LINES_PER_FILE = 100;
const logContents = await Promise.all(
logFiles.map(async file => {
const content = await fs.readFile(path.join(this.logPath, file), 'utf-8');
const lines = content.split('\n');
const truncated = lines.length > MAX_LINES_PER_FILE;
const limitedContent = lines.slice(-MAX_LINES_PER_FILE).join('\n');
return `=== ${file} ${truncated ? `(showing last ${MAX_LINES_PER_FILE} lines of ${lines.length})` : ''} ===\n${limitedContent}\n`;
})
);
return {
content: [{
type: "text",
text: logContents.join('\n')
}]
};
} catch (error) {
if (error instanceof Error) {
throw new McpError(
ErrorCode.InternalError,
`Failed to read log files: ${error.message}`
);
}
throw error;
}
}
throw new McpError(
ErrorCode.InvalidRequest,
`Unknown tool name: ${request.params.name}`
);
});
}
async run(): Promise<void> {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error("Test logs MCP server running on stdio");
}
}
const server = new TestingServer();
server.run().catch(console.error);