Skip to content

fix(x402): read v2 discovery resource description from top-level field - #1460

Open
0rkz wants to merge 1 commit into
coinbase:mainfrom
0rkz:fix/x402-v2-discovery-description
Open

fix(x402): read v2 discovery resource description from top-level field#1460
0rkz wants to merge 1 commit into
coinbase:mainfrom
0rkz:fix/x402-v2-discovery-description

Conversation

@0rkz

@0rkz 0rkz commented Aug 21, 2026

Copy link
Copy Markdown

Description

The x402 Bazaar discovery API (https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources)
returns v2 resources with description as a top-level field on the resource
(resource.description), not under metadata.description.

Context/disclosure: we are BYTEDev Inc, operator of PayPerByte — an x402 seller listed in the CDP
Bazaar — and noticed that our, and nearly all v2, resources never surface through
discover_x402_services. The defect and the fix are ecosystem-wide, not specific to any seller.

getResourceDescription() (TypeScript) and _get_resource_description() (Python) only checked
metadata.description for v2 resources. As a result, filterByDescription() /
filter_by_description() — and filterByKeyword() / filter_by_keyword(), which use the same
helper — discard the large majority of v2 resources returned by the live discovery API today,
since essentially none of them populate metadata.description.

Scope of the defect (full-corpus verification, not a sample)

Verified by paging the entire discovery API (limit=1000&offset=N), reproducible the same way:

Metric Value
pagination.total 15,155 (15,157 items actually observed across pages)
x402Version 2 14,766
x402Version 1 391
Rows with a metadata object at all 0 — it is null on every single row
Rows with a non-empty top-level description 15,072

Simulating the default discovery chain an agent actually runs (x402Versions: [1, 2], a
base-mainnet wallet, no keyword/price filter): of 14,917 base-network resources, 366 survive
the current description filter (2.5%)
— those are the v1 remainder plus a handful of edge
shapes. With this fix, 14,832 survive instead. This is not a case of the filter returning
nothing; it discards roughly 97.5% of base-network resources that would otherwise be discoverable.

This affects the published package too, not just main: the latest @coinbase/agentkit on npm
(0.10.4, 2025-12-19) carries the same metadata.description-only check at
dist/action-providers/x402/utils.js:129-142 — the defect is already in the installable artifact,
not just in-progress work on the default branch.

Fix

For v2 resources, both getResourceDescription()/_get_resource_description() now check, in
order:

  1. resource.description (top-level — the live API's actual shape)
  2. resource.metadata.description (fallback, for any resource still using the older/documented shape)
  3. accepts[].description (fallback, mirroring the v1 path)

The v1 path (accepts[].description only) is unchanged. DiscoveryResource (TypeScript interface
/ Python TypedDict) gained an optional top-level description field to match.

This isn't a trivial one-liner: 47 insertions across the 4 changed logic files
(utils.ts+constants.ts, utils.py+constants.py), not counting the new test files, the
changeset, or the changelog entry — the fallback chain and its ordering matter, and each layer is
covered by its own test case below.

Tests

Being upfront about the nature of this testing: I verified this with unit tests plus live API
shape evidence
, not by running an actual chatbot/agent example end-to-end. I don't have CDP
credentials configured (intentionally, to keep this change unit-test-only), so I could not
exercise the full discoverX402Services action against a live wallet. The full-corpus numbers
above are from our own verification run against the live discovery API (paging the whole
corpus, not a sample) — reproducible by anyone with limit=1000&offset=N against the URL in the
Description section.

TypeScript (typescript/agentkit/src/action-providers/x402/utils.test.ts, new file):

  • v2 resource with a top-level description is kept
  • v2 resource with only metadata.description is kept (fallback still works)
  • top-level description wins when both are present
  • v2 resource with no description anywhere is dropped
  • v1 accepts[].description path is unchanged
  • the discovery API's default placeholder ("Access to protected content") is still dropped

Ran via pnpm --filter @coinbase/agentkit test (whole package, not just this file): 903/903
passed
, including the 6 new cases. pnpm --filter @coinbase/agentkit lint and tsc --noEmit
both clean.

Python (python/coinbase-agentkit/tests/action_providers/x402/test_utils.py, new file):
same 6 cases, mirrored. Ran via uv run pytest -m "not (e2e or integration)" (whole package):
669 passed (35 e2e/integration tests correctly deselected — no credentials configured).
uv run ruff check . and uv run ruff format . --check both clean.

Also confirmed neither test run makes any live network request: the wallet-provider test suites
mock sendAnalyticsEvent (and global.fetch, on the TS side) at the module level, and this
change doesn't touch analytics/telemetry code at all — no CDP keys were configured anywhere in
either test run.

Checklist

  • Added a changeset (TypeScript: typescript/.changeset/fix-x402-v2-discovery-description.md)
  • Added a changelog entry (Python: python/coinbase-agentkit/changelog.d/fix-x402-v2-discovery-description.bugfix.md)
  • README.md — not updated. Checked typescript/agentkit/src/action-providers/x402/README.md;
    it doesn't document the specific location of the description field (v1 vs v2), so there
    was nothing inaccurate to correct. Flag in review if a maintainer wants it documented
    explicitly now that the shape is pinned down.

@0rkz
0rkz requested a review from murrlincoln as a code owner August 21, 2026 02:39
@cb-heimdall

cb-heimdall commented Aug 21, 2026

Copy link
Copy Markdown

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@github-actions github-actions Bot added documentation Improvements or additions to documentation action provider New action provider python typescript labels Aug 21, 2026
@0rkz
0rkz force-pushed the fix/x402-v2-discovery-description branch from 38219c4 to 7fb3736 Compare August 21, 2026 03:53
The x402 Bazaar discovery API returns v2 resources with description
as a top-level field on the resource (resource.description), not
under metadata.description. getResourceDescription()/
_get_resource_description() only checked metadata.description for
v2 resources, so filterByDescription()/filter_by_description() (and
filterByKeyword/filter_by_keyword, which use the same helper)
discard the large majority of v2 resources returned by the live
discovery API today.

Full-corpus verification (paging the entire discovery API,
limit=1000&offset=N, reproducible the same way): pagination.total
15,155 (15,157 items observed); x402Version 2 = 14,766, v1 = 391;
rows with a metadata object at all = 0 (it is null on every row);
rows with a non-empty top-level description = 15,072. Simulating the
default discovery chain (x402Versions [1,2], base-mainnet wallet, no
keyword/price filter): of 14,917 base-network resources, 366 survive
the current filter (2.5%) -- discarding ~97.5%, not returning
nothing. With this fix, 14,832 survive instead.

Also affects the published package: npm's latest @coinbase/agentkit
(0.10.4, 2025-12-19) carries the same metadata.description-only
check at dist/action-providers/x402/utils.js:129-142 -- the defect
is already in the installable artifact.

Fixed both the TypeScript and Python x402 action providers to check
resource.description first, then metadata.description, then
accepts[].description, for v2 resources. The v1 path is unchanged.
47 insertions across the 4 changed logic files (utils.ts+
constants.ts, utils.py+constants.py), not counting tests/changeset/
changelog.

TS: added typescript/agentkit/src/action-providers/x402/utils.test.ts
covering the v2 top-level/metadata-only/neither cases and confirming
the v1 path and the 'Access to protected content' placeholder are
unaffected. Added a changeset.

Python: mirrored the fix and tests in
python/coinbase-agentkit/tests/action_providers/x402/test_utils.py.
Added a changelog.d entry.

Signed-off-by: 0rkz <paperm2m@gmail.com>
@0rkz
0rkz force-pushed the fix/x402-v2-discovery-description branch from 7fb3736 to 7638f11 Compare August 21, 2026 03:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action provider New action provider documentation Improvements or additions to documentation python typescript

Development

Successfully merging this pull request may close these issues.

2 participants