AgentStack
MCP verified MIT Self-run

Faxdrop Mcp

mcp-klodr-faxdrop-mcp · by klodr

Send real faxes from any MCP-enabled AI assistant. Wraps the FaxDrop API (PDF/DOCX/JPG/PNG, international numbers, status polling) with rate limits, dry-run mode, and audit logging.

No reviews yet
0 installs
14 views
0.0% view→install

Install

$ agentstack add mcp-klodr-faxdrop-mcp

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Faxdrop Mcp? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

📠 faxdrop-mcp

> Send real faxes from any MCP-enabled AI assistant. Wraps the FaxDrop HTTP API.

[](https://github.com/klodr/faxdrop-mcp/actions/workflows/ci.yml) [](https://github.com/klodr/faxdrop-mcp/actions/workflows/codeql.yml) [](https://vitest.dev) [](https://codecov.io/gh/klodr/faxdrop-mcp) [](https://scorecard.dev/viewer/?uri=github.com/klodr/faxdrop-mcp) [](https://www.bestpractices.dev/projects/12578) [](https://socket.dev/npm/package/faxdrop-mcp) [](https://coderabbit.ai)

[](https://www.npmjs.com/package/faxdrop-mcp) [](https://www.npmjs.com/package/faxdrop-mcp) [](https://nodejs.org) [](https://modelcontextprotocol.io) [](https://modelcontextprotocol.io) [](https://github.com/klodr/faxdrop-mcp/pulls)

[](https://github.com/sponsors/klodr) [](https://www.patreon.com/klodr) [](https://ko-fi.com/klodr)

A Model Context Protocol (MCP) server that lets AI assistants (Claude, Cursor, Continue, OpenClaw…) send real faxes through the FaxDrop API.

✨ Why this MCP?

Faxing is still required by US healthcare, government forms, and a long tail of legal/financial workflows. FaxDrop is a hosted fax service with a clean HTTP API and a free tier (2 faxes/month). This MCP exposes it to LLMs with the safeguards an agent platform actually needs.

🤔 Why not just call the FaxDrop API directly?

You can. But every agent that does ends up re-implementing the same handful of guards. This MCP gives them to you for free:

  • Input validation — absolute-path + extension + 10 MB cap on the upload (all before the file is opened); E.164 regex on the fax number; no SSRF, no path traversal.
  • TOCTOU-safe read — file descriptor pinned with fs.open(), size enforced continuously while reading.
  • No secret leakage — error objects strip the response body; the audit log keeps only an explicit allowlist of FaxDrop response-shape fields (recipientNumber, faxId, id) in clear, blocks the credential set (apiKey / authorization / password / …), and elides every other field with a length marker ([ELIDED:NNN]). Property-tested with fast-check.
  • Dry-run + audit logFAXDROP_MCP_DRY_RUN=true to test prompts without sending; FAXDROP_MCP_AUDIT_LOG=/abs/path for a JSONL trail (mode 0o600).
  • Clean errors — FaxDrop's 402 / 429 / 4xx surfaced as MCP isError with error_type, hint, retry_after.
  • Drop-in for any MCP client — one npx -y faxdrop-mcp line in Claude Desktop / Code / Cursor / Continue / OpenClaw.
  • Verifiable releases — Sigstore-signed + SLSA in-toto attestation + npm provenance ([verify](.github/SECURITY.md#verifying-releases)).

A ~12 KB wrapper that turns a one-week security review into a one-line config change.

📦 Installation

npm install -g faxdrop-mcp

Or use directly with npx:

npx faxdrop-mcp

⚙️ Configuration

The server reads FAXDROP_API_KEY from the environment. Get your key at faxdrop.com/account (Developer API → Generate Key). Keys look like fd_live_.

🤖 Claude Desktop / Claude Code

Add to ~/Library/Application Support/Claude/claude_desktop_config.json (or ~/.claude.json for Claude Code):

{
  "mcpServers": {
    "faxdrop": {
      "command": "npx",
      "args": ["-y", "faxdrop-mcp"],
      "env": {
        "FAXDROP_API_KEY": "fd_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

🖱️ Cursor

Add to ~/.cursor/mcp.json:

{
  "mcpServers": {
    "faxdrop": {
      "command": "npx",
      "args": ["-y", "faxdrop-mcp"],
      "env": {
        "FAXDROP_API_KEY": "fd_live_..."
      }
    }
  }
}

🦀 OpenClaw

Add to ~/.openclaw/openclaw.json, then restart the gateway (docker restart openclaw-openclaw-gateway-1 or your equivalent).

🛠️ Tools (3)

📤 faxdrop_send_fax

Send a fax. Uploads a local document from the outbox (default ~/FaxOutbox/) to a fax number in international (E.164) format.

Required:

  • filePath (string, absolute) — PDF, DOCX, JPEG, or PNG, ≤10 MB. Must live inside the outbox.
  • recipientNumber (string) — E.164, e.g. +12125551234. Subject to the 3-layer phone gate (TYPE → COUNTRY → per-number).
  • senderName (string)
  • senderEmail (string)

Optional cover-page fields (printed only when includeCover is true):

  • includeCover (boolean) — free accounts always include a branded cover; paid accounts default to false
  • coverNote (string, ≤500) — message body
  • recipientName (≤50), subject (≤200), senderCompany (≤100), senderPhone (validated E.164)

Notifications:

  • sendEmail (boolean) — whether FaxDrop should email the sender a per-fax delivery confirmation. Defaults to false (suppress) — the MCP is built for batch/agent workflows where one email per fax floods the inbox, and the same information is already retrievable via faxdrop_get_fax_status. Set to true to opt back in. Failed-fax emails, operator alerts, refunds and status pages are unaffected. The send response echoes deliveryEmail: enabled | suppressed.

Returns: { success, faxId, status, statusUrl, deliveryEmail }

🔗 faxdrop_pair_number

Add a fax number to the paired whitelist (~/.faxdrop-mcp/paired.json). Only effective when FAXDROP_MCP_NUMBER_GATE=pairing (default). The number must still pass the TYPE and COUNTRY checks (no bypass). Always confirm with the user before pairing — paired numbers can be faxed without further per-number approval.

Required:

  • recipientNumber (string) — E.164

Returns: { paired, country, type }

📊 faxdrop_get_fax_status

Check the delivery status of a previously sent fax. Terminal statuses (delivered / failed / partial) are cached process-wide (LRU 100 entries, whitelist-sliced) — re-polling a finished fax short-circuits with a _cached: true marker to spare your FaxDrop quota.

Recommended polling cadence: every ~5s for the first 2 min, then every ~30s for up to 10 min, stop on terminal status.

Required:

  • faxId (string)

Returns: { id, status, recipientNumber?, pages?, completedAt?, _cached? }

🛡️ Safeguards

| Knob | Env var | Default | Notes | |---|---|---|---| | Outbox jail | FAXDROP_MCP_WORK_DIR=/abs/path | ~/FaxOutbox/ (auto-created mode 0o700) | Every filePath must live inside this directory after realpath canonicalization. Symlinks to outside the outbox are rejected. | | Number gate | FAXDROP_MCP_NUMBER_GATE=open\|pairing\|closed | pairing | pairing requires HITL approval via faxdrop_pair_number before a new number can be faxed. closed disables runtime pairing (paired.json edited out-of-band). | | Allowed types | FAXDROP_MCP_ALLOWED_TYPES=... | FIXED_LINE,FIXED_LINE_OR_MOBILE,VOIP,TOLL_FREE | libphonenumber NumberType allow-list. | | Allowed countries | FAXDROP_MCP_ALLOWED_COUNTRIES=... | US,CA,PR,GU,VI,AS,MP | ISO-3166-1 alpha-2 allow-list (US/CA + US territories). | | State directory | FAXDROP_MCP_STATE_DIR=/abs/path | ~/.faxdrop-mcp/ (mode 0o700) | Where paired.json lives (mode 0o600, atomic write). | | Dry run | FAXDROP_MCP_DRY_RUN=true | off | Write tools (faxdrop_send_fax, faxdrop_pair_number) return the would-be payload (passed through the same allowlist redaction as the audit log — see row below) and never call FaxDrop or touch paired.json. | | Audit log | FAXDROP_MCP_AUDIT_LOG=/abs/path/audit.log | off | Append-only JSON Lines (file mode 0o600). Allowlist-based redaction: only FaxDrop response-shape fields (recipientNumber, faxId, id) are kept in clear; known credential keys (apiKey / authorization / password / secret / token) are replaced with [REDACTED]; every other field is elided with a [ELIDED:NNN] length marker. This is a fail-closed design: a new API field added upstream is hidden by default, not leaked. |

⚠️ Error catalog

Every failure is returned as isError: true with a structured error_type, message, and (when applicable) hint and retry_after. Programmatic consumers can match on error_type (in structuredContent) to drive retry logic.

| error_type | Layer | Trigger | Suggested action | |---|---|---|---| | phone_parse | input | Recipient number can't be parsed by libphonenumber. | Ask user for an E.164 number. | | phone_type | policy | Phone type (e.g. MOBILE) not in FAXDROP_MCP_ALLOWED_TYPES. | Use a fax line, or extend the env var. | | phone_country | policy | Country not in FAXDROP_MCP_ALLOWED_COUNTRIES. | Confirm with the user; extend the env var if intentional. | | phone_gate | policy | Number not in paired.json and gate is pairing or closed. | In pairing mode: call faxdrop_pair_number first. In closed: edit paired.json out-of-band. | | pair_disabled | policy | faxdrop_pair_number called outside pairing mode. | Set FAXDROP_MCP_NUMBER_GATE=pairing. | | bad_request | filesystem | Path is relative, outside outbox, leaf-symlink, missing, oversized, or has an unsupported extension. | The accompanying hint describes the exact remedy. | | unauthorized | upstream | FaxDrop returned 401. | Check FAXDROP_API_KEY in your MCP client config. | | payment_required | upstream | FaxDrop returned 402 (out of credits). | Top up at the FaxDrop pricing page. | | rate_limited | upstream | FaxDrop returned 429. | Wait retry_after seconds; the hint shows the bucket that was hit. | | invalid_response | upstream | FaxDrop returned a non-JSON body (proxy interception, incident page). | Body is discarded for safety; check FaxDrop status page. | | fax_error | upstream (fallback) | FaxDrop returned an error with no error_type field. | Read the message; treat as transient. |

🚦 Rate limits & quotas

Two independent caps gate every fax send, both enforced by FaxDrop:

  • Per-key rate limits (per-minute / per-hour / per-day buckets) — 429 rate_limited with retry_after and X-RateLimit-* headers.
  • Account credit balance402 payment_required when you run out, with a top-up hint.

The MCP does not add its own limiter; it forwards FaxDrop's response as a clean isError: true with error_type, hint, and retry_after. See FaxDrop's API docs for the current numbers.

🔒 Security

  • Always confirm with the user (recipient, file, cover-page) before invoking faxdrop_send_fax. This is also baked into the tool description.
  • The MCP reads files from the user's local filesystem — only expose this server to agents you trust.
  • Test prompts safely with FAXDROP_MCP_DRY_RUN=true.
  • See [SECURITY.md](.github/SECURITY.md) for the vulnerability reporting process.

🗺️ Roadmap

See [ROADMAP.md](docs/ROADMAP.md).

🌐 Ecosystem

Other MCP servers in the klodr family:

🤝 Contributing

PRs welcome. See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for the test/build/lint checklist and release process.

📄 License

MIT — see [LICENSE](./LICENSE).

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.