Skip to main content
Glama

delete_footnote_robust

Remove footnotes from Word documents entirely, including from document.xml, footnotes.xml, and relationships, while automatically cleaning up orphaned references.

Instructions

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

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
clean_orphansNo
filenameYes
footnote_idNo
search_textNo

Implementation Reference

  • MCP tool registration for 'delete_footnote_robust' which wraps the async tool handler
    @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 )
  • Async tool handler function that performs input validation and delegates to core delete_footnote_robust
    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 implementation that handles the actual deletion by parsing and modifying document.xml, footnotes.xml, cleaning relationships and orphans
    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

Other Tools

Related Tools

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