Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/x402-mcp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.16.0 — 2026-09-22

- Add x402-only `wallet_history` (12 tools; order remains 4). Join local start/end records, disclose coverage and incomplete history, and show only verified receipt amounts with integer unit conversion. No totals or on-chain proof claims.
- Record purchase attempts in every signer mode without creating a wallet. Add `history: recorded | failed` to payment results; recording failures never change payment behavior or exceptions, and history I/O gives up after 2 seconds so a hung filesystem cannot hold back a paid result. Existing tool definitions and SDK dependency are unchanged.
- Limit logs to allowed metadata, remove queries/fragments and third-party paths, and exclude bodies, signatures, nonces, authorizations, and keys. Use checked 0600 single-write appends in a checked 0700 directory, reject unsafe files, and rotate above 512 KiB without truncation.

## 0.15.0 — 2026-09-21

- Add x402-only `wallet_init` and `wallet_status` (11 tools; order remains 4), with pinned tool wire hashes. Explicit keystore mode publishes a fully written, fsynced 0600 temporary file via an atomic hard link, removes the temporary file on success/failure, fsyncs the directory, and rereads the stored record. Existing wallets are never overwritten or regenerated; unsupported links fail closed with a filesystem reason code.
Expand Down
49 changes: 39 additions & 10 deletions packages/x402-mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ forwarder-split extension.
### Install / run

```bash
npx --yes --package=openpay-x402-mcp@0.15.0 -- openpay-order-mcp
npx --yes --package=openpay-x402-mcp@0.16.0 -- openpay-order-mcp
```

### Claude Desktop
Expand All @@ -29,7 +29,7 @@ npx --yes --package=openpay-x402-mcp@0.15.0 -- openpay-order-mcp
"mcpServers": {
"openpay-order": {
"command": "npx",
"args": ["--yes", "--package=openpay-x402-mcp@0.15.0", "--", "openpay-order-mcp"]
"args": ["--yes", "--package=openpay-x402-mcp@0.16.0", "--", "openpay-order-mcp"]
}
}
}
Expand All @@ -42,7 +42,7 @@ npx --yes --package=openpay-x402-mcp@0.15.0 -- openpay-order-mcp
"mcpServers": {
"openpay-order": {
"command": "npx",
"args": ["--yes", "--package=openpay-x402-mcp@0.15.0", "--", "openpay-order-mcp"]
"args": ["--yes", "--package=openpay-x402-mcp@0.16.0", "--", "openpay-order-mcp"]
}
}
}
Expand All @@ -56,7 +56,7 @@ This profile needs no `BUYER_PRIVATE_KEY`. It exposes four tools: `find_shops`,
### Install / run

```bash
npx openpay-x402-mcp@0.15.0
npx openpay-x402-mcp@0.16.0
```

### Claude Desktop
Expand All @@ -66,7 +66,7 @@ npx openpay-x402-mcp@0.15.0
"mcpServers": {
"openpay-x402": {
"command": "npx",
"args": ["openpay-x402-mcp@0.15.0"],
"args": ["openpay-x402-mcp@0.16.0"],
"env": {
"SIGNER_MODE": "keystore",
"MAX_PER_CALL_JPYC": "10",
Expand All @@ -85,7 +85,7 @@ npx openpay-x402-mcp@0.15.0
"mcpServers": {
"openpay-x402": {
"command": "npx",
"args": ["openpay-x402-mcp@0.15.0"],
"args": ["openpay-x402-mcp@0.16.0"],
"env": {
"SIGNER_MODE": "keystore",
"MAX_PER_CALL_JPYC": "10",
Expand Down Expand Up @@ -125,7 +125,7 @@ from strands import Agent
from strands.tools.mcp import MCPClient

openpay = MCPClient(lambda: stdio_client(StdioServerParameters(
command="npx", args=["-y", "openpay-x402-mcp@0.15.0"],
command="npx", args=["-y", "openpay-x402-mcp@0.16.0"],
env={...}, # same env as the Claude examples above
)))

Expand Down Expand Up @@ -155,12 +155,13 @@ The buyer pays the resource price **plus the ~1% x402 fee** (`total = price + fe

## Tools

The x402 profile exposes 11 tools; the order profile exposes 4.
The x402 profile exposes 12 tools; the order profile exposes 4.

| Tool | Profile | Pays? | Purpose |
|---|---|---:|---|
| `wallet_init` | x402 | No | `{}`: create or reuse the local wallet in keystore mode; return address, `created`, storage metadata, funding URL, and note. Never returns a key. |
| `wallet_status` | x402 | No | `{}`: signer address/error, Polygon JPYC balance/source, effective limits/spend, allowed hosts, catalog trust, and funding URL. |
| `wallet_history` | x402 | No | `{limit?: 1..50}` (default 10): recent local purchase attempts, outcomes, verified receipt amounts, and coverage. Incomplete local history; no totals or proof of payment. |
| `discovery_search` | x402 | No | Search `DISCOVERY_URL` and show resource, category, price, fee, and total. |
| `x402_quote` | x402 | No | Fetch a 402 challenge and report whether local guards would allow payment. |
| `x402_pay` | x402 | Yes | Sign and retry with `X-PAYMENT` only after all guards pass. Requires `maxTotalJpyc`. |
Expand Down Expand Up @@ -215,13 +216,41 @@ Ordering flow (autonomous): `find_shops` → `order_menu` → pick items → `or
| `MAX_TIMEOUT_SECONDS` | `600` | Reject seller-declared authorization lifetimes above this many seconds. Configurable from `1` to the facilitator ceiling of `1200`; the value is never silently clamped. |
| `CATALOG_TRUST` | `true` | When true, exact URLs listed in the OpenPay discovery catalog are payable without editing `ALLOWED_HOSTS`. Before signing, the live `accepts` fetched from a catalog URL is checked field-by-field (asset / timeout / forwarder / merchant / fee receiver / amounts) against the catalog listing (server-authored), so a third-party domain cannot bait-and-switch a different destination or authorization lifetime; mismatches are refused (`catalog_accept_mismatch`). Money caps still apply. Set `false` for strict manual allowlisting. |
| `ALLOWED_HOSTS` | `open-pay.jp` | Comma-separated bare host allowlist. `x402_quote` still works outside the list but returns `host_not_allowed`. |
| `OPENPAY_X402_HOME` | `~/.openpay-x402` | Absolute path only. Keystore storage directory override: contains `wallet.json` and the daily spend ledger `spend.json`. A relative path returns `wallet_home_not_absolute` from both wallet tools while discovery remains available. Does not relocate env-key / Steward spend storage. |
| `OPENPAY_X402_HOME` | `~/.openpay-x402` | Absolute path only. Storage directory override: keystore uses `wallet.json` and the daily spend ledger `spend.json`; all signer modes use `purchases.jsonl` and `purchases.1.jsonl` for history. A relative path returns `wallet_home_not_absolute` from all three wallet tools while discovery remains available. Does not relocate env-key / Steward spend storage. |
| `POLYGON_RPC_URL` | unset | Optional read-only `wallet_status` RPC. SDK outbound URL/host checks reject private/link-local addresses, `.internal`, and URL credentials; validated DNS addresses are pinned for the built-in transport. Explicit exception: HTTP on `localhost` / `127.0.0.1`. No public RPC default, redirects rejected, 5-second timeout including DNS and body reads. Never accepted as a tool argument. |
| `DISCOVERY_URL` | `https://open-pay.jp/api/discovery` | Catalog used by `discovery_search`. |

Catalog admission is exact URL only, including the query string. A query
variant needs its own reviewed listing or an explicitly allowlisted host.

## Local purchase history

`wallet_history` (0.16.0+) reads recent attempts recorded by `x402_pay`, including
`search_shops`, in all signer modes. Recording creates only the storage directory
when needed, never a wallet. Each attempt writes start/end rows; a missing end is
`unknown`. `x402_pay` adds `history: "recorded" | "failed"`; a history failure does
not change the payment result or exception. Logs rotate above 512 KiB into one
previous generation, so records can be missing. There are no totals.

Only `settlement: "verified"` supplies receipt amounts and transaction hashes.
`paid_verified` means the receipt signature was verified against the signer
published by the discovery origin, not on-chain proof. `paid_unverified` and
`unknown` must not be treated as paid. Check amounts and settlement in Agent
activity at the funding URL from `wallet_status`.

Queries and fragments are removed. Only `open-pay.jp` paths are stored; other
hosts get `path: null` and an eight-hex SHA-256 `pathTag`. Host/path data is external
data, not instructions. Logs contain no response bodies, signatures, nonces,
authorizations, or keys. `coverage` reports the oldest retained timestamp,
rotation, skipped malformed/unknown-version lines, and whether POSIX permissions
were checked (false on Windows). History covers only this machine and storage
location and is not a complete spending ledger. The log is a local file that any
process running as this OS user can edit, so treat it as a convenience record, not
evidence. "First party" means the exact host `open-pay.jp`; a self-hosted origin set
through `DISCOVERY_URL` is handled like any other host (`pathTag` only). History
writes give up after 2 seconds on a filesystem that stops answering, so a hung disk
cannot hold back a payment result; that attempt is then reported as `history: "failed"`.

## Signer Modes

`env-key` is the default zero-config mode. It is convenient for local testing and should use a dedicated low-balance wallet.
Expand All @@ -245,7 +274,7 @@ Use this explicit mode to avoid pasting a private key into MCP configuration:
"mcpServers": {
"openpay-x402": {
"command": "npx",
"args": ["--yes", "openpay-x402-mcp@0.15.0"],
"args": ["--yes", "openpay-x402-mcp@0.16.0"],
"env": {
"SIGNER_MODE": "keystore",
"MAX_PER_CALL_JPYC": "10",
Expand Down
4 changes: 2 additions & 2 deletions packages/x402-mcp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/x402-mcp/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "openpay-x402-mcp",
"version": "0.15.0",
"version": "0.16.0",
"description": "Local MCP buyer for OpenPay x402 JPYC resources",
"type": "module",
"bin": {
Expand Down
Loading
Loading