replace_block_between_manual_anchors
Automatically replace content between specified start and end anchor texts in Microsoft Word documents using defined paragraphs.
Instructions
Replace all content between start_anchor_text and end_anchor_text (or next logical header if not provided).
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| end_anchor_text | No | ||
| filename | Yes | ||
| match_fn | No | ||
| new_paragraph_style | No | ||
| new_paragraphs | Yes | ||
| start_anchor_text | Yes |
Implementation Reference
- word_document_server/main.py:397-400 (registration)MCP tool registration with @mcp.tool() decorator. This is the entry point for the tool named 'replace_block_between_manual_anchors', which delegates to the async 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)
- Async tool handler function that performs the core operation by calling the utility function 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)
- Core implementation of the replace_block_between_manual_anchors functionality. This utility function handles the low-level XML manipulation to find anchors, remove content between them, and insert 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."