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
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It states this is a deletion operation but doesn't mention whether this is destructive/permanent, whether it modifies the original file or creates a new one, what permissions are required, or what happens on success/failure. For a mutation tool with zero annotation coverage, this is inadequate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences that efficiently convey the core functionality and identification options. No wasted words, and the most important information (what the tool does) is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive operation with 4 parameters, 0% schema coverage, no annotations, and no output schema, the description is insufficient. It doesn't explain the mutation behavior, file handling (in-place vs new file), error conditions, or relationship to sibling tools. The agent would struggle to use this correctly without additional context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage and 4 parameters, the description adds some value by explaining the two identification methods (ID vs search_text). However, it doesn't cover filename or output_filename parameters, nor does it explain the relationship between footnote_id and search_text (mutually exclusive? both required?). The description partially compensates but leaves significant gaps.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Delete') and resource ('footnote from a Word document'), making the purpose immediately understandable. However, it doesn't differentiate from sibling 'delete_footnote_robust' - both appear to delete footnotes, so the distinction isn't explained.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided about when to use this tool versus alternatives like 'delete_footnote_robust' or other deletion tools. The description mentions two identification methods but doesn't explain when to choose one over the other or any prerequisites for usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other 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