Skip to main content
Glama
GongRzhe

Office Word MCP Server

delete_footnote_robust

Remove footnotes from Word documents while cleaning up references and orphaned elements to maintain document integrity.

Instructions

Delete footnote with comprehensive cleanup and orphan removal. Ensures complete removal from document.xml, footnotes.xml, and relationships.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filenameYes
footnote_idNo
search_textNo
clean_orphansNo

Implementation Reference

  • MCP tool registration for 'delete_footnote_robust'. This decorated function registers the tool and serves as the entry point, delegating to the async handler in footnote_tools.
    @mcp.tool()
    def delete_footnote_robust(filename: str, footnote_id: int = None,
                              search_text: str = None, clean_orphans: bool = True):
        """Delete footnote with comprehensive cleanup and orphan removal.
        Ensures complete removal from document.xml, footnotes.xml, and relationships."""
        return footnote_tools.delete_footnote_robust_tool(
            filename, footnote_id, search_text, clean_orphans
        )
  • Primary async handler function for the delete_footnote_robust tool. Handles input validation, file checks, and delegates to core implementation.
    async def delete_footnote_robust_tool(
        filename: str,
        footnote_id: Optional[int] = None,
        search_text: Optional[str] = None,
        clean_orphans: bool = True
    ) -> Dict[str, Any]:
        """
        Delete a footnote with comprehensive cleanup.
        
        Args:
            filename: Path to the Word document
            footnote_id: ID of footnote to delete
            search_text: Text near footnote reference
            clean_orphans: Whether to remove orphaned content
        
        Returns:
            Dict with success status, message, and optional details
        """
        filename = ensure_docx_extension(filename)
        
        # Check if file is writeable
        is_writeable, error_message = check_file_writeable(filename)
        if not is_writeable:
            return {
                "success": False,
                "message": f"Cannot modify document: {error_message}",
                "details": None
            }
        
        # Convert footnote_id if provided as string
        if footnote_id is not None:
            try:
                footnote_id = int(footnote_id)
            except (ValueError, TypeError):
                return {
                    "success": False,
                    "message": "Invalid parameter: footnote_id must be an integer",
                    "details": None
                }
        
        # Call robust implementation
        success, message, details = delete_footnote_robust(
            filename=filename,
            footnote_id=footnote_id,
            search_text=search_text,
            clean_orphans=clean_orphans
        )
        
        return {
            "success": success,
            "message": message,
            "details": details
        }
  • Core helper function implementing the actual DOCX XML manipulation: removes footnote references, content, cleans orphans, and rewrites the ZIP archive.
    def delete_footnote_robust(
        filename: str,
        footnote_id: Optional[int] = None,
        search_text: Optional[str] = None,
        output_filename: Optional[str] = None,
        clean_orphans: bool = True
    ) -> Tuple[bool, str, Optional[Dict[str, Any]]]:
        """Delete a footnote with comprehensive cleanup."""
        
        if not footnote_id and not search_text:
            return False, "Must provide either footnote_id or search_text", None
        
        if not os.path.exists(filename):
            return False, f"File not found: {filename}", None
        
        # Set working file
        working_file = output_filename if output_filename else filename
        if output_filename and filename != output_filename:
            import shutil
            shutil.copy2(filename, output_filename)
        
        try:
            # Read document parts
            with zipfile.ZipFile(filename, 'r') as zin:
                doc_xml = zin.read('word/document.xml')
                
                if 'word/footnotes.xml' not in zin.namelist():
                    return False, "No footnotes in document", None
                
                footnotes_xml = zin.read('word/footnotes.xml')
            
            # Parse documents
            doc_root = etree.fromstring(doc_xml)
            footnotes_root = etree.fromstring(footnotes_xml)
            nsmap = {'w': W_NS}
            
            # Find footnote to delete
            if search_text:
                # Find footnote reference near text
                for para in doc_root.xpath('//w:p', namespaces=nsmap):
                    para_text = ''.join(para.xpath('.//w:t/text()', namespaces=nsmap))
                    if search_text in para_text:
                        # Look for footnote reference in this paragraph
                        fn_refs = para.xpath('.//w:footnoteReference', namespaces=nsmap)
                        if fn_refs:
                            footnote_id = int(fn_refs[0].get(f'{{{W_NS}}}id'))
                            break
                
                if not footnote_id:
                    return False, f"No footnote found near text '{search_text}'", None
            
            # Remove footnote reference from document
            refs_removed = 0
            for fn_ref in doc_root.xpath(f'//w:footnoteReference[@w:id="{footnote_id}"]', namespaces=nsmap):
                # Remove the entire run containing the reference
                run = fn_ref.getparent()
                if run is not None and run.tag == f'{{{W_NS}}}r':
                    para = run.getparent()
                    if para is not None:
                        para.remove(run)
                        refs_removed += 1
            
            if refs_removed == 0:
                return False, f"Footnote {footnote_id} not found", None
            
            # Remove footnote content
            content_removed = 0
            for fn in footnotes_root.xpath(f'//w:footnote[@w:id="{footnote_id}"]', namespaces=nsmap):
                footnotes_root.remove(fn)
                content_removed += 1
            
            # Clean orphans if requested
            orphans_removed = []
            if clean_orphans:
                # Find all referenced IDs
                referenced_ids = set()
                for ref in doc_root.xpath('//w:footnoteReference', namespaces=nsmap):
                    ref_id = ref.get(f'{{{W_NS}}}id')
                    if ref_id:
                        referenced_ids.add(ref_id)
                
                # Remove unreferenced footnotes (except separators)
                for fn in footnotes_root.xpath('//w:footnote', namespaces=nsmap):
                    fn_id = fn.get(f'{{{W_NS}}}id')
                    if fn_id and fn_id not in referenced_ids and fn_id not in ['-1', '0']:
                        footnotes_root.remove(fn)
                        orphans_removed.append(fn_id)
            
            # Write modified document
            temp_file = working_file + '.tmp'
            with zipfile.ZipFile(temp_file, 'w', zipfile.ZIP_DEFLATED) as zout:
                with zipfile.ZipFile(filename, 'r') as zin:
                    for item in zin.infolist():
                        if item.filename == 'word/document.xml':
                            zout.writestr(item,
                                etree.tostring(doc_root, encoding='UTF-8', xml_declaration=True, standalone="yes"))
                        elif item.filename == 'word/footnotes.xml':
                            zout.writestr(item,
                                etree.tostring(footnotes_root, encoding='UTF-8', xml_declaration=True, standalone="yes"))
                        else:
                            zout.writestr(item, zin.read(item.filename))
            
            os.replace(temp_file, working_file)
            
            details = {
                'footnote_id': footnote_id,
                'references_removed': refs_removed,
                'content_removed': content_removed,
                'orphans_removed': orphans_removed
            }
            
            message = f"Successfully deleted footnote {footnote_id}"
            if orphans_removed:
                message += f" and {len(orphans_removed)} orphaned footnotes"
            
            return True, message, details
            
        except Exception as e:
            return False, f"Error deleting footnote: {str(e)}", None

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/GongRzhe/Office-Word-MCP-Server'

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