Skip to content

Engineering blog

Charging an agent for an answer it never got — the metering contract of a paid MCP server

· Jithox engineering

Agents retry. That is not a bug in the agent — it is what a well-built client does when a tool call times out, a source is down, or a rate limit bites. But it collides with the default billing model of paid tools, which is "per call". If a retry loop pays for every attempt, the agent's owner pays for garbage, and the first invoice becomes the last one. Building the metering for our own paid MCP servers turned out to be mostly an honesty problem, not a billing problem. This is the contract we ended up with, and why every piece of it is machine-readable.

The one question the pricing page must answer before the first call

The question is not "what does a call cost". It is: what does a call that could not answer cost? Our answer is EUR 0.00, and the important part is where that answer lives: not in prose, but in the pricing contract itself. GET https://jithox.com/api/pricing/v1 publishes, next to the price per accepted call, two explicit lists for our founding product (E-Invoice) at the top level:

  • chargeable_result_states — outcomes that are real answers, such as valid, invalid, not_registered and no_match.
  • non_chargeable_error_states — outcomes where no answer happened, such as provider_outage, unavailable, timeout, rate_limited and internal_error.

Each server offered for sale then carries its own complete pair of lists in the per_product section of the same document — the states differ per product (a sanctions screen has outcomes an invoice check does not), and the per-product lists are the contract for that server. An agent — or the developer configuring one — can fetch that JSON before the first call and know, per server, which outcomes will spend budget. The price of a call is set by the server, and the pricing document is the published source for what it is.

The distinction that looks pedantic until it costs you money

Notice that not_registered is on the chargeable list while unavailable is not. That is deliberate, and it is the line most metering schemes blur.

  • not_registered is an answer. You asked whether a counterparty is registered on the Peppol network; the registry was reachable; the truthful answer is "no such participant". The tool did its work. An answer you hoped would be different is still an answer.
  • unavailable is not an answer. The registry did not respond, so the tool does not know. Charging for "I don't know" would quietly train the vendor to prefer guessing over admitting an outage.

The EU VIES register — the official source for VAT number status — makes this distinction load-bearing in practice: it has per-member-state downtime and its own error codes for exactly that case. A validation service that maps "source unreachable" onto "VAT number invalid" will confidently tell an agent to block a real counterparty. Our server instruction says it in one line: Truthful "unavailable" beats a guessed answer. For a VAT check those three outcomes are valid or invalid versus unavailable — different states, and only real answers are charged. We wrote about that three-outcome rule before, from the compliance side, in missing evidence is not a green light; this is what it does to the meter.

Retries become safe when the states are honest

Once the two lists exist, retry policy stops being guesswork:

  • A timeout or provider_outage cost nothing and is safe to try again later.
  • An invalid or not_registered is a paid, real answer — retrying it is asking the same question twice.
  • A rate_limited costs nothing and tells the client to slow down, not to give up.

That is the whole point of publishing the states machine-readably: the agent's retry loop can be correct instead of optimistic.

Trial semantics are budget semantics

The second place where silence costs money is the free tier. Ours is 25 accepted calls per workspace, valid for 14 days, shared across every server in the fleet — explicitly not 25 per server. We say that loudly because the misreading is so natural: connect three servers, assume three budgets. One shared allowance, one shared prepaid balance, and each server is its own OAuth resource — so a token minted for one server is refused by another, and a misconfigured client fails closed instead of spending somewhere you did not intend.

Try the shapes before spending anything

Discovery is open and free on every server: initialize and tools/list need no token and cost nothing. The playground shows full request and response shapes for every tool with synthetic data, and the pricing contract is public JSON. If you are building a paid tool for agents yourself, the portable lesson is small: publish which outcomes charge, make "no answer" free, and say your trial semantics out loud — the agents calling you cannot read between lines, and neither should they have to.

Run this check on your own fleet

From nothing to a verified MCP call with a signed receipt — no card, no approval.