word-to-pdf-mcp is a secure, production-ready remote MCP server that converts .docx files to PDF using LibreOffice as a persistent unoserver daemon.
mcp-pdf
Remote MCP server that converts .docx to PDF. Works with any MCP client — Claude (web & CLI), Cursor, Windsurf, or anything that speaks the Model Context Protocol.
Runs as a Docker container with LibreOffice as a persistent daemon (unoserver). First conversion triggers a warmup; subsequent conversions take ~0.3s.
Setup
cp .env.example .env
touch credentials.json
docker compose up -d --build
docker compose exec mcp-pdf python manage_keys.py add yourname
Requires an external Docker network (docker network create caddy_net) and a reverse proxy for TLS (see Reverse proxy).
Manage credentials
docker compose exec mcp-pdf python manage_keys.py add <name>
docker compose exec mcp-pdf python manage_keys.py list
docker compose exec mcp-pdf python manage_keys.py revoke <client_id>
No restart needed — changes take effect immediately.
Connect your MCP client
Claude web (claude.ai)
Add a remote MCP server in Claude's settings with your server URL:
https://your-domain.com/
Claude will handle OAuth automatically. When the authorize page opens, paste your API key (sk_... from manage_keys.py add).
Claude Code (CLI)
Add to ~/.claude/claude_code_config.json:
{
"mcpServers": {
"pdf-tools": {
"type": "sse",
"url": "https://your-domain.com/sse",
"headers": {
"Authorization": "Bearer <your_sk_key>"
}
}
}
}
Cursor / Windsurf / other MCP clients
Use the SSE transport config:
{
"mcpServers": {
"pdf-tools": {
"type": "sse",
"url": "https://your-domain.com/sse",
"headers": {
"Authorization": "Bearer <your_sk_key>"
}
}
}
}
If your client supports Streamable HTTP, use the root URL (https://your-domain.com/) instead.
Tool: convert_docx_to_pdf
Input: base64-encoded .docx file
Output:
{
"success": true,
"download_url": "/download/<id>",
"pdf_size_bytes": 53248,
"expires_in_seconds": 300
}
The download link is single-use and expires after 5 minutes.
Reverse proxy
The server needs TLS termination. Example Caddyfile:
your-domain.com {
reverse_proxy mcp-pdf:8080 {
flush_interval -1
}
}
flush_interval -1 disables buffering so SSE events stream through.
How it works
- unoserver keeps LibreOffice running as a daemon — no cold start per conversion
- Warmup on boot preloads fonts, renderer, and filter plugins so the first real conversion is fast
- OAuth 2.0 + PKCE for browser-based clients (claude.ai), Bearer tokens for CLI clients
- Hardened container: read-only filesystem, all capabilities dropped, no-new-privileges, pid limits