Skip to main content
Glama
GongRzhe

Office Word MCP Server

validate_document_footnotes

Check footnotes in Word documents for ID conflicts, orphaned content, and missing styles. Returns a detailed validation report.

Instructions

Validate all footnotes in document for coherence and compliance. Returns detailed report on ID conflicts, orphaned content, missing styles, etc.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filenameYes

Implementation Reference

  • Core handler function that implements the footnote validation logic by parsing DOCX XML parts and checking for coherence issues, orphans, styles, etc.
    def validate_document_footnotes(filename: str) -> Tuple[bool, str, Dict[str, Any]]:
        """Validate all footnotes in a document for coherence and compliance."""
        
        if not os.path.exists(filename):
            return False, f"File not found: {filename}", {}
        
        report = {
            'total_references': 0,
            'total_content': 0,
            'id_conflicts': [],
            'orphaned_content': [],
            'missing_references': [],
            'invalid_locations': [],
            'missing_styles': [],
            'coherence_issues': []
        }
        
        try:
            with zipfile.ZipFile(filename, 'r') as zf:
                # Check document.xml
                doc_xml = zf.read('word/document.xml')
                doc_root = etree.fromstring(doc_xml)
                nsmap = {'w': W_NS}
                
                # Get all footnote references
                ref_ids = set()
                for ref in doc_root.xpath('//w:footnoteReference', namespaces=nsmap):
                    ref_id = ref.get(f'{{{W_NS}}}id')
                    if ref_id:
                        ref_ids.add(ref_id)
                        report['total_references'] += 1
                        
                        # Check location
                        parent = ref.getparent()
                        while parent is not None:
                            if parent.tag in [f'{{{W_NS}}}hdr', f'{{{W_NS}}}ftr']:
                                report['invalid_locations'].append(ref_id)
                                break
                            parent = parent.getparent()
                
                # Check footnotes.xml
                if 'word/footnotes.xml' in zf.namelist():
                    footnotes_xml = zf.read('word/footnotes.xml')
                    footnotes_root = etree.fromstring(footnotes_xml)
                    
                    content_ids = set()
                    for fn in footnotes_root.xpath('//w:footnote', namespaces=nsmap):
                        fn_id = fn.get(f'{{{W_NS}}}id')
                        if fn_id:
                            content_ids.add(fn_id)
                            if fn_id not in ['-1', '0']:  # Exclude separators
                                report['total_content'] += 1
                    
                    # Find orphans and missing
                    report['orphaned_content'] = list(content_ids - ref_ids - {'-1', '0'})
                    report['missing_references'] = list(ref_ids - content_ids)
                else:
                    if report['total_references'] > 0:
                        report['coherence_issues'].append('References exist but no footnotes.xml')
                
                # Check relationships
                if 'word/_rels/document.xml.rels' in zf.namelist():
                    rels_xml = zf.read('word/_rels/document.xml.rels')
                    rels_root = etree.fromstring(rels_xml)
                    rel_nsmap = {'r': REL_NS}
                    
                    fn_rels = rels_root.xpath(
                        "//r:Relationship[contains(@Type, 'footnotes')]",
                        namespaces=rel_nsmap
                    )
                    
                    if report['total_content'] > 0 and len(fn_rels) == 0:
                        report['coherence_issues'].append('Missing footnotes relationship')
                    elif len(fn_rels) > 1:
                        report['coherence_issues'].append(f'Multiple footnote relationships: {len(fn_rels)}')
                
                # Check content types
                if '[Content_Types].xml' in zf.namelist():
                    ct_xml = zf.read('[Content_Types].xml')
                    ct_root = etree.fromstring(ct_xml)
                    ct_nsmap = {'ct': CT_NS}
                    
                    fn_overrides = ct_root.xpath(
                        "//ct:Override[@PartName='/word/footnotes.xml']",
                        namespaces=ct_nsmap
                    )
                    
                    if report['total_content'] > 0 and len(fn_overrides) == 0:
                        report['coherence_issues'].append('Missing footnotes content type')
                    elif len(fn_overrides) > 1:
                        report['coherence_issues'].append(f'Multiple footnote content types: {len(fn_overrides)}')
                
                # Check styles
                if 'word/styles.xml' in zf.namelist():
                    styles_xml = zf.read('word/styles.xml')
                    styles_root = etree.fromstring(styles_xml)
                    
                    ref_style = styles_root.xpath('//w:style[@w:styleId="FootnoteReference"]', namespaces=nsmap)
                    text_style = styles_root.xpath('//w:style[@w:styleId="FootnoteText"]', namespaces=nsmap)
                    
                    if not ref_style:
                        report['missing_styles'].append('FootnoteReference')
                    if not text_style:
                        report['missing_styles'].append('FootnoteText')
            
            # Determine if valid
            is_valid = (
                len(report['id_conflicts']) == 0 and
                len(report['orphaned_content']) == 0 and
                len(report['missing_references']) == 0 and
                len(report['invalid_locations']) == 0 and
                len(report['coherence_issues']) == 0
            )
            
            if is_valid:
                message = "Document footnotes are valid"
            else:
                message = "Document has footnote issues"
            
            return is_valid, message, report
            
        except Exception as e:
            return False, f"Error validating document: {str(e)}", report
  • MCP tool registration for validate_document_footnotes, delegating to footnote_tools.validate_footnotes_tool.
    @mcp.tool()
    def validate_document_footnotes(filename: str):
        """Validate all footnotes in document for coherence and compliance.
        Returns detailed report on ID conflicts, orphaned content, missing styles, etc."""
        return footnote_tools.validate_footnotes_tool(filename)
  • Wrapper function in footnote_tools that calls the core validate_document_footnotes and formats the response as a dict for the MCP tool.
    async def validate_footnotes_tool(filename: str) -> Dict[str, Any]:
        """
        Validate all footnotes in a document.
        
        Provides comprehensive validation report including:
        - ID conflicts
        - Orphaned content
        - Missing styles
        - Invalid locations
        - Coherence issues
        
        Args:
            filename: Path to the Word document
        
        Returns:
            Dict with validation status and detailed report
        """
        filename = ensure_docx_extension(filename)
        
        if not os.path.exists(filename):
            return {
                "valid": False,
                "message": f"Document {filename} does not exist",
                "report": {}
            }
        
        # Call validation
        is_valid, message, report = validate_document_footnotes(filename)
        
        return {
            "valid": is_valid,
            "message": message,
            "report": report
        }
Behavior2/5

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

No annotations are provided, so the description carries the full burden. It states the tool returns a 'detailed report on ID conflicts, orphaned content, missing styles, etc.', which gives some behavioral context about output content. However, it lacks critical details: whether this is a read-only operation, if it modifies the document, permission requirements, error handling, or report format. For a validation tool with zero annotation coverage, this is insufficient.

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?

The description is appropriately sized and front-loaded. The first sentence states the purpose clearly, and the second adds valuable output details. Both sentences earn their place with no wasted words, making it efficient and easy to parse.

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?

Given the complexity (validation tool with potential side effects), no annotations, no output schema, and low schema coverage, the description is incomplete. It covers purpose and output content but misses parameter semantics, behavioral traits (e.g., safety, permissions), and detailed usage context. For a tool that likely interacts with document integrity, more guidance is needed.

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

Parameters2/5

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

Schema description coverage is 0%, and the description provides no information about the single parameter 'filename'. It doesn't explain what the filename refers to (e.g., path, document name), expected format, or constraints. The description fails to compensate for the schema's lack of parameter documentation, leaving the parameter's meaning unclear.

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 tool's purpose: 'Validate all footnotes in document for coherence and compliance.' This specifies the verb (validate), resource (footnotes in document), and scope (all footnotes). It distinguishes from sibling tools that add, delete, or customize footnotes rather than validate them. However, it doesn't explicitly differentiate from potential validation of other document elements.

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?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites (e.g., document must exist), exclusions (e.g., not for endnotes), or compare with sibling tools like 'customize_footnote_style' or 'delete_footnote_from_document'. Usage is implied only from the purpose statement.

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