For agents · Model Context Protocol
Connect an assistant to billoquy
mcp.billoquy.com is an MCP server. Point an MCP-capable assistant at it and, on a signed-in person's behalf, it can discover the four checks, start one, read the finding with cited sources, preview a prepared fix, and hand the person off to approve and pay. It cannot approve, file, send, or pay — those are the person's actions.
Connect
The server is a streamable-HTTP MCP endpoint. Configure your client with the endpoint and the person's agent key as a bearer token:
| Server name | billoquy (v1.0.0) |
|---|---|
| Endpoint | https://mcp.billoquy.com/mcp |
| Auth header | Authorization: Bearer <agent key> |
A typical client config looks like this:
{
"mcpServers": {
"billoquy": {
"url": "https://mcp.billoquy.com/mcp",
"headers": { "Authorization": "Bearer <agent key>" }
}
}
}list_checks and the two resources need no auth, so a client can discover the surface before a key is attached. Every tool that touches an account needs the key. Clients that cannot send headers may instead pass the key as an accessToken argument on each account-scoped tool.
Auth: the agent key
An agent key is a billoquy session bound to a person's account with the purposeagent. It acts on that person's behalf: it is owner-scoped to their account and nothing else. It is the same token model the web app uses, with one added purpose — there is no parallel auth scheme.
- Minted by the person, human-only. The signed-in account owner creates a key with
POST /v1/agent-tokens. Minting is a human action — an agent cannot mint or revoke its own credentials. The key is returned once and never readable again. - Revoked by the person.
DELETE /v1/agent-tokensrevokes a key by value (owner-checked). See the developer API for both routes. - Fails cleanly when absent. An account-scoped tool called without a key returns an agent-legible error pointing to where the person creates one — never a silent or misleading result.
The approval guardrail
An agent can prepare — only the human approves
An agent connected to billoquy can discover checks, start them, read findings, preview a prepared fix, and hand the person a link to approve or pay. It can neverapprove, file, send, deliver, or pay. This is not a convention — it holds at three layers:
1. The edge (the real gate). Approving or declining a prepared fix requires ahuman token. An agent key — owner-scoped though it is — gets a 403 at the approval endpoint, no matter what any client does.
2. The MCP surface. There is simply no approve, decline, file, send, or pay tool. The client the tools are built on has no such method, so it cannot reach that endpoint.
3. Preview, then hand off. get_prepared_fix returns the drafted artifact and an approval link for the person to open in their own signed-in session — nothing more.
Tools
Seven tools. Only list_checks is unauthenticated.
| Tool | Auth | What it does |
|---|---|---|
list_checks | none | Discover the four checks and the approval policy. |
start_check | agent key | Start a free check for the owner. |
get_check | agent key | Poll/read a check and its finding. |
get_prepared_fix | agent key | Preview a prepared fix + get the approval link. |
start_fix_checkout | agent key | Get a Stripe Checkout link to hand the person. |
get_billing_portal | agent key | Get the self-serve billing portal link. |
list_account | agent key | List the account's checks, fixes, and receipts. |
list_checks
No input, no auth. Returns { checks, approvalPolicy }. Each entry inchecks carries id, name, tagline, available, finds, record, recordUrl, worth, input[], requiresDocument, documentHint and, when not available yet, anavailabilityNote.
→ list_checks {}
← {
"checks": [
{
"id": "energy",
"name": "Energy Check",
"available": true,
"finds": "the wrong rate plan",
"record": "public utility rate database",
"recordUrl": "https://apps.openei.org/USURDB/",
"worth": "$200–600/yr",
"input": [{ "name": "zip", "required": true, "description": "5-digit US ZIP code" }],
"requiresDocument": true,
"documentHint": "a photo or PDF of a recent utility bill"
}
// ...three more
],
"approvalPolicy": "billoquy prepares; the person approves. ..."
}start_check
Inputs: product (one of the four ids), intake (the structured fields for that check — see per-check intake), and for document-centric checks either documentUrl (a public https link to a PDF or photo) or uploadKey(already staged with billoquy). Optional accessToken.
- A partial-coverage check (Medical) still starts, but if the person's hospital isn't loaded yet the diagnostic resolves to
status: "unsupported"onget_check, with aregisterInterestUrl— nothing is charged. - A document check with neither
documentUrlnoruploadKeyreturns an error telling you to pass one (or have the person upload it). - On success:
{ status, diagnosticId, next }. Pollget_checkwith the id.
→ start_check {
"product": "energy",
"intake": { "zip": "78704" },
"documentUrl": "https://example.com/my-utility-bill.pdf"
}
← { "status": "received", "diagnosticId": "diag_…", "next": "Poll get_check …" }get_check
Input: diagnosticId (+ optional accessToken). The response depends on status:
- complete:
{ status, product, headline, detail, dollars, confidence, worthFixing, citations }, wheredollarsis{ amount, basis: "annual" | "one-time", currency: "USD" }ornull, and each citation is{ label, source, url }. - unsupported:
{ status, product, message, registerInterestUrl }. - failed:
{ status, product, reason }. - otherwise (still running):
{ status, product, note }— poll again shortly.
→ get_check { "diagnosticId": "diag_…" }
← {
"status": "complete",
"product": "energy",
"headline": "You're on the wrong rate plan",
"detail": "Switching to Plan B saves about …",
"dollars": { "amount": 340, "basis": "annual", "currency": "USD" },
"confidence": "high",
"worthFixing": true,
"citations": [
{ "label": "Utility tariff", "source": "OpenEI USURDB", "url": "https://apps.openei.org/USURDB/" }
]
}get_prepared_fix
Input: diagnosticId (+ optional accessToken). If a fix has been drafted (the person paid), returns{ prepared: true, title, fixStatus, artifactPreview, approval } whereapproval is { required: true, by: "human", url, note } — the link the person opens to approve. If not, returns{ prepared: false, message, diagnosticPage }. This tool never approves.
start_fix_checkout
Input: diagnosticId (+ optional accessToken). Returns{ checkoutUrl, note } — a Stripe Checkout link to hand the person. The agent cannot pay; the person completes payment themselves, after which billoquy drafts the fix and it waits for their approval.
get_billing_portal
Input: optional accessToken. Returns { portalUrl, note } — the self-serve Stripe billing portal link for the person. No money moves through the agent.
list_account
Input: optional accessToken. Owner-scoped summary:{ user, diagnostics, preparedFixes, receipts } — diagnostics with status and finding headline, prepared fixes with their approval status (approvedVia), and receipts (whatWeDid, whatItMeans, whatHappensNext).
Per-check intake
The intake object for start_check, per product. Calllist_checks for the authoritative, machine-readable version.
| product | intake fields | document | available |
|---|---|---|---|
medical | zip (5-digit) | Yes — an itemized hospital bill | rolling out |
energy | zip (5-digit) | Yes — a utility bill | yes |
internet | zip (5-digit) | Yes — an internet or TV bill | yes |
fees | (none) | Yes — a bank or card statement | yes |
Resources
Two read-only resources describe the surface without any tool call:
billoquy://checks— the four checks as JSON (application/json): what each finds, its public record, inputs, and availability. The same datalist_checksreturns.billoquy://approval-policy— the "we prepare, you approve" guarantee in prose (text/plain): what an agent can and cannot do.
A worked example
Running an Energy Check for the connected person and handing them the payment and approval links:
- Connect with the person's agent key as a bearer token.
list_checks→ confirmenergyis available and needs a document.start_checkwith{ product: "energy", intake: { zip }, documentUrl }→ adiagnosticId.get_checkon that id untilstatus: "complete"→ the finding, withdollarsand citations.- If
worthFixing:start_fix_checkout→ hand the personcheckoutUrlto pay. - After they pay:
get_prepared_fix→ hand themapproval.url. They approve on the web; the agent never does.
Honest limits
- Availability. Medical depends on data loaded hospital by hospital, so a started check may come back from
get_checkasunsupportedwith aregisterInterestUrl. Relay it — do not lead the person into a dead end. - Polling.
get_checkis a single fetch; there is no server-side long-poll. Poll it until the status is terminal. - Documents.
documentUrlmust be a publicly fetchablehttps link (it is fetched under strict SSRF guards). Documents behind a login must be uploaded by the person; the tool's error copy says so. - Prefer raw HTTP? The MCP is a pure client of the same edge API — see thedeveloper API reference.