Skip to main content
Glama

edit_document

Make line-based edits to markdown documents by replacing specific text sequences, generating git-style diffs to track changes.

Instructions

Make line-based edits to a markdown document. Each edit replaces exact line sequences with new content. Returns a git-style diff showing the changes made.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathYes
editsYes
dryRunNo

Implementation Reference

  • The core handler function in DocumentHandler class that reads a document, applies sequential text replacements (with fuzzy line matching), generates a unified diff, and optionally writes changes back. Supports dry-run mode.
    async editDocument( docPath: string, edits: Array<{ oldText: string; newText: string }>, dryRun = false ): Promise<ToolResponse> { try { const validPath = await this.validatePath(docPath); // Read file content and normalize line endings const content = normalizeLineEndings( await fs.readFile(validPath, "utf-8") ); // Apply edits sequentially let modifiedContent = content; for (const edit of edits) { const normalizedOld = normalizeLineEndings(edit.oldText); const normalizedNew = normalizeLineEndings(edit.newText); // If exact match exists, use it if (modifiedContent.includes(normalizedOld)) { modifiedContent = modifiedContent.replace( normalizedOld, normalizedNew ); continue; } // Otherwise, try line-by-line matching with flexibility for whitespace const oldLines = normalizedOld.split("\n"); const contentLines = modifiedContent.split("\n"); let matchFound = false; for (let i = 0; i <= contentLines.length - oldLines.length; i++) { const potentialMatch = contentLines.slice(i, i + oldLines.length); // Compare lines with normalized whitespace const isMatch = oldLines.every((oldLine, j) => { const contentLine = potentialMatch[j]; return oldLine.trim() === contentLine.trim(); }); if (isMatch) { // Preserve original indentation of first line const originalIndent = contentLines[i].match(/^\s*/)?.[0] || ""; const newLines = normalizedNew.split("\n").map((line, j) => { if (j === 0) return originalIndent + line.trimStart(); // For subsequent lines, try to preserve relative indentation const oldIndent = oldLines[j]?.match(/^\s*/)?.[0] || ""; const newIndent = line.match(/^\s*/)?.[0] || ""; if (oldIndent && newIndent) { const relativeIndent = newIndent.length - oldIndent.length; return ( originalIndent + " ".repeat(Math.max(0, relativeIndent)) + line.trimStart() ); } return line; }); contentLines.splice(i, oldLines.length, ...newLines); modifiedContent = contentLines.join("\n"); matchFound = true; break; } } if (!matchFound) { throw new Error( `Could not find exact match for edit:\n${edit.oldText}` ); } } // Create unified diff const diff = createUnifiedDiff(content, modifiedContent, docPath); // Format diff with appropriate number of backticks let numBackticks = 3; while (diff.includes("`".repeat(numBackticks))) { numBackticks++; } const formattedDiff = `${"`".repeat( numBackticks )}diff\n${diff}${"`".repeat(numBackticks)}\n\n`; if (!dryRun) { await fs.writeFile(validPath, modifiedContent, "utf-8"); } return { content: [{ type: "text", text: formattedDiff }], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error editing document: ${errorMessage}` }, ], isError: true, }; } }
  • Zod schema defining the input for edit_document tool: path to document, array of edits (oldText/newText pairs), optional dryRun flag.
    export const EditDocumentSchema = ToolInputSchema.extend({ path: z.string(), edits: z.array( z.object({ oldText: z.string(), newText: z.string(), }) ), dryRun: z.boolean().default(false), });
  • src/index.ts:216-222 (registration)
    Tool registration in the MCP server's listTools response, defining name, description, and input schema.
    description: "Make line-based edits to a markdown document. Each edit replaces exact line sequences " + "with new content. Returns a git-style diff showing the changes made.", inputSchema: zodToJsonSchema(EditDocumentSchema) as any, }, { name: "list_documents",
  • src/index.ts:335-347 (registration)
    Dispatch logic in the main CallToolRequest handler that parses input with EditDocumentSchema and delegates to documentHandler.editDocument.
    case "edit_document": { const parsed = EditDocumentSchema.safeParse(args); if (!parsed.success) { throw new Error( `Invalid arguments for edit_document: ${parsed.error}` ); } return await documentHandler.editDocument( parsed.data.path, parsed.data.edits, parsed.data.dryRun ); }
  • Helper function to generate unified diff patch used in editDocument response.
    function createUnifiedDiff( originalContent: string, newContent: string, filepath: string = "file" ): string { // Ensure consistent line endings for diff const normalizedOriginal = normalizeLineEndings(originalContent); const normalizedNew = normalizeLineEndings(newContent); return createTwoFilesPatch( filepath, filepath, normalizedOriginal, normalizedNew, "original", "modified" ); }

Latest Blog Posts

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/alekspetrov/mcp-docs-service'

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