dev-mcps/coding-agent-mcp/src/coding-agent-index.js

128 lines
4.4 KiB
JavaScript

#!/usr/bin/env node
import process from "node:process";
import { startHttpToolServer } from "./mcp-http.js";
import { startToolServer } from "./mcp-stdio.js";
import { parseServerArgs } from "./root-args.js";
import { TOOL_DEFINITIONS } from "./tool-definitions.js";
import { COMMAND_TOOL_DEFINITIONS } from "./command-tool-definitions.js";
import { WorkspaceService } from "./workspace-service.js";
import { CommandService } from "./command-service.js";
import { AuditLog } from "./audit-log.js";
const SERVER_NAME = "coding-agent-mcp";
const SERVER_VERSION = "1.0.0";
function parseApiKeys() {
const rawValue = process.env.CODING_AGENT_MCP_API_KEYS ?? process.env.MCP_API_KEYS ?? "";
return rawValue
.split(",")
.map((value) => value.trim())
.filter(Boolean);
}
function parseCorsOrigins() {
return (process.env.CODING_AGENT_MCP_CORS_ORIGINS ?? "")
.split(",")
.map((value) => value.trim())
.filter(Boolean);
}
function resolveExecutionBackend(transport) {
const configured = process.env.CODING_AGENT_MCP_EXECUTION_BACKEND;
if (configured) return configured;
return transport === "stdio" ? "local" : "disabled";
}
async function main() {
const options = parseServerArgs(process.argv.slice(2), {
binaryName: SERVER_NAME,
envVarName: "CODING_AGENT_MCP_ROOTS"
});
const workspaceService = new WorkspaceService({ roots: options.roots });
const auditLog = new AuditLog(process.env.CODING_AGENT_MCP_AUDIT_LOG);
const executionBackend = resolveExecutionBackend(options.transport);
const commandService = new CommandService({
roots: options.roots,
executionBackend
});
// Do not advertise a command tool that this deployment intentionally cannot execute. An agent
// cannot recover from that failure, and seeing it in tools/list makes it waste a turn trying.
const tools = executionBackend === "local"
? [...TOOL_DEFINITIONS, ...COMMAND_TOOL_DEFINITIONS]
: TOOL_DEFINITIONS;
const callTool = async (toolName, toolArguments, context) => {
switch (toolName) {
case "list_files":
return workspaceService.listFiles(toolArguments, context);
case "read_file":
return workspaceService.readFile(toolArguments, context);
case "read_files":
return workspaceService.readFiles(toolArguments, context);
case "search_text":
return workspaceService.searchText(toolArguments, context);
case "write_file":
return workspaceService.writeFile(toolArguments, context);
case "read_binary_file":
return workspaceService.readBinaryFile(toolArguments, context);
case "write_binary_file":
return workspaceService.writeBinaryFile(toolArguments, context);
case "apply_patch":
return workspaceService.applyPatch(toolArguments, context);
case "make_directory":
return workspaceService.makeDirectory(toolArguments, context);
case "delete_path":
return workspaceService.deletePath(toolArguments, context);
case "move_path":
return workspaceService.movePath(toolArguments, context);
case "rename_path":
return workspaceService.renamePath(toolArguments, context);
case "file_info":
return workspaceService.fileInfo(toolArguments, context);
case "run_task":
return commandService.runTask({
...toolArguments,
onEvent: context?.emitEvent,
sessionId: context?.sessionId ?? null,
workspaceSubpath: context?.workspaceSubpath ?? null
});
default:
throw new Error(`Unknown tool: ${toolName}`);
}
};
if (options.transport === "stdio" || options.transport === "both") {
startToolServer({
serverName: SERVER_NAME,
serverVersion: SERVER_VERSION,
tools,
callTool,
auditLog
});
}
if (options.transport === "http" || options.transport === "both") {
const listener = await startHttpToolServer({
serverName: SERVER_NAME,
serverVersion: SERVER_VERSION,
tools,
callTool,
onSessionInitialize: (context) => workspaceService.ensureWorkspace(context),
auditLog,
host: options.host,
port: options.port,
apiKeys: parseApiKeys(),
corsOrigins: parseCorsOrigins(),
requireAuth: true
});
process.stderr.write(
`${SERVER_NAME} HTTP listening on http://${listener.host}:${listener.port}\n`
);
}
}
main().catch((error) => {
process.stderr.write(`${error instanceof Error ? error.stack : String(error)}\n`);
process.exit(1);
});