Skip to main content
Glama
KazKozDev
by KazKozDev

Undo Last Changes

undo

Reverts recent editing operations in Markdown documents to correct mistakes or restore previous content states.

Instructions

Reverts the last N operations on the document.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_pathYes
countNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
successNo
reverted_countNoNumber of operations reverted

Implementation Reference

  • MCP tool registration for 'undo' including input schema (file_path required, count optional) and output schema.
    Tool(
        name="undo",
        title="Undo Last Changes",
        description="Reverts the last N operations on the document.",
        inputSchema={
            "type": "object",
            "properties": {
                "file_path": {"type": "string", "examples": ["./document.md"]},
                "count": {"type": "integer", "default": 1, "examples": [1, 2, 5]},
            },
            "required": ["file_path"],
            "additionalProperties": False,
        },
        outputSchema={
            "type": "object",
            "properties": {
                "success": {"type": "boolean"},
                "reverted_count": {
                    "type": "integer",
                    "description": "Number of operations reverted",
                },
            },
        },
    ),
  • Dispatch logic in MCP server's call_tool method for the 'undo' tool.
    elif name == "undo":
        res = await undo_changes(file_path, arguments.get("count", 1))
        return CallToolResult(
            content=[TextContent(type="text", text="Undo performed")],
            structuredContent=res,
            isError="error" in res,
        )
  • Exported async handler function 'undo_changes' called by the MCP server for 'undo' tool invocations.
    async def undo_changes(file_path: str, count: int = 1):
        return await _instance.undo(file_path, count)
  • EditTool.undo method: handles file locking, invokes Document.undo, performs atomic file write, and manages cache/journal.
    async def undo(self, file_path: str, count: int = 1) -> Dict[str, Any]:
        abs_path = resolve_path(file_path)
        with FileLock(abs_path):
            doc = self.get_doc(file_path)
            result = doc.undo(count)
            if "success" in result:
                try:
                    self._atomic_write(file_path, doc.get_content())
                    self._update_cache_mtime(abs_path)
                    doc.confirm_journal()
                except Exception as e:
                    self.invalidate_cache(file_path)
                    return {"error": f"Failed to write file: {e}"}
        return result
  • Core Document class undo method: reverses the last N journaled operations (replace, delete, insert, metadata) by popping journal and applying inverse actions.
    def undo(self, count: int = 1) -> Dict[str, Any]:
        """Undo last operations"""
        if not self.journal:
            return {"error": "Journal is empty"}
    
        undone = []
        errors = []
    
        for _ in range(min(count, len(self.journal))):
            entry = self.journal.pop()
            success = False
    
            # Apply reverse operation
            if entry.operation == "replace":
                # Prefer ID-based lookup for reliability
                element = None
                if entry.element_id:
                    element = self.find_by_id(entry.element_id)
                if not element:
                    element = self.find_by_path(entry.path)
    
                if element and entry.old_value is not None:
                    element.content = entry.old_value
                    success = True
                else:
                    errors.append(
                        f"Cannot undo replace: element not found at {entry.path}"
                    )
    
            elif entry.operation == "delete":
                # Restore deleted element at its original position
                success = self._restore_element_at_position(entry)
                if not success:
                    errors.append(
                        f"Cannot undo delete: failed to restore element at {entry.path}"
                    )
    
            elif entry.operation in ("insert_after", "insert_before"):
                # Remove the inserted element
                success = self._remove_inserted_element(entry)
                if not success:
                    errors.append(
                        f"Cannot undo {entry.operation}: element not found at {entry.path}"
                    )
    
            elif entry.operation == "update_metadata":
                # Restore old metadata
                if entry.old_value:
                    try:
                        old_metadata = json.loads(entry.old_value)
                        self.metadata = old_metadata
                        success = True
                    except (json.JSONDecodeError, TypeError):
                        errors.append("Cannot undo update_metadata: invalid old value")
                else:
                    errors.append("Cannot undo update_metadata: no old value stored")
    
            if success:
                undone.append(entry.operation)
    
        self._rebuild_raw_content()
        self._save_journal()
    
        result = {"success": True, "undone_operations": undone}
        if errors:
            result["warnings"] = errors
    
        return result

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/KazKozDev/markdown-editor-mcp-server'

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