{
  "markdown": "# docmcp\n\n**Live: https://docmcp.onrender.com** — free key at `POST /keys/free`, 10 docs/month.\nPaid: $10/mo for 100, $30/mo for 500. Quotas live in `PLANS` in [src/store.ts](src/store.ts).\n\nA paid MCP server that turns structured data into **real** `.docx` and `.xlsx` files.\nAgents are good at prose and bad at file formats — they emit Markdown and hope. This\ngives them a tool that returns a Word or Excel file with actual headings, tables,\nbold headers, frozen panes and RTL support.\n\nReturns a short-lived download URL rather than a base64 blob: a 200KB docx inlined\ninto a tool result costs the caller ~70k tokens.\n\nTwo modes, one codebase:\n\n- **`--stdio`** — local client (Claude Desktop / Code). No auth, no quota, no expiry;\n  files are written to `OUT_DIR` and the tool returns the path.\n- **default** — hosted HTTP at `/mcp`. API key required, quota metered, files served\n  as URLs that expire after 24h. This is the product.\n\n## Local\n\n```bash\nnpm install\n```\n\nAlready registered in Claude Desktop as `docmcp` (output → `~/Documents/docmcp`).\nTo register elsewhere:\n\n```json\n{\n  \"mcpServers\": {\n    \"docmcp\": {\n      \"command\": \"/usr/local/bin/node\",\n      \"args\": [\"<repo>/node_modules/tsx/dist/cli.mjs\", \"<repo>/src/index.ts\", \"--stdio\"],\n      \"env\": { \"DATABASE_URL\": \"postgres://…\", \"OUT_DIR\": \"<somewhere>\" }\n    }\n  }\n}\n```\n\nAbsolute paths matter — the client launches with a minimal `PATH`.\n\n## Hosted\n\n```bash\nnpm start          # http://localhost:8787/mcp\ncurl -X POST localhost:8787/keys/free\n```\n\n```json\n{\n  \"mcpServers\": {\n    \"docmcp\": {\n      \"type\": \"http\",\n      \"url\": \"https://your-host/mcp\",\n      \"headers\": { \"Authorization\": \"Bearer dk_...\" }\n    }\n  }\n}\n```\n\n## Tools\n\n| Tool | Does |\n|---|---|\n| `create_docx` | headings, paragraphs, bullet/numbered lists, tables, page breaks, `rtl` for Hebrew/Arabic |\n| `create_xlsx` | multi-sheet, bold + frozen header, auto-filter, sized columns, numbers stay numbers |\n| `usage` | calls used / quota this month |\n\n## Env\n\n| Var | Default | Notes |\n|---|---|---|\n| `PORT` | `8787` | |\n| `BASE_URL` | `http://localhost:$PORT` | Must be the public URL — it goes in download links |\n| `DATABASE_URL` | — | **Required.** Any Postgres (Koyeb, Neon, Supabase) |\n| `OUT_DIR` | `out` | Generated files, swept hourly, 24h TTL |\n| `STRIPE_SECRET_KEY` | — | Omit to run without billing |\n| `STRIPE_WEBHOOK_SECRET` | — | For `/stripe/webhook` |\n| `STRIPE_PRICE_STARTER`, `STRIPE_PRICE_PRO` | — | Price IDs behind `/buy/:plan` |\n\nPlans and quotas live in `PLANS` in [src/store.ts](src/store.ts).\n\n## Deploy\n\nAny Docker host with a Postgres URL. There is no persistent disk requirement:\ngenerated files live on the container filesystem and expire in 24h anyway, so a\nrestart only breaks links that were about to die. Everything that must survive —\nkeys, quotas, the IP salt — is in Postgres.\n\nCurrently **Render** (web service, free tier) + **Neon** (Postgres, free, no expiry).\nDeliberately two providers: state lives somewhere that isn't tied to whoever is\nhosting the container this month. Render builds the Dockerfile straight from GitHub.\n\n`BASE_URL` must be the public URL — it is baked into every download link handed to a\nclient, so getting it wrong produces links to a host that does not exist. You only\nlearn the URL after the first deploy, so set it then and redeploy.\n\nRender's free tier sleeps after 15 minutes idle and takes 30–50s to wake. That is the\ncost of not having a card on file; fix it by upgrading once a customer pays.\n\n## Billing flow\n\n`/buy/starter` → Stripe Checkout → webhook `checkout.session.completed` → key issued →\ncustomer lands on `/success` and **sees the key there**. No email provider needed.\n`customer.subscription.deleted` deactivates it. There are no user accounts: the key\nis the account.\n\n## Test\n\n```bash\nnpm test\n```\n\nCovers both generators (including a ragged table row and a `/` in a sheet name — both\nthings an LLM will send eventually), number preservation through an xlsx round-trip,\nand that quota stops exactly at the plan limit.\n\n## Known gaps\n\n- **No PDF.** Good PDF output needs headless Chrome or LibreOffice, which triples the\n  deploy. Word and Excel are the actual gap — everyone already ships Markdown→PDF.\n  Add it when a paying user asks.\n- **A lost key cannot be recovered.** It is shown once on `/success`. Add email\n  delivery or a \"resend by email\" route when the first customer asks.\n- **Files are on the container filesystem.** Deliberate — see Deploy. Move to object\n  storage only if 24h links breaking on restart ever actually bothers someone.\n- **Free keys are rate limited, not abuse-proof.** One per IP per 24h plus a global\n  daily ceiling (`FREE_KEYS_PER_DAY`, default 200). Rotating VPNs still defeats it —\n  nothing stops that, and no free tier anywhere survives a determined attacker. The\n  limits exist to bound the worst case, not to eliminate it.\n- **`/admin` needs `ADMIN_TOKEN`.** Unset, the route 404s, so an unconfigured deploy\n  never exposes it. Visit `/admin?token=…`.\n",
  "bytes": 5021,
  "sha": "498680a00287991968e2e264d94c5b0f3471d5a41ff50dffb9e362adfc48ad4a",
  "repo_slug": "adirdavi/docmcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_adirdavi_docmcp_51e7a561/readme"
}