cursor-background-agent-mcp-server
Version:
MCP Server for Cursor Background Agents API - run autonomous coding agents from any MCP client
223 lines • 8.17 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.MCPStdioServer = void 0;
const process_1 = require("process");
const cursorApiClient_1 = require("./infrastructure/cursorApiClient");
const toolExecutionService_1 = require("./application/toolExecutionService");
const toolInvocationController_1 = require("./presentation/toolInvocationController");
const manifestController_1 = require("./presentation/manifestController");
const toolInvocationController_2 = require("./presentation/toolInvocationController");
const resourceAccessController_1 = require("./presentation/resourceAccessController");
const errors_1 = require("./domain/errors");
class MCPStdioServer {
constructor() {
this.initialized = false;
this.setupDependencies();
this.setupStdio();
}
setupDependencies() {
const apiKey = process.env.CURSOR_API_KEY;
if (!apiKey) {
this.writeError('CURSOR_API_KEY environment variable is required');
process.exit(1);
}
const cursorClient = new cursorApiClient_1.HttpCursorApiClient(apiKey);
const toolExecutionService = new toolExecutionService_1.CursorToolExecutionService(cursorClient);
(0, toolInvocationController_1.setToolExecutor)(toolExecutionService);
}
setupStdio() {
let buffer = '';
process_1.stdin.setEncoding('utf8');
process_1.stdin.on('data', (chunk) => {
buffer += chunk;
const messages = buffer.split('\n');
buffer = messages.pop() || '';
messages.forEach(message => {
if (message.trim()) {
this.handleMessage(message.trim());
}
});
});
process_1.stdin.on('end', () => {
process.exit(0);
});
process.on('SIGTERM', () => {
process.exit(0);
});
process.on('SIGINT', () => {
process.exit(0);
});
}
async handleMessage(message) {
try {
const request = JSON.parse(message);
if (!request.jsonrpc || request.jsonrpc !== '2.0') {
this.sendError(request.id, -32600, 'Invalid Request', 'Invalid JSON-RPC version');
return;
}
await this.processRequest(request);
}
catch (error) {
this.sendError(undefined, -32700, 'Parse error', 'Invalid JSON');
}
}
async processRequest(request) {
try {
switch (request.method) {
case 'initialize':
await this.handleInitialize(request);
break;
case 'initialized':
this.handleInitialized(request);
break;
case 'tools/list':
await this.handleToolsList(request);
break;
case 'tools/call':
await this.handleToolCall(request);
break;
case 'resources/list':
await this.handleResourcesList(request);
break;
case 'resources/read':
await this.handleResourceRead(request);
break;
case 'ping':
this.sendResponse(request.id, {});
break;
default:
this.sendError(request.id, -32601, 'Method not found', `Unknown method: ${request.method}`);
}
}
catch (error) {
const errorResponse = (0, errors_1.createMCPErrorResponse)(error);
this.sendError(request.id, -32603, 'Internal error', errorResponse.error);
}
}
async handleInitialize(request) {
const { params } = request;
this.sendResponse(request.id, {
protocolVersion: '2024-11-05',
capabilities: {
tools: {},
resources: {}
},
serverInfo: {
name: 'cursor-background-agents-mcp',
version: '1.0.0'
}
});
}
handleInitialized(request) {
this.initialized = true;
}
async handleToolsList(request) {
try {
const manifest = (await (0, manifestController_1.getManifest)());
const tools = manifest.tools.map(tool => ({
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema
}));
this.sendResponse(request.id, { tools });
}
catch (error) {
const errorResponse = (0, errors_1.createMCPErrorResponse)(error);
this.sendError(request.id, -32603, 'Failed to list tools', errorResponse.error);
}
}
async handleToolCall(request) {
try {
const { name, arguments: args } = request.params;
if (!name || typeof name !== 'string') {
this.sendError(request.id, -32602, 'Invalid params', 'Tool name is required');
return;
}
const result = await (0, toolInvocationController_2.invokeTool)(name, args || {});
this.sendResponse(request.id, {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2)
}
]
});
}
catch (error) {
const errorResponse = (0, errors_1.createMCPErrorResponse)(error);
this.sendError(request.id, -32603, 'Tool execution failed', errorResponse.error);
}
}
async handleResourcesList(request) {
try {
const manifest = (await (0, manifestController_1.getManifest)());
const resources = manifest.resources.map(resource => ({
uri: `cursor://${resource.name}`,
name: resource.name,
description: resource.description,
mimeType: 'application/json'
}));
this.sendResponse(request.id, { resources });
}
catch (error) {
const errorResponse = (0, errors_1.createMCPErrorResponse)(error);
this.sendError(request.id, -32603, 'Failed to list resources', errorResponse.error);
}
}
async handleResourceRead(request) {
try {
const { uri } = request.params;
if (!uri || typeof uri !== 'string') {
this.sendError(request.id, -32602, 'Invalid params', 'Resource URI is required');
return;
}
const resourceName = uri.replace('cursor://', '');
const result = await (0, resourceAccessController_1.accessResource)(resourceName);
this.sendResponse(request.id, {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(result, null, 2)
}
]
});
}
catch (error) {
const errorResponse = (0, errors_1.createMCPErrorResponse)(error);
this.sendError(request.id, -32603, 'Failed to read resource', errorResponse.error);
}
}
sendResponse(id, result) {
const response = {
jsonrpc: '2.0',
id,
result
};
this.writeMessage(JSON.stringify(response));
}
sendError(id, code, message, data) {
const response = {
jsonrpc: '2.0',
id,
error: {
code,
message,
data
}
};
this.writeMessage(JSON.stringify(response));
}
writeMessage(message) {
process_1.stdout.write(message + '\n');
}
writeError(message) {
process_1.stderr.write(`Error: ${message}\n`);
}
}
exports.MCPStdioServer = MCPStdioServer;
if (require.main === module) {
new MCPStdioServer();
}
//# sourceMappingURL=mcpStdioServer.js.map
;