Skip to main content
Glama
GongRzhe

Office Word MCP Server

delete_footnote_from_document

Remove footnotes from Word documents by specifying the footnote ID or searching for nearby text. This tool helps clean up document formatting and eliminate unwanted references.

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
NameRequiredDescriptionDefault
filenameYes
footnote_idNo
search_textNo
output_filenameNo

Implementation Reference

  • Registers the MCP tool 'delete_footnote_from_document' using FastMCP decorator and delegates to the implementation in footnote_tools.
    @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
        )
  • Direct handler called by the registered tool. Performs file checks and invokes the core delete_footnote_robust implementation from core/footnotes.
    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 handler function that performs the actual deletion by parsing and modifying the DOCX XML parts (document.xml and footnotes.xml), removing references and content, and optionally cleaning orphan footnotes.
    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