Skip to main content
Glama

amend_document

Create amended versions of cancelled documents in Frappe by generating new documents with updated naming and proper field linkage.

Instructions

    Amend a document in Frappe (create a new amended version of a cancelled document).
    
    This tool handles document amendment by creating a new document with an amended name
    (e.g., DOC-001-1, DOC-001-2) and copying all relevant field values from the original
    cancelled document, establishing proper linkage via the 'amended_from' field.
    
    Args:
        doctype: DocType name
        name: Document name (case-sensitive) - must be a cancelled document
        
    Returns:
        Success message with new amended document name if successful, or detailed 
        error information if amendment fails due to validation errors or constraints.
    

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
doctypeYes
nameYes

Implementation Reference

  • The main handler function for the 'amend_document' tool. It checks if the document is cancelled (docstatus=2), generates a new amended document name (e.g., DOC-001-1), copies fields from the original excluding system fields, sets 'amended_from', clears child names, and creates the new draft document via Frappe API.
    @mcp.tool()
    async def amend_document(
        doctype: str,
        name: str
    ) -> str:
        """
        Amend a document in Frappe (create a new amended version of a cancelled document).
        
        This tool handles document amendment by creating a new document with an amended name
        (e.g., DOC-001-1, DOC-001-2) and copying all relevant field values from the original
        cancelled document, establishing proper linkage via the 'amended_from' field.
        
        Args:
            doctype: DocType name
            name: Document name (case-sensitive) - must be a cancelled document
            
        Returns:
            Success message with new amended document name if successful, or detailed 
            error information if amendment fails due to validation errors or constraints.
        """
        try:
            client = get_client()
            
            # First, get the current document to check its status and get full data
            try:
                doc_response = await client.get(f"api/resource/{doctype}/{name}")
                doc_data = doc_response.get("data", {})
                current_docstatus = doc_data.get("docstatus", None)
                
                if current_docstatus is None:
                    return f"Error: Could not retrieve document {doctype} '{name}'. Document may not exist."
                    
                if current_docstatus == 0:
                    return f"Document {doctype} '{name}' is in Draft status. Only cancelled documents (docstatus=2) can be amended."
                    
                if current_docstatus == 1:
                    return f"Document {doctype} '{name}' is submitted. You must cancel it first before amending."
                    
                if current_docstatus != 2:
                    return f"Document {doctype} '{name}' has unexpected status (docstatus={current_docstatus}). Only cancelled documents (docstatus=2) can be amended."
                    
            except Exception as get_error:
                return f"Error retrieving document for amendment: {get_error}"
            
            # Generate amended document name
            base_name = name
            amended_counter = 1
            
            # Check if this document is already an amendment (contains dash and number)
            if '-' in name:
                parts = name.rsplit('-', 1)
                if len(parts) == 2 and parts[1].isdigit():
                    base_name = parts[0]
                    amended_counter = int(parts[1]) + 1
            
            # Find the next available amended name
            amended_name = f"{base_name}-{amended_counter}"
            while True:
                try:
                    # Check if amended name already exists
                    check_response = await client.get(f"api/resource/{doctype}/{amended_name}")
                    if "data" in check_response:
                        # Name exists, try next number
                        amended_counter += 1
                        amended_name = f"{base_name}-{amended_counter}"
                    else:
                        # Name doesn't exist, we can use it
                        break
                except FrappeApiError as e:
                    # If we get 404, the name doesn't exist and we can use it
                    if e.status_code == 404:
                        break
                    else:
                        # Some other error, we should handle it
                        raise e
            
            # Prepare amended document data
            amended_doc = doc_data.copy()
            
            # Clear system fields that should not be copied
            system_fields = [
                'name', 'creation', 'modified', 'modified_by', 'owner', 
                'docstatus', 'idx', '_user_tags', '_comments', '_assign', '_liked_by'
            ]
            for field in system_fields:
                amended_doc.pop(field, None)
            
            # Set amendment fields
            amended_doc['name'] = amended_name
            amended_doc['amended_from'] = name
            amended_doc['docstatus'] = 0  # New document starts as draft
            
            # Clear any child table names to let Frappe generate new ones
            for key, value in amended_doc.items():
                if isinstance(value, list):
                    for item in value:
                        if isinstance(item, dict):
                            item.pop('name', None)  # Clear child table row names
                            item['parent'] = amended_name  # Update parent reference
            
            # Create the amended document
            response = await client.post(
                f"api/resource/{doctype}",
                json_data=amended_doc
            )
            
            if "data" in response:
                created_doc = response["data"]
                created_name = created_doc.get('name', amended_name)
                return f"✅ Document successfully amended: {doctype} '{created_name}' created from cancelled document '{name}'. The amended document is in Draft status and ready for editing."
            else:
                return f"⚠️ Amendment may have succeeded but response format unexpected: {json.dumps(response, indent=2)}"
                
        except FrappeApiError as api_error:
            # Handle specific Frappe API errors with detailed information
            if api_error.response_data:
                error_data = api_error.response_data
                
                # Check for validation errors in the response
                if "exception" in error_data:
                    exception_msg = error_data["exception"]
                    
                    # Extract user-friendly error messages
                    if "ValidationError" in str(exception_msg):
                        # Common amendment validation errors
                        if "amended_from" in str(exception_msg).lower():
                            return (
                                f"❌ Amendment failed: {doctype} does not have an 'amended_from' field configured. "
                                f"This DocType may not support amendments. Contact your system administrator to enable amendment functionality."
                            )
                        elif "DuplicateEntryError" in str(exception_msg) or "duplicate" in str(exception_msg).lower():
                            return f"❌ Amendment failed: Document name conflict. The amended name may already exist. Please try again."
                        else:
                            # Generic validation error
                            return f"❌ Validation error: {exception_msg}. Please fix the validation issues before amending."
                    
                    elif "PermissionError" in str(exception_msg):
                        return f"❌ Permission denied: You don't have sufficient permissions to amend {doctype} documents."
                    
                    else:
                        # Other exceptions
                        return f"❌ Amendment failed: {exception_msg}"
                
                # Check for server messages with more details
                if "_server_messages" in error_data:
                    try:
                        messages = json.loads(error_data["_server_messages"])
                        if messages:
                            msg_data = json.loads(messages[0])
                            user_message = msg_data.get("message", "Unknown error")
                            return f"❌ Amendment failed: {user_message}"
                    except (json.JSONDecodeError, KeyError, IndexError):
                        pass
            
            return f"❌ Amendment failed: {api_error}"
            
        except Exception as error:
            return _format_error_response(error, "amend_document")
  • src/server.py:40-40 (registration)
    Invocation of documents.register_tools(mcp), which registers the amend_document tool (and other document tools) with the MCP server instance.
    documents.register_tools(mcp)

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/appliedrelevance/frappe-mcp-server'

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