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

@0rkz0rkz 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:

MetricValue
pagination.total15,155 (15,157 items actually observed across pages)
x402Version 214,766
x402Version 1391
Rows with a metadata object at all0 — it is null on every single row
Rows with a non-empty top-level description15,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 ownerAugust 21, 2026 02:39
@cb-heimdall

cb-heimdall commented Aug 21, 2026

Copy link
Copy Markdown

🟡 Heimdall Review Status

RequirementStatusMore Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider python typescript labels Aug 21, 2026
@0rkz
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 38219c4 to 7fb3736CompareAugust 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
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 7fb3736 to 7638f11CompareAugust 21, 2026 03:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationpythontypescript

Development

Successfully merging this pull request may close these issues.

2 participants

@0rkz@cb-heimdall
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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

@0rkz0rkz 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:

MetricValue
pagination.total15,155 (15,157 items actually observed across pages)
x402Version 214,766
x402Version 1391
Rows with a metadata object at all0 — it is null on every single row
Rows with a non-empty top-level description15,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 ownerAugust 21, 2026 02:39
@cb-heimdall

cb-heimdall commented Aug 21, 2026

Copy link
Copy Markdown

🟡 Heimdall Review Status

RequirementStatusMore Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider python typescript labels Aug 21, 2026
@0rkz
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 38219c4 to 7fb3736CompareAugust 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
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 7fb3736 to 7638f11CompareAugust 21, 2026 03:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationpythontypescript

Development

Successfully merging this pull request may close these issues.

2 participants

@0rkz@cb-heimdall
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@0rkz0rkz 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:

MetricValue
pagination.total15,155 (15,157 items actually observed across pages)
x402Version 214,766
x402Version 1391
Rows with a metadata object at all0 — it is null on every single row
Rows with a non-empty top-level description15,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 ownerAugust 21, 2026 02:39
@cb-heimdall

cb-heimdall commented Aug 21, 2026

Copy link
Copy Markdown

🟡 Heimdall Review Status

RequirementStatusMore Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider python typescript labels Aug 21, 2026
@0rkz
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 38219c4 to 7fb3736CompareAugust 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
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 7fb3736 to 7638f11CompareAugust 21, 2026 03:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationpythontypescript

Development

Successfully merging this pull request may close these issues.

2 participants

@0rkz@cb-heimdall
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@0rkz0rkz 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:

MetricValue
pagination.total15,155 (15,157 items actually observed across pages)
x402Version 214,766
x402Version 1391
Rows with a metadata object at all0 — it is null on every single row
Rows with a non-empty top-level description15,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 ownerAugust 21, 2026 02:39
@cb-heimdall

cb-heimdall commented Aug 21, 2026

Copy link
Copy Markdown

🟡 Heimdall Review Status

RequirementStatusMore Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider python typescript labels Aug 21, 2026
@0rkz
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 38219c4 to 7fb3736CompareAugust 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
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 7fb3736 to 7638f11CompareAugust 21, 2026 03:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationpythontypescript

Development

Successfully merging this pull request may close these issues.

2 participants

@0rkz@cb-heimdall
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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

@0rkz0rkz 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:

MetricValue
pagination.total15,155 (15,157 items actually observed across pages)
x402Version 214,766
x402Version 1391
Rows with a metadata object at all0 — it is null on every single row
Rows with a non-empty top-level description15,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 ownerAugust 21, 2026 02:39
@cb-heimdall

cb-heimdall commented Aug 21, 2026

Copy link
Copy Markdown

🟡 Heimdall Review Status

RequirementStatusMore Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider python typescript labels Aug 21, 2026
@0rkz
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 38219c4 to 7fb3736CompareAugust 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
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 7fb3736 to 7638f11CompareAugust 21, 2026 03:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationpythontypescript

Development

Successfully merging this pull request may close these issues.

2 participants

@0rkz@cb-heimdall
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@0rkz0rkz 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:

MetricValue
pagination.total15,155 (15,157 items actually observed across pages)
x402Version 214,766
x402Version 1391
Rows with a metadata object at all0 — it is null on every single row
Rows with a non-empty top-level description15,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 ownerAugust 21, 2026 02:39
@cb-heimdall

cb-heimdall commented Aug 21, 2026

Copy link
Copy Markdown

🟡 Heimdall Review Status

RequirementStatusMore Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider python typescript labels Aug 21, 2026
@0rkz
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 38219c4 to 7fb3736CompareAugust 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
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 7fb3736 to 7638f11CompareAugust 21, 2026 03:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationpythontypescript

Development

Successfully merging this pull request may close these issues.

2 participants

@0rkz@cb-heimdall
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@0rkz0rkz 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:

MetricValue
pagination.total15,155 (15,157 items actually observed across pages)
x402Version 214,766
x402Version 1391
Rows with a metadata object at all0 — it is null on every single row
Rows with a non-empty top-level description15,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 ownerAugust 21, 2026 02:39
@cb-heimdall

cb-heimdall commented Aug 21, 2026

Copy link
Copy Markdown

🟡 Heimdall Review Status

RequirementStatusMore Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider python typescript labels Aug 21, 2026
@0rkz
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 38219c4 to 7fb3736CompareAugust 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
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 7fb3736 to 7638f11CompareAugust 21, 2026 03:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationpythontypescript

Development

Successfully merging this pull request may close these issues.

2 participants

@0rkz@cb-heimdall
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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

@0rkz0rkz 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:

MetricValue
pagination.total15,155 (15,157 items actually observed across pages)
x402Version 214,766
x402Version 1391
Rows with a metadata object at all0 — it is null on every single row
Rows with a non-empty top-level description15,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 ownerAugust 21, 2026 02:39
@cb-heimdall

cb-heimdall commented Aug 21, 2026

Copy link
Copy Markdown

🟡 Heimdall Review Status

RequirementStatusMore Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider python typescript labels Aug 21, 2026
@0rkz
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 38219c4 to 7fb3736CompareAugust 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
0rkzforce-pushed the fix/x402-v2-discovery-description branch from 7fb3736 to 7638f11CompareAugust 21, 2026 03:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationpythontypescript

Development

Successfully merging this pull request may close these issues.

2 participants

@0rkz@cb-heimdall