@agentcash/discovery 0.1.1 → 0.1.2

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 CHANGED
@@ -98,6 +98,37 @@ const harness = await auditContextHarness({
98
98
  });
99
99
  ```
100
100
 
101
+ ## MCP Adapter Contract
102
+
103
+ This section is the canonical contract for MCP-facing discovery adapters:
104
+
105
+ - `discoverForMcp(options)` for L2 resources + L4 guidance policy projection.
106
+ - `inspectEndpointForMcp(options)` for L3 OpenAPI advisory projection.
107
+
108
+ `discoverForMcp` guarantees:
109
+
110
+ - `guidanceAvailable` is always present.
111
+ - `guidanceTokens` is present when guidance exists.
112
+ - `guidance` is included when `includeGuidance=true`, excluded when
113
+ `includeGuidance=false`, and auto-included under the token threshold when not
114
+ specified.
115
+
116
+ `inspectEndpointForMcp` guarantees:
117
+
118
+ - Spec-derived HTTP methods for the selected endpoint path.
119
+ - Per-method advisory data: summary, estimated price, protocols, auth mode, and
120
+ input schema.
121
+
122
+ Ownership boundary:
123
+
124
+ - `@agentcash/discovery` owns discovery/advisory contracts.
125
+ - Runtime probe truth (live 402 parsing/payment option extraction/divergence)
126
+ belongs to the `agentcash` MCP package.
127
+
128
+ Reference integration boundary:
129
+
130
+ - `https://github.com/Merit-Systems/agentcash/blob/main/packages/external/mcp/docs/discovery-boundary.md`
131
+
101
132
  ## Discovery Waterfall
102
133
 
103
134
  Default order:
package/dist/cli.cjs CHANGED
@@ -637,7 +637,7 @@ function renderJson(run, options) {
637
637
  }
638
638
 
639
639
  // src/mmm-enabled.ts
640
- var isMmmEnabled = () => "0.1.1".includes("-mmm");
640
+ var isMmmEnabled = () => "0.1.2".includes("-mmm");
641
641
 
642
642
  // src/core/constants.ts
643
643
  var OPENAPI_PATH_CANDIDATES = ["/openapi.json", "/.well-known/openapi.json"];
package/dist/cli.js CHANGED
@@ -611,7 +611,7 @@ function renderJson(run, options) {
611
611
  }
612
612
 
613
613
  // src/mmm-enabled.ts
614
- var isMmmEnabled = () => "0.1.1".includes("-mmm");
614
+ var isMmmEnabled = () => "0.1.2".includes("-mmm");
615
615
 
616
616
  // src/core/constants.ts
617
617
  var OPENAPI_PATH_CANDIDATES = ["/openapi.json", "/.well-known/openapi.json"];
package/dist/index.cjs CHANGED
@@ -30,7 +30,9 @@ __export(index_exports, {
30
30
  defaultHarnessProbeCandidates: () => defaultHarnessProbeCandidates,
31
31
  discover: () => discover,
32
32
  discoverDetailed: () => discoverDetailed,
33
+ discoverForMcp: () => discoverForMcp,
33
34
  getHarnessClientProfile: () => getHarnessClientProfile,
35
+ inspectEndpointForMcp: () => inspectEndpointForMcp,
34
36
  listHarnessClientProfiles: () => listHarnessClientProfiles
35
37
  });
36
38
  module.exports = __toCommonJS(index_exports);
@@ -49,7 +51,7 @@ var STRICT_ESCALATION_CODES = [
49
51
  ];
50
52
 
51
53
  // src/mmm-enabled.ts
52
- var isMmmEnabled = () => "0.1.1".includes("-mmm");
54
+ var isMmmEnabled = () => "0.1.2".includes("-mmm");
53
55
 
54
56
  // src/core/constants.ts
55
57
  var OPENAPI_PATH_CANDIDATES = ["/openapi.json", "/.well-known/openapi.json"];
@@ -1437,6 +1439,396 @@ async function runDiscovery({
1437
1439
  };
1438
1440
  }
1439
1441
 
1442
+ // src/adapters/mcp.ts
1443
+ var DEFAULT_GUIDANCE_AUTO_INCLUDE_MAX_TOKENS = 1e3;
1444
+ var OPENAPI_METHODS = [
1445
+ "GET",
1446
+ "POST",
1447
+ "PUT",
1448
+ "DELETE",
1449
+ "PATCH",
1450
+ "HEAD",
1451
+ "OPTIONS",
1452
+ "TRACE"
1453
+ ];
1454
+ function isRecord4(value) {
1455
+ return value != null && typeof value === "object" && !Array.isArray(value);
1456
+ }
1457
+ function toFiniteNumber(value) {
1458
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1459
+ if (typeof value === "string" && value.trim().length > 0) {
1460
+ const parsed = Number(value);
1461
+ if (Number.isFinite(parsed)) return parsed;
1462
+ }
1463
+ return void 0;
1464
+ }
1465
+ function formatPriceHintFromResource(resource) {
1466
+ const pricing = resource.pricing;
1467
+ if (pricing) {
1468
+ if (pricing.pricingMode === "fixed" && pricing.price) {
1469
+ return pricing.price.startsWith("$") ? pricing.price : `$${pricing.price}`;
1470
+ }
1471
+ if (pricing.pricingMode === "range" && pricing.minPrice && pricing.maxPrice) {
1472
+ const min = pricing.minPrice.startsWith("$") ? pricing.minPrice : `$${pricing.minPrice}`;
1473
+ const max = pricing.maxPrice.startsWith("$") ? pricing.maxPrice : `$${pricing.maxPrice}`;
1474
+ return `${min}-${max}`;
1475
+ }
1476
+ }
1477
+ if (!resource.priceHint) return void 0;
1478
+ const hint = resource.priceHint.trim();
1479
+ if (hint.length === 0) return void 0;
1480
+ if (hint.startsWith("$")) return hint;
1481
+ if (hint.includes("-")) {
1482
+ const [min, max] = hint.split("-", 2).map((part) => part.trim());
1483
+ if (min && max) return `$${min}-$${max}`;
1484
+ }
1485
+ return `$${hint}`;
1486
+ }
1487
+ function deriveMcpAuthMode(resource) {
1488
+ if (!resource) return void 0;
1489
+ const hint = resource.authHint;
1490
+ if (hint === "paid" || hint === "siwx" || hint === "apiKey" || hint === "unprotected") {
1491
+ return hint;
1492
+ }
1493
+ return void 0;
1494
+ }
1495
+ function inferFailureCause(warnings) {
1496
+ const openapiWarnings = warnings.filter((w) => w.stage === "openapi");
1497
+ const parseWarning = openapiWarnings.find(
1498
+ (w) => w.code === "PARSE_FAILED" || w.code === "OPENAPI_TOP_LEVEL_INVALID" || w.code === "OPENAPI_OPERATION_INVALID" || w.code === "OPENAPI_PRICING_INVALID"
1499
+ );
1500
+ if (parseWarning) {
1501
+ return { cause: "parse", message: parseWarning.message };
1502
+ }
1503
+ const fetchWarning = openapiWarnings.find((w) => w.code === "FETCH_FAILED");
1504
+ if (fetchWarning) {
1505
+ const msg = fetchWarning.message.toLowerCase();
1506
+ if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("abort")) {
1507
+ return { cause: "timeout", message: fetchWarning.message };
1508
+ }
1509
+ return { cause: "network", message: fetchWarning.message };
1510
+ }
1511
+ return { cause: "not_found" };
1512
+ }
1513
+ function getOpenApiInfo(document) {
1514
+ if (!isRecord4(document) || !isRecord4(document.info)) return void 0;
1515
+ if (typeof document.info.title !== "string") return void 0;
1516
+ return {
1517
+ title: document.info.title,
1518
+ ...typeof document.info.version === "string" ? { version: document.info.version } : {},
1519
+ ...typeof document.info.description === "string" ? { description: document.info.description } : {}
1520
+ };
1521
+ }
1522
+ function getGuidanceUrl(result) {
1523
+ const fromTrace = result.trace.find((entry) => entry.links?.llmsTxtUrl)?.links?.llmsTxtUrl;
1524
+ if (fromTrace) return fromTrace;
1525
+ const fromResource = result.resources.find((resource) => resource.links?.llmsTxtUrl)?.links?.llmsTxtUrl;
1526
+ if (fromResource) return fromResource;
1527
+ return void 0;
1528
+ }
1529
+ async function resolveGuidance(options) {
1530
+ let guidanceText = typeof options.result.rawSources?.llmsTxt === "string" ? options.result.rawSources.llmsTxt : void 0;
1531
+ if (!guidanceText) {
1532
+ const guidanceUrl = getGuidanceUrl(options.result) ?? `${options.result.origin}/llms.txt`;
1533
+ const fetcher = options.fetcher ?? fetch;
1534
+ try {
1535
+ const response = await fetcher(guidanceUrl, {
1536
+ method: "GET",
1537
+ headers: { Accept: "text/plain", ...options.headers },
1538
+ signal: options.signal
1539
+ });
1540
+ if (response.ok) guidanceText = await response.text();
1541
+ } catch {
1542
+ }
1543
+ }
1544
+ if (!guidanceText) {
1545
+ return { guidanceAvailable: false };
1546
+ }
1547
+ const guidanceTokens = estimateTokenCount(guidanceText);
1548
+ const threshold = options.guidanceAutoIncludeMaxTokens ?? DEFAULT_GUIDANCE_AUTO_INCLUDE_MAX_TOKENS;
1549
+ const shouldInclude = options.includeGuidance === true || options.includeGuidance == null && guidanceTokens <= threshold;
1550
+ return {
1551
+ guidanceAvailable: true,
1552
+ guidanceTokens,
1553
+ ...shouldInclude ? { guidance: guidanceText } : {}
1554
+ };
1555
+ }
1556
+ function extractPathsDocument(document) {
1557
+ if (!isRecord4(document)) return void 0;
1558
+ if (!isRecord4(document.paths)) return void 0;
1559
+ return document.paths;
1560
+ }
1561
+ function findMatchingOpenApiPath(paths, targetPath) {
1562
+ const exact = paths[targetPath];
1563
+ if (isRecord4(exact)) return { matchedPath: targetPath, pathItem: exact };
1564
+ for (const [specPath, entry] of Object.entries(paths)) {
1565
+ if (!isRecord4(entry)) continue;
1566
+ const pattern = specPath.replace(/\{[^}]+\}/g, "[^/]+");
1567
+ const regex = new RegExp(`^${pattern}$`);
1568
+ if (regex.test(targetPath)) {
1569
+ return { matchedPath: specPath, pathItem: entry };
1570
+ }
1571
+ }
1572
+ return null;
1573
+ }
1574
+ function resolveRef(document, ref, seen) {
1575
+ if (!ref.startsWith("#/")) return void 0;
1576
+ if (seen.has(ref)) return { $circular: ref };
1577
+ seen.add(ref);
1578
+ const parts = ref.slice(2).split("/");
1579
+ let current = document;
1580
+ for (const part of parts) {
1581
+ if (!isRecord4(current)) return void 0;
1582
+ current = current[part];
1583
+ if (current === void 0) return void 0;
1584
+ }
1585
+ if (isRecord4(current)) return resolveRefs(document, current, seen);
1586
+ return current;
1587
+ }
1588
+ function resolveRefs(document, obj, seen, depth = 0) {
1589
+ if (depth > 4) return obj;
1590
+ const resolved = {};
1591
+ for (const [key, value] of Object.entries(obj)) {
1592
+ if (key === "$ref" && typeof value === "string") {
1593
+ const deref = resolveRef(document, value, seen);
1594
+ if (isRecord4(deref)) {
1595
+ Object.assign(resolved, deref);
1596
+ } else {
1597
+ resolved[key] = value;
1598
+ }
1599
+ continue;
1600
+ }
1601
+ if (isRecord4(value)) {
1602
+ resolved[key] = resolveRefs(document, value, seen, depth + 1);
1603
+ continue;
1604
+ }
1605
+ if (Array.isArray(value)) {
1606
+ resolved[key] = value.map(
1607
+ (item) => isRecord4(item) ? resolveRefs(document, item, seen, depth + 1) : item
1608
+ );
1609
+ continue;
1610
+ }
1611
+ resolved[key] = value;
1612
+ }
1613
+ return resolved;
1614
+ }
1615
+ function extractRequestBodySchema(operationSchema) {
1616
+ const requestBody = operationSchema.requestBody;
1617
+ if (!isRecord4(requestBody)) return void 0;
1618
+ const content = requestBody.content;
1619
+ if (!isRecord4(content)) return void 0;
1620
+ const jsonMediaType = content["application/json"];
1621
+ if (isRecord4(jsonMediaType) && isRecord4(jsonMediaType.schema)) {
1622
+ return jsonMediaType.schema;
1623
+ }
1624
+ for (const mediaType of Object.values(content)) {
1625
+ if (isRecord4(mediaType) && isRecord4(mediaType.schema)) {
1626
+ return mediaType.schema;
1627
+ }
1628
+ }
1629
+ return void 0;
1630
+ }
1631
+ function extractParameters(operationSchema) {
1632
+ const params = operationSchema.parameters;
1633
+ if (!Array.isArray(params)) return [];
1634
+ return params.filter((value) => isRecord4(value));
1635
+ }
1636
+ function extractInputSchema(operationSchema) {
1637
+ const requestBody = extractRequestBodySchema(operationSchema);
1638
+ const parameters = extractParameters(operationSchema);
1639
+ if (!requestBody && parameters.length === 0) return void 0;
1640
+ if (requestBody && parameters.length === 0) return requestBody;
1641
+ return {
1642
+ ...requestBody ? { requestBody } : {},
1643
+ ...parameters.length > 0 ? { parameters } : {}
1644
+ };
1645
+ }
1646
+ function parseOperationPrice(operation) {
1647
+ const paymentInfo = operation["x-payment-info"];
1648
+ if (!isRecord4(paymentInfo)) return void 0;
1649
+ const fixed = toFiniteNumber(paymentInfo.price);
1650
+ if (fixed != null) return `$${String(fixed)}`;
1651
+ const min = toFiniteNumber(paymentInfo.minPrice);
1652
+ const max = toFiniteNumber(paymentInfo.maxPrice);
1653
+ if (min != null && max != null) return `$${String(min)}-$${String(max)}`;
1654
+ return void 0;
1655
+ }
1656
+ function parseOperationProtocols(operation) {
1657
+ const paymentInfo = operation["x-payment-info"];
1658
+ if (!isRecord4(paymentInfo) || !Array.isArray(paymentInfo.protocols)) return void 0;
1659
+ const protocols = paymentInfo.protocols.filter(
1660
+ (protocol) => typeof protocol === "string" && protocol.length > 0
1661
+ );
1662
+ return protocols.length > 0 ? protocols : void 0;
1663
+ }
1664
+ function parseOperationAuthMode(operation) {
1665
+ const authExtension = operation["x-agentcash-auth"];
1666
+ if (isRecord4(authExtension)) {
1667
+ const mode = authExtension.mode;
1668
+ if (mode === "paid" || mode === "siwx" || mode === "apiKey" || mode === "unprotected") {
1669
+ return mode;
1670
+ }
1671
+ }
1672
+ if (isRecord4(operation["x-payment-info"])) return "paid";
1673
+ return void 0;
1674
+ }
1675
+ function createResourceMap(result) {
1676
+ const map = /* @__PURE__ */ new Map();
1677
+ for (const resource of result.resources) {
1678
+ map.set(resource.resourceKey, resource);
1679
+ }
1680
+ return map;
1681
+ }
1682
+ function toMcpEndpointSummary(resource) {
1683
+ const method = parseMethod(resource.method);
1684
+ if (!method) return void 0;
1685
+ const price = formatPriceHintFromResource(resource);
1686
+ const authMode = deriveMcpAuthMode(resource);
1687
+ return {
1688
+ path: resource.path,
1689
+ method,
1690
+ summary: resource.summary ?? `${method} ${resource.path}`,
1691
+ ...price ? { price } : {},
1692
+ ...resource.protocolHints?.length ? { protocols: [...resource.protocolHints] } : {},
1693
+ ...authMode ? { authMode } : {}
1694
+ };
1695
+ }
1696
+ async function discoverForMcp(options) {
1697
+ const result = await runDiscovery({
1698
+ detailed: true,
1699
+ options: {
1700
+ target: options.target,
1701
+ compatMode: options.compatMode ?? "off",
1702
+ rawView: "full",
1703
+ fetcher: options.fetcher,
1704
+ headers: options.headers,
1705
+ signal: options.signal
1706
+ }
1707
+ });
1708
+ if (result.resources.length === 0) {
1709
+ const failure = inferFailureCause(result.warnings);
1710
+ return {
1711
+ found: false,
1712
+ origin: result.origin,
1713
+ cause: failure.cause,
1714
+ ...failure.message ? { message: failure.message } : {},
1715
+ warnings: result.warnings
1716
+ };
1717
+ }
1718
+ const source = result.selectedStage ?? "openapi";
1719
+ const endpoints = result.resources.map(toMcpEndpointSummary).filter((endpoint) => endpoint != null).sort((a, b) => {
1720
+ if (a.path !== b.path) return a.path.localeCompare(b.path);
1721
+ return a.method.localeCompare(b.method);
1722
+ });
1723
+ const guidance = await resolveGuidance({
1724
+ result,
1725
+ includeGuidance: options.includeGuidance,
1726
+ guidanceAutoIncludeMaxTokens: options.guidanceAutoIncludeMaxTokens,
1727
+ fetcher: options.fetcher,
1728
+ headers: options.headers,
1729
+ signal: options.signal
1730
+ });
1731
+ return {
1732
+ found: true,
1733
+ origin: result.origin,
1734
+ source,
1735
+ ...getOpenApiInfo(result.rawSources?.openapi) ? { info: getOpenApiInfo(result.rawSources?.openapi) } : {},
1736
+ ...guidance,
1737
+ endpoints,
1738
+ warnings: result.warnings
1739
+ };
1740
+ }
1741
+ async function inspectEndpointForMcp(options) {
1742
+ const endpoint = new URL(options.endpointUrl);
1743
+ const origin = normalizeOrigin(endpoint.origin);
1744
+ const path = normalizePath(endpoint.pathname || "/");
1745
+ const result = await runDiscovery({
1746
+ detailed: true,
1747
+ options: {
1748
+ target: options.target,
1749
+ compatMode: options.compatMode ?? "off",
1750
+ rawView: "full",
1751
+ fetcher: options.fetcher,
1752
+ headers: options.headers,
1753
+ signal: options.signal
1754
+ }
1755
+ });
1756
+ const document = result.rawSources?.openapi;
1757
+ const paths = extractPathsDocument(document);
1758
+ const resourceMap = createResourceMap(result);
1759
+ const advisories = {};
1760
+ const specMethods = [];
1761
+ if (paths) {
1762
+ const matched = findMatchingOpenApiPath(paths, path);
1763
+ if (matched) {
1764
+ for (const candidate of OPENAPI_METHODS) {
1765
+ const operation = matched.pathItem[candidate.toLowerCase()];
1766
+ if (!isRecord4(operation) || !isRecord4(document)) continue;
1767
+ specMethods.push(candidate);
1768
+ const resolvedOperation = resolveRefs(document, operation, /* @__PURE__ */ new Set());
1769
+ const resource = resourceMap.get(toResourceKey(origin, candidate, matched.matchedPath));
1770
+ const formattedPrice = resource ? formatPriceHintFromResource(resource) : void 0;
1771
+ const operationSummary = typeof resolvedOperation.summary === "string" ? resolvedOperation.summary : typeof resolvedOperation.description === "string" ? resolvedOperation.description : void 0;
1772
+ const operationPrice = parseOperationPrice(resolvedOperation);
1773
+ const operationProtocols = parseOperationProtocols(resolvedOperation);
1774
+ const operationAuthMode = parseOperationAuthMode(resolvedOperation);
1775
+ const inputSchema = extractInputSchema(resolvedOperation);
1776
+ advisories[candidate] = {
1777
+ method: candidate,
1778
+ ...resource?.summary || operationSummary ? {
1779
+ summary: resource?.summary ?? operationSummary
1780
+ } : {},
1781
+ ...formattedPrice || operationPrice ? {
1782
+ estimatedPrice: formattedPrice ?? operationPrice
1783
+ } : {},
1784
+ ...resource?.protocolHints?.length || operationProtocols ? {
1785
+ protocols: resource?.protocolHints ?? operationProtocols
1786
+ } : {},
1787
+ ...deriveMcpAuthMode(resource) || operationAuthMode ? {
1788
+ authMode: deriveMcpAuthMode(resource) ?? operationAuthMode
1789
+ } : {},
1790
+ ...inputSchema ? { inputSchema } : {},
1791
+ operationSchema: resolvedOperation
1792
+ };
1793
+ }
1794
+ }
1795
+ }
1796
+ if (specMethods.length === 0) {
1797
+ for (const method of OPENAPI_METHODS) {
1798
+ const resource = resourceMap.get(toResourceKey(origin, method, path));
1799
+ if (!resource) continue;
1800
+ specMethods.push(method);
1801
+ const formattedPrice = formatPriceHintFromResource(resource);
1802
+ advisories[method] = {
1803
+ method,
1804
+ ...resource.summary ? { summary: resource.summary } : {},
1805
+ ...formattedPrice ? { estimatedPrice: formattedPrice } : {},
1806
+ ...resource.protocolHints?.length ? { protocols: [...resource.protocolHints] } : {},
1807
+ ...deriveMcpAuthMode(resource) ? { authMode: deriveMcpAuthMode(resource) } : {}
1808
+ };
1809
+ }
1810
+ }
1811
+ if (specMethods.length === 0) {
1812
+ const failure = inferFailureCause(result.warnings);
1813
+ return {
1814
+ found: false,
1815
+ origin,
1816
+ path,
1817
+ cause: failure.cause,
1818
+ ...failure.message ? { message: failure.message } : {},
1819
+ warnings: result.warnings
1820
+ };
1821
+ }
1822
+ return {
1823
+ found: true,
1824
+ origin,
1825
+ path,
1826
+ specMethods,
1827
+ advisories,
1828
+ warnings: result.warnings
1829
+ };
1830
+ }
1831
+
1440
1832
  // src/harness.ts
1441
1833
  var INTENT_TRIGGERS = ["x402", "mpp", "pay for", "micropayment", "agentic commerce"];
1442
1834
  var CLIENT_PROFILES = {
@@ -1696,6 +2088,8 @@ async function discoverDetailed(options) {
1696
2088
  defaultHarnessProbeCandidates,
1697
2089
  discover,
1698
2090
  discoverDetailed,
2091
+ discoverForMcp,
1699
2092
  getHarnessClientProfile,
2093
+ inspectEndpointForMcp,
1700
2094
  listHarnessClientProfiles
1701
2095
  });
package/dist/index.d.cts CHANGED
@@ -94,6 +94,88 @@ interface DiscoverDetailedOptions extends DiscoverOptions {
94
94
  includeInteropMpp?: boolean;
95
95
  }
96
96
 
97
+ type McpFailureCause = 'not_found' | 'network' | 'timeout' | 'parse';
98
+ type McpAuthMode = 'paid' | 'siwx' | 'apiKey' | 'unprotected';
99
+ interface McpOpenApiInfo {
100
+ title: string;
101
+ version?: string;
102
+ description?: string;
103
+ }
104
+ interface McpEndpointSummary {
105
+ path: string;
106
+ method: HttpMethod;
107
+ summary: string;
108
+ price?: string;
109
+ protocols?: string[];
110
+ authMode?: McpAuthMode;
111
+ }
112
+ interface McpGuidance {
113
+ guidanceAvailable: boolean;
114
+ guidanceTokens?: number;
115
+ guidance?: string;
116
+ }
117
+ interface DiscoverForMcpOptions {
118
+ target: string;
119
+ includeGuidance?: boolean;
120
+ guidanceAutoIncludeMaxTokens?: number;
121
+ compatMode?: CompatibilityMode;
122
+ fetcher?: DiscoveryFetcher;
123
+ headers?: Record<string, string>;
124
+ signal?: AbortSignal;
125
+ }
126
+ interface DiscoverForMcpSuccess extends McpGuidance {
127
+ found: true;
128
+ origin: string;
129
+ source: string;
130
+ info?: McpOpenApiInfo;
131
+ endpoints: McpEndpointSummary[];
132
+ warnings: DiscoveryWarning[];
133
+ }
134
+ interface DiscoverForMcpFailure {
135
+ found: false;
136
+ origin: string;
137
+ cause: McpFailureCause;
138
+ message?: string;
139
+ warnings: DiscoveryWarning[];
140
+ }
141
+ type DiscoverForMcpResult = DiscoverForMcpSuccess | DiscoverForMcpFailure;
142
+ interface EndpointMethodAdvisory {
143
+ method: HttpMethod;
144
+ summary?: string;
145
+ estimatedPrice?: string;
146
+ protocols?: string[];
147
+ authMode?: McpAuthMode;
148
+ inputSchema?: Record<string, unknown>;
149
+ operationSchema?: Record<string, unknown>;
150
+ }
151
+ interface InspectEndpointForMcpOptions {
152
+ target: string;
153
+ endpointUrl: string;
154
+ compatMode?: CompatibilityMode;
155
+ fetcher?: DiscoveryFetcher;
156
+ headers?: Record<string, string>;
157
+ signal?: AbortSignal;
158
+ }
159
+ interface InspectEndpointForMcpSuccess {
160
+ found: true;
161
+ origin: string;
162
+ path: string;
163
+ specMethods: HttpMethod[];
164
+ advisories: Partial<Record<HttpMethod, EndpointMethodAdvisory>>;
165
+ warnings: DiscoveryWarning[];
166
+ }
167
+ interface InspectEndpointForMcpFailure {
168
+ found: false;
169
+ origin: string;
170
+ path: string;
171
+ cause: McpFailureCause;
172
+ message?: string;
173
+ warnings: DiscoveryWarning[];
174
+ }
175
+ type InspectEndpointForMcpResult = InspectEndpointForMcpSuccess | InspectEndpointForMcpFailure;
176
+ declare function discoverForMcp(options: DiscoverForMcpOptions): Promise<DiscoverForMcpResult>;
177
+ declare function inspectEndpointForMcp(options: InspectEndpointForMcpOptions): Promise<InspectEndpointForMcpResult>;
178
+
97
179
  declare const UPGRADE_WARNING_CODES: readonly ["LEGACY_WELL_KNOWN_USED", "LEGACY_DNS_USED", "LEGACY_DNS_PLAIN_URL", "LEGACY_INSTRUCTIONS_USED", "LEGACY_OWNERSHIP_PROOFS_USED", "OPENAPI_AUTH_MODE_MISSING", "OPENAPI_TOP_LEVEL_INVALID"];
98
180
  declare function computeUpgradeSignal(warnings: DiscoveryWarning[]): {
99
181
  upgradeSuggested: boolean;
@@ -207,4 +289,4 @@ declare function defaultHarnessProbeCandidates(report: ContextHarnessReport): Pr
207
289
  declare function discover(options: DiscoverOptions): Promise<DiscoverResult>;
208
290
  declare function discoverDetailed(options: DiscoverDetailedOptions): Promise<DiscoverDetailedResult>;
209
291
 
210
- export { type AuthMode, CompatibilityMode, type ContextHarnessL0, type ContextHarnessL1, type ContextHarnessL2, type ContextHarnessL2Entry, type ContextHarnessL3, type ContextHarnessL4, type ContextHarnessL5, type ContextHarnessReport, type DiscoverDetailedOptions, type DiscoverDetailedResult, type DiscoverOptions, type DiscoverResult, type DiscoveryFetcher, type DiscoveryResource, type DiscoveryStage, type DiscoveryWarning, type HarnessClientId, type HarnessClientProfile, type HttpMethod, type PricingMode, type ProbeCandidate, type RawSources, type StageTrace, type TrustTier, type TxtResolver, UPGRADE_WARNING_CODES, type WarningSeverity, auditContextHarness, buildContextHarnessReport, computeUpgradeSignal, defaultHarnessProbeCandidates, discover, discoverDetailed, getHarnessClientProfile, listHarnessClientProfiles };
292
+ export { type AuthMode, CompatibilityMode, type ContextHarnessL0, type ContextHarnessL1, type ContextHarnessL2, type ContextHarnessL2Entry, type ContextHarnessL3, type ContextHarnessL4, type ContextHarnessL5, type ContextHarnessReport, type DiscoverDetailedOptions, type DiscoverDetailedResult, type DiscoverForMcpFailure, type DiscoverForMcpOptions, type DiscoverForMcpResult, type DiscoverForMcpSuccess, type DiscoverOptions, type DiscoverResult, type DiscoveryFetcher, type DiscoveryResource, type DiscoveryStage, type DiscoveryWarning, type EndpointMethodAdvisory, type HarnessClientId, type HarnessClientProfile, type HttpMethod, type InspectEndpointForMcpFailure, type InspectEndpointForMcpOptions, type InspectEndpointForMcpResult, type InspectEndpointForMcpSuccess, type PricingMode, type ProbeCandidate, type RawSources, type StageTrace, type TrustTier, type TxtResolver, UPGRADE_WARNING_CODES, type WarningSeverity, auditContextHarness, buildContextHarnessReport, computeUpgradeSignal, defaultHarnessProbeCandidates, discover, discoverDetailed, discoverForMcp, getHarnessClientProfile, inspectEndpointForMcp, listHarnessClientProfiles };
package/dist/index.d.ts CHANGED
@@ -94,6 +94,88 @@ interface DiscoverDetailedOptions extends DiscoverOptions {
94
94
  includeInteropMpp?: boolean;
95
95
  }
96
96
 
97
+ type McpFailureCause = 'not_found' | 'network' | 'timeout' | 'parse';
98
+ type McpAuthMode = 'paid' | 'siwx' | 'apiKey' | 'unprotected';
99
+ interface McpOpenApiInfo {
100
+ title: string;
101
+ version?: string;
102
+ description?: string;
103
+ }
104
+ interface McpEndpointSummary {
105
+ path: string;
106
+ method: HttpMethod;
107
+ summary: string;
108
+ price?: string;
109
+ protocols?: string[];
110
+ authMode?: McpAuthMode;
111
+ }
112
+ interface McpGuidance {
113
+ guidanceAvailable: boolean;
114
+ guidanceTokens?: number;
115
+ guidance?: string;
116
+ }
117
+ interface DiscoverForMcpOptions {
118
+ target: string;
119
+ includeGuidance?: boolean;
120
+ guidanceAutoIncludeMaxTokens?: number;
121
+ compatMode?: CompatibilityMode;
122
+ fetcher?: DiscoveryFetcher;
123
+ headers?: Record<string, string>;
124
+ signal?: AbortSignal;
125
+ }
126
+ interface DiscoverForMcpSuccess extends McpGuidance {
127
+ found: true;
128
+ origin: string;
129
+ source: string;
130
+ info?: McpOpenApiInfo;
131
+ endpoints: McpEndpointSummary[];
132
+ warnings: DiscoveryWarning[];
133
+ }
134
+ interface DiscoverForMcpFailure {
135
+ found: false;
136
+ origin: string;
137
+ cause: McpFailureCause;
138
+ message?: string;
139
+ warnings: DiscoveryWarning[];
140
+ }
141
+ type DiscoverForMcpResult = DiscoverForMcpSuccess | DiscoverForMcpFailure;
142
+ interface EndpointMethodAdvisory {
143
+ method: HttpMethod;
144
+ summary?: string;
145
+ estimatedPrice?: string;
146
+ protocols?: string[];
147
+ authMode?: McpAuthMode;
148
+ inputSchema?: Record<string, unknown>;
149
+ operationSchema?: Record<string, unknown>;
150
+ }
151
+ interface InspectEndpointForMcpOptions {
152
+ target: string;
153
+ endpointUrl: string;
154
+ compatMode?: CompatibilityMode;
155
+ fetcher?: DiscoveryFetcher;
156
+ headers?: Record<string, string>;
157
+ signal?: AbortSignal;
158
+ }
159
+ interface InspectEndpointForMcpSuccess {
160
+ found: true;
161
+ origin: string;
162
+ path: string;
163
+ specMethods: HttpMethod[];
164
+ advisories: Partial<Record<HttpMethod, EndpointMethodAdvisory>>;
165
+ warnings: DiscoveryWarning[];
166
+ }
167
+ interface InspectEndpointForMcpFailure {
168
+ found: false;
169
+ origin: string;
170
+ path: string;
171
+ cause: McpFailureCause;
172
+ message?: string;
173
+ warnings: DiscoveryWarning[];
174
+ }
175
+ type InspectEndpointForMcpResult = InspectEndpointForMcpSuccess | InspectEndpointForMcpFailure;
176
+ declare function discoverForMcp(options: DiscoverForMcpOptions): Promise<DiscoverForMcpResult>;
177
+ declare function inspectEndpointForMcp(options: InspectEndpointForMcpOptions): Promise<InspectEndpointForMcpResult>;
178
+
97
179
  declare const UPGRADE_WARNING_CODES: readonly ["LEGACY_WELL_KNOWN_USED", "LEGACY_DNS_USED", "LEGACY_DNS_PLAIN_URL", "LEGACY_INSTRUCTIONS_USED", "LEGACY_OWNERSHIP_PROOFS_USED", "OPENAPI_AUTH_MODE_MISSING", "OPENAPI_TOP_LEVEL_INVALID"];
98
180
  declare function computeUpgradeSignal(warnings: DiscoveryWarning[]): {
99
181
  upgradeSuggested: boolean;
@@ -207,4 +289,4 @@ declare function defaultHarnessProbeCandidates(report: ContextHarnessReport): Pr
207
289
  declare function discover(options: DiscoverOptions): Promise<DiscoverResult>;
208
290
  declare function discoverDetailed(options: DiscoverDetailedOptions): Promise<DiscoverDetailedResult>;
209
291
 
210
- export { type AuthMode, CompatibilityMode, type ContextHarnessL0, type ContextHarnessL1, type ContextHarnessL2, type ContextHarnessL2Entry, type ContextHarnessL3, type ContextHarnessL4, type ContextHarnessL5, type ContextHarnessReport, type DiscoverDetailedOptions, type DiscoverDetailedResult, type DiscoverOptions, type DiscoverResult, type DiscoveryFetcher, type DiscoveryResource, type DiscoveryStage, type DiscoveryWarning, type HarnessClientId, type HarnessClientProfile, type HttpMethod, type PricingMode, type ProbeCandidate, type RawSources, type StageTrace, type TrustTier, type TxtResolver, UPGRADE_WARNING_CODES, type WarningSeverity, auditContextHarness, buildContextHarnessReport, computeUpgradeSignal, defaultHarnessProbeCandidates, discover, discoverDetailed, getHarnessClientProfile, listHarnessClientProfiles };
292
+ export { type AuthMode, CompatibilityMode, type ContextHarnessL0, type ContextHarnessL1, type ContextHarnessL2, type ContextHarnessL2Entry, type ContextHarnessL3, type ContextHarnessL4, type ContextHarnessL5, type ContextHarnessReport, type DiscoverDetailedOptions, type DiscoverDetailedResult, type DiscoverForMcpFailure, type DiscoverForMcpOptions, type DiscoverForMcpResult, type DiscoverForMcpSuccess, type DiscoverOptions, type DiscoverResult, type DiscoveryFetcher, type DiscoveryResource, type DiscoveryStage, type DiscoveryWarning, type EndpointMethodAdvisory, type HarnessClientId, type HarnessClientProfile, type HttpMethod, type InspectEndpointForMcpFailure, type InspectEndpointForMcpOptions, type InspectEndpointForMcpResult, type InspectEndpointForMcpSuccess, type PricingMode, type ProbeCandidate, type RawSources, type StageTrace, type TrustTier, type TxtResolver, UPGRADE_WARNING_CODES, type WarningSeverity, auditContextHarness, buildContextHarnessReport, computeUpgradeSignal, defaultHarnessProbeCandidates, discover, discoverDetailed, discoverForMcp, getHarnessClientProfile, inspectEndpointForMcp, listHarnessClientProfiles };
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ var STRICT_ESCALATION_CODES = [
12
12
  ];
13
13
 
14
14
  // src/mmm-enabled.ts
15
- var isMmmEnabled = () => "0.1.1".includes("-mmm");
15
+ var isMmmEnabled = () => "0.1.2".includes("-mmm");
16
16
 
17
17
  // src/core/constants.ts
18
18
  var OPENAPI_PATH_CANDIDATES = ["/openapi.json", "/.well-known/openapi.json"];
@@ -1400,6 +1400,396 @@ async function runDiscovery({
1400
1400
  };
1401
1401
  }
1402
1402
 
1403
+ // src/adapters/mcp.ts
1404
+ var DEFAULT_GUIDANCE_AUTO_INCLUDE_MAX_TOKENS = 1e3;
1405
+ var OPENAPI_METHODS = [
1406
+ "GET",
1407
+ "POST",
1408
+ "PUT",
1409
+ "DELETE",
1410
+ "PATCH",
1411
+ "HEAD",
1412
+ "OPTIONS",
1413
+ "TRACE"
1414
+ ];
1415
+ function isRecord4(value) {
1416
+ return value != null && typeof value === "object" && !Array.isArray(value);
1417
+ }
1418
+ function toFiniteNumber(value) {
1419
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1420
+ if (typeof value === "string" && value.trim().length > 0) {
1421
+ const parsed = Number(value);
1422
+ if (Number.isFinite(parsed)) return parsed;
1423
+ }
1424
+ return void 0;
1425
+ }
1426
+ function formatPriceHintFromResource(resource) {
1427
+ const pricing = resource.pricing;
1428
+ if (pricing) {
1429
+ if (pricing.pricingMode === "fixed" && pricing.price) {
1430
+ return pricing.price.startsWith("$") ? pricing.price : `$${pricing.price}`;
1431
+ }
1432
+ if (pricing.pricingMode === "range" && pricing.minPrice && pricing.maxPrice) {
1433
+ const min = pricing.minPrice.startsWith("$") ? pricing.minPrice : `$${pricing.minPrice}`;
1434
+ const max = pricing.maxPrice.startsWith("$") ? pricing.maxPrice : `$${pricing.maxPrice}`;
1435
+ return `${min}-${max}`;
1436
+ }
1437
+ }
1438
+ if (!resource.priceHint) return void 0;
1439
+ const hint = resource.priceHint.trim();
1440
+ if (hint.length === 0) return void 0;
1441
+ if (hint.startsWith("$")) return hint;
1442
+ if (hint.includes("-")) {
1443
+ const [min, max] = hint.split("-", 2).map((part) => part.trim());
1444
+ if (min && max) return `$${min}-$${max}`;
1445
+ }
1446
+ return `$${hint}`;
1447
+ }
1448
+ function deriveMcpAuthMode(resource) {
1449
+ if (!resource) return void 0;
1450
+ const hint = resource.authHint;
1451
+ if (hint === "paid" || hint === "siwx" || hint === "apiKey" || hint === "unprotected") {
1452
+ return hint;
1453
+ }
1454
+ return void 0;
1455
+ }
1456
+ function inferFailureCause(warnings) {
1457
+ const openapiWarnings = warnings.filter((w) => w.stage === "openapi");
1458
+ const parseWarning = openapiWarnings.find(
1459
+ (w) => w.code === "PARSE_FAILED" || w.code === "OPENAPI_TOP_LEVEL_INVALID" || w.code === "OPENAPI_OPERATION_INVALID" || w.code === "OPENAPI_PRICING_INVALID"
1460
+ );
1461
+ if (parseWarning) {
1462
+ return { cause: "parse", message: parseWarning.message };
1463
+ }
1464
+ const fetchWarning = openapiWarnings.find((w) => w.code === "FETCH_FAILED");
1465
+ if (fetchWarning) {
1466
+ const msg = fetchWarning.message.toLowerCase();
1467
+ if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("abort")) {
1468
+ return { cause: "timeout", message: fetchWarning.message };
1469
+ }
1470
+ return { cause: "network", message: fetchWarning.message };
1471
+ }
1472
+ return { cause: "not_found" };
1473
+ }
1474
+ function getOpenApiInfo(document) {
1475
+ if (!isRecord4(document) || !isRecord4(document.info)) return void 0;
1476
+ if (typeof document.info.title !== "string") return void 0;
1477
+ return {
1478
+ title: document.info.title,
1479
+ ...typeof document.info.version === "string" ? { version: document.info.version } : {},
1480
+ ...typeof document.info.description === "string" ? { description: document.info.description } : {}
1481
+ };
1482
+ }
1483
+ function getGuidanceUrl(result) {
1484
+ const fromTrace = result.trace.find((entry) => entry.links?.llmsTxtUrl)?.links?.llmsTxtUrl;
1485
+ if (fromTrace) return fromTrace;
1486
+ const fromResource = result.resources.find((resource) => resource.links?.llmsTxtUrl)?.links?.llmsTxtUrl;
1487
+ if (fromResource) return fromResource;
1488
+ return void 0;
1489
+ }
1490
+ async function resolveGuidance(options) {
1491
+ let guidanceText = typeof options.result.rawSources?.llmsTxt === "string" ? options.result.rawSources.llmsTxt : void 0;
1492
+ if (!guidanceText) {
1493
+ const guidanceUrl = getGuidanceUrl(options.result) ?? `${options.result.origin}/llms.txt`;
1494
+ const fetcher = options.fetcher ?? fetch;
1495
+ try {
1496
+ const response = await fetcher(guidanceUrl, {
1497
+ method: "GET",
1498
+ headers: { Accept: "text/plain", ...options.headers },
1499
+ signal: options.signal
1500
+ });
1501
+ if (response.ok) guidanceText = await response.text();
1502
+ } catch {
1503
+ }
1504
+ }
1505
+ if (!guidanceText) {
1506
+ return { guidanceAvailable: false };
1507
+ }
1508
+ const guidanceTokens = estimateTokenCount(guidanceText);
1509
+ const threshold = options.guidanceAutoIncludeMaxTokens ?? DEFAULT_GUIDANCE_AUTO_INCLUDE_MAX_TOKENS;
1510
+ const shouldInclude = options.includeGuidance === true || options.includeGuidance == null && guidanceTokens <= threshold;
1511
+ return {
1512
+ guidanceAvailable: true,
1513
+ guidanceTokens,
1514
+ ...shouldInclude ? { guidance: guidanceText } : {}
1515
+ };
1516
+ }
1517
+ function extractPathsDocument(document) {
1518
+ if (!isRecord4(document)) return void 0;
1519
+ if (!isRecord4(document.paths)) return void 0;
1520
+ return document.paths;
1521
+ }
1522
+ function findMatchingOpenApiPath(paths, targetPath) {
1523
+ const exact = paths[targetPath];
1524
+ if (isRecord4(exact)) return { matchedPath: targetPath, pathItem: exact };
1525
+ for (const [specPath, entry] of Object.entries(paths)) {
1526
+ if (!isRecord4(entry)) continue;
1527
+ const pattern = specPath.replace(/\{[^}]+\}/g, "[^/]+");
1528
+ const regex = new RegExp(`^${pattern}$`);
1529
+ if (regex.test(targetPath)) {
1530
+ return { matchedPath: specPath, pathItem: entry };
1531
+ }
1532
+ }
1533
+ return null;
1534
+ }
1535
+ function resolveRef(document, ref, seen) {
1536
+ if (!ref.startsWith("#/")) return void 0;
1537
+ if (seen.has(ref)) return { $circular: ref };
1538
+ seen.add(ref);
1539
+ const parts = ref.slice(2).split("/");
1540
+ let current = document;
1541
+ for (const part of parts) {
1542
+ if (!isRecord4(current)) return void 0;
1543
+ current = current[part];
1544
+ if (current === void 0) return void 0;
1545
+ }
1546
+ if (isRecord4(current)) return resolveRefs(document, current, seen);
1547
+ return current;
1548
+ }
1549
+ function resolveRefs(document, obj, seen, depth = 0) {
1550
+ if (depth > 4) return obj;
1551
+ const resolved = {};
1552
+ for (const [key, value] of Object.entries(obj)) {
1553
+ if (key === "$ref" && typeof value === "string") {
1554
+ const deref = resolveRef(document, value, seen);
1555
+ if (isRecord4(deref)) {
1556
+ Object.assign(resolved, deref);
1557
+ } else {
1558
+ resolved[key] = value;
1559
+ }
1560
+ continue;
1561
+ }
1562
+ if (isRecord4(value)) {
1563
+ resolved[key] = resolveRefs(document, value, seen, depth + 1);
1564
+ continue;
1565
+ }
1566
+ if (Array.isArray(value)) {
1567
+ resolved[key] = value.map(
1568
+ (item) => isRecord4(item) ? resolveRefs(document, item, seen, depth + 1) : item
1569
+ );
1570
+ continue;
1571
+ }
1572
+ resolved[key] = value;
1573
+ }
1574
+ return resolved;
1575
+ }
1576
+ function extractRequestBodySchema(operationSchema) {
1577
+ const requestBody = operationSchema.requestBody;
1578
+ if (!isRecord4(requestBody)) return void 0;
1579
+ const content = requestBody.content;
1580
+ if (!isRecord4(content)) return void 0;
1581
+ const jsonMediaType = content["application/json"];
1582
+ if (isRecord4(jsonMediaType) && isRecord4(jsonMediaType.schema)) {
1583
+ return jsonMediaType.schema;
1584
+ }
1585
+ for (const mediaType of Object.values(content)) {
1586
+ if (isRecord4(mediaType) && isRecord4(mediaType.schema)) {
1587
+ return mediaType.schema;
1588
+ }
1589
+ }
1590
+ return void 0;
1591
+ }
1592
+ function extractParameters(operationSchema) {
1593
+ const params = operationSchema.parameters;
1594
+ if (!Array.isArray(params)) return [];
1595
+ return params.filter((value) => isRecord4(value));
1596
+ }
1597
+ function extractInputSchema(operationSchema) {
1598
+ const requestBody = extractRequestBodySchema(operationSchema);
1599
+ const parameters = extractParameters(operationSchema);
1600
+ if (!requestBody && parameters.length === 0) return void 0;
1601
+ if (requestBody && parameters.length === 0) return requestBody;
1602
+ return {
1603
+ ...requestBody ? { requestBody } : {},
1604
+ ...parameters.length > 0 ? { parameters } : {}
1605
+ };
1606
+ }
1607
+ function parseOperationPrice(operation) {
1608
+ const paymentInfo = operation["x-payment-info"];
1609
+ if (!isRecord4(paymentInfo)) return void 0;
1610
+ const fixed = toFiniteNumber(paymentInfo.price);
1611
+ if (fixed != null) return `$${String(fixed)}`;
1612
+ const min = toFiniteNumber(paymentInfo.minPrice);
1613
+ const max = toFiniteNumber(paymentInfo.maxPrice);
1614
+ if (min != null && max != null) return `$${String(min)}-$${String(max)}`;
1615
+ return void 0;
1616
+ }
1617
+ function parseOperationProtocols(operation) {
1618
+ const paymentInfo = operation["x-payment-info"];
1619
+ if (!isRecord4(paymentInfo) || !Array.isArray(paymentInfo.protocols)) return void 0;
1620
+ const protocols = paymentInfo.protocols.filter(
1621
+ (protocol) => typeof protocol === "string" && protocol.length > 0
1622
+ );
1623
+ return protocols.length > 0 ? protocols : void 0;
1624
+ }
1625
+ function parseOperationAuthMode(operation) {
1626
+ const authExtension = operation["x-agentcash-auth"];
1627
+ if (isRecord4(authExtension)) {
1628
+ const mode = authExtension.mode;
1629
+ if (mode === "paid" || mode === "siwx" || mode === "apiKey" || mode === "unprotected") {
1630
+ return mode;
1631
+ }
1632
+ }
1633
+ if (isRecord4(operation["x-payment-info"])) return "paid";
1634
+ return void 0;
1635
+ }
1636
+ function createResourceMap(result) {
1637
+ const map = /* @__PURE__ */ new Map();
1638
+ for (const resource of result.resources) {
1639
+ map.set(resource.resourceKey, resource);
1640
+ }
1641
+ return map;
1642
+ }
1643
+ function toMcpEndpointSummary(resource) {
1644
+ const method = parseMethod(resource.method);
1645
+ if (!method) return void 0;
1646
+ const price = formatPriceHintFromResource(resource);
1647
+ const authMode = deriveMcpAuthMode(resource);
1648
+ return {
1649
+ path: resource.path,
1650
+ method,
1651
+ summary: resource.summary ?? `${method} ${resource.path}`,
1652
+ ...price ? { price } : {},
1653
+ ...resource.protocolHints?.length ? { protocols: [...resource.protocolHints] } : {},
1654
+ ...authMode ? { authMode } : {}
1655
+ };
1656
+ }
1657
+ async function discoverForMcp(options) {
1658
+ const result = await runDiscovery({
1659
+ detailed: true,
1660
+ options: {
1661
+ target: options.target,
1662
+ compatMode: options.compatMode ?? "off",
1663
+ rawView: "full",
1664
+ fetcher: options.fetcher,
1665
+ headers: options.headers,
1666
+ signal: options.signal
1667
+ }
1668
+ });
1669
+ if (result.resources.length === 0) {
1670
+ const failure = inferFailureCause(result.warnings);
1671
+ return {
1672
+ found: false,
1673
+ origin: result.origin,
1674
+ cause: failure.cause,
1675
+ ...failure.message ? { message: failure.message } : {},
1676
+ warnings: result.warnings
1677
+ };
1678
+ }
1679
+ const source = result.selectedStage ?? "openapi";
1680
+ const endpoints = result.resources.map(toMcpEndpointSummary).filter((endpoint) => endpoint != null).sort((a, b) => {
1681
+ if (a.path !== b.path) return a.path.localeCompare(b.path);
1682
+ return a.method.localeCompare(b.method);
1683
+ });
1684
+ const guidance = await resolveGuidance({
1685
+ result,
1686
+ includeGuidance: options.includeGuidance,
1687
+ guidanceAutoIncludeMaxTokens: options.guidanceAutoIncludeMaxTokens,
1688
+ fetcher: options.fetcher,
1689
+ headers: options.headers,
1690
+ signal: options.signal
1691
+ });
1692
+ return {
1693
+ found: true,
1694
+ origin: result.origin,
1695
+ source,
1696
+ ...getOpenApiInfo(result.rawSources?.openapi) ? { info: getOpenApiInfo(result.rawSources?.openapi) } : {},
1697
+ ...guidance,
1698
+ endpoints,
1699
+ warnings: result.warnings
1700
+ };
1701
+ }
1702
+ async function inspectEndpointForMcp(options) {
1703
+ const endpoint = new URL(options.endpointUrl);
1704
+ const origin = normalizeOrigin(endpoint.origin);
1705
+ const path = normalizePath(endpoint.pathname || "/");
1706
+ const result = await runDiscovery({
1707
+ detailed: true,
1708
+ options: {
1709
+ target: options.target,
1710
+ compatMode: options.compatMode ?? "off",
1711
+ rawView: "full",
1712
+ fetcher: options.fetcher,
1713
+ headers: options.headers,
1714
+ signal: options.signal
1715
+ }
1716
+ });
1717
+ const document = result.rawSources?.openapi;
1718
+ const paths = extractPathsDocument(document);
1719
+ const resourceMap = createResourceMap(result);
1720
+ const advisories = {};
1721
+ const specMethods = [];
1722
+ if (paths) {
1723
+ const matched = findMatchingOpenApiPath(paths, path);
1724
+ if (matched) {
1725
+ for (const candidate of OPENAPI_METHODS) {
1726
+ const operation = matched.pathItem[candidate.toLowerCase()];
1727
+ if (!isRecord4(operation) || !isRecord4(document)) continue;
1728
+ specMethods.push(candidate);
1729
+ const resolvedOperation = resolveRefs(document, operation, /* @__PURE__ */ new Set());
1730
+ const resource = resourceMap.get(toResourceKey(origin, candidate, matched.matchedPath));
1731
+ const formattedPrice = resource ? formatPriceHintFromResource(resource) : void 0;
1732
+ const operationSummary = typeof resolvedOperation.summary === "string" ? resolvedOperation.summary : typeof resolvedOperation.description === "string" ? resolvedOperation.description : void 0;
1733
+ const operationPrice = parseOperationPrice(resolvedOperation);
1734
+ const operationProtocols = parseOperationProtocols(resolvedOperation);
1735
+ const operationAuthMode = parseOperationAuthMode(resolvedOperation);
1736
+ const inputSchema = extractInputSchema(resolvedOperation);
1737
+ advisories[candidate] = {
1738
+ method: candidate,
1739
+ ...resource?.summary || operationSummary ? {
1740
+ summary: resource?.summary ?? operationSummary
1741
+ } : {},
1742
+ ...formattedPrice || operationPrice ? {
1743
+ estimatedPrice: formattedPrice ?? operationPrice
1744
+ } : {},
1745
+ ...resource?.protocolHints?.length || operationProtocols ? {
1746
+ protocols: resource?.protocolHints ?? operationProtocols
1747
+ } : {},
1748
+ ...deriveMcpAuthMode(resource) || operationAuthMode ? {
1749
+ authMode: deriveMcpAuthMode(resource) ?? operationAuthMode
1750
+ } : {},
1751
+ ...inputSchema ? { inputSchema } : {},
1752
+ operationSchema: resolvedOperation
1753
+ };
1754
+ }
1755
+ }
1756
+ }
1757
+ if (specMethods.length === 0) {
1758
+ for (const method of OPENAPI_METHODS) {
1759
+ const resource = resourceMap.get(toResourceKey(origin, method, path));
1760
+ if (!resource) continue;
1761
+ specMethods.push(method);
1762
+ const formattedPrice = formatPriceHintFromResource(resource);
1763
+ advisories[method] = {
1764
+ method,
1765
+ ...resource.summary ? { summary: resource.summary } : {},
1766
+ ...formattedPrice ? { estimatedPrice: formattedPrice } : {},
1767
+ ...resource.protocolHints?.length ? { protocols: [...resource.protocolHints] } : {},
1768
+ ...deriveMcpAuthMode(resource) ? { authMode: deriveMcpAuthMode(resource) } : {}
1769
+ };
1770
+ }
1771
+ }
1772
+ if (specMethods.length === 0) {
1773
+ const failure = inferFailureCause(result.warnings);
1774
+ return {
1775
+ found: false,
1776
+ origin,
1777
+ path,
1778
+ cause: failure.cause,
1779
+ ...failure.message ? { message: failure.message } : {},
1780
+ warnings: result.warnings
1781
+ };
1782
+ }
1783
+ return {
1784
+ found: true,
1785
+ origin,
1786
+ path,
1787
+ specMethods,
1788
+ advisories,
1789
+ warnings: result.warnings
1790
+ };
1791
+ }
1792
+
1403
1793
  // src/harness.ts
1404
1794
  var INTENT_TRIGGERS = ["x402", "mpp", "pay for", "micropayment", "agentic commerce"];
1405
1795
  var CLIENT_PROFILES = {
@@ -1658,6 +2048,8 @@ export {
1658
2048
  defaultHarnessProbeCandidates,
1659
2049
  discover,
1660
2050
  discoverDetailed,
2051
+ discoverForMcp,
1661
2052
  getHarnessClientProfile,
2053
+ inspectEndpointForMcp,
1662
2054
  listHarnessClientProfiles
1663
2055
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/discovery",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Canonical OpenAPI-first discovery runtime for the agentcash ecosystem",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",