interactive_edit_session
Initiate an interactive editing session to modify complex files using clear instructions. Part of the Edit-MCP server, enabling precise file operations and refactoring.
Instructions
Start an interactive editing session for complex edits
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| files | Yes | List of files to edit | |
| instructions | No | Instructions for the editing session |
Input Schema (JSON Schema)
{
"properties": {
"files": {
"description": "List of files to edit",
"type": "array"
},
"instructions": {
"description": "Instructions for the editing session",
"type": "string"
}
},
"required": [
"files"
],
"type": "object"
}
Implementation Reference
- src/router/operation-router.ts:442-445 (handler)Handler for 'interactive_edit_session' tool: creates an edit session using EditInstanceManager and returns the session ID without closing the session.case 'interactive_edit_session': // Return the session ID for the client to use return { sessionId };
- Core implementation of creating an interactive edit session: spawns EditInstance process, opens specified files, returns sessionId.public async createEditSession(files: string[]): Promise<string> { const sessionId = uuidv4(); const instance = await this.createInstance(sessionId); // Open all files for (const file of files) { await instance.openFile(file); } return sessionId; }
- src/index.ts:306-329 (registration)Registers the 'interactive_edit_session' tool with the MCP server, defining name, description, input schema, and annotations.mcpServer.registerTool({ name: 'interactive_edit_session', description: 'Start an interactive editing session for complex edits', inputSchema: { type: 'object', properties: { files: { type: 'array', description: 'List of files to edit' }, instructions: { type: 'string', description: 'Instructions for the editing session' } }, required: ['files'] }, annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false } });
- src/index.ts:309-322 (schema)Input schema definition for the 'interactive_edit_session' tool: requires array of files, optional instructions.inputSchema: { type: 'object', properties: { files: { type: 'array', description: 'List of files to edit' }, instructions: { type: 'string', description: 'Instructions for the editing session' } }, required: ['files'] },
- EditInstance class: manages individual edit process instance, handles commands like open, edit, save via child process.export class EditInstance extends EventEmitter { private process: ChildProcess; private sessionId: string; private openFiles: Set<string> = new Set(); private activeFile?: string; private running: boolean = false; private outputBuffer: string = ''; private errorBuffer: string = ''; private commandQueue: Array<{ command: string; resolve: (result: string) => void; reject: (error: Error) => void; }> = []; private commandInProgress: boolean = false; constructor(process: ChildProcess, sessionId: string) { super(); this.process = process; this.sessionId = sessionId; this.setupProcessHandlers(); } private setupProcessHandlers(): void { this.process.stdout?.on('data', (data: Buffer) => { const output = data.toString(); this.outputBuffer += output; this.emit('stdout', output); this.checkCommandCompletion(); }); this.process.stderr?.on('data', (data: Buffer) => { const error = data.toString(); this.errorBuffer += error; this.emit('stderr', error); }); this.process.on('exit', (code: number | null) => { this.running = false; this.emit('exit', code); // Reject any pending commands while (this.commandQueue.length > 0) { const command = this.commandQueue.shift(); if (command) { command.reject(new Error(`Edit process exited with code ${code}`)); } } }); this.running = true; } private checkCommandCompletion(): void { if (this.commandQueue.length === 0 || !this.commandInProgress) { return; } // Check if the command has completed // This is a simplified approach; in a real implementation, we would need // to look for specific markers in the output to determine completion if (this.outputBuffer.includes('Command completed')) { const command = this.commandQueue.shift(); if (command) { this.commandInProgress = false; command.resolve(this.outputBuffer); this.outputBuffer = ''; this.errorBuffer = ''; this.processNextCommand(); } } } private processNextCommand(): void { if (this.commandQueue.length === 0 || this.commandInProgress) { return; } const command = this.commandQueue[0]; this.commandInProgress = true; this.process.stdin?.write(command.command + '\n'); } public async openFile(filePath: string): Promise<void> { await this.executeCommand(`open ${filePath}`); this.openFiles.add(filePath); this.activeFile = filePath; } public async closeFile(filePath: string): Promise<void> { await this.executeCommand(`close ${filePath}`); this.openFiles.delete(filePath); if (this.activeFile === filePath) { this.activeFile = undefined; } } public async executeCommand(command: string): Promise<string> { return new Promise<string>((resolve, reject) => { if (!this.running) { reject(new Error('Edit process is not running')); return; } this.commandQueue.push({ command, resolve, reject }); if (!this.commandInProgress) { this.processNextCommand(); } }); } public async getState(): Promise<EditInstanceState> { return { sessionId: this.sessionId, openFiles: Array.from(this.openFiles), activeFile: this.activeFile, running: this.running }; } public async terminate(): Promise<void> { if (!this.running) { return; } // Try to gracefully exit try { await this.executeCommand('exit'); } catch (error) { // Ignore errors } // Force kill if still running if (this.running) { this.process.kill(); } } }