Skip to content

test(mcp): cover bazaar_search, ListTools, and unknown-tool paths - #524

Open
AGWAM001 wants to merge 4 commits into
Divinity-Labs:mainfrom
AGWAM001:mcp-spend-controls-and-tool-coverage
Open

AGWAM001 wants to merge 4 commits into
Divinity-Labs:mainfrom
AGWAM001:mcp-spend-controls-and-tool-coverage

Conversation

@AGWAM001

Copy link
Copy Markdown

Closes #421

Summary

tests/mcp.test.ts only exercised the error paths of paid_call. This PR adds coverage for bazaar_search, ListTools, and the unknown-tool path, and makes simulated upstream errors resemble what the real library throws, so text-matching fragility is no longer hidden from the suite. This is a test-only change: no production code under src/mcp/ is modified.

Changes

bazaar_search

Success with a query and without a query
Empty result set
Non-2xx upstream response, asserting a structured error is returned instead of a best-effort result
Malformed response body, asserting a structured error

ListTools

Asserts the advertised tool names
Validates that each tool’s inputSchema is a well-formed JSON Schema object, so a malformed schema fails the suite

Unknown tool

Calling a tool name that isn’t registered returns the expected structured error rather than throwing or returning partial output

Simulated errors

Upstream errors are now built as Error instances with the properties the real library sets (name, message, and any code or cause fields), rather than plain strings
Added a case showing the current mapping behavior against these shapes
Testing
npx vitest run tests/mcp.test.ts: the four existing paid_call tests still pass unchanged, and the new tests pass
npm test: full suite run; Load test (prices) fails on main already and is unrelated to this PR
Safety notes
All tests use mocked transports and a throwaway test signer. No real or mainnet secret key is read, logged, echoed, or used.
No MCP_AGENT_SECRET_KEY_MAINNET is required to run these tests.
Addresses, contract ids, and keys in fixtures are written out in full, with none shortened or invented. Where a value isn’t needed, fixtures leave it out.
Compatibility

Test-only. No changes to BazaarMcpServer, src/mcp/codes.ts, or the example server and agent.

Checklist
bazaar_search tests for success, empty, non-2xx, and malformed body
ListTools exercised and each schema validated
Unknown-tool path covered
Simulated errors resemble real library errors
Existing four tests pass
No real secrets used or logged

AGWAM001 and others added 4 commits October 17, 2026 23:01
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.
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Only the error paths of one tool are tested

1 participant