fix(x402): read v2 discovery resource description from top-level field - #1460
Open
0rkz wants to merge 1 commit into
Open
fix(x402): read v2 discovery resource description from top-level field#14600rkz wants to merge 1 commit into
0rkz wants to merge 1 commit into
Conversation
🟡 Heimdall Review Status
|
0rkz
force-pushed
the
fix/x402-v2-discovery-description
branch
from
August 21, 2026 03:53
38219c4 to
7fb3736
Compare
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
force-pushed
the
fix/x402-v2-discovery-description
branch
from
August 21, 2026 03:54
7fb3736 to
7638f11
Compare
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.
Description
The x402 Bazaar discovery API (
https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources)returns v2 resources with
descriptionas a top-level field on the resource(
resource.description), not undermetadata.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 checkedmetadata.descriptionfor v2 resources. As a result,filterByDescription()/filter_by_description()— andfilterByKeyword()/filter_by_keyword(), which use the samehelper — 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:pagination.totalmetadataobject at allnullon every single rowdescriptionSimulating the default discovery chain an agent actually runs (
x402Versions: [1, 2], abase-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/agentkiton npm(
0.10.4, 2025-12-19) carries the samemetadata.description-only check atdist/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, inorder:
resource.description(top-level — the live API's actual shape)resource.metadata.description(fallback, for any resource still using the older/documented shape)accepts[].description(fallback, mirroring the v1 path)The v1 path (
accepts[].descriptiononly) is unchanged.DiscoveryResource(TypeScript interface/ Python
TypedDict) gained an optional top-leveldescriptionfield 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, thechangeset, 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
discoverX402Servicesaction against a live wallet. The full-corpus numbersabove 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=Nagainst the URL in theDescription section.
TypeScript (
typescript/agentkit/src/action-providers/x402/utils.test.ts, new file):descriptionis keptmetadata.descriptionis kept (fallback still works)descriptionwins when both are presentaccepts[].descriptionpath is unchanged"Access to protected content") is still droppedRan via
pnpm --filter @coinbase/agentkit test(whole package, not just this file): 903/903passed, including the 6 new cases.
pnpm --filter @coinbase/agentkit lintandtsc --noEmitboth 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 .anduv run ruff format . --checkboth clean.Also confirmed neither test run makes any live network request: the wallet-provider test suites
mock
sendAnalyticsEvent(andglobal.fetch, on the TS side) at the module level, and thischange doesn't touch analytics/telemetry code at all — no CDP keys were configured anywhere in
either test run.
Checklist
typescript/.changeset/fix-x402-v2-discovery-description.md)python/coinbase-agentkit/changelog.d/fix-x402-v2-discovery-description.bugfix.md)typescript/agentkit/src/action-providers/x402/README.md;it doesn't document the specific location of the
descriptionfield (v1 vs v2), so therewas nothing inaccurate to correct. Flag in review if a maintainer wants it documented
explicitly now that the shape is pinned down.