@agentcash/discovery 0.1.1 → 0.1.3
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 +51 -0
- package/dist/cli.cjs +4 -1
- package/dist/cli.js +4 -1
- package/dist/index.cjs +868 -3
- package/dist/index.d.cts +167 -1
- package/dist/index.d.ts +167 -1
- package/dist/index.js +862 -2
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -24,14 +24,19 @@ __export(index_exports, {
|
|
|
24
24
|
LEGACY_SUNSET_DATE: () => LEGACY_SUNSET_DATE,
|
|
25
25
|
STRICT_ESCALATION_CODES: () => STRICT_ESCALATION_CODES,
|
|
26
26
|
UPGRADE_WARNING_CODES: () => UPGRADE_WARNING_CODES,
|
|
27
|
+
VALIDATION_CODES: () => VALIDATION_CODES,
|
|
27
28
|
auditContextHarness: () => auditContextHarness,
|
|
28
29
|
buildContextHarnessReport: () => buildContextHarnessReport,
|
|
29
30
|
computeUpgradeSignal: () => computeUpgradeSignal,
|
|
30
31
|
defaultHarnessProbeCandidates: () => defaultHarnessProbeCandidates,
|
|
31
32
|
discover: () => discover,
|
|
32
33
|
discoverDetailed: () => discoverDetailed,
|
|
34
|
+
discoverForMcp: () => discoverForMcp,
|
|
35
|
+
evaluateMetadataCompleteness: () => evaluateMetadataCompleteness,
|
|
33
36
|
getHarnessClientProfile: () => getHarnessClientProfile,
|
|
34
|
-
|
|
37
|
+
inspectEndpointForMcp: () => inspectEndpointForMcp,
|
|
38
|
+
listHarnessClientProfiles: () => listHarnessClientProfiles,
|
|
39
|
+
validatePaymentRequiredDetailed: () => validatePaymentRequiredDetailed
|
|
35
40
|
});
|
|
36
41
|
module.exports = __toCommonJS(index_exports);
|
|
37
42
|
|
|
@@ -49,7 +54,7 @@ var STRICT_ESCALATION_CODES = [
|
|
|
49
54
|
];
|
|
50
55
|
|
|
51
56
|
// src/mmm-enabled.ts
|
|
52
|
-
var isMmmEnabled = () => "0.1.
|
|
57
|
+
var isMmmEnabled = () => "0.1.3".includes("-mmm");
|
|
53
58
|
|
|
54
59
|
// src/core/constants.ts
|
|
55
60
|
var OPENAPI_PATH_CANDIDATES = ["/openapi.json", "/.well-known/openapi.json"];
|
|
@@ -1437,6 +1442,861 @@ async function runDiscovery({
|
|
|
1437
1442
|
};
|
|
1438
1443
|
}
|
|
1439
1444
|
|
|
1445
|
+
// src/validation/codes.ts
|
|
1446
|
+
var VALIDATION_CODES = {
|
|
1447
|
+
COINBASE_SCHEMA_INVALID: "COINBASE_SCHEMA_INVALID",
|
|
1448
|
+
X402_NOT_OBJECT: "X402_NOT_OBJECT",
|
|
1449
|
+
X402_VERSION_MISSING: "X402_VERSION_MISSING",
|
|
1450
|
+
X402_VERSION_UNSUPPORTED: "X402_VERSION_UNSUPPORTED",
|
|
1451
|
+
X402_ACCEPTS_MISSING: "X402_ACCEPTS_MISSING",
|
|
1452
|
+
X402_ACCEPTS_INVALID: "X402_ACCEPTS_INVALID",
|
|
1453
|
+
X402_ACCEPTS_EMPTY: "X402_ACCEPTS_EMPTY",
|
|
1454
|
+
X402_ACCEPT_ENTRY_INVALID: "X402_ACCEPT_ENTRY_INVALID",
|
|
1455
|
+
NETWORK_CAIP2_INVALID: "NETWORK_CAIP2_INVALID",
|
|
1456
|
+
NETWORK_EIP155_REFERENCE_INVALID: "NETWORK_EIP155_REFERENCE_INVALID",
|
|
1457
|
+
NETWORK_SOLANA_ALIAS_INVALID: "NETWORK_SOLANA_ALIAS_INVALID",
|
|
1458
|
+
NETWORK_SOLANA_ALIAS_COMPAT: "NETWORK_SOLANA_ALIAS_COMPAT",
|
|
1459
|
+
NETWORK_REFERENCE_UNKNOWN: "NETWORK_REFERENCE_UNKNOWN",
|
|
1460
|
+
SCHEMA_INPUT_MISSING: "SCHEMA_INPUT_MISSING",
|
|
1461
|
+
SCHEMA_OUTPUT_MISSING: "SCHEMA_OUTPUT_MISSING",
|
|
1462
|
+
METADATA_TITLE_MISSING: "METADATA_TITLE_MISSING",
|
|
1463
|
+
METADATA_DESCRIPTION_MISSING: "METADATA_DESCRIPTION_MISSING",
|
|
1464
|
+
METADATA_FAVICON_MISSING: "METADATA_FAVICON_MISSING",
|
|
1465
|
+
METADATA_OG_IMAGE_MISSING: "METADATA_OG_IMAGE_MISSING"
|
|
1466
|
+
};
|
|
1467
|
+
|
|
1468
|
+
// src/validation/metadata.ts
|
|
1469
|
+
function hasText(value) {
|
|
1470
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
1471
|
+
}
|
|
1472
|
+
function hasOgImage(metadata) {
|
|
1473
|
+
const images = metadata.ogImages;
|
|
1474
|
+
if (!Array.isArray(images) || images.length === 0) return false;
|
|
1475
|
+
return images.some((entry) => {
|
|
1476
|
+
if (typeof entry === "string") {
|
|
1477
|
+
return entry.trim().length > 0;
|
|
1478
|
+
}
|
|
1479
|
+
if (!entry || typeof entry !== "object") return false;
|
|
1480
|
+
return hasText(entry.url);
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
function evaluateMetadataCompleteness(metadata) {
|
|
1484
|
+
const issues = [];
|
|
1485
|
+
if (!hasText(metadata.title)) {
|
|
1486
|
+
issues.push({
|
|
1487
|
+
code: VALIDATION_CODES.METADATA_TITLE_MISSING,
|
|
1488
|
+
severity: "warn",
|
|
1489
|
+
message: "Metadata title is missing",
|
|
1490
|
+
hint: "Provide a page title to improve discovery quality and UX.",
|
|
1491
|
+
path: "metadata.title",
|
|
1492
|
+
stage: "metadata"
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
if (!hasText(metadata.description)) {
|
|
1496
|
+
issues.push({
|
|
1497
|
+
code: VALIDATION_CODES.METADATA_DESCRIPTION_MISSING,
|
|
1498
|
+
severity: "warn",
|
|
1499
|
+
message: "Metadata description is missing",
|
|
1500
|
+
hint: "Provide a concise description for integrators and search previews.",
|
|
1501
|
+
path: "metadata.description",
|
|
1502
|
+
stage: "metadata"
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
if (!hasText(metadata.favicon)) {
|
|
1506
|
+
issues.push({
|
|
1507
|
+
code: VALIDATION_CODES.METADATA_FAVICON_MISSING,
|
|
1508
|
+
severity: "warn",
|
|
1509
|
+
message: "Metadata favicon is missing",
|
|
1510
|
+
hint: "Expose a favicon URL for stronger brand attribution.",
|
|
1511
|
+
path: "metadata.favicon",
|
|
1512
|
+
stage: "metadata"
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
if (!hasOgImage(metadata)) {
|
|
1516
|
+
issues.push({
|
|
1517
|
+
code: VALIDATION_CODES.METADATA_OG_IMAGE_MISSING,
|
|
1518
|
+
severity: "warn",
|
|
1519
|
+
message: "Metadata OG image is missing",
|
|
1520
|
+
hint: "Provide an Open Graph image to improve sharing previews.",
|
|
1521
|
+
path: "metadata.ogImages",
|
|
1522
|
+
stage: "metadata"
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
return issues;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
// src/validation/payment-required.ts
|
|
1529
|
+
var import_schemas = require("@x402/core/schemas");
|
|
1530
|
+
var SOLANA_CANONICAL_BY_REF = {
|
|
1531
|
+
"5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": "solana",
|
|
1532
|
+
EtWTRABZaYq6iMfeYKouRu166VU2xqa1: "solana-devnet",
|
|
1533
|
+
"4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z": "solana-testnet"
|
|
1534
|
+
};
|
|
1535
|
+
var SOLANA_ALIAS_BY_REF = {
|
|
1536
|
+
mainnet: "solana",
|
|
1537
|
+
devnet: "solana-devnet",
|
|
1538
|
+
testnet: "solana-testnet"
|
|
1539
|
+
};
|
|
1540
|
+
function isRecord4(value) {
|
|
1541
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1542
|
+
}
|
|
1543
|
+
function asNonEmptyString(value) {
|
|
1544
|
+
if (typeof value !== "string") return void 0;
|
|
1545
|
+
const trimmed = value.trim();
|
|
1546
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1547
|
+
}
|
|
1548
|
+
function asPositiveNumber(value) {
|
|
1549
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
1550
|
+
}
|
|
1551
|
+
function pushIssue(issues, issue) {
|
|
1552
|
+
issues.push({
|
|
1553
|
+
stage: issue.stage ?? "payment_required",
|
|
1554
|
+
...issue
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
function summarizeIssues(issues) {
|
|
1558
|
+
const summary = {
|
|
1559
|
+
errorCount: 0,
|
|
1560
|
+
warnCount: 0,
|
|
1561
|
+
infoCount: 0,
|
|
1562
|
+
byCode: {}
|
|
1563
|
+
};
|
|
1564
|
+
for (const issue of issues) {
|
|
1565
|
+
if (issue.severity === "error") summary.errorCount += 1;
|
|
1566
|
+
else if (issue.severity === "warn") summary.warnCount += 1;
|
|
1567
|
+
else summary.infoCount += 1;
|
|
1568
|
+
summary.byCode[issue.code] = (summary.byCode[issue.code] ?? 0) + 1;
|
|
1569
|
+
}
|
|
1570
|
+
return summary;
|
|
1571
|
+
}
|
|
1572
|
+
function outputSchemaMissingSeverity(compatMode) {
|
|
1573
|
+
return compatMode === "strict" ? "error" : "warn";
|
|
1574
|
+
}
|
|
1575
|
+
function zodPathToString(path) {
|
|
1576
|
+
if (path.length === 0) return "$";
|
|
1577
|
+
let out = "";
|
|
1578
|
+
for (const segment of path) {
|
|
1579
|
+
if (typeof segment === "number") out += `[${segment}]`;
|
|
1580
|
+
else out += out.length === 0 ? segment : `.${segment}`;
|
|
1581
|
+
}
|
|
1582
|
+
return out;
|
|
1583
|
+
}
|
|
1584
|
+
function parseWithCoinbaseStructuralGate(payload, issues) {
|
|
1585
|
+
const baseParsed = (0, import_schemas.parsePaymentRequired)(payload);
|
|
1586
|
+
if (baseParsed.success) {
|
|
1587
|
+
return baseParsed.data;
|
|
1588
|
+
}
|
|
1589
|
+
for (const issue of baseParsed.error.issues) {
|
|
1590
|
+
pushIssue(issues, {
|
|
1591
|
+
code: VALIDATION_CODES.COINBASE_SCHEMA_INVALID,
|
|
1592
|
+
severity: "error",
|
|
1593
|
+
message: `Coinbase schema validation failed: ${issue.message}`,
|
|
1594
|
+
path: zodPathToString(issue.path),
|
|
1595
|
+
actual: issue.code
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
return void 0;
|
|
1599
|
+
}
|
|
1600
|
+
function validateV2Network(networkRaw, index, issues) {
|
|
1601
|
+
if (!/^[^:\s]+:[^:\s]+$/.test(networkRaw)) {
|
|
1602
|
+
pushIssue(issues, {
|
|
1603
|
+
code: VALIDATION_CODES.NETWORK_CAIP2_INVALID,
|
|
1604
|
+
severity: "error",
|
|
1605
|
+
message: `Accept at index ${index} has invalid CAIP-2 network format`,
|
|
1606
|
+
hint: "Expected '<namespace>:<reference>', e.g. 'eip155:8453' or 'solana:5eykt4...'.",
|
|
1607
|
+
path: `accepts[${index}].network`,
|
|
1608
|
+
expected: "<namespace>:<reference>",
|
|
1609
|
+
actual: networkRaw
|
|
1610
|
+
});
|
|
1611
|
+
return void 0;
|
|
1612
|
+
}
|
|
1613
|
+
const [namespace, reference] = networkRaw.split(":");
|
|
1614
|
+
if (namespace === "eip155") {
|
|
1615
|
+
if (!/^\d+$/.test(reference)) {
|
|
1616
|
+
pushIssue(issues, {
|
|
1617
|
+
code: VALIDATION_CODES.NETWORK_EIP155_REFERENCE_INVALID,
|
|
1618
|
+
severity: "error",
|
|
1619
|
+
message: `Accept at index ${index} has invalid eip155 reference`,
|
|
1620
|
+
hint: "Use a numeric chain id, e.g. 'eip155:8453'.",
|
|
1621
|
+
path: `accepts[${index}].network`,
|
|
1622
|
+
expected: "eip155:<numeric-chain-id>",
|
|
1623
|
+
actual: networkRaw
|
|
1624
|
+
});
|
|
1625
|
+
return void 0;
|
|
1626
|
+
}
|
|
1627
|
+
return networkRaw;
|
|
1628
|
+
}
|
|
1629
|
+
if (namespace === "solana") {
|
|
1630
|
+
const canonical = SOLANA_CANONICAL_BY_REF[reference];
|
|
1631
|
+
if (canonical) return canonical;
|
|
1632
|
+
const alias = SOLANA_ALIAS_BY_REF[reference];
|
|
1633
|
+
if (alias) {
|
|
1634
|
+
pushIssue(issues, {
|
|
1635
|
+
code: VALIDATION_CODES.NETWORK_SOLANA_ALIAS_COMPAT,
|
|
1636
|
+
severity: "warn",
|
|
1637
|
+
message: `Accept at index ${index} uses compatibility Solana reference '${reference}'`,
|
|
1638
|
+
hint: "Use canonical Solana CAIP references for deterministic behavior.",
|
|
1639
|
+
path: `accepts[${index}].network`,
|
|
1640
|
+
actual: networkRaw
|
|
1641
|
+
});
|
|
1642
|
+
return alias;
|
|
1643
|
+
}
|
|
1644
|
+
pushIssue(issues, {
|
|
1645
|
+
code: VALIDATION_CODES.NETWORK_REFERENCE_UNKNOWN,
|
|
1646
|
+
severity: "error",
|
|
1647
|
+
message: `Accept at index ${index} uses unknown Solana reference '${reference}'`,
|
|
1648
|
+
hint: "Use canonical references for mainnet/devnet/testnet.",
|
|
1649
|
+
path: `accepts[${index}].network`,
|
|
1650
|
+
actual: networkRaw
|
|
1651
|
+
});
|
|
1652
|
+
return void 0;
|
|
1653
|
+
}
|
|
1654
|
+
return networkRaw;
|
|
1655
|
+
}
|
|
1656
|
+
function validateV1Network(networkRaw, index, issues) {
|
|
1657
|
+
if (networkRaw === "solana-mainnet-beta") {
|
|
1658
|
+
pushIssue(issues, {
|
|
1659
|
+
code: VALIDATION_CODES.NETWORK_SOLANA_ALIAS_INVALID,
|
|
1660
|
+
severity: "error",
|
|
1661
|
+
message: `Accept at index ${index} uses invalid Solana network alias`,
|
|
1662
|
+
hint: "Use 'solana' (v1) or canonical Solana CAIP reference (v2).",
|
|
1663
|
+
path: `accepts[${index}].network`,
|
|
1664
|
+
expected: "solana",
|
|
1665
|
+
actual: networkRaw
|
|
1666
|
+
});
|
|
1667
|
+
return void 0;
|
|
1668
|
+
}
|
|
1669
|
+
if (networkRaw.startsWith("solana-") && networkRaw !== "solana-devnet") {
|
|
1670
|
+
pushIssue(issues, {
|
|
1671
|
+
code: VALIDATION_CODES.NETWORK_SOLANA_ALIAS_INVALID,
|
|
1672
|
+
severity: "error",
|
|
1673
|
+
message: `Accept at index ${index} uses unsupported Solana network alias`,
|
|
1674
|
+
hint: "Use 'solana' or 'solana-devnet' for v1 payloads.",
|
|
1675
|
+
path: `accepts[${index}].network`,
|
|
1676
|
+
actual: networkRaw
|
|
1677
|
+
});
|
|
1678
|
+
return void 0;
|
|
1679
|
+
}
|
|
1680
|
+
if (networkRaw.includes(":")) {
|
|
1681
|
+
return validateV2Network(networkRaw, index, issues);
|
|
1682
|
+
}
|
|
1683
|
+
return networkRaw;
|
|
1684
|
+
}
|
|
1685
|
+
function validateAccept(acceptRaw, index, version, issues) {
|
|
1686
|
+
if (!isRecord4(acceptRaw)) {
|
|
1687
|
+
pushIssue(issues, {
|
|
1688
|
+
code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
|
|
1689
|
+
severity: "error",
|
|
1690
|
+
message: `Accept at index ${index} must be an object`,
|
|
1691
|
+
path: `accepts[${index}]`
|
|
1692
|
+
});
|
|
1693
|
+
return null;
|
|
1694
|
+
}
|
|
1695
|
+
const scheme = asNonEmptyString(acceptRaw.scheme);
|
|
1696
|
+
const networkRaw = asNonEmptyString(acceptRaw.network);
|
|
1697
|
+
const payTo = asNonEmptyString(acceptRaw.payTo);
|
|
1698
|
+
const asset = asNonEmptyString(acceptRaw.asset);
|
|
1699
|
+
const maxTimeoutSeconds = asPositiveNumber(acceptRaw.maxTimeoutSeconds);
|
|
1700
|
+
const amount = version === 2 ? asNonEmptyString(acceptRaw.amount) : asNonEmptyString(acceptRaw.maxAmountRequired);
|
|
1701
|
+
if (!scheme) {
|
|
1702
|
+
pushIssue(issues, {
|
|
1703
|
+
code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
|
|
1704
|
+
severity: "error",
|
|
1705
|
+
message: `Accept at index ${index} is missing scheme`,
|
|
1706
|
+
path: `accepts[${index}].scheme`
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
if (!networkRaw) {
|
|
1710
|
+
pushIssue(issues, {
|
|
1711
|
+
code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
|
|
1712
|
+
severity: "error",
|
|
1713
|
+
message: `Accept at index ${index} is missing network`,
|
|
1714
|
+
path: `accepts[${index}].network`
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
if (!amount) {
|
|
1718
|
+
pushIssue(issues, {
|
|
1719
|
+
code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
|
|
1720
|
+
severity: "error",
|
|
1721
|
+
message: `Accept at index ${index} is missing ${version === 2 ? "amount" : "maxAmountRequired"}`,
|
|
1722
|
+
path: `accepts[${index}].${version === 2 ? "amount" : "maxAmountRequired"}`
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
if (!payTo) {
|
|
1726
|
+
pushIssue(issues, {
|
|
1727
|
+
code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
|
|
1728
|
+
severity: "error",
|
|
1729
|
+
message: `Accept at index ${index} is missing payTo`,
|
|
1730
|
+
path: `accepts[${index}].payTo`
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
if (!asset) {
|
|
1734
|
+
pushIssue(issues, {
|
|
1735
|
+
code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
|
|
1736
|
+
severity: "error",
|
|
1737
|
+
message: `Accept at index ${index} is missing asset`,
|
|
1738
|
+
path: `accepts[${index}].asset`
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
if (!maxTimeoutSeconds) {
|
|
1742
|
+
pushIssue(issues, {
|
|
1743
|
+
code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
|
|
1744
|
+
severity: "error",
|
|
1745
|
+
message: `Accept at index ${index} is missing or has invalid maxTimeoutSeconds`,
|
|
1746
|
+
path: `accepts[${index}].maxTimeoutSeconds`
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
let normalizedNetwork;
|
|
1750
|
+
if (networkRaw) {
|
|
1751
|
+
normalizedNetwork = version === 2 ? validateV2Network(networkRaw, index, issues) : validateV1Network(networkRaw, index, issues);
|
|
1752
|
+
}
|
|
1753
|
+
return {
|
|
1754
|
+
index,
|
|
1755
|
+
network: normalizedNetwork ?? networkRaw ?? "unknown",
|
|
1756
|
+
networkRaw: networkRaw ?? "unknown",
|
|
1757
|
+
scheme,
|
|
1758
|
+
asset,
|
|
1759
|
+
payTo,
|
|
1760
|
+
amount,
|
|
1761
|
+
maxTimeoutSeconds
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
function getSchemaPresence(payload, accepts, version) {
|
|
1765
|
+
if (version === 1) {
|
|
1766
|
+
const first = accepts[0];
|
|
1767
|
+
if (!isRecord4(first)) {
|
|
1768
|
+
return { hasInputSchema: false, hasOutputSchema: false };
|
|
1769
|
+
}
|
|
1770
|
+
const outputSchema = isRecord4(first.outputSchema) ? first.outputSchema : void 0;
|
|
1771
|
+
return {
|
|
1772
|
+
hasInputSchema: outputSchema?.input !== void 0 && outputSchema.input !== null,
|
|
1773
|
+
hasOutputSchema: outputSchema?.output !== void 0 && outputSchema.output !== null
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
const extensions = isRecord4(payload.extensions) ? payload.extensions : void 0;
|
|
1777
|
+
const bazaar = extensions && isRecord4(extensions.bazaar) ? extensions.bazaar : void 0;
|
|
1778
|
+
const info = bazaar && isRecord4(bazaar.info) ? bazaar.info : void 0;
|
|
1779
|
+
return {
|
|
1780
|
+
hasInputSchema: info?.input !== void 0 && info.input !== null,
|
|
1781
|
+
hasOutputSchema: info?.output !== void 0 && info.output !== null
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
function validatePaymentRequiredDetailed(payload, options = {}) {
|
|
1785
|
+
const issues = [];
|
|
1786
|
+
const compatMode = options.compatMode ?? DEFAULT_COMPAT_MODE;
|
|
1787
|
+
const requireInputSchema = options.requireInputSchema ?? true;
|
|
1788
|
+
const requireOutputSchema = options.requireOutputSchema ?? true;
|
|
1789
|
+
if (!isRecord4(payload)) {
|
|
1790
|
+
pushIssue(issues, {
|
|
1791
|
+
code: VALIDATION_CODES.X402_NOT_OBJECT,
|
|
1792
|
+
severity: "error",
|
|
1793
|
+
message: "Payment required payload must be a JSON object",
|
|
1794
|
+
path: "$"
|
|
1795
|
+
});
|
|
1796
|
+
if (options.metadata) {
|
|
1797
|
+
issues.push(...evaluateMetadataCompleteness(options.metadata));
|
|
1798
|
+
}
|
|
1799
|
+
return {
|
|
1800
|
+
valid: false,
|
|
1801
|
+
issues,
|
|
1802
|
+
summary: summarizeIssues(issues)
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
const coinbaseParsed = parseWithCoinbaseStructuralGate(payload, issues);
|
|
1806
|
+
const versionRaw = payload.x402Version;
|
|
1807
|
+
let version;
|
|
1808
|
+
if (versionRaw === 1 || versionRaw === 2) {
|
|
1809
|
+
version = versionRaw;
|
|
1810
|
+
} else if (versionRaw === void 0) {
|
|
1811
|
+
pushIssue(issues, {
|
|
1812
|
+
code: VALIDATION_CODES.X402_VERSION_MISSING,
|
|
1813
|
+
severity: "error",
|
|
1814
|
+
message: "x402Version is required",
|
|
1815
|
+
path: "x402Version"
|
|
1816
|
+
});
|
|
1817
|
+
} else {
|
|
1818
|
+
pushIssue(issues, {
|
|
1819
|
+
code: VALIDATION_CODES.X402_VERSION_UNSUPPORTED,
|
|
1820
|
+
severity: "error",
|
|
1821
|
+
message: `Unsupported x402Version '${String(versionRaw)}'`,
|
|
1822
|
+
path: "x402Version",
|
|
1823
|
+
expected: "1 | 2",
|
|
1824
|
+
actual: String(versionRaw)
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
const acceptsRaw = payload.accepts;
|
|
1828
|
+
let accepts = [];
|
|
1829
|
+
if (acceptsRaw === void 0) {
|
|
1830
|
+
pushIssue(issues, {
|
|
1831
|
+
code: VALIDATION_CODES.X402_ACCEPTS_MISSING,
|
|
1832
|
+
severity: "error",
|
|
1833
|
+
message: "accepts is required",
|
|
1834
|
+
path: "accepts"
|
|
1835
|
+
});
|
|
1836
|
+
} else if (!Array.isArray(acceptsRaw)) {
|
|
1837
|
+
pushIssue(issues, {
|
|
1838
|
+
code: VALIDATION_CODES.X402_ACCEPTS_INVALID,
|
|
1839
|
+
severity: "error",
|
|
1840
|
+
message: "accepts must be an array",
|
|
1841
|
+
path: "accepts"
|
|
1842
|
+
});
|
|
1843
|
+
} else {
|
|
1844
|
+
accepts = acceptsRaw;
|
|
1845
|
+
if (accepts.length === 0) {
|
|
1846
|
+
pushIssue(issues, {
|
|
1847
|
+
code: VALIDATION_CODES.X402_ACCEPTS_EMPTY,
|
|
1848
|
+
severity: "error",
|
|
1849
|
+
message: "accepts must contain at least one payment requirement",
|
|
1850
|
+
path: "accepts"
|
|
1851
|
+
});
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
const normalizedAccepts = [];
|
|
1855
|
+
if (version && Array.isArray(accepts)) {
|
|
1856
|
+
accepts.forEach((accept, index) => {
|
|
1857
|
+
const normalized = validateAccept(accept, index, version, issues);
|
|
1858
|
+
if (normalized) normalizedAccepts.push(normalized);
|
|
1859
|
+
});
|
|
1860
|
+
const schemaPresence = getSchemaPresence(payload, accepts, version);
|
|
1861
|
+
if (requireInputSchema && !schemaPresence.hasInputSchema) {
|
|
1862
|
+
pushIssue(issues, {
|
|
1863
|
+
code: VALIDATION_CODES.SCHEMA_INPUT_MISSING,
|
|
1864
|
+
severity: "error",
|
|
1865
|
+
message: "Input schema is missing",
|
|
1866
|
+
hint: "Include input schema details so clients can invoke the endpoint correctly.",
|
|
1867
|
+
path: version === 1 ? "accepts[0].outputSchema.input" : "extensions.bazaar.info.input"
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
if (requireOutputSchema && !schemaPresence.hasOutputSchema) {
|
|
1871
|
+
pushIssue(issues, {
|
|
1872
|
+
code: VALIDATION_CODES.SCHEMA_OUTPUT_MISSING,
|
|
1873
|
+
severity: outputSchemaMissingSeverity(compatMode),
|
|
1874
|
+
message: "Output schema is missing",
|
|
1875
|
+
hint: "Include output schema details so clients can validate responses.",
|
|
1876
|
+
path: version === 1 ? "accepts[0].outputSchema.output" : "extensions.bazaar.info.output"
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
if (options.metadata) {
|
|
1880
|
+
issues.push(...evaluateMetadataCompleteness(options.metadata));
|
|
1881
|
+
}
|
|
1882
|
+
const summary2 = summarizeIssues(issues);
|
|
1883
|
+
return {
|
|
1884
|
+
valid: summary2.errorCount === 0,
|
|
1885
|
+
version,
|
|
1886
|
+
parsed: coinbaseParsed ?? payload,
|
|
1887
|
+
normalized: {
|
|
1888
|
+
version,
|
|
1889
|
+
accepts: normalizedAccepts,
|
|
1890
|
+
hasInputSchema: schemaPresence.hasInputSchema,
|
|
1891
|
+
hasOutputSchema: schemaPresence.hasOutputSchema
|
|
1892
|
+
},
|
|
1893
|
+
issues,
|
|
1894
|
+
summary: summary2
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
if (options.metadata) {
|
|
1898
|
+
issues.push(...evaluateMetadataCompleteness(options.metadata));
|
|
1899
|
+
}
|
|
1900
|
+
const summary = summarizeIssues(issues);
|
|
1901
|
+
return {
|
|
1902
|
+
valid: summary.errorCount === 0,
|
|
1903
|
+
version,
|
|
1904
|
+
parsed: coinbaseParsed ?? payload,
|
|
1905
|
+
issues,
|
|
1906
|
+
summary
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
// src/adapters/mcp.ts
|
|
1911
|
+
var DEFAULT_GUIDANCE_AUTO_INCLUDE_MAX_TOKENS = 1e3;
|
|
1912
|
+
var OPENAPI_METHODS = [
|
|
1913
|
+
"GET",
|
|
1914
|
+
"POST",
|
|
1915
|
+
"PUT",
|
|
1916
|
+
"DELETE",
|
|
1917
|
+
"PATCH",
|
|
1918
|
+
"HEAD",
|
|
1919
|
+
"OPTIONS",
|
|
1920
|
+
"TRACE"
|
|
1921
|
+
];
|
|
1922
|
+
function isRecord5(value) {
|
|
1923
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
1924
|
+
}
|
|
1925
|
+
function toFiniteNumber(value) {
|
|
1926
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1927
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
1928
|
+
const parsed = Number(value);
|
|
1929
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
1930
|
+
}
|
|
1931
|
+
return void 0;
|
|
1932
|
+
}
|
|
1933
|
+
function formatPriceHintFromResource(resource) {
|
|
1934
|
+
const pricing = resource.pricing;
|
|
1935
|
+
if (pricing) {
|
|
1936
|
+
if (pricing.pricingMode === "fixed" && pricing.price) {
|
|
1937
|
+
return pricing.price.startsWith("$") ? pricing.price : `$${pricing.price}`;
|
|
1938
|
+
}
|
|
1939
|
+
if (pricing.pricingMode === "range" && pricing.minPrice && pricing.maxPrice) {
|
|
1940
|
+
const min = pricing.minPrice.startsWith("$") ? pricing.minPrice : `$${pricing.minPrice}`;
|
|
1941
|
+
const max = pricing.maxPrice.startsWith("$") ? pricing.maxPrice : `$${pricing.maxPrice}`;
|
|
1942
|
+
return `${min}-${max}`;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
if (!resource.priceHint) return void 0;
|
|
1946
|
+
const hint = resource.priceHint.trim();
|
|
1947
|
+
if (hint.length === 0) return void 0;
|
|
1948
|
+
if (hint.startsWith("$")) return hint;
|
|
1949
|
+
if (hint.includes("-")) {
|
|
1950
|
+
const [min, max] = hint.split("-", 2).map((part) => part.trim());
|
|
1951
|
+
if (min && max) return `$${min}-$${max}`;
|
|
1952
|
+
}
|
|
1953
|
+
return `$${hint}`;
|
|
1954
|
+
}
|
|
1955
|
+
function deriveMcpAuthMode(resource) {
|
|
1956
|
+
if (!resource) return void 0;
|
|
1957
|
+
const hint = resource.authHint;
|
|
1958
|
+
if (hint === "paid" || hint === "siwx" || hint === "apiKey" || hint === "unprotected") {
|
|
1959
|
+
return hint;
|
|
1960
|
+
}
|
|
1961
|
+
return void 0;
|
|
1962
|
+
}
|
|
1963
|
+
function inferFailureCause(warnings) {
|
|
1964
|
+
const openapiWarnings = warnings.filter((w) => w.stage === "openapi");
|
|
1965
|
+
const parseWarning = openapiWarnings.find(
|
|
1966
|
+
(w) => w.code === "PARSE_FAILED" || w.code === "OPENAPI_TOP_LEVEL_INVALID" || w.code === "OPENAPI_OPERATION_INVALID" || w.code === "OPENAPI_PRICING_INVALID"
|
|
1967
|
+
);
|
|
1968
|
+
if (parseWarning) {
|
|
1969
|
+
return { cause: "parse", message: parseWarning.message };
|
|
1970
|
+
}
|
|
1971
|
+
const fetchWarning = openapiWarnings.find((w) => w.code === "FETCH_FAILED");
|
|
1972
|
+
if (fetchWarning) {
|
|
1973
|
+
const msg = fetchWarning.message.toLowerCase();
|
|
1974
|
+
if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("abort")) {
|
|
1975
|
+
return { cause: "timeout", message: fetchWarning.message };
|
|
1976
|
+
}
|
|
1977
|
+
return { cause: "network", message: fetchWarning.message };
|
|
1978
|
+
}
|
|
1979
|
+
return { cause: "not_found" };
|
|
1980
|
+
}
|
|
1981
|
+
function getOpenApiInfo(document) {
|
|
1982
|
+
if (!isRecord5(document) || !isRecord5(document.info)) return void 0;
|
|
1983
|
+
if (typeof document.info.title !== "string") return void 0;
|
|
1984
|
+
return {
|
|
1985
|
+
title: document.info.title,
|
|
1986
|
+
...typeof document.info.version === "string" ? { version: document.info.version } : {},
|
|
1987
|
+
...typeof document.info.description === "string" ? { description: document.info.description } : {}
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
function getGuidanceUrl(result) {
|
|
1991
|
+
const fromTrace = result.trace.find((entry) => entry.links?.llmsTxtUrl)?.links?.llmsTxtUrl;
|
|
1992
|
+
if (fromTrace) return fromTrace;
|
|
1993
|
+
const fromResource = result.resources.find((resource) => resource.links?.llmsTxtUrl)?.links?.llmsTxtUrl;
|
|
1994
|
+
if (fromResource) return fromResource;
|
|
1995
|
+
return void 0;
|
|
1996
|
+
}
|
|
1997
|
+
async function resolveGuidance(options) {
|
|
1998
|
+
let guidanceText = typeof options.result.rawSources?.llmsTxt === "string" ? options.result.rawSources.llmsTxt : void 0;
|
|
1999
|
+
if (!guidanceText) {
|
|
2000
|
+
const guidanceUrl = getGuidanceUrl(options.result) ?? `${options.result.origin}/llms.txt`;
|
|
2001
|
+
const fetcher = options.fetcher ?? fetch;
|
|
2002
|
+
try {
|
|
2003
|
+
const response = await fetcher(guidanceUrl, {
|
|
2004
|
+
method: "GET",
|
|
2005
|
+
headers: { Accept: "text/plain", ...options.headers },
|
|
2006
|
+
signal: options.signal
|
|
2007
|
+
});
|
|
2008
|
+
if (response.ok) guidanceText = await response.text();
|
|
2009
|
+
} catch {
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
if (!guidanceText) {
|
|
2013
|
+
return { guidanceAvailable: false };
|
|
2014
|
+
}
|
|
2015
|
+
const guidanceTokens = estimateTokenCount(guidanceText);
|
|
2016
|
+
const threshold = options.guidanceAutoIncludeMaxTokens ?? DEFAULT_GUIDANCE_AUTO_INCLUDE_MAX_TOKENS;
|
|
2017
|
+
const shouldInclude = options.includeGuidance === true || options.includeGuidance == null && guidanceTokens <= threshold;
|
|
2018
|
+
return {
|
|
2019
|
+
guidanceAvailable: true,
|
|
2020
|
+
guidanceTokens,
|
|
2021
|
+
...shouldInclude ? { guidance: guidanceText } : {}
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
2024
|
+
function extractPathsDocument(document) {
|
|
2025
|
+
if (!isRecord5(document)) return void 0;
|
|
2026
|
+
if (!isRecord5(document.paths)) return void 0;
|
|
2027
|
+
return document.paths;
|
|
2028
|
+
}
|
|
2029
|
+
function findMatchingOpenApiPath(paths, targetPath) {
|
|
2030
|
+
const exact = paths[targetPath];
|
|
2031
|
+
if (isRecord5(exact)) return { matchedPath: targetPath, pathItem: exact };
|
|
2032
|
+
for (const [specPath, entry] of Object.entries(paths)) {
|
|
2033
|
+
if (!isRecord5(entry)) continue;
|
|
2034
|
+
const pattern = specPath.replace(/\{[^}]+\}/g, "[^/]+");
|
|
2035
|
+
const regex = new RegExp(`^${pattern}$`);
|
|
2036
|
+
if (regex.test(targetPath)) {
|
|
2037
|
+
return { matchedPath: specPath, pathItem: entry };
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
return null;
|
|
2041
|
+
}
|
|
2042
|
+
function resolveRef(document, ref, seen) {
|
|
2043
|
+
if (!ref.startsWith("#/")) return void 0;
|
|
2044
|
+
if (seen.has(ref)) return { $circular: ref };
|
|
2045
|
+
seen.add(ref);
|
|
2046
|
+
const parts = ref.slice(2).split("/");
|
|
2047
|
+
let current = document;
|
|
2048
|
+
for (const part of parts) {
|
|
2049
|
+
if (!isRecord5(current)) return void 0;
|
|
2050
|
+
current = current[part];
|
|
2051
|
+
if (current === void 0) return void 0;
|
|
2052
|
+
}
|
|
2053
|
+
if (isRecord5(current)) return resolveRefs(document, current, seen);
|
|
2054
|
+
return current;
|
|
2055
|
+
}
|
|
2056
|
+
function resolveRefs(document, obj, seen, depth = 0) {
|
|
2057
|
+
if (depth > 4) return obj;
|
|
2058
|
+
const resolved = {};
|
|
2059
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
2060
|
+
if (key === "$ref" && typeof value === "string") {
|
|
2061
|
+
const deref = resolveRef(document, value, seen);
|
|
2062
|
+
if (isRecord5(deref)) {
|
|
2063
|
+
Object.assign(resolved, deref);
|
|
2064
|
+
} else {
|
|
2065
|
+
resolved[key] = value;
|
|
2066
|
+
}
|
|
2067
|
+
continue;
|
|
2068
|
+
}
|
|
2069
|
+
if (isRecord5(value)) {
|
|
2070
|
+
resolved[key] = resolveRefs(document, value, seen, depth + 1);
|
|
2071
|
+
continue;
|
|
2072
|
+
}
|
|
2073
|
+
if (Array.isArray(value)) {
|
|
2074
|
+
resolved[key] = value.map(
|
|
2075
|
+
(item) => isRecord5(item) ? resolveRefs(document, item, seen, depth + 1) : item
|
|
2076
|
+
);
|
|
2077
|
+
continue;
|
|
2078
|
+
}
|
|
2079
|
+
resolved[key] = value;
|
|
2080
|
+
}
|
|
2081
|
+
return resolved;
|
|
2082
|
+
}
|
|
2083
|
+
function extractRequestBodySchema(operationSchema) {
|
|
2084
|
+
const requestBody = operationSchema.requestBody;
|
|
2085
|
+
if (!isRecord5(requestBody)) return void 0;
|
|
2086
|
+
const content = requestBody.content;
|
|
2087
|
+
if (!isRecord5(content)) return void 0;
|
|
2088
|
+
const jsonMediaType = content["application/json"];
|
|
2089
|
+
if (isRecord5(jsonMediaType) && isRecord5(jsonMediaType.schema)) {
|
|
2090
|
+
return jsonMediaType.schema;
|
|
2091
|
+
}
|
|
2092
|
+
for (const mediaType of Object.values(content)) {
|
|
2093
|
+
if (isRecord5(mediaType) && isRecord5(mediaType.schema)) {
|
|
2094
|
+
return mediaType.schema;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
return void 0;
|
|
2098
|
+
}
|
|
2099
|
+
function extractParameters(operationSchema) {
|
|
2100
|
+
const params = operationSchema.parameters;
|
|
2101
|
+
if (!Array.isArray(params)) return [];
|
|
2102
|
+
return params.filter((value) => isRecord5(value));
|
|
2103
|
+
}
|
|
2104
|
+
function extractInputSchema(operationSchema) {
|
|
2105
|
+
const requestBody = extractRequestBodySchema(operationSchema);
|
|
2106
|
+
const parameters = extractParameters(operationSchema);
|
|
2107
|
+
if (!requestBody && parameters.length === 0) return void 0;
|
|
2108
|
+
if (requestBody && parameters.length === 0) return requestBody;
|
|
2109
|
+
return {
|
|
2110
|
+
...requestBody ? { requestBody } : {},
|
|
2111
|
+
...parameters.length > 0 ? { parameters } : {}
|
|
2112
|
+
};
|
|
2113
|
+
}
|
|
2114
|
+
function parseOperationPrice(operation) {
|
|
2115
|
+
const paymentInfo = operation["x-payment-info"];
|
|
2116
|
+
if (!isRecord5(paymentInfo)) return void 0;
|
|
2117
|
+
const fixed = toFiniteNumber(paymentInfo.price);
|
|
2118
|
+
if (fixed != null) return `$${String(fixed)}`;
|
|
2119
|
+
const min = toFiniteNumber(paymentInfo.minPrice);
|
|
2120
|
+
const max = toFiniteNumber(paymentInfo.maxPrice);
|
|
2121
|
+
if (min != null && max != null) return `$${String(min)}-$${String(max)}`;
|
|
2122
|
+
return void 0;
|
|
2123
|
+
}
|
|
2124
|
+
function parseOperationProtocols(operation) {
|
|
2125
|
+
const paymentInfo = operation["x-payment-info"];
|
|
2126
|
+
if (!isRecord5(paymentInfo) || !Array.isArray(paymentInfo.protocols)) return void 0;
|
|
2127
|
+
const protocols = paymentInfo.protocols.filter(
|
|
2128
|
+
(protocol) => typeof protocol === "string" && protocol.length > 0
|
|
2129
|
+
);
|
|
2130
|
+
return protocols.length > 0 ? protocols : void 0;
|
|
2131
|
+
}
|
|
2132
|
+
function parseOperationAuthMode(operation) {
|
|
2133
|
+
const authExtension = operation["x-agentcash-auth"];
|
|
2134
|
+
if (isRecord5(authExtension)) {
|
|
2135
|
+
const mode = authExtension.mode;
|
|
2136
|
+
if (mode === "paid" || mode === "siwx" || mode === "apiKey" || mode === "unprotected") {
|
|
2137
|
+
return mode;
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
if (isRecord5(operation["x-payment-info"])) return "paid";
|
|
2141
|
+
return void 0;
|
|
2142
|
+
}
|
|
2143
|
+
function createResourceMap(result) {
|
|
2144
|
+
const map = /* @__PURE__ */ new Map();
|
|
2145
|
+
for (const resource of result.resources) {
|
|
2146
|
+
map.set(resource.resourceKey, resource);
|
|
2147
|
+
}
|
|
2148
|
+
return map;
|
|
2149
|
+
}
|
|
2150
|
+
function toMcpEndpointSummary(resource) {
|
|
2151
|
+
const method = parseMethod(resource.method);
|
|
2152
|
+
if (!method) return void 0;
|
|
2153
|
+
const price = formatPriceHintFromResource(resource);
|
|
2154
|
+
const authMode = deriveMcpAuthMode(resource);
|
|
2155
|
+
return {
|
|
2156
|
+
path: resource.path,
|
|
2157
|
+
method,
|
|
2158
|
+
summary: resource.summary ?? `${method} ${resource.path}`,
|
|
2159
|
+
...price ? { price } : {},
|
|
2160
|
+
...resource.protocolHints?.length ? { protocols: [...resource.protocolHints] } : {},
|
|
2161
|
+
...authMode ? { authMode } : {}
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
async function discoverForMcp(options) {
|
|
2165
|
+
const result = await runDiscovery({
|
|
2166
|
+
detailed: true,
|
|
2167
|
+
options: {
|
|
2168
|
+
target: options.target,
|
|
2169
|
+
compatMode: options.compatMode ?? "off",
|
|
2170
|
+
rawView: "full",
|
|
2171
|
+
fetcher: options.fetcher,
|
|
2172
|
+
headers: options.headers,
|
|
2173
|
+
signal: options.signal
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
if (result.resources.length === 0) {
|
|
2177
|
+
const failure = inferFailureCause(result.warnings);
|
|
2178
|
+
return {
|
|
2179
|
+
found: false,
|
|
2180
|
+
origin: result.origin,
|
|
2181
|
+
cause: failure.cause,
|
|
2182
|
+
...failure.message ? { message: failure.message } : {},
|
|
2183
|
+
warnings: result.warnings
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
const source = result.selectedStage ?? "openapi";
|
|
2187
|
+
const endpoints = result.resources.map(toMcpEndpointSummary).filter((endpoint) => endpoint != null).sort((a, b) => {
|
|
2188
|
+
if (a.path !== b.path) return a.path.localeCompare(b.path);
|
|
2189
|
+
return a.method.localeCompare(b.method);
|
|
2190
|
+
});
|
|
2191
|
+
const guidance = await resolveGuidance({
|
|
2192
|
+
result,
|
|
2193
|
+
includeGuidance: options.includeGuidance,
|
|
2194
|
+
guidanceAutoIncludeMaxTokens: options.guidanceAutoIncludeMaxTokens,
|
|
2195
|
+
fetcher: options.fetcher,
|
|
2196
|
+
headers: options.headers,
|
|
2197
|
+
signal: options.signal
|
|
2198
|
+
});
|
|
2199
|
+
return {
|
|
2200
|
+
found: true,
|
|
2201
|
+
origin: result.origin,
|
|
2202
|
+
source,
|
|
2203
|
+
...getOpenApiInfo(result.rawSources?.openapi) ? { info: getOpenApiInfo(result.rawSources?.openapi) } : {},
|
|
2204
|
+
...guidance,
|
|
2205
|
+
endpoints,
|
|
2206
|
+
warnings: result.warnings
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
async function inspectEndpointForMcp(options) {
|
|
2210
|
+
const endpoint = new URL(options.endpointUrl);
|
|
2211
|
+
const origin = normalizeOrigin(endpoint.origin);
|
|
2212
|
+
const path = normalizePath(endpoint.pathname || "/");
|
|
2213
|
+
const result = await runDiscovery({
|
|
2214
|
+
detailed: true,
|
|
2215
|
+
options: {
|
|
2216
|
+
target: options.target,
|
|
2217
|
+
compatMode: options.compatMode ?? "off",
|
|
2218
|
+
rawView: "full",
|
|
2219
|
+
fetcher: options.fetcher,
|
|
2220
|
+
headers: options.headers,
|
|
2221
|
+
signal: options.signal
|
|
2222
|
+
}
|
|
2223
|
+
});
|
|
2224
|
+
const document = result.rawSources?.openapi;
|
|
2225
|
+
const paths = extractPathsDocument(document);
|
|
2226
|
+
const resourceMap = createResourceMap(result);
|
|
2227
|
+
const advisories = {};
|
|
2228
|
+
const specMethods = [];
|
|
2229
|
+
if (paths) {
|
|
2230
|
+
const matched = findMatchingOpenApiPath(paths, path);
|
|
2231
|
+
if (matched) {
|
|
2232
|
+
for (const candidate of OPENAPI_METHODS) {
|
|
2233
|
+
const operation = matched.pathItem[candidate.toLowerCase()];
|
|
2234
|
+
if (!isRecord5(operation) || !isRecord5(document)) continue;
|
|
2235
|
+
specMethods.push(candidate);
|
|
2236
|
+
const resolvedOperation = resolveRefs(document, operation, /* @__PURE__ */ new Set());
|
|
2237
|
+
const resource = resourceMap.get(toResourceKey(origin, candidate, matched.matchedPath));
|
|
2238
|
+
const formattedPrice = resource ? formatPriceHintFromResource(resource) : void 0;
|
|
2239
|
+
const operationSummary = typeof resolvedOperation.summary === "string" ? resolvedOperation.summary : typeof resolvedOperation.description === "string" ? resolvedOperation.description : void 0;
|
|
2240
|
+
const operationPrice = parseOperationPrice(resolvedOperation);
|
|
2241
|
+
const operationProtocols = parseOperationProtocols(resolvedOperation);
|
|
2242
|
+
const operationAuthMode = parseOperationAuthMode(resolvedOperation);
|
|
2243
|
+
const inputSchema = extractInputSchema(resolvedOperation);
|
|
2244
|
+
advisories[candidate] = {
|
|
2245
|
+
method: candidate,
|
|
2246
|
+
...resource?.summary || operationSummary ? {
|
|
2247
|
+
summary: resource?.summary ?? operationSummary
|
|
2248
|
+
} : {},
|
|
2249
|
+
...formattedPrice || operationPrice ? {
|
|
2250
|
+
estimatedPrice: formattedPrice ?? operationPrice
|
|
2251
|
+
} : {},
|
|
2252
|
+
...resource?.protocolHints?.length || operationProtocols ? {
|
|
2253
|
+
protocols: resource?.protocolHints ?? operationProtocols
|
|
2254
|
+
} : {},
|
|
2255
|
+
...deriveMcpAuthMode(resource) || operationAuthMode ? {
|
|
2256
|
+
authMode: deriveMcpAuthMode(resource) ?? operationAuthMode
|
|
2257
|
+
} : {},
|
|
2258
|
+
...inputSchema ? { inputSchema } : {},
|
|
2259
|
+
operationSchema: resolvedOperation
|
|
2260
|
+
};
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
if (specMethods.length === 0) {
|
|
2265
|
+
for (const method of OPENAPI_METHODS) {
|
|
2266
|
+
const resource = resourceMap.get(toResourceKey(origin, method, path));
|
|
2267
|
+
if (!resource) continue;
|
|
2268
|
+
specMethods.push(method);
|
|
2269
|
+
const formattedPrice = formatPriceHintFromResource(resource);
|
|
2270
|
+
advisories[method] = {
|
|
2271
|
+
method,
|
|
2272
|
+
...resource.summary ? { summary: resource.summary } : {},
|
|
2273
|
+
...formattedPrice ? { estimatedPrice: formattedPrice } : {},
|
|
2274
|
+
...resource.protocolHints?.length ? { protocols: [...resource.protocolHints] } : {},
|
|
2275
|
+
...deriveMcpAuthMode(resource) ? { authMode: deriveMcpAuthMode(resource) } : {}
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
if (specMethods.length === 0) {
|
|
2280
|
+
const failure = inferFailureCause(result.warnings);
|
|
2281
|
+
return {
|
|
2282
|
+
found: false,
|
|
2283
|
+
origin,
|
|
2284
|
+
path,
|
|
2285
|
+
cause: failure.cause,
|
|
2286
|
+
...failure.message ? { message: failure.message } : {},
|
|
2287
|
+
warnings: result.warnings
|
|
2288
|
+
};
|
|
2289
|
+
}
|
|
2290
|
+
return {
|
|
2291
|
+
found: true,
|
|
2292
|
+
origin,
|
|
2293
|
+
path,
|
|
2294
|
+
specMethods,
|
|
2295
|
+
advisories,
|
|
2296
|
+
warnings: result.warnings
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
|
|
1440
2300
|
// src/harness.ts
|
|
1441
2301
|
var INTENT_TRIGGERS = ["x402", "mpp", "pay for", "micropayment", "agentic commerce"];
|
|
1442
2302
|
var CLIENT_PROFILES = {
|
|
@@ -1690,12 +2550,17 @@ async function discoverDetailed(options) {
|
|
|
1690
2550
|
LEGACY_SUNSET_DATE,
|
|
1691
2551
|
STRICT_ESCALATION_CODES,
|
|
1692
2552
|
UPGRADE_WARNING_CODES,
|
|
2553
|
+
VALIDATION_CODES,
|
|
1693
2554
|
auditContextHarness,
|
|
1694
2555
|
buildContextHarnessReport,
|
|
1695
2556
|
computeUpgradeSignal,
|
|
1696
2557
|
defaultHarnessProbeCandidates,
|
|
1697
2558
|
discover,
|
|
1698
2559
|
discoverDetailed,
|
|
2560
|
+
discoverForMcp,
|
|
2561
|
+
evaluateMetadataCompleteness,
|
|
1699
2562
|
getHarnessClientProfile,
|
|
1700
|
-
|
|
2563
|
+
inspectEndpointForMcp,
|
|
2564
|
+
listHarnessClientProfiles,
|
|
2565
|
+
validatePaymentRequiredDetailed
|
|
1701
2566
|
});
|