A proposal is the least interesting document you write and the one that most often decides whether
you get paid. The MCP Docx server turns one sentence into a real .docx: letterhead, cover
title, summary, scope, deliverables, a timeline table, a priced investment table and a signature block.
It writes Word files rather than PDFs on purpose, because the client is going to want a change to clause
four and you want them to be able to make it.
claude mcp add docx -- npx -y @theluckystrike/mcp-docx
Cursor and Claude Desktop take the same server as a config block:
{
"mcpServers": {
"docx": {
"command": "npx",
"args": ["-y", "@theluckystrike/mcp-docx"]
}
}
}
The npm publish is pending, so until it lands use the .mcpb bundle from the latest
release or a clone and build. Per client paths are on the
setup pages.
business_set stores the sender block printed on every document: name, address, email,
VAT id, IBAN, bank, logo, letterhead colour, default currency, tax rate and payment terms. It is the
same field set as MCP Invoice, so one profile serves both and the proposal you
accepted becomes the invoice you send. A missing profile never blocks a document; the response tells you
the sender block is a placeholder.
You: Write a proposal for Beta Corp. Checkout rebuild, 4,500 EUR, 50% on
signature 50% on delivery, three phases: discovery 1 week, build 3 weeks,
launch 1 week. Valid until the end of the year.
proposal_create {
client: "Beta Corp", project_title: "Checkout rebuild",
scope: ["Audit the current funnel", "Rebuild the checkout", "Ship and measure"],
timeline: [{phase: "Discovery", duration: "1 week"}, ... ],
price: {amount: 4500, currency: "EUR", terms: "50% on signature, 50% on delivery"},
valid_until: "2026-12-31"
}
-> PROP-2026-0001, EUR 4,500.00
-> ~/.local/share/mcp-servers/docx/documents/checkout-rebuild.docx
The file opens in Word, Pages, LibreOffice and Google Docs. Every amount carries its currency code,
so there are no bare numbers for a client to misread. References are PROP-YYYY-NNNN for
proposals and AGR-YYYY-NNNN for agreements, and the counter is written before the record is
stored, so a crash burns a number rather than reusing one on a second sent document.
contract_create writes a freelance service agreement skeleton: parties, services, term,
fee, IP, confidentiality, contractor status, termination, liability and governing law, with
[BRACKETED PLACEHOLDERS] where a decision is yours. The document itself says it is a
drafting template and not legal advice, because nothing here has been reviewed by a lawyer in any
jurisdiction. Treat it as the thing you send to a lawyer, not the thing you send to a client.
doc_fill_template replaces {{placeholders}} in an existing
.docx and writes a new file, keeping every style, table, header, footer and image, because
everything except the paragraphs it rewrites is copied byte for byte. Call it with no
values and it lists the placeholders the template actually contains, which is the fastest
way to end an argument about a name that did not get replaced.
The part worth knowing is why so many template fillers fail on real files. A .docx
paragraph is a sequence of runs, and Word routinely breaks a placeholder you typed as
{{client}} into three runs after an edit or a spell-check pass: {{cli,
ent, }}. Per-run replacement finds nothing and the document comes back with
the placeholder still in it, silently. This server substitutes on the joined text of each paragraph
instead, writes the result into the first run so its formatting survives, and blanks the remaining runs
of that paragraph. The trade is stated plainly: a paragraph that mixes bold and regular text around a
placeholder comes back in the first run's formatting. A placeholder with no value is left in place and
reported, never blanked.
Reading a document back uses no dependency at all. A .docx is a ZIP, so
node:zlib opens it and a small WordprocessingML walk pulls out paragraphs, heading levels
from w:pStyle, list items and tables in document order. One detail decides whether
doc_read is useful: in OOXML a numbered list and a bullet list are the same element. The
distinction lives nowhere in the paragraph itself. It is recorded only by resolving that paragraph's
w:numId against word/numbering.xml. Skip that resolution and every numbered
list in the file reads back as bullets, which quietly destroys the structure of exactly the documents
people want to read back: contracts, scopes of work and anything with numbered clauses.
Every pure JavaScript path from Word to PDF needs a native dependency, a headless Chromium or a cloud
API. This collection ships none of those, so npx works on any machine with Node and nothing
else. doc_to_html writes semantic HTML with a print stylesheet: open it and print to PDF.
The tool description says the same thing, so the model does not promise a file it cannot produce.
Free covers doc_create, doc_from_markdown, doc_read and
doc_to_html without limit, 3 proposals or contracts per calendar month combined, and
templates with up to 10 placeholders. Pro ($19 once) removes those limits and adds your logo and brand
colour on the letterhead. Product page: MCP Docx. Side by side with the
alternatives: docx comparison.
No, and that is a deliberate choice. Every pure JavaScript route from .docx to PDF needs LibreOffice, a Chromium binary or a cloud API, none of which this collection ships. doc_to_html writes semantic HTML with a print stylesheet, so you open it and print to PDF.
Call doc_fill_template with no values and it lists the placeholders the file actually contains. Names are matched exactly, whitespace inside the braces is ignored, and the response names every key you passed that the template does not have. Placeholders split across runs by Word are handled, because substitution runs on the joined paragraph text.
Not as it stands. contract_create writes a drafting skeleton with bracketed placeholders and prints on the document that it is a template and not legal advice. Nothing in it has been reviewed by a lawyer in any jurisdiction. Send it to yours.
Yes. doc_read extracts headings with levels, paragraphs, list items and tables in document order, and format json returns the block structure. It reads .docx only; .doc, .rtf and Pages files are refused with a message that says so. It does not report fonts, colours, comments, tracked changes or footnotes.
Under ~/.local/share/mcp-servers/docx/, with generated files in a documents subfolder when you do not pass out_path. Every mutating call runs inside an advisory lock, so two clients on one data directory cannot allocate the same reference number. The server makes no network request of any kind.