Skip to main content
Glama
GongRzhe

Office Word MCP Server

replace_block_between_manual_anchors

Replace content between specified anchor texts in Word documents to update document sections while maintaining structure.

Instructions

Replace all content between start_anchor_text and end_anchor_text (or next logical header if not provided).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filenameYes
start_anchor_textYes
new_paragraphsYes
end_anchor_textNo
match_fnNo
new_paragraph_styleNo

Implementation Reference

  • Core handler function that implements the logic to replace block between manual anchors by parsing document XML elements, finding anchors, removing intervening elements, and inserting new paragraphs.
    def replace_block_between_manual_anchors(
        doc_path: str,
        start_anchor_text: str,
        new_paragraphs: list,
        end_anchor_text: str = None,
        match_fn=None,
        new_paragraph_style: str = None
    ) -> str:
        """
        Replace all content (paragraphs, tables, etc.) between start_anchor_text and end_anchor_text (or next logical header if not provided).
        If end_anchor_text is None, deletes until next visually distinct paragraph (bold, all caps, or different font size), or end of document.
        Inserts new_paragraphs after the start anchor.
        """
        from docx import Document
        import os
        if not os.path.exists(doc_path):
            return f"Document {doc_path} not found."
        doc = Document(doc_path)
        body = doc.element.body
        elements = list(body)
        start_idx = None
        end_idx = None
        # Find start anchor
        for i, el in enumerate(elements):
            if el.tag == CT_P.tag:
                p_text = "".join([node.text or '' for node in el.iter() if node.tag.endswith('}t')]).strip()
                if match_fn:
                    if match_fn(p_text, el):
                        start_idx = i
                        break
                elif p_text == start_anchor_text.strip():
                    start_idx = i
                    break
        if start_idx is None:
            return f"Start anchor '{start_anchor_text}' not found."
        # Find end anchor
        if end_anchor_text:
            for i in range(start_idx + 1, len(elements)):
                el = elements[i]
                if el.tag == CT_P.tag:
                    p_text = "".join([node.text or '' for node in el.iter() if node.tag.endswith('}t')]).strip()
                    if match_fn:
                        if match_fn(p_text, el, is_end=True):
                            end_idx = i
                            break
                    elif p_text == end_anchor_text.strip():
                        end_idx = i
                        break
        else:
            # Heuristic: next visually distinct paragraph (bold, all caps, or different font size), or end of document
            for i in range(start_idx + 1, len(elements)):
                el = elements[i]
                if el.tag == CT_P.tag:
                    # Check for bold, all caps, or font size
                    runs = [node for node in el.iter() if node.tag.endswith('}r')]
                    for run in runs:
                        rpr = run.find(qn('w:rPr'))
                        if rpr is not None:
                            if rpr.find(qn('w:b')) is not None or rpr.find(qn('w:caps')) is not None or rpr.find(qn('w:sz')) is not None:
                                end_idx = i
                                break
                    if end_idx is not None:
                        break
        # Mark elements for removal
        to_remove = []
        for i in range(start_idx + 1, end_idx if end_idx is not None else len(elements)):
            to_remove.append(elements[i])
        for el in to_remove:
            body.remove(el)
        doc.save(doc_path)
        # Reload and find start anchor for insertion
        doc = Document(doc_path)
        paras = doc.paragraphs
        anchor_idx = None
        for i, para in enumerate(paras):
            if para.text.strip() == start_anchor_text.strip():
                anchor_idx = i
                break
        if anchor_idx is None:
            return f"Start anchor '{start_anchor_text}' not found after deletion (unexpected)."
        anchor_para = paras[anchor_idx]
        style_to_use = new_paragraph_style or "Normal"
        for text in new_paragraphs:
            new_para = doc.add_paragraph(text, style=style_to_use)
            anchor_para._element.addnext(new_para._element)
            anchor_para = new_para
        doc.save(doc_path)
        return f"Replaced content between '{start_anchor_text}' and '{end_anchor_text or 'next logical header'}' with {len(new_paragraphs)} paragraph(s), style: {style_to_use}, removed {len(to_remove)} elements."
  • Registers the MCP tool 'replace_block_between_manual_anchors' by defining a wrapper function decorated with @mcp.tool() that delegates to the content_tools handler.
    @mcp.tool()
    def replace_block_between_manual_anchors(filename: str, start_anchor_text: str, new_paragraphs: list, end_anchor_text: str = None, match_fn=None, new_paragraph_style: str = None):
        """Replace all content between start_anchor_text and end_anchor_text (or next logical header if not provided)."""
        return replace_block_between_manual_anchors_tool(filename, start_anchor_text, new_paragraphs, end_anchor_text, match_fn, new_paragraph_style)
  • Thin async wrapper function in content_tools that calls the core implementation in document_utils.
    async def replace_block_between_manual_anchors_tool(filename: str, start_anchor_text: str, new_paragraphs: list, end_anchor_text: str = None, match_fn=None, new_paragraph_style: str = None) -> str:
        """Replace all content between start_anchor_text and end_anchor_text (or next logical header if not provided)."""
        return replace_block_between_manual_anchors(filename, start_anchor_text, new_paragraphs, end_anchor_text, match_fn, new_paragraph_style)

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