Skip to main content
Find what to pay for on Stellar. apps/agent is the agent-facing half of STELLARSIGHT: an MCP server that drops the Stellar Bazaar directly into an AI agent’s runtime. The agent searches the bazaar in natural language, reads a resource’s exact call contract, and then actually pays for it — the full discover → 402 → sign → retry → settle x402 loop happens inside a single tool call. No API key. No vendor account. No human in the loop. Just a wallet. This is RFP requirement 3.3. Testnet only. No relayer, no third-party channel service — the payment is signed locally with @x402/stellar and settled by the STELLARSIGHT facilitator on stellar:testnet.

Quick start

Everything here starts and degrades cleanly with the stack down. If the index or the seller is not reachable you get a coded rejection with an explanation, never a stack trace.

Wiring it into Claude

Claude Desktop — claude_desktop_config.json

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

Claude Code

or in .mcp.json at the project root:
PAYER_SECRET and friends are read from the repo-root /.env when they are not in env, so the plain form above works once scripts/setup-testnet.mjs has run. Diagnostics go to stderr only — stdout is the JSON-RPC transport.

Tools

All four tools return both a JSON text block (for the model) and structuredContent (for the host). Every result is { ok: true, ... } or { ok: false, code, reason }. reason is never null on a rejection. Ranked natural-language search over the bazaar. Each candidate carries the index’s _explain breakdown, so the ranking is auditable rather than a black box.

stellarsight_browse

Unranked catalogue listing. Use it to see what exists, or to enumerate one seller’s endpoints.

stellarsight_describe

Full discovery metadata for one resource, flattened into a call-construction brief: every parameter with its type, whether it is required, and its description — so the agent can build a valid call with no external documentation.

stellarsight_pay

The whole x402 loop in one call: request → 402 challenge → sign the Soroban auth entry with the operator’s PAYER_SECRET → retry with the payment header → return the unlocked body plus the settled transaction hash and its explorer link. Spends real testnet funds. Set maxPrice to cap it.
A free resource is not an error: it returns { ok: true, paid: false, body }. timings is always present, including on rejections — the UI reads it.

Error codes

Every rejection is { ok: false, code, reason } with a non-null, human-readable reason. Nothing throws out of a tool handler. The enum is exported as ERROR_CODES from src/pay.mjs, and facilitator failure strings are mapped onto it deterministically by classifySettleFailure().

The terminal demo

Four movements, narrated live:

Using the payment client directly

payAndFetch never throws for a protocol or network failure — it resolves to a coded result.

Tests

Proves that a replayed PAYMENT-SIGNATURE header and an expired authorization entry are both refused, each with a non-null reason and the right code, and that no rejection path in the client can produce a null reason. The first three tests are hermetic (local stub seller, throwaway keypair, no network). The fourth exercises the live facilitator and skips cleanly when it is not running.

Environment

Read from the repo-root /.env, overridable by process.env or the MCP env block.

Protocol notes

Verified against the installed @x402/core@2.21 build, not assumed:
  • 402 challenge arrives in the PAYMENT-REQUIRED response header (base64 JSON). STELLARSIGHT also accepts the same object in the JSON body, which is what several v2 servers emit.
  • The signed payload goes out as PAYMENT-SIGNATURE (x402 v2); STELLARSIGHT mirrors it onto X-PAYMENT so v1-shaped sellers work unchanged.
  • Settlement comes back in PAYMENT-RESPONSE / X-PAYMENT-RESPONSE as { success, errorReason, transaction, network, payer }.
  • Bazaar extension status rides on EXTENSION-RESPONSES (base64 JSON) and is surfaced as extensions.bazaar on the result.