letterwriter-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@letterwriter-mcpwrite Ms. Okonkwo a closing letter for her Riverside case"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
letterwriter-mcp
An MCP server that generates DOCX letters on your organization's own letterhead and returns the document as an attachment.
Point an AI assistant at it and "write Ms. Okonkwo a closing letter for her Riverside case" produces a correctly branded Word document, signed by the right office, that someone can review and send.
It speaks MCP over streamable HTTP, so it works with LibreChat, Claude Desktop, or anything else that speaks the protocol. It is useful to any organization that sends letters on headed paper — legal aid, clinics, nonprofits — and knows nothing about the deployment it came from.
Tools
Tool | Does |
| Renders a letter and returns the |
| Lists the letterhead IDs and labels available |
create_letter returns the document as MCP binary content — an embedded resource
carrying the bytes — rather than a download link. There is no object storage here, no
credentials to leak, and no URL that expires and turns a letter from last month into a
dead link.
Related MCP server: docs-mcp
Your letterheads never live in this repository
This repository ships one template: a 1.3 KB placeholder that says
[ YOUR LETTERHEAD ARTWORK GOES HERE ]. Your own letterheads stay outside it.
At runtime the server reads a registry — a letterheads.json describing your offices
— and the .docx files it names:
${LETTERHEAD_DIR}/ # default /data/letterheads
├── letterheads.json
├── baltimore-city.docx
├── montgomery-county.docx
└── ...If ${LETTERHEAD_DIR}/letterheads.json exists, that registry and those templates are
used. If it does not, the server falls back to the copy committed here, so a fresh
install starts and can produce a letter before anyone has uploaded anything.
/healthz reports which one is in force:
{"ok":true,"service":"letterwriter-mcp","version":"1.0.0",
"letterheads":{"count":13,"default":"generic",
"registry":"/data/letterheads/letterheads.json",
"using_bundled_registry":false}}Check using_bundled_registry first when every letter comes out on the wrong
letterhead. A LETTERHEAD_DIR that is empty or never mounted leaves the placeholder in
charge, and everything else looks perfectly healthy.
The registry
{
"defaultId": "generic",
"stopWords": ["office", "example legal aid"],
"letterheads": [
{
"id": "riverside",
"label": "Riverside",
"file": "riverside.docx",
"aliases": ["riverside", "riverside branch", "north county"],
"legalserver_offices": ["Riverside Branch"],
"include_unit_name": true
}
]
}Field | Means |
| Stable machine name; what |
| Human name, shown by |
| A bare filename in the same directory as the registry |
| Free-text spellings an assistant might be handed for this office |
| Exact office names as LegalServer spells them, if you use it |
| When true, a |
| Which letterhead to use when nothing matches |
| Words ignored when matching. Add your organization's own name, so "Example Legal Aid — Riverside Office" matches |
The whole registry is validated at startup and every problem is reported at once — a
missing .docx, a duplicate id, a defaultId matching nothing. Finding those one letter
at a time, in production, while someone waits to send mail to a client, is the experience
this avoids.
Matching prefers the longest alias, so riverside suite 300 beats riverside rather
than depending on the order of the file.
Making a template
Copy templates/letterheads/generic.docx, put your artwork in it, and keep the
placeholders. They are docxtemplater tags:
{today} {client_name} {address1} {address2} {honorific} {client_last_name}
{message_body} {attorney} {signature_lines}
{signature_lines} is filled with your organization name, the office name, and — for
offices setting include_unit_name — the unit.
Running it
docker run -d --name letterwriter-mcp \
-e ORGANIZATION_NAME="Example Legal Aid" \
-e MCP_ALLOWED_HOSTS=letterwriter-mcp,localhost,127.0.0.1 \
-v /srv/letterheads:/data/letterheads:ro \
-p 127.0.0.1:3002:3002 \
ghcr.io/marylandlegalaid/letterwriter-mcp:v1.0.0Variable | Default | Means |
|
| First line of every signature block. Set this. |
|
| Your registry and templates |
|
|
|
|
| |
| unset | See below |
| unset | Optional shared-secret header; no-ops when unset |
| — | Override just the registry path |
| — | Override just the template directory |
MCP_ALLOWED_HOSTS will bite you. The MCP SDK rejects requests whose Host header
it does not recognize. Behind Docker Compose the caller sends the service name, so that
name has to be in this list or every request is refused. Symptom: the server is healthy,
the client reports a connection error, and nothing obviously connects the two.
With LibreChat
mcpSettings:
allowedAddresses:
- "letterwriter-mcp:3002" # LibreChat blocks internal hostnames without this
mcpServers:
LetterWriter:
type: streamable-http
url: http://letterwriter-mcp:3002/letter-writer/mcp
description: "Create DOCX letters on organization letterhead"
chatMenu: trueallowedAddresses is not optional. LibreChat treats a Docker service name as an SSRF
target and refuses every connection without it, reporting Domain ... is not allowed.
A worked deployment — Compose service, storage, backups — is in MarylandLegalAid/librechat-azure.
Trust boundary
This server has no authentication by default, on purpose. It is built to run on a private container network reachable only by the application that calls it, and that network is the boundary.
Set MCP_SHARED_SECRET if you ever put it anywhere else. Note also that it renders
whatever text it is given onto your letterhead: whoever can reach it can produce a
document that looks official.
Development
npm install
npm test # 29 tests, no network, no fixtures on disk
npm startLicense
MIT — see LICENSE. No warranty; see the license text.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityDmaintenanceMCP server for Word document (.docx) creation and manipulation — the production-grade document automation tool for AI agents.Last updated961MIT
- FlicenseAqualityDmaintenanceMCP server for reading and writing .docx files. Exposes four paginated tools so agents can batch-read document content and styles, write content, and union style definitions.Last updated4
- AlicenseAqualityCmaintenanceMCP server that lets agents edit real Microsoft Word (.docx) documents - tracked changes, tables, styles, comments, content controls, and document properties - with every edit validated and previewed before saving. Built on the Open XML SDK (no Word automation); reads and writes documents in place through filesystem or SharePoint storage.Last updated712MIT
Related MCP Connectors
Use your own Word templates to convert Markdown → DOCX/PDF/HTML from any MCP-compatible AI.
Generate PDF/DOCX/XLSX/PPTX from templates+JSON. Convert Office/HTML/MD to PDF. Universal templating
Fill standard legal agreement templates (NDAs, SAFEs, NVCA docs, employment) as DOCX files.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/MarylandLegalAid/letterwriter-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server