Repository navigation
Conversation
paid_call signs with a real mainnet key, and maxPrice only bounds one mistake — a caller in a retry loop could spend without limit. Add a cumulative USD ceiling that reserves each call's maximum before the payment runs and settles it after, record every attempt in an audit ledger, and refuse to spend at all when a durable ledger was asked for and cannot be written. The same tools are also served over Streamable HTTP, which refuses to start without a bearer token since it exposes paid_call to the network. Cover Divinity-Labs#421: bazaar_search, ListTools and the unknown-tool path had no tests, and the simulated upstream errors were plain strings that hid how handlePaidCallError classifies on lowercased message substrings. Fixtures now use the shapes that @x402/fetch, @x402/core and @x402/stellar really throw, including one Horizon error whose op_no_trust signal is invisible to that matcher.
8 tasks done
The payment ledger registered mcp_payment_* counters on the shared registry, but the MCP process serves no listener that reads it, so the spend of an agent was still only recoverable from the audit file. Serve GET /metrics from the MCP HTTP transport, on the same bearer token as paid_call, and refuse to start when MCP_HTTP_PATH would put the MCP handshake on that path. The network label was always unknown in the shipped server because run.ts only passed the host allow-list. Default the network domain to the two networks the signer is configured for, overridable with MCP_METRIC_NETWORKS, so a series per network exists without opening the label to whatever a seller echoes.
…ibraries throw The original four fixtures were duck-typed stubs: bare Error strings and plain-object responses. Rewire them on the actual chain: the @x402/stellar simulation guard wrapped by @x402/fetch's payment-payload frame, and real Response objects for the 402 and success paths. Drop the hand-rolled handler captures for the file's createHarness.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
src/mcp/budget.ts):paid_callreserves each call's per-call maximum before the payment runs and settles or releases it after, against a UTC daily ceiling. State survives restarts; an unreadable state file is treated as fully spent, not as a clean slate.src/mcp/ledger.ts): everypaid_callattempt is appended to a JSONL trail with outcome, error code, settlement hash and spend class. Signed envelopes and secret material are never recorded; the server refuses to spend when a durable ledger cannot be written. Prometheus counters use a bounded label set.GET /metricson the HTTP transport, same bearer token), with the network label defaulted to the configured signers' networks.bazaar_search(with/without query, empty, non-2xx, malformed body, transport failure),ListTools(advertised set, SDK schema parse, per-toolinputSchemavalidation), and the unknown-tool path. Simulated upstream errors now use the shapes the real libraries throw - the@x402/fetchFailed to create payment payload: ...wrapper around the@x402/stellarsimulation guard, undici'sTypeError: fetch failedwithcause, and a HorizonBadResponsewhoseop_no_trustsignal lives inextras.result_codes(pinned as a characterization test of the substring classifier's blind spot, not an endorsement).Related issue
Closes #405
Closes #406
Closes #419
Closes #421
Addresses #425
Supersedes the closed-unmerged #491. Note for maintainers: the spend-ceiling commits duplicate open #492 (same author) and the HTTP transport overlaps the #419 scope of open #450 - merging this can make both redundant.
Type of change
Checklist
npx tsc --noEmitpassesnpm run buildpassesvitest run tests/mcp.test.ts: 40/40.env.example, three changesets