Skip to main content
Glama
edit-tools.ts10.4 kB
import * as vscode from 'vscode'; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from 'zod'; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; /** * Creates a new file in the VS Code workspace using WorkspaceEdit * @param workspacePath The path within the workspace to the file * @param content The content to write to the file * @param overwrite Whether to overwrite if the file exists * @param ignoreIfExists Whether to ignore if the file exists * @returns Promise that resolves when the edit operation completes */ export async function createWorkspaceFile( workspacePath: string, content: string, overwrite: boolean = false, ignoreIfExists: boolean = false ): Promise<void> { console.log(`[createWorkspaceFile] Starting with path: ${workspacePath}, overwrite: ${overwrite}, ignoreIfExists: ${ignoreIfExists}`); if (!vscode.workspace.workspaceFolders) { throw new Error('No workspace folder is open'); } const workspaceFolder = vscode.workspace.workspaceFolders[0]; const workspaceUri = workspaceFolder.uri; // Create URI for the target file const fileUri = vscode.Uri.joinPath(workspaceUri, workspacePath); console.log(`[createWorkspaceFile] File URI: ${fileUri.fsPath}`); try { // Create a WorkspaceEdit const workspaceEdit = new vscode.WorkspaceEdit(); // Convert content to Uint8Array const contentBuffer = new TextEncoder().encode(content); // Add createFile operation to the edit workspaceEdit.createFile(fileUri, { contents: contentBuffer, overwrite: overwrite, ignoreIfExists: ignoreIfExists }); // Apply the edit const success = await vscode.workspace.applyEdit(workspaceEdit); if (success) { console.log(`[createWorkspaceFile] File created successfully: ${fileUri.fsPath}`); // Open the document to trigger linting const document = await vscode.workspace.openTextDocument(fileUri); await vscode.window.showTextDocument(document); console.log(`[createWorkspaceFile] File opened in editor`); } else { throw new Error(`Failed to create file: ${fileUri.fsPath}`); } } catch (error) { console.error('[createWorkspaceFile] Error:', error); throw error; } } /** * Replaces specific lines in a file in the VS Code workspace * @param workspacePath The path within the workspace to the file * @param startLine The start line number (0-based, inclusive) * @param endLine The end line number (0-based, inclusive) * @param content The new content to replace the lines with * @param originalCode The original code for validation * @returns Promise that resolves when the edit operation completes */ export async function replaceWorkspaceFileLines( workspacePath: string, startLine: number, endLine: number, content: string, originalCode: string ): Promise<void> { console.log(`[replaceWorkspaceFileLines] Starting with path: ${workspacePath}, lines: ${startLine}-${endLine}`); if (!vscode.workspace.workspaceFolders) { throw new Error('No workspace folder is open'); } const workspaceFolder = vscode.workspace.workspaceFolders[0]; const workspaceUri = workspaceFolder.uri; // Create URI for the target file const fileUri = vscode.Uri.joinPath(workspaceUri, workspacePath); console.log(`[replaceWorkspaceFileLines] File URI: ${fileUri.fsPath}`); try { // Open the document (or get it if already open) const document = await vscode.workspace.openTextDocument(fileUri); // Validate line numbers if (startLine < 0 || startLine >= document.lineCount) { throw new Error(`Start line ${startLine + 1} is out of range (1-${document.lineCount})`); } if (endLine < startLine || endLine >= document.lineCount) { throw new Error(`End line ${endLine + 1} is out of range (${startLine + 1}-${document.lineCount})`); } // Get the current content of the lines const currentLines = []; for (let i = startLine; i <= endLine; i++) { currentLines.push(document.lineAt(i).text); } const currentContent = currentLines.join('\n'); // Compare with the provided original code if (currentContent !== originalCode) { throw new Error(`Original code validation failed. The current content does not match the provided original code.`); } // Create a range for the lines to replace const startPos = new vscode.Position(startLine, 0); const endPos = new vscode.Position(endLine, document.lineAt(endLine).text.length); const range = new vscode.Range(startPos, endPos); // Get the active text editor or show the document let editor = vscode.window.activeTextEditor; if (!editor || editor.document.uri.toString() !== fileUri.toString()) { editor = await vscode.window.showTextDocument(document); } // Apply the edit const success = await editor.edit((editBuilder) => { editBuilder.replace(range, content); }); if (success) { console.log(`[replaceWorkspaceFileLines] Lines replaced successfully`); // Save the document to persist changes await document.save(); console.log(`[replaceWorkspaceFileLines] Document saved`); } else { throw new Error(`Failed to replace lines in file: ${fileUri.fsPath}`); } } catch (error) { console.error('[replaceWorkspaceFileLines] Error:', error); throw error; } } /** * Registers MCP edit-related tools with the server * @param server MCP server instance */ export function registerEditTools(server: McpServer): void { // Add create_file tool server.tool( 'create_file_code', `Creates new files or completely rewrites existing files. WHEN TO USE: New files, large modifications (>10 lines), complete file rewrites. USE replace_lines_code instead for: small edits ≤10 lines where you have exact original content. File handling: Use overwrite=true to replace existing files, ignoreIfExists=true to skip if file exists. Always check with list_files_code first unless you specifically want to overwrite.`, { path: z.string().describe('The path to the file to create'), content: z.string().describe('The content to write to the file'), overwrite: z.boolean().optional().default(false).describe('Whether to overwrite if the file exists'), ignoreIfExists: z.boolean().optional().default(false).describe('Whether to ignore if the file exists') }, async ({ path, content, overwrite = false, ignoreIfExists = false }): Promise<CallToolResult> => { console.log(`[create_file] Tool called with path=${path}, overwrite=${overwrite}, ignoreIfExists=${ignoreIfExists}`); try { console.log('[create_file] Creating file'); await createWorkspaceFile(path, content, overwrite, ignoreIfExists); const result: CallToolResult = { content: [ { type: 'text', text: `File ${path} created successfully` } ] }; console.log('[create_file] Successfully completed'); return result; } catch (error) { console.error('[create_file] Error in tool:', error); throw error; } } ); // Add replace_lines_code tool server.tool( 'replace_lines_code', `Replaces specific lines in existing files with exact content validation. WHEN TO USE: Modifications ≤10 lines where you have exact original text, or inserts of any size. USE create_file_code instead for: new files, large modifications (>10 lines, hard to match exact content), or when original text is uncertain. CRITICAL: originalCode parameter must match current file content exactly or tool fails. If tool fails: run read_file_code on target lines to get current content, then retry. Parameters use 1-based line numbers. Always verify line numbers with read_file_code if unsure.`, { path: z.string().describe('The path to the file to modify'), startLine: z.number().describe('The start line number (1-based, inclusive)'), endLine: z.number().describe('The end line number (1-based, inclusive)'), content: z.string().describe('The new content to replace the lines with'), originalCode: z.string().describe('The original code for validation - must match exactly') }, async ({ path, startLine, endLine, content, originalCode }): Promise<CallToolResult> => { console.log(`[replace_lines_code] Tool called with path=${path}, startLine=${startLine}, endLine=${endLine}`); // Convert 1-based input to 0-based for VS Code API const zeroBasedStartLine = startLine - 1; const zeroBasedEndLine = endLine - 1; try { console.log('[replace_lines_code] Replacing lines'); await replaceWorkspaceFileLines(path, zeroBasedStartLine, zeroBasedEndLine, content, originalCode); const result: CallToolResult = { content: [ { type: 'text', text: `Lines ${startLine}-${endLine} in file ${path} replaced successfully` } ] }; console.log('[replace_lines_code] Successfully completed'); return result; } catch (error) { console.error('[replace_lines_code] Error in tool:', error); throw error; } } ); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/juehang/vscode-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server