delete_footnote_from_document
Remove footnotes from a Word document by specifying the footnote ID or searching for nearby text. Automate footnote deletion for cleaner document formatting.
Instructions
Delete a footnote from a Word document. Identify the footnote either by ID (1, 2, 3, etc.) or by searching for text near it.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filename | Yes | ||
| footnote_id | No | ||
| output_filename | No | ||
| search_text | No |
Implementation Reference
- word_document_server/main.py:337-344 (registration)MCP tool registration and handler for 'delete_footnote_from_document'. This decorated function serves as the entry point for the tool, delegating execution to the footnote_tools module.@mcp.tool() def delete_footnote_from_document(filename: str, footnote_id: int = None, search_text: str = None, output_filename: str = None): """Delete a footnote from a Word document. Identify the footnote either by ID (1, 2, 3, etc.) or by searching for text near it.""" return footnote_tools.delete_footnote_from_document( filename, footnote_id, search_text, output_filename )
- Intermediate handler in footnote_tools that performs basic validation and delegates to the core delete_footnote_robust implementation in core/footnotes.py.async def delete_footnote_from_document(filename: str, footnote_id: Optional[int] = None, search_text: Optional[str] = None, output_filename: Optional[str] = None) -> str: """Delete a footnote from a Word document. You can identify the footnote to delete either by: 1. footnote_id: The numeric ID of the footnote (1, 2, 3, etc.) 2. search_text: Text near the footnote reference to find and delete Args: filename: Path to the Word document footnote_id: Optional ID of the footnote to delete (1-based) search_text: Optional text to search near the footnote reference output_filename: Optional output filename (if None, modifies in place) """ filename = ensure_docx_extension(filename) if not os.path.exists(filename): return f"Document {filename} does not exist" # Check if file is writeable is_writeable, error_message = check_file_writeable(filename) if not is_writeable: return f"Cannot modify document: {error_message}. Consider creating a copy first." try: # Use robust implementation with orphan cleanup success, message, details = delete_footnote_robust( filename=filename, footnote_id=footnote_id, search_text=search_text, output_filename=output_filename, clean_orphans=True ) return message except Exception as e: return f"Failed to delete footnote: {str(e)}"
- Core implementation of footnote deletion. Directly manipulates the Word document's XML structure (document.xml and footnotes.xml) within the ZIP archive, removes references and content, optionally cleans orphans, and rewrites the file.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