faxdrop-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., "@faxdrop-mcpsend a fax to +12125551234 with the contract.pdf file"
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.
π faxdrop-mcp
Send real faxes from any MCP-enabled AI assistant. Wraps the FaxDrop HTTP API.
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 log β
FAXDROP_MCP_DRY_RUN=trueto test prompts without sending;FAXDROP_MCP_AUDIT_LOG=/abs/pathfor a JSONL trail (mode0o600).Clean errors β FaxDrop's 402 / 429 / 4xx surfaced as MCP
isErrorwitherror_type,hint,retry_after.Drop-in for any MCP client β one
npx -y faxdrop-mcpline in Claude Desktop / Code / Cursor / Continue / OpenClaw.Verifiable releases β Sigstore-signed + SLSA in-toto attestation + npm provenance (verify).
A ~12 KB wrapper that turns a one-week security review into a one-line config change.
π¦ Installation
npm install -g faxdrop-mcpOr 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_<32 hex>.
π€ 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 falsecoverNote(string, β€500) β message bodyrecipientName(β€50),subject(β€200),senderCompany(β€100),senderPhone(validated E.164)
Returns: { success, faxId, status, statusUrl }
π 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 |
|
| Every |
Number gate |
|
|
|
Allowed types |
|
| libphonenumber |
Allowed countries |
|
| ISO-3166-1 alpha-2 allow-list (US/CA + US territories). |
State directory |
|
| Where |
Dry run |
| off | Write tools ( |
Audit log |
| off | Append-only JSON Lines (file mode |
β οΈ 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.
| Layer | Trigger | Suggested action |
| input | Recipient number can't be parsed by libphonenumber. | Ask user for an E.164 number. |
| policy | Phone type (e.g. MOBILE) not in | Use a fax line, or extend the env var. |
| policy | Country not in | Confirm with the user; extend the env var if intentional. |
| policy | Number not in | In |
| policy |
| Set |
| filesystem | Path is relative, outside outbox, leaf-symlink, missing, oversized, or has an unsupported extension. | The accompanying |
| upstream | FaxDrop returned 401. | Check |
| upstream | FaxDrop returned 402 (out of credits). | Top up at the FaxDrop pricing page. |
| upstream | FaxDrop returned 429. | Wait |
| upstream | FaxDrop returned a non-JSON body (proxy interception, incident page). | Body is discarded for safety; check FaxDrop status page. |
| upstream (fallback) | FaxDrop returned an error with no | 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_limitedwithretry_afterandX-RateLimit-*headers.Account credit balance β
402 payment_requiredwhen 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 for the vulnerability reporting process.
πΊοΈ Roadmap
See ROADMAP.md.
π Ecosystem
Other MCP servers in the klodr family:
π§ klodr/gmail-mcp β Gmail
π klodr/faxdrop-mcp β Send real faxes via FaxDrop (you are here)
π¦ klodr/mercury-invoicing-mcp β Mercury banking + invoicing
π€ Contributing
PRs welcome. See CONTRIBUTING.md for the test/build/lint checklist and release process.
π License
MIT β see LICENSE.
Maintenance
Latest Blog Posts
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/klodr/faxdrop-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server