RCMSoft
Automation

Creating an MCP Server for Python to Automate Microsoft Word Document Editing and Creation

By Robert Martin — RCMSoft — August 5, 2026

If you've been using Claude Code (or any AI tool that supports MCP), you've probably noticed that its real power comes from connecting it to external systems. In this article I'll walk through building a custom MCP server in Python that lets AI tools create, edit, and format Microsoft Word documents using the python-docx library.

By the end, you'll have a working MCP server you can register with Claude Code and then instruct it in plain English to generate Word documents: contracts, reports, form letters — whatever your business needs.

What is MCP?

Model Context Protocol (MCP) is an open standard developed by Anthropic that defines how AI assistants communicate with external tools. Think of it as a plugin system: you write a server that exposes a set of "tools" (functions), and any MCP-compatible AI client — including Claude Code — can discover and call those tools.

The protocol uses JSON-RPC over stdio or HTTP. Your MCP server is just a process that responds to tool-call requests. The AI describes what it wants to do; your server does the actual work and returns a result.

Prerequisites

Install the required packages:

pip install mcp python-docx
Note The mcp Python package is Anthropic's official SDK for building MCP servers. It handles all the protocol plumbing so you can focus on writing your tools.

Project structure

We'll keep this simple:

word-mcp-server/
├── server.py        # The MCP server
└── requirements.txt

Building the server

Here's the complete server.py. I'll explain each section below.

from mcp.server.fastmcp import FastMCP
from docx import Document
from docx.shared import Pt, RGBColor, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
import os

mcp = FastMCP("Word Automation")


@mcp.tool()
def create_document(file_path: str, title: str = "") -> str:
    """Create a new Word document at the given path, with an optional title heading."""
    doc = Document()
    if title:
        heading = doc.add_heading(title, level=0)
        heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
    doc.save(file_path)
    return f"Created document: {file_path}"


@mcp.tool()
def add_paragraph(file_path: str, text: str, style: str = "Normal") -> str:
    """Add a paragraph to an existing Word document.

    Style options: Normal, Heading1, Heading2, Heading3, Quote
    """
    doc = Document(file_path)
    style_map = {
        "Normal":   "Normal",
        "Heading1": "Heading 1",
        "Heading2": "Heading 2",
        "Heading3": "Heading 3",
        "Quote":    "Quote",
    }
    doc.add_paragraph(text, style=style_map.get(style, "Normal"))
    doc.save(file_path)
    return f"Added paragraph to {file_path}"


@mcp.tool()
def add_table(file_path: str, headers: list[str], rows: list[list[str]]) -> str:
    """Add a table to an existing Word document.

    headers: list of column header strings
    rows: list of rows, each row is a list of cell strings
    """
    doc = Document(file_path)
    table = doc.add_table(rows=1, cols=len(headers))
    table.style = "Table Grid"

    # Header row
    header_row = table.rows[0].cells
    for i, header in enumerate(headers):
        header_row[i].text = header
        run = header_row[i].paragraphs[0].runs[0]
        run.bold = True

    # Data rows
    for row_data in rows:
        row = table.add_row().cells
        for i, cell_text in enumerate(row_data):
            row[i].text = cell_text

    doc.save(file_path)
    return f"Added {len(rows)}-row table to {file_path}"


@mcp.tool()
def add_page_break(file_path: str) -> str:
    """Add a page break to an existing Word document."""
    doc = Document(file_path)
    doc.add_page_break()
    doc.save(file_path)
    return f"Added page break to {file_path}"


@mcp.tool()
def read_document_text(file_path: str) -> str:
    """Read all text content from an existing Word document."""
    doc = Document(file_path)
    paragraphs = [p.text for p in doc.paragraphs if p.text.strip()]
    return "\n\n".join(paragraphs) if paragraphs else "(document is empty)"


@mcp.tool()
def list_document_styles(file_path: str) -> str:
    """List all available paragraph styles in a Word document."""
    doc = Document(file_path)
    styles = [s.name for s in doc.styles if s.type.name == "PARAGRAPH"]
    return "\n".join(sorted(styles))


if __name__ == "__main__":
    mcp.run()

What each tool does

create_document — Creates a new .docx file at a given path. Optionally adds a centered title heading. This is always the first tool called when generating a new document from scratch.

add_paragraph — Appends a paragraph to an existing document with an optional style. The style map keeps the Claude-facing API simple while mapping to Word's verbose internal style names.

add_table — Adds a formatted table given a list of headers and a list of rows. Each row is a list of strings. The header row is automatically bolded.

add_page_break — Adds a page break, useful for multi-section documents like reports.

read_document_text — Returns the plain text content of a document, useful for verifying what was generated or for editing existing documents.

list_document_styles — Lists available paragraph styles so Claude can make informed choices about styling.

Registering the server with Claude Code

Claude Code reads MCP server configurations from its settings file. On Windows this is at %USERPROFILE%\.claude\settings.json. Add your server under the mcpServers key:

{
  "mcpServers": {
    "word": {
      "command": "python",
      "args": ["C:/path/to/word-mcp-server/server.py"],
      "env": {}
    }
  }
}

After saving, restart Claude Code. You can verify it loaded by running /mcp in a Claude Code session — you should see your "word" server listed with its tools.

Using it in a Claude Code session

Once registered, you can direct Claude Code to generate Word documents in plain English:

Create a one-page service agreement at C:/Docs/service-agreement.docx.
Include: title "Service Agreement", a section for Services Provided,
a section for Payment Terms, a section for Term and Termination,
and a signature block at the end.

Claude Code will call your MCP tools in sequence — create_document, then multiple add_paragraph calls with appropriate styles — and produce a properly formatted .docx file.

Tip The more specific your instructions, the better the output. Tell Claude what sections you want, what tone to use, and any content that must appear verbatim (e.g., legal language). It will fill in the structure and boilerplate.

Extending the server

The six tools above are a useful foundation, but you can go much further. Some extensions worth adding:

The pattern is always the same: write a Python function, decorate it with @mcp.tool(), give it a clear docstring (Claude reads this to understand when and how to use the tool), and save the file.

Real-world uses

For Atlanta businesses, a Word automation MCP server is immediately useful for:

In each case, a few minutes of prompting replaces what used to be 30–60 minutes of manual document assembly.

Next steps

If you want to go deeper: the python-docx documentation covers the full API for styles, sections, and formatting. And Anthropic's MCP specification documents the full protocol if you want to add features like prompts or resource subscriptions beyond simple tools.

If you're building something like this for your business and want help with the implementation, feel free to get in touch. Document automation is one of the more satisfying problems to solve — the value is immediately obvious and the ROI is usually fast.

Need custom automation for your business?

I build Python automation, Word/Excel integrations, and AI-powered workflows for Atlanta-area businesses.

Start a conversation