x402-surface-check 0.2.38 → 0.2.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/x402-surface-check.mjs +94 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ npx --yes x402-surface-check --strict-proof https://api.example.com/openapi.json
|
|
|
20
20
|
|
|
21
21
|
## What It Checks
|
|
22
22
|
|
|
23
|
-
- Manifest endpoint discovery from `items[]`, `endpoints[]`, object-valued `endpoints`, string-valued endpoint maps, `tools` maps, `resources[]`, `x402Endpoints`, category arrays, raw resource URL strings, method-prefixed resource strings, and OpenAPI paths
|
|
23
|
+
- Manifest endpoint discovery from `items[]`, `endpoints[]`, marketplace `skills[]` / `catalog.skills[]`, `agents[].tools[]` resource URLs, object-valued `endpoints`, string-valued endpoint maps, `tools` maps, `resources[]`, `x402Endpoints`, category arrays, raw resource URL strings, method-prefixed resource strings, and OpenAPI paths
|
|
24
24
|
- Streamable HTTP MCP tool catalogs via safe JSON-RPC `tools/list` probes with `Accept: application/json, text/event-stream`
|
|
25
25
|
- Object-valued manifest endpoint query examples, public catalog/discovery GETs, and payment-bearing two-phase operations without treating expected public catalog reads as failed payment gates
|
|
26
26
|
- Linked discovery documents via `discovery_url`, `discoveryUrl`, `resources_url`, `resourcesUrl`, string `discovery` links, nested `discovery.x402_json` / OpenAPI links, or manifest-level OpenAPI links
|
|
@@ -358,6 +358,40 @@ function manifestEndpointBody(endpoint, document) {
|
|
|
358
358
|
return exampleValue(body, document)
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
+
function marketplaceSkillBody(skill) {
|
|
362
|
+
return skill?.example?.input
|
|
363
|
+
?? skill?.exampleInput
|
|
364
|
+
?? skill?.input?.example
|
|
365
|
+
?? skill?.input?.safe_example
|
|
366
|
+
?? skill?.input?.safeExample
|
|
367
|
+
?? manifestEndpointBody(skill, {})
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function marketplaceSkillPriceUsd(skill) {
|
|
371
|
+
const value = skill?.price?.amount
|
|
372
|
+
?? skill?.price?.usd
|
|
373
|
+
?? skill?.priceUsd
|
|
374
|
+
?? skill?.price_usd
|
|
375
|
+
?? skill?.pricePerCall
|
|
376
|
+
return numberFromDecimal(value)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function endpointRawPath(endpoint) {
|
|
380
|
+
if (!endpoint || typeof endpoint !== 'object') return undefined
|
|
381
|
+
const values = [
|
|
382
|
+
endpoint.url,
|
|
383
|
+
endpoint.endpoint,
|
|
384
|
+
endpoint.resourceUrl,
|
|
385
|
+
endpoint.resourceURL,
|
|
386
|
+
endpoint.resource_url,
|
|
387
|
+
endpoint.resource?.url,
|
|
388
|
+
endpoint.resource?.uri,
|
|
389
|
+
endpoint.resource,
|
|
390
|
+
endpoint.path,
|
|
391
|
+
]
|
|
392
|
+
return values.find(value => typeof value === 'string' && value.trim())
|
|
393
|
+
}
|
|
394
|
+
|
|
361
395
|
function manifestEndpointUrl(rawPath, endpoint, baseUrl, sourceUrl) {
|
|
362
396
|
const url = new URL(endpointUrl(rawPath, baseUrl, sourceUrl))
|
|
363
397
|
const parameters = endpoint?.parameters
|
|
@@ -404,9 +438,64 @@ function endpointEntries(document, sourceUrl, limit) {
|
|
|
404
438
|
}
|
|
405
439
|
}
|
|
406
440
|
|
|
441
|
+
const marketplaceSkillLists = [
|
|
442
|
+
document.skills,
|
|
443
|
+
document.services,
|
|
444
|
+
document.catalog?.skills,
|
|
445
|
+
document.catalog?.services,
|
|
446
|
+
].filter(Array.isArray)
|
|
447
|
+
|
|
448
|
+
for (const skillList of marketplaceSkillLists) {
|
|
449
|
+
for (const skill of skillList) {
|
|
450
|
+
if (!skill || typeof skill !== 'object') continue
|
|
451
|
+
const rawPath = endpointRawPath(skill)
|
|
452
|
+
if (!rawPath) continue
|
|
453
|
+
if (redactedCredentialUrl(rawPath)) continue
|
|
454
|
+
entries.push({
|
|
455
|
+
name: skill.slug ?? skill.id ?? skill.name ?? String(rawPath).split('/').filter(Boolean).at(-1) ?? String(rawPath),
|
|
456
|
+
url: endpointUrl(rawPath, baseUrl, sourceUrl),
|
|
457
|
+
method: String(skill.method ?? 'POST').toUpperCase(),
|
|
458
|
+
expectedPriceUsd: marketplaceSkillPriceUsd(skill),
|
|
459
|
+
requestBody: marketplaceSkillBody(skill),
|
|
460
|
+
})
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const agentCatalogLists = [
|
|
465
|
+
document.agents,
|
|
466
|
+
document.catalog?.agents,
|
|
467
|
+
document.marketplace?.agents,
|
|
468
|
+
].filter(Array.isArray)
|
|
469
|
+
|
|
470
|
+
for (const agentList of agentCatalogLists) {
|
|
471
|
+
for (const agent of agentList) {
|
|
472
|
+
if (!agent || typeof agent !== 'object' || !Array.isArray(agent.tools)) continue
|
|
473
|
+
const agentName = agent.agentId ?? agent.agent_id ?? agent.slug ?? agent.id ?? agent.name
|
|
474
|
+
for (const tool of agent.tools) {
|
|
475
|
+
if (!tool || typeof tool !== 'object') continue
|
|
476
|
+
const rawPath = endpointRawPath(tool)
|
|
477
|
+
if (!rawPath) continue
|
|
478
|
+
if (redactedCredentialUrl(rawPath)) continue
|
|
479
|
+
const method = String(tool.method ?? 'POST').toUpperCase()
|
|
480
|
+
const paymentSignal = Math.max(manifestEndpointPaymentSignal(agent), manifestEndpointPaymentSignal(tool))
|
|
481
|
+
const hasPathParameters = /\{[^}]+\}/.test(String(rawPath))
|
|
482
|
+
if (paymentSignal === 0 && (method !== 'GET' || hasPathParameters)) continue
|
|
483
|
+
const toolName = tool.slug ?? tool.id ?? tool.name ?? String(rawPath).split('/').filter(Boolean).at(-1)
|
|
484
|
+
entries.push({
|
|
485
|
+
name: agentName && toolName ? `${agentName}/${toolName}` : toolName ?? agentName ?? String(rawPath),
|
|
486
|
+
url: manifestEndpointUrl(rawPath, tool, baseUrl, sourceUrl),
|
|
487
|
+
method,
|
|
488
|
+
expectedPriceUsd: marketplaceSkillPriceUsd(tool) ?? marketplaceSkillPriceUsd(agent),
|
|
489
|
+
requestBody: marketplaceSkillBody(tool) ?? marketplaceSkillBody(agent),
|
|
490
|
+
publicDiscovery: paymentSignal === 0,
|
|
491
|
+
})
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
407
496
|
if (Array.isArray(document.endpoints)) {
|
|
408
497
|
for (const endpoint of document.endpoints) {
|
|
409
|
-
const rawPath = endpoint
|
|
498
|
+
const rawPath = endpointRawPath(endpoint)
|
|
410
499
|
if (!rawPath) continue
|
|
411
500
|
entries.push({
|
|
412
501
|
name: endpoint.id ?? endpoint.name ?? String(rawPath).split('/').filter(Boolean).at(-1) ?? String(rawPath),
|
|
@@ -446,7 +535,7 @@ function endpointEntries(document, sourceUrl, limit) {
|
|
|
446
535
|
if (Array.isArray(document.tools)) {
|
|
447
536
|
for (const tool of document.tools) {
|
|
448
537
|
if (!tool || typeof tool !== 'object') continue
|
|
449
|
-
const rawPath = tool
|
|
538
|
+
const rawPath = endpointRawPath(tool)
|
|
450
539
|
if (!rawPath) continue
|
|
451
540
|
const method = String(tool.method ?? 'POST').toUpperCase()
|
|
452
541
|
const paymentSignal = manifestEndpointPaymentSignal(tool)
|
|
@@ -475,7 +564,7 @@ function endpointEntries(document, sourceUrl, limit) {
|
|
|
475
564
|
}
|
|
476
565
|
if (!endpoint || typeof endpoint !== 'object') continue
|
|
477
566
|
const keyPath = key.match(/^(GET|POST|PUT|PATCH|DELETE)\s+(\S+)/i)?.[2] ?? key
|
|
478
|
-
const rawPath = endpoint
|
|
567
|
+
const rawPath = endpointRawPath(endpoint) ?? keyPath
|
|
479
568
|
if (!rawPath) continue
|
|
480
569
|
const method = String(endpoint.method ?? 'POST').toUpperCase()
|
|
481
570
|
const paymentSignal = manifestEndpointPaymentSignal(endpoint)
|
|
@@ -494,7 +583,7 @@ function endpointEntries(document, sourceUrl, limit) {
|
|
|
494
583
|
if (Array.isArray(document.items)) {
|
|
495
584
|
for (const item of document.items) {
|
|
496
585
|
if (item?.type && item.type !== 'http') continue
|
|
497
|
-
const rawPath = item
|
|
586
|
+
const rawPath = endpointRawPath(item)
|
|
498
587
|
if (!rawPath) continue
|
|
499
588
|
entries.push({
|
|
500
589
|
name: item.metadata?.name ?? item.id ?? item.name ?? String(rawPath).split('/').filter(Boolean).at(-1) ?? String(rawPath),
|
|
@@ -547,7 +636,7 @@ function endpointEntries(document, sourceUrl, limit) {
|
|
|
547
636
|
}
|
|
548
637
|
|
|
549
638
|
if (!resource || typeof resource !== 'object') continue
|
|
550
|
-
const rawPath = resource
|
|
639
|
+
const rawPath = endpointRawPath(resource)
|
|
551
640
|
if (!rawPath) continue
|
|
552
641
|
entries.push({
|
|
553
642
|
name: resource.id
|