Giving AI Agents a Fax Machine

October 8, 2026

projects development ai

I added an agent interface to FaxOnline. An AI agent can now prepare a PDF fax, hand you a checkout link, and check whether it was delivered.

Five years ago I wrote about building an online fax service. That started with needing to send a fax myself and finding a market full of subscriptions for something people might need once. This update started with a much shorter question: could we use MCP and WebMCP to let an agent send a fax?

Apparently the fax machine gets an AI interface before it gets to retire.

Two ways into the same service

MCP lets a server expose tools an agent can discover and call. FaxOnline's server lives at https://faxonline.app/mcp/fax and exposes two tools: prepare_fax and get_fax_status.

WebMCP exposes tools through a supported browser's page context. An agent visiting the site can discover an explicit operation instead of figuring out which button to click and which form field wants the fax number. Browser support is still experimental.

For FaxOnline, those browser tools call the same Laravel MCP endpoint. The browser gets the tool descriptions and input schemas from the server, so the accepted inputs aren't maintained in two separate places. The page also offers tools to open the regular fax form and retrieve pricing.

The actual fax work stays in Laravel. There's already a document pipeline, a fax provider, queues, and Stripe. This adds another way to use them.

Preparing a fax isn't permission to send it

The interesting part was deciding where the agent stops.

prepare_fax takes a US destination number, notification email, filename, PDF encoded as base64, and a private draft token. It validates the document, counts its pages, stores it privately, and creates a Stripe Checkout session. The result includes the recipient, price, and checkout URL.

At that point, nothing has been sent and no payment has been charged. The person follows the checkout link and reviews the destination and document details before authorizing the transaction.

That approval uses the existing Stripe connection. A verified webhook checks that the payment authorization matches the prepared fax before queuing it. The payment is configured for manual capture, with capture following successful delivery.

This makes the division of work pretty concrete: the agent handles preparation and status checks; checkout records the person's approval to send to that recipient.

Retries matter here too. A dropped response shouldn't create another fax. The agent generates a random draft token and reuses it with identical inputs. The server returns the existing draft and checkout. Trying to change the recipient or document under that token gets rejected. The same secret token lets the agent check status, so it needs to stay private.

Getting the tools discovered

An endpoint needs instructions somewhere an agent or its user can find them. I added a connection guide, a public MCP manifest, and links from the site's crawler overview. I also published the server in the official MCP registry.

Cloudflare added an unexpected wrinkle. Its WebMCP preview can inject a browser bridge at the edge, including a Content Credentials pack for inspecting image provenance. A scan of FaxOnline found a schema problem in that image tool: the description required exactly one of two arguments, but the schema allowed neither or both.

That tool wasn't in the application repo. It came from Cloudflare's injected pack.

I disabled the image pack through the Cloudflare CLI and kept the Site MCP Server pack. I also set its endpoint to /mcp/fax; the default was /mcp. Fetching the live page confirmed that Cloudflare was injecting the intended pack and URL.

The scan also caught a smaller issue in my own tool: "US destination fax number" didn't tell an agent what format to use. The description now gives ten-digit, country-code, and formatted examples, with tests confirming that they normalize to the same number.