Money Machine · Documentation
Clean Table
Deterministic CSV & Table Normalization
What it does
clean_table takes a delimited table and a schema you state — which source column becomes which output field, and of what type — and returns normalized rows against exactly that schema.
A value that cannot be read with certainty is never guessed. It is returned in a separate exceptions array with its row number, the raw source row and a plain reason. Every delivered cell is independently re-checked against its source token before the response is built, and the verification counts are returned with the data. If that check fails, nothing is returned and the payment is cancelled.
Deterministic: the same input and schema produce the same output.
Measured on 4,464 held-out rows from 8 published UK government spend files (Open Government Licence v3.0, four publishers; the held-out split was made before anything was measured): 100% field accuracy, 99.51% straight-through, 0.49% exceptions, 0 silent errors. That is a benchmark on public data, reproducible from the same files — not a guarantee about your data.
Connecting
- MCP endpoint
https://money-machine.pradooskee.workers.dev/mcp- Transport
- MCP over Streamable HTTP (
POST). A browserGETis refused. - Tool
clean_table- Discovery
tools/listis free and unauthenticated and returns the full input schema.
Input
| Property | Type | Rules |
|---|---|---|
csv | string, required | The delimited table including its header row. 1–48,000 characters, at most 2,000 data rows. Blank lines are skipped. |
fields | array, required | 1–12 entries. Each maps one source column to one output field. |
fields[].field | string, required | Output field name, 1–64 characters. |
fields[].column | string, required | Source column header, 1–128 characters, matched case-insensitively with whitespace collapsed. |
fields[].type | enum, required | text, date, money or code — see below. |
fields[].required | boolean | Default true. A missing value, or a missing column, sends the row to exceptions. |
dedupeKey | string | An output field to deduplicate on, case-insensitively; the first occurrence is kept. Omit for no deduplication. |
dateOrder | enum | DMY or MDY: your system's date order. The file's own evidence always wins over this. |
Field types
text— internal whitespace collapsed to single spaces, then trimmed.code— astext, then upper-cased.money— a number. Spaces, thousands separators,$ £ €and a leadingUSD,GBPorEURare removed; parentheses mean negative; rounded to 2 decimal places. Anything that is not then a plain number is an exception, never zero.date— ISOYYYY-MM-DD. AcceptsYYYY-MM-DD,D/M/YYYYorM/D/YYYY(with/or-) and5 Jan 2023. The day/month order is inferred from the file's unambiguous values; if the file proves both orders, every ambiguous date is refused; if it proves neither,dateOrderis used, and without it ambiguous dates are refused.
Output
rows— one object per delivered row, keyed by yourfieldnames.exceptions— one object per refused row:row(its line number in the input, counting the header as line 1),reason, andraw(the whole source row as read).metadata—rowsIn,rowsDelivered,exceptions,duplicatesRemoved,columnsMatched,dateOrder(what was inferred and used, and why),verification(cellsChecked,cellsFailed, method),inputSha256,outputSha256,network,retentionandfulfilledAt.
Example
Request (sent without payment, this returns the payment terms; sent with a valid payment, it returns the result):
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "clean_table",
"arguments": {
"csv": "Supplier,Date,Amount,Ref\nAcme Ltd,25/01/2023,\"£1,234.50\",inv-001\nBeta Co,03/04/2023,(99.00),inv-002\nGamma plc,31/13/2023,12.5,inv-003",
"fields": [
{
"field": "payee",
"column": "Supplier",
"type": "text"
},
{
"field": "date",
"column": "Date",
"type": "date"
},
{
"field": "amount",
"column": "Amount",
"type": "money"
},
{
"field": "ref",
"column": "Ref",
"type": "code"
}
]
}
}
}
Delivered rows and exceptions:
{
"rows": [
{
"payee": "Acme Ltd",
"date": "2023-01-25",
"amount": 1234.5,
"ref": "INV-001"
},
{
"payee": "Beta Co",
"date": "2023-04-03",
"amount": -99,
"ref": "INV-002"
}
],
"exceptions": [
{
"row": 4,
"reason": "unparseable or ambiguous date \"31/13/2023\" in date",
"raw": "Gamma plc,31/13/2023,12.5,inv-003"
}
]
}
Row 3's 03/04/2023 is ambiguous on its own; row 2's 25/01/2023 proves the file is day-first, so it is read as 3 April. Row 4's month 13 cannot be a date, so the row is refused rather than repaired.
Limits
- 48,000 characters of table text, 2,000 data rows and 12 fields per call.
- The whole MCP request must also fit in 64 KiB, which matters for text with many multi-byte characters.
- Inputs beyond these limits are refused with a reason before any payment is requested.
Pricing and payment
- Price
- $0.02 per paid call (
20000atomic units) - Network
- Base mainnet (
eip155:8453) - Asset
- Native Circle USDC:
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 - Pay to
0xF51ABC29be3f0Cc86F189FfC634124d08776A065- Payment
- USDC via Circle Gateway / x402
- Call
clean_tablewithout payment. The response is an x402 v2PaymentRequiredwith exactly one requirement: schemeexact, the price, network, asset and pay-to address above. - Sign that requirement as an EIP-3009 authorization against Circle's GatewayWallet contract named in the requirement's
extra(GatewayWalletBatched). Gateway requires the authorization to stay valid for at least 7 days (minValiditySeconds604800). - Resend the same call with the payment payload. The service verifies the payment with Circle, runs the job, re-checks every delivered cell and returns the result in the same response.
- Circle Gateway settles payments in batches, so a paid call has no transaction hash of its own when it is served; the sale is identified by its payment reference and reconciled to a batch transaction later.
- If the verification pass fails, nothing is returned and the payment is cancelled.
Privacy and retention
- The table content you send is not stored or logged.
- Delivered output is retained for 7 days for paid-response redelivery and then deleted automatically.
- Input/output SHA-256 hashes and transaction records are retained for accounting and reconciliation.
The stored output exists so that a paid response lost in transit can be delivered again without a second charge. It is the delivered result — including exception rows, which contain the raw source row.
One detail: if none of the columns named in your schema appears in the file, the call is refused and not charged, and the refusal message — which lists the column names from your schema, not any table content — is kept in the transaction record.
Errors
- A row that cannot be read with certainty is an exception: the call still succeeds and the row is returned in
exceptionswith its reason. - An input that breaks a limit, or a call whose output fails the verification pass, is refused with a machine-readable code and message, and nothing is charged.