@tabai/sdk 0.2.2 → 0.2.4

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.
Files changed (155) hide show
  1. package/README.md +16 -12
  2. package/dist/_shared/abi.d.ts.map +1 -1
  3. package/dist/_shared/abi.js +6 -3
  4. package/dist/_shared/abi.js.map +1 -1
  5. package/dist/_shared/chains.d.ts +4 -6
  6. package/dist/_shared/chains.d.ts.map +1 -1
  7. package/dist/_shared/chains.js +1 -2
  8. package/dist/_shared/chains.js.map +1 -1
  9. package/dist/_shared/keccak256.d.ts +6 -6
  10. package/dist/_shared/keccak256.js +6 -6
  11. package/dist/_shared/keccak256.js.map +1 -1
  12. package/dist/_shared/result.d.ts +5 -6
  13. package/dist/_shared/result.d.ts.map +1 -1
  14. package/dist/_shared/result.js +5 -6
  15. package/dist/_shared/result.js.map +1 -1
  16. package/dist/cli/client-config.d.ts +4 -5
  17. package/dist/cli/client-config.d.ts.map +1 -1
  18. package/dist/cli/client-config.js +4 -5
  19. package/dist/cli/client-config.js.map +1 -1
  20. package/dist/cli/connect.d.ts +1 -3
  21. package/dist/cli/connect.d.ts.map +1 -1
  22. package/dist/cli/connect.js +1 -3
  23. package/dist/cli/connect.js.map +1 -1
  24. package/dist/cli/doctor.d.ts +1 -3
  25. package/dist/cli/doctor.d.ts.map +1 -1
  26. package/dist/cli/doctor.js +1 -3
  27. package/dist/cli/doctor.js.map +1 -1
  28. package/dist/cli/index.d.ts +1 -1
  29. package/dist/cli/index.js +1 -1
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/cli/main.d.ts +1 -3
  32. package/dist/cli/main.d.ts.map +1 -1
  33. package/dist/cli/main.js +2 -4
  34. package/dist/cli/main.js.map +1 -1
  35. package/dist/errors.d.ts +2 -4
  36. package/dist/errors.d.ts.map +1 -1
  37. package/dist/errors.js +2 -4
  38. package/dist/errors.js.map +1 -1
  39. package/dist/http/client-402.d.ts +11 -13
  40. package/dist/http/client-402.d.ts.map +1 -1
  41. package/dist/http/client-402.js +12 -14
  42. package/dist/http/client-402.js.map +1 -1
  43. package/dist/http/headers.d.ts +9 -11
  44. package/dist/http/headers.d.ts.map +1 -1
  45. package/dist/http/headers.js +9 -11
  46. package/dist/http/headers.js.map +1 -1
  47. package/dist/http/index.d.ts +0 -2
  48. package/dist/http/index.d.ts.map +1 -1
  49. package/dist/http/index.js +0 -2
  50. package/dist/http/index.js.map +1 -1
  51. package/dist/http/metering-claim.d.ts +0 -2
  52. package/dist/http/metering-claim.d.ts.map +1 -1
  53. package/dist/http/metering-claim.js +0 -2
  54. package/dist/http/metering-claim.js.map +1 -1
  55. package/dist/index.d.ts +15 -12
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +15 -12
  58. package/dist/index.js.map +1 -1
  59. package/dist/logger.d.ts +1 -4
  60. package/dist/logger.d.ts.map +1 -1
  61. package/dist/logger.js +1 -4
  62. package/dist/logger.js.map +1 -1
  63. package/dist/mcp/index.d.ts +1 -1
  64. package/dist/mcp/index.js +1 -1
  65. package/dist/mcp/index.js.map +1 -1
  66. package/dist/mcp/json-schema.d.ts +1 -3
  67. package/dist/mcp/json-schema.d.ts.map +1 -1
  68. package/dist/mcp/json-schema.js +1 -3
  69. package/dist/mcp/json-schema.js.map +1 -1
  70. package/dist/mcp/json.d.ts +0 -2
  71. package/dist/mcp/json.d.ts.map +1 -1
  72. package/dist/mcp/json.js +0 -2
  73. package/dist/mcp/json.js.map +1 -1
  74. package/dist/mcp/registry-client.d.ts +0 -2
  75. package/dist/mcp/registry-client.d.ts.map +1 -1
  76. package/dist/mcp/registry-client.js +3 -4
  77. package/dist/mcp/registry-client.js.map +1 -1
  78. package/dist/mcp/schemas.d.ts +10 -8
  79. package/dist/mcp/schemas.d.ts.map +1 -1
  80. package/dist/mcp/schemas.js +5 -7
  81. package/dist/mcp/schemas.js.map +1 -1
  82. package/dist/mcp/server.d.ts +0 -2
  83. package/dist/mcp/server.d.ts.map +1 -1
  84. package/dist/mcp/server.js +4 -7
  85. package/dist/mcp/server.js.map +1 -1
  86. package/dist/mcp/settings.d.ts +0 -2
  87. package/dist/mcp/settings.d.ts.map +1 -1
  88. package/dist/mcp/settings.js +0 -2
  89. package/dist/mcp/settings.js.map +1 -1
  90. package/dist/mcp/toolset.d.ts +2 -5
  91. package/dist/mcp/toolset.d.ts.map +1 -1
  92. package/dist/mcp/toolset.js +3 -6
  93. package/dist/mcp/toolset.js.map +1 -1
  94. package/dist/payments/config.d.ts +6 -8
  95. package/dist/payments/config.d.ts.map +1 -1
  96. package/dist/payments/config.js +5 -7
  97. package/dist/payments/config.js.map +1 -1
  98. package/dist/payments/index.d.ts +0 -2
  99. package/dist/payments/index.d.ts.map +1 -1
  100. package/dist/payments/index.js +0 -2
  101. package/dist/payments/index.js.map +1 -1
  102. package/dist/payments/registry.d.ts +11 -12
  103. package/dist/payments/registry.d.ts.map +1 -1
  104. package/dist/payments/registry.js +11 -12
  105. package/dist/payments/registry.js.map +1 -1
  106. package/dist/payments/strategy.d.ts +9 -5
  107. package/dist/payments/strategy.d.ts.map +1 -1
  108. package/dist/payments/strategy.js +9 -5
  109. package/dist/payments/strategy.js.map +1 -1
  110. package/dist/proxy/hooks.d.ts +3 -5
  111. package/dist/proxy/hooks.d.ts.map +1 -1
  112. package/dist/proxy/hooks.js +2 -4
  113. package/dist/proxy/hooks.js.map +1 -1
  114. package/dist/proxy/index.d.ts +2 -2
  115. package/dist/proxy/index.js +2 -2
  116. package/dist/proxy/index.js.map +1 -1
  117. package/dist/proxy/proxy.d.ts +3 -5
  118. package/dist/proxy/proxy.d.ts.map +1 -1
  119. package/dist/proxy/proxy.js +2 -4
  120. package/dist/proxy/proxy.js.map +1 -1
  121. package/dist/server/adapters/express.d.ts +0 -2
  122. package/dist/server/adapters/express.d.ts.map +1 -1
  123. package/dist/server/adapters/express.js +0 -2
  124. package/dist/server/adapters/express.js.map +1 -1
  125. package/dist/server/adapters/hono.d.ts +0 -2
  126. package/dist/server/adapters/hono.d.ts.map +1 -1
  127. package/dist/server/adapters/hono.js +0 -2
  128. package/dist/server/adapters/hono.js.map +1 -1
  129. package/dist/server/adapters/next.d.ts +0 -2
  130. package/dist/server/adapters/next.d.ts.map +1 -1
  131. package/dist/server/adapters/next.js +0 -2
  132. package/dist/server/adapters/next.js.map +1 -1
  133. package/dist/server/index.d.ts +5 -5
  134. package/dist/server/index.js +5 -5
  135. package/dist/server/index.js.map +1 -1
  136. package/dist/server/metering.d.ts +9 -11
  137. package/dist/server/metering.d.ts.map +1 -1
  138. package/dist/server/metering.js +6 -8
  139. package/dist/server/metering.js.map +1 -1
  140. package/dist/server/post-paid.d.ts +8 -10
  141. package/dist/server/post-paid.d.ts.map +1 -1
  142. package/dist/server/post-paid.js +7 -9
  143. package/dist/server/post-paid.js.map +1 -1
  144. package/dist/x402/client.d.ts +5 -6
  145. package/dist/x402/client.d.ts.map +1 -1
  146. package/dist/x402/client.js +6 -7
  147. package/dist/x402/client.js.map +1 -1
  148. package/dist/x402/server.d.ts +5 -5
  149. package/dist/x402/server.d.ts.map +1 -1
  150. package/dist/x402/server.js +2 -2
  151. package/dist/x402/server.js.map +1 -1
  152. package/dist/x402/wire.d.ts +1 -1
  153. package/dist/x402/wire.js +1 -1
  154. package/dist/x402/wire.js.map +1 -1
  155. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -2,20 +2,19 @@
2
2
  * `@tabai/sdk` is the published client surface. It may draw on `./_shared/index.js`
3
3
  * and on nothing else inside the workspace.
4
4
  *
5
- * Three surfaces, and the shape of the product is visible in how they relate:
5
+ * Five surfaces, and the shape of the product is visible in how they relate:
6
6
  *
7
7
  * - **`payments/`** - the strategy seam. The interface a Settlement goes through,
8
8
  * the Monad strategy this package ships, and the three ways a consumer adds a
9
- * strategy of their own without editing a file in here (R23.1, R23.6).
10
- * - **`http/`**, the wire contract and the Agent's side of it. `headers.ts` is the
9
+ * strategy of their own without editing a file in here.
10
+ * - **`http/`** - the wire contract and the Agent's side of it. `headers.ts` is the
11
11
  * single definition of the `Tab-*` format, and `client-402.ts` is the post-paid
12
- * 402 client that reads it (R23.2).
13
- * - **`server/`**, the Service's side. `tabPostPaid` accrues after a delivery and
14
- * never withholds a response, with adapters for Hono, Express, and Next.js
15
- * (R23.3).
12
+ * 402 client that reads it.
13
+ * - **`server/`** - the Service's side. `tabPostPaid` accrues after a delivery and
14
+ * never withholds a response, with adapters for Hono, Express, and Next.js.
16
15
  * - **`proxy/`** - the Service's front door. `createTabProxy` forwards a request
17
- * upstream under the metering plugin and runs hooks around it (R23.4), and a
18
- * hook can attach the Settlement that covers a proxied request (R23.5).
16
+ * upstream under the metering plugin and runs hooks around it, and a
17
+ * hook can attach the Settlement that covers a proxied request.
19
18
  * - **`x402/`** - the prepaid protocol beside the credit one. A Tab `402` can
20
19
  * offer an x402 payment for the one call it refused, an Agent with an x402
21
20
  * signer can take it, and a Service can front an x402 upstream and meter the
@@ -27,10 +26,14 @@
27
26
  * between two.
28
27
  *
29
28
  * Nothing exported from this package throws. Every fallible call returns a
30
- * `Result` from `./_shared/index.js` (R21.5). The one exception is named and deliberate:
31
- * the Hono and Next.js adapters re-raise a handler's own thrown value, because a
29
+ * `Result` from `./_shared/index.js`. Two exceptions are named and deliberate. The
30
+ * Hono and Next.js adapters re-raise a handler's own thrown value, because a
32
31
  * framework's contract for a failed handler is an exception and the adapter is the
33
- * boundary where a `Result` becomes whatever the host expects.
32
+ * boundary where a `Result` becomes whatever the host expects. And
33
+ * `createX402UpstreamPricing` refuses a non-positive `unitBaseUnits` at
34
+ * construction with a `RangeError`: a pricing object that cannot price is a
35
+ * programming error to surface at startup, and the gateway validates the same
36
+ * value from its configuration before it ever calls the factory.
34
37
  */
35
38
  export declare const WORKSPACE_ID_SDK: "@tabai/sdk";
36
39
  export declare const SHARED_WORKSPACE_ID: "./_shared/index.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,eAAO,MAAM,gBAAgB,EAAG,YAAqB,CAAC;AAEtD,eAAO,MAAM,mBAAmB,iBAAe,CAAC;AAKhD,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACvD,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAI5F,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9D,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAIH,eAAO,MAAM,gBAAgB,EAAG,YAAqB,CAAC;AAEtD,eAAO,MAAM,mBAAmB,iBAAe,CAAC;AAKhD,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACvD,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAI5F,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9D,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -2,20 +2,19 @@
2
2
  * `@tabai/sdk` is the published client surface. It may draw on `./_shared/index.js`
3
3
  * and on nothing else inside the workspace.
4
4
  *
5
- * Three surfaces, and the shape of the product is visible in how they relate:
5
+ * Five surfaces, and the shape of the product is visible in how they relate:
6
6
  *
7
7
  * - **`payments/`** - the strategy seam. The interface a Settlement goes through,
8
8
  * the Monad strategy this package ships, and the three ways a consumer adds a
9
- * strategy of their own without editing a file in here (R23.1, R23.6).
10
- * - **`http/`**, the wire contract and the Agent's side of it. `headers.ts` is the
9
+ * strategy of their own without editing a file in here.
10
+ * - **`http/`** - the wire contract and the Agent's side of it. `headers.ts` is the
11
11
  * single definition of the `Tab-*` format, and `client-402.ts` is the post-paid
12
- * 402 client that reads it (R23.2).
13
- * - **`server/`**, the Service's side. `tabPostPaid` accrues after a delivery and
14
- * never withholds a response, with adapters for Hono, Express, and Next.js
15
- * (R23.3).
12
+ * 402 client that reads it.
13
+ * - **`server/`** - the Service's side. `tabPostPaid` accrues after a delivery and
14
+ * never withholds a response, with adapters for Hono, Express, and Next.js.
16
15
  * - **`proxy/`** - the Service's front door. `createTabProxy` forwards a request
17
- * upstream under the metering plugin and runs hooks around it (R23.4), and a
18
- * hook can attach the Settlement that covers a proxied request (R23.5).
16
+ * upstream under the metering plugin and runs hooks around it, and a
17
+ * hook can attach the Settlement that covers a proxied request.
19
18
  * - **`x402/`** - the prepaid protocol beside the credit one. A Tab `402` can
20
19
  * offer an x402 payment for the one call it refused, an Agent with an x402
21
20
  * signer can take it, and a Service can front an x402 upstream and meter the
@@ -27,10 +26,14 @@
27
26
  * between two.
28
27
  *
29
28
  * Nothing exported from this package throws. Every fallible call returns a
30
- * `Result` from `./_shared/index.js` (R21.5). The one exception is named and deliberate:
31
- * the Hono and Next.js adapters re-raise a handler's own thrown value, because a
29
+ * `Result` from `./_shared/index.js`. Two exceptions are named and deliberate. The
30
+ * Hono and Next.js adapters re-raise a handler's own thrown value, because a
32
31
  * framework's contract for a failed handler is an exception and the adapter is the
33
- * boundary where a `Result` becomes whatever the host expects.
32
+ * boundary where a `Result` becomes whatever the host expects. And
33
+ * `createX402UpstreamPricing` refuses a non-positive `unitBaseUnits` at
34
+ * construction with a `RangeError`: a pricing object that cannot price is a
35
+ * programming error to surface at startup, and the gateway validates the same
36
+ * value from its configuration before it ever calls the factory.
34
37
  */
35
38
  import { WORKSPACE_ID } from "./_shared/index.js";
36
39
  export const WORKSPACE_ID_SDK = "@tabai/sdk";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAqB,CAAC;AAEtD,MAAM,CAAC,MAAM,mBAAmB,GAAG,YAAY,CAAC;AAEhD,+EAA+E;AAC/E,iFAAiF;AACjF,mDAAmD;AACnD,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGvD,gFAAgF;AAChF,gEAAgE;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC","sourcesContent":["/**\n * `@tabai/sdk` is the published client surface. It may draw on `./_shared/index.js`\n * and on nothing else inside the workspace.\n *\n * Three surfaces, and the shape of the product is visible in how they relate:\n *\n * - **`payments/`** - the strategy seam. The interface a Settlement goes through,\n * the Monad strategy this package ships, and the three ways a consumer adds a\n * strategy of their own without editing a file in here (R23.1, R23.6).\n * - **`http/`**, the wire contract and the Agent's side of it. `headers.ts` is the\n * single definition of the `Tab-*` format, and `client-402.ts` is the post-paid\n * 402 client that reads it (R23.2).\n * - **`server/`**, the Service's side. `tabPostPaid` accrues after a delivery and\n * never withholds a response, with adapters for Hono, Express, and Next.js\n * (R23.3).\n * - **`proxy/`** - the Service's front door. `createTabProxy` forwards a request\n * upstream under the metering plugin and runs hooks around it (R23.4), and a\n * hook can attach the Settlement that covers a proxied request (R23.5).\n * - **`x402/`** - the prepaid protocol beside the credit one. A Tab `402` can\n * offer an x402 payment for the one call it refused, an Agent with an x402\n * signer can take it, and a Service can front an x402 upstream and meter the\n * Agent for what it paid.\n *\n * **`http/headers.ts` is deliberately the only place the wire format exists.** The\n * client parses with it and the server formats with it, so the two halves cannot\n * drift apart without a test failing in one file, rather than mis-parsing silently\n * between two.\n *\n * Nothing exported from this package throws. Every fallible call returns a\n * `Result` from `./_shared/index.js` (R21.5). The one exception is named and deliberate:\n * the Hono and Next.js adapters re-raise a handler's own thrown value, because a\n * framework's contract for a failed handler is an exception and the adapter is the\n * boundary where a `Result` becomes whatever the host expects.\n */\n\nimport { WORKSPACE_ID } from \"./_shared/index.js\";\n\nexport const WORKSPACE_ID_SDK = \"@tabai/sdk\" as const;\n\nexport const SHARED_WORKSPACE_ID = WORKSPACE_ID;\n\n// Every fallible call returns a `Result`, so a consumer needs its type and its\n// constructors from the package it installed; `./_shared/index.js` is inlined at pack\n// time and has no name on npm to import them from.\nexport { ok, err, wrap, causeOf } from \"./_shared/index.js\";\nexport type { Result, TabError, ErrorCategory, Address, Bytes32, Hex } from \"./_shared/index.js\";\n\n// Where this project hosts its read API and demo Service on each network, which\n// the MCP server falls back to when nothing else is configured.\nexport { TAB_HOSTED } from \"./_shared/index.js\";\nexport type { TabHosted, HostedService } from \"./_shared/index.js\";\n\nexport * from \"./logger.js\";\nexport * from \"./errors.js\";\nexport * from \"./payments/index.js\";\nexport * from \"./http/index.js\";\nexport * from \"./server/index.js\";\nexport * from \"./proxy/index.js\";\nexport * from \"./x402/index.js\";\nexport * from \"./mcp/index.js\";\nexport * from \"./cli/index.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAqB,CAAC;AAEtD,MAAM,CAAC,MAAM,mBAAmB,GAAG,YAAY,CAAC;AAEhD,+EAA+E;AAC/E,iFAAiF;AACjF,mDAAmD;AACnD,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGvD,gFAAgF;AAChF,gEAAgE;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC","sourcesContent":["/**\n * `@tabai/sdk` is the published client surface. It may draw on `./_shared/index.js`\n * and on nothing else inside the workspace.\n *\n * Five surfaces, and the shape of the product is visible in how they relate:\n *\n * - **`payments/`** - the strategy seam. The interface a Settlement goes through,\n * the Monad strategy this package ships, and the three ways a consumer adds a\n * strategy of their own without editing a file in here.\n * - **`http/`** - the wire contract and the Agent's side of it. `headers.ts` is the\n * single definition of the `Tab-*` format, and `client-402.ts` is the post-paid\n * 402 client that reads it.\n * - **`server/`** - the Service's side. `tabPostPaid` accrues after a delivery and\n * never withholds a response, with adapters for Hono, Express, and Next.js.\n * - **`proxy/`** - the Service's front door. `createTabProxy` forwards a request\n * upstream under the metering plugin and runs hooks around it, and a\n * hook can attach the Settlement that covers a proxied request.\n * - **`x402/`** - the prepaid protocol beside the credit one. A Tab `402` can\n * offer an x402 payment for the one call it refused, an Agent with an x402\n * signer can take it, and a Service can front an x402 upstream and meter the\n * Agent for what it paid.\n *\n * **`http/headers.ts` is deliberately the only place the wire format exists.** The\n * client parses with it and the server formats with it, so the two halves cannot\n * drift apart without a test failing in one file, rather than mis-parsing silently\n * between two.\n *\n * Nothing exported from this package throws. Every fallible call returns a\n * `Result` from `./_shared/index.js`. Two exceptions are named and deliberate. The\n * Hono and Next.js adapters re-raise a handler's own thrown value, because a\n * framework's contract for a failed handler is an exception and the adapter is the\n * boundary where a `Result` becomes whatever the host expects. And\n * `createX402UpstreamPricing` refuses a non-positive `unitBaseUnits` at\n * construction with a `RangeError`: a pricing object that cannot price is a\n * programming error to surface at startup, and the gateway validates the same\n * value from its configuration before it ever calls the factory.\n */\n\nimport { WORKSPACE_ID } from \"./_shared/index.js\";\n\nexport const WORKSPACE_ID_SDK = \"@tabai/sdk\" as const;\n\nexport const SHARED_WORKSPACE_ID = WORKSPACE_ID;\n\n// Every fallible call returns a `Result`, so a consumer needs its type and its\n// constructors from the package it installed; `./_shared/index.js` is inlined at pack\n// time and has no name on npm to import them from.\nexport { ok, err, wrap, causeOf } from \"./_shared/index.js\";\nexport type { Result, TabError, ErrorCategory, Address, Bytes32, Hex } from \"./_shared/index.js\";\n\n// Where this project hosts its read API and demo Service on each network, which\n// the MCP server falls back to when nothing else is configured.\nexport { TAB_HOSTED } from \"./_shared/index.js\";\nexport type { TabHosted, HostedService } from \"./_shared/index.js\";\n\nexport * from \"./logger.js\";\nexport * from \"./errors.js\";\nexport * from \"./payments/index.js\";\nexport * from \"./http/index.js\";\nexport * from \"./server/index.js\";\nexport * from \"./proxy/index.js\";\nexport * from \"./x402/index.js\";\nexport * from \"./mcp/index.js\";\nexport * from \"./cli/index.js\";\n"]}
package/dist/logger.d.ts CHANGED
@@ -4,14 +4,11 @@
4
4
  * The seam needs somewhere to put a warning that is not an error: a duplicate
5
5
  * strategy id replaces the earlier registration rather than throwing, and a
6
6
  * consumer has to be able to see that happen without the SDK deciding for them
7
- * that it deserves a crash. Design section 9.3 names the behaviour; this is the
8
- * sink it writes to.
7
+ * that it deserves a crash. This is the sink that warning goes to.
9
8
  *
10
9
  * The interface is four methods and one optional field bag, so a consumer can
11
10
  * hand in `pino`, `winston`, or a closure over an array in a test without an
12
11
  * adapter. Nothing here formats, filters, or buffers.
13
- *
14
- * Requirements: 23.6
15
12
  */
16
13
  /** Structured fields attached to one log line. Values are rendered by the sink. */
17
14
  export type LogFields = Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;CAClD;AAED,oEAAoE;AACpE,eAAO,MAAM,YAAY,EAAE,MAK1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAK3B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,MAAsB,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;CAClD;AAED,oEAAoE;AACpE,eAAO,MAAM,YAAY,EAAE,MAK1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAK3B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,MAAsB,CAAC"}
package/dist/logger.js CHANGED
@@ -4,14 +4,11 @@
4
4
  * The seam needs somewhere to put a warning that is not an error: a duplicate
5
5
  * strategy id replaces the earlier registration rather than throwing, and a
6
6
  * consumer has to be able to see that happen without the SDK deciding for them
7
- * that it deserves a crash. Design section 9.3 names the behaviour; this is the
8
- * sink it writes to.
7
+ * that it deserves a crash. This is the sink that warning goes to.
9
8
  *
10
9
  * The interface is four methods and one optional field bag, so a consumer can
11
10
  * hand in `pino`, `winston`, or a closure over an array in a test without an
12
11
  * adapter. Nothing here formats, filters, or buffers.
13
- *
14
- * Requirements: 23.6
15
12
  */
16
13
  /** Discards every line. The right default inside a library test. */
17
14
  export const silentLogger = {
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAYH,oEAAoE;AACpE,MAAM,CAAC,MAAM,YAAY,GAAW;IAClC,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAW;IACnC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;IAC1D,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC;IACxD,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC;IACxD,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;CAC3D,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAW,aAAa,CAAC;AAEnD,SAAS,IAAI,CAAC,KAA0C,EAAE,OAAe,EAAE,MAAkB;IAC3F,MAAM,IAAI,GAAG,QAAQ,OAAO,EAAE,CAAC;IAC/B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC/B,CAAC","sourcesContent":["/**\n * The SDK logger.\n *\n * The seam needs somewhere to put a warning that is not an error: a duplicate\n * strategy id replaces the earlier registration rather than throwing, and a\n * consumer has to be able to see that happen without the SDK deciding for them\n * that it deserves a crash. Design section 9.3 names the behaviour; this is the\n * sink it writes to.\n *\n * The interface is four methods and one optional field bag, so a consumer can\n * hand in `pino`, `winston`, or a closure over an array in a test without an\n * adapter. Nothing here formats, filters, or buffers.\n *\n * Requirements: 23.6\n */\n\n/** Structured fields attached to one log line. Values are rendered by the sink. */\nexport type LogFields = Record<string, unknown>;\n\nexport interface Logger {\n debug(message: string, fields?: LogFields): void;\n info(message: string, fields?: LogFields): void;\n warn(message: string, fields?: LogFields): void;\n error(message: string, fields?: LogFields): void;\n}\n\n/** Discards every line. The right default inside a library test. */\nexport const silentLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Writes to the host console, prefixing every line with `tab:` so a Service\n * operator can tell an SDK line from their own.\n */\nexport const consoleLogger: Logger = {\n debug: (message, fields) => emit(\"debug\", message, fields),\n info: (message, fields) => emit(\"info\", message, fields),\n warn: (message, fields) => emit(\"warn\", message, fields),\n error: (message, fields) => emit(\"error\", message, fields),\n};\n\n/**\n * The logger used when a caller supplies none.\n *\n * The console rather than silence, because the one thing this sink exists to\n * carry, a strategy registration that replaced another, is invisible\n * otherwise, and a silently swapped payment strategy is the kind of surprise\n * that costs money.\n */\nexport const defaultLogger: Logger = consoleLogger;\n\nfunction emit(level: \"debug\" | \"info\" | \"warn\" | \"error\", message: string, fields?: LogFields): void {\n const line = `tab: ${message}`;\n if (fields === undefined) {\n console[level](line);\n return;\n }\n console[level](line, fields);\n}\n"]}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAYH,oEAAoE;AACpE,MAAM,CAAC,MAAM,YAAY,GAAW;IAClC,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAW;IACnC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;IAC1D,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC;IACxD,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC;IACxD,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;CAC3D,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAW,aAAa,CAAC;AAEnD,SAAS,IAAI,CAAC,KAA0C,EAAE,OAAe,EAAE,MAAkB;IAC3F,MAAM,IAAI,GAAG,QAAQ,OAAO,EAAE,CAAC;IAC/B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC/B,CAAC","sourcesContent":["/**\n * The SDK logger.\n *\n * The seam needs somewhere to put a warning that is not an error: a duplicate\n * strategy id replaces the earlier registration rather than throwing, and a\n * consumer has to be able to see that happen without the SDK deciding for them\n * that it deserves a crash. This is the sink that warning goes to.\n *\n * The interface is four methods and one optional field bag, so a consumer can\n * hand in `pino`, `winston`, or a closure over an array in a test without an\n * adapter. Nothing here formats, filters, or buffers.\n */\n\n/** Structured fields attached to one log line. Values are rendered by the sink. */\nexport type LogFields = Record<string, unknown>;\n\nexport interface Logger {\n debug(message: string, fields?: LogFields): void;\n info(message: string, fields?: LogFields): void;\n warn(message: string, fields?: LogFields): void;\n error(message: string, fields?: LogFields): void;\n}\n\n/** Discards every line. The right default inside a library test. */\nexport const silentLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Writes to the host console, prefixing every line with `tab:` so a Service\n * operator can tell an SDK line from their own.\n */\nexport const consoleLogger: Logger = {\n debug: (message, fields) => emit(\"debug\", message, fields),\n info: (message, fields) => emit(\"info\", message, fields),\n warn: (message, fields) => emit(\"warn\", message, fields),\n error: (message, fields) => emit(\"error\", message, fields),\n};\n\n/**\n * The logger used when a caller supplies none.\n *\n * The console rather than silence, because the one thing this sink exists to\n * carry, a strategy registration that replaced another, is invisible\n * otherwise, and a silently swapped payment strategy is the kind of surprise\n * that costs money.\n */\nexport const defaultLogger: Logger = consoleLogger;\n\nfunction emit(level: \"debug\" | \"info\" | \"warn\" | \"error\", message: string, fields?: LogFields): void {\n const line = `tab: ${message}`;\n if (fields === undefined) {\n console[level](line);\n return;\n }\n console[level](line, fields);\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The MCP surface: four tools, their declared schemas, and the server that
3
- * serves them. (R25.1, R25.2, R25.3)
3
+ * serves them.
4
4
  *
5
5
  * `tab_discover` finds Services, `tab_call` uses one and is metered onto the
6
6
  * Agent's Open Tab, `tab_status` reports what the Agent owes and may still
package/dist/mcp/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The MCP surface: four tools, their declared schemas, and the server that
3
- * serves them. (R25.1, R25.2, R25.3)
3
+ * serves them.
4
4
  *
5
5
  * `tab_discover` finds Services, `tab_call` uses one and is metered onto the
6
6
  * Agent's Open Tab, `tab_status` reports what the Agent owes and may still
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC","sourcesContent":["/**\n * The MCP surface: four tools, their declared schemas, and the server that\n * serves them. (R25.1, R25.2, R25.3)\n *\n * `tab_discover` finds Services, `tab_call` uses one and is metered onto the\n * Agent's Open Tab, `tab_status` reports what the Agent owes and may still\n * spend, and `tab_settle` pays it down. Three of the four are keyless reads.\n * Only `tab_settle` signs, and it signs through the same payment-strategy seam\n * the rest of this package settles through.\n */\n\nexport * from \"./json-schema.js\";\nexport * from \"./schemas.js\";\nexport * from \"./assets.js\";\nexport * from \"./json.js\";\nexport * from \"./registry-client.js\";\nexport * from \"./settings.js\";\nexport * from \"./toolset.js\";\nexport * from \"./server.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC","sourcesContent":["/**\n * The MCP surface: four tools, their declared schemas, and the server that\n * serves them.\n *\n * `tab_discover` finds Services, `tab_call` uses one and is metered onto the\n * Agent's Open Tab, `tab_status` reports what the Agent owes and may still\n * spend, and `tab_settle` pays it down. Three of the four are keyless reads.\n * Only `tab_settle` signs, and it signs through the same payment-strategy seam\n * the rest of this package settles through.\n */\n\nexport * from \"./json-schema.js\";\nexport * from \"./schemas.js\";\nexport * from \"./assets.js\";\nexport * from \"./json.js\";\nexport * from \"./registry-client.js\";\nexport * from \"./settings.js\";\nexport * from \"./toolset.js\";\nexport * from \"./server.js\";\n"]}
@@ -9,7 +9,7 @@
9
9
  * `limit` or emits an amount as a `number` has broken the schema its caller was
10
10
  * reasoning against, and nothing in the process notices. So the four tools in
11
11
  * this package validate every input against the schema they publish, and the
12
- * test suite validates every output against the schema they publish (task 16.3).
12
+ * test suite validates every output against the schema they publish.
13
13
  *
14
14
  * The validator is 200 lines rather than a dependency because the schemas here
15
15
  * use ten keywords between them, this package adds no dependency for the MCP
@@ -25,8 +25,6 @@
25
25
  * as an `INTERNAL` `Result` at a tool boundary is reported to the model as a
26
26
  * failed call, and a bug that surfaces as a thrown value takes the stdio
27
27
  * transport down with it.
28
- *
29
- * Requirements: 21.5, 25.1, 25.2
30
28
  */
31
29
  import type { Result } from "../_shared/index.js";
32
30
  /** The seven JSON Schema primitive type names. */
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema.d.ts","sourceRoot":"","sources":["../../src/mcp/json-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAK5C,kDAAkD;AAClD,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,SAAS,GACT,MAAM,CAAC;AAEX,mGAAmG;AACnG,eAAO,MAAM,kBAAkB,iOAmBrB,CAAC;AAEX;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,cAAc,GAAG,SAAS,cAAc,EAAE,CAAC;IAC3D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;IAC9D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAClD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CACxC;AAED,4EAA4E;AAC5E,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;CAC3D;AA2HD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,IAAI,SAAoB,GACvB,MAAM,CAAC,CAAC,CAAC,CAoBX;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAQ7E"}
1
+ {"version":3,"file":"json-schema.d.ts","sourceRoot":"","sources":["../../src/mcp/json-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAK5C,kDAAkD;AAClD,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,SAAS,GACT,MAAM,CAAC;AAEX,mGAAmG;AACnG,eAAO,MAAM,kBAAkB,iOAmBrB,CAAC;AAEX;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,cAAc,GAAG,SAAS,cAAc,EAAE,CAAC;IAC3D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;IAC9D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAClD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CACxC;AAED,4EAA4E;AAC5E,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;CAC3D;AA2HD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,IAAI,SAAoB,GACvB,MAAM,CAAC,CAAC,CAAC,CAoBX;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAQ7E"}
@@ -9,7 +9,7 @@
9
9
  * `limit` or emits an amount as a `number` has broken the schema its caller was
10
10
  * reasoning against, and nothing in the process notices. So the four tools in
11
11
  * this package validate every input against the schema they publish, and the
12
- * test suite validates every output against the schema they publish (task 16.3).
12
+ * test suite validates every output against the schema they publish.
13
13
  *
14
14
  * The validator is 200 lines rather than a dependency because the schemas here
15
15
  * use ten keywords between them, this package adds no dependency for the MCP
@@ -25,8 +25,6 @@
25
25
  * as an `INTERNAL` `Result` at a tool boundary is reported to the model as a
26
26
  * failed call, and a bug that surfaces as a thrown value takes the stdio
27
27
  * transport down with it.
28
- *
29
- * Requirements: 21.5, 25.1, 25.2
30
28
  */
31
29
  import { ok } from "../_shared/index.js";
32
30
  import { fail, validationError } from "../errors.js";
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema.js","sourceRoot":"","sources":["../../src/mcp/json-schema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The JSON Schema subset the MCP tools declare, and a validator for it.\n *\n * ## Why this is written here rather than pulled in\n *\n * An MCP tool declares its input and output shape as JSON Schema, and a model\n * reads that declaration to decide what to send. A declaration nothing enforces\n * is a promise, not a contract: the first tool that accepts an out-of-range\n * `limit` or emits an amount as a `number` has broken the schema its caller was\n * reasoning against, and nothing in the process notices. So the four tools in\n * this package validate every input against the schema they publish, and the\n * test suite validates every output against the schema they publish (task 16.3).\n *\n * The validator is 200 lines rather than a dependency because the schemas here\n * use ten keywords between them, this package adds no dependency for the MCP\n * surface, and a validator whose supported keyword set is visible in one file\n * cannot silently ignore a keyword a schema relies on. {@link validateJsonValue}\n * fails loudly on a keyword it does not implement instead of passing the value,\n * which is the property a hand-written validator has to have to be trustworthy.\n *\n * ## Everything returns a Result\n *\n * Nothing here throws, including on a malformed schema. A schema is authored in\n * this package, so a bad one is a bug rather than input, but a bug that surfaces\n * as an `INTERNAL` `Result` at a tool boundary is reported to the model as a\n * failed call, and a bug that surfaces as a thrown value takes the stdio\n * transport down with it.\n *\n * Requirements: 21.5, 25.1, 25.2\n */\n\nimport type { Result } from \"../_shared/index.js\";\nimport { ok } from \"../_shared/index.js\";\n\nimport { fail, validationError } from \"../errors.js\";\n\n/** The seven JSON Schema primitive type names. */\nexport type JsonSchemaType =\n | \"object\"\n | \"array\"\n | \"string\"\n | \"number\"\n | \"integer\"\n | \"boolean\"\n | \"null\";\n\n/** Every keyword {@link validateJsonValue} understands. Anything else is a failure, not a pass. */\nexport const SUPPORTED_KEYWORDS = [\n \"type\",\n \"title\",\n \"description\",\n \"properties\",\n \"required\",\n \"additionalProperties\",\n \"items\",\n \"enum\",\n \"const\",\n \"pattern\",\n \"minLength\",\n \"maxLength\",\n \"minimum\",\n \"maximum\",\n \"minItems\",\n \"maxItems\",\n \"default\",\n \"examples\",\n] as const;\n\n/**\n * One node of the schema subset.\n *\n * `type` may be an array, which is how a nullable field is declared here:\n * `{ type: [\"string\", \"null\"] }`. There is no `nullable` keyword, because that\n * one is OpenAPI's rather than JSON Schema's and a model reading the tool\n * declaration would be reading a keyword that does not mean what it says.\n */\nexport interface JsonSchema {\n readonly type?: JsonSchemaType | readonly JsonSchemaType[];\n readonly title?: string;\n readonly description?: string;\n readonly properties?: Readonly<Record<string, JsonSchema>>;\n readonly required?: readonly string[];\n readonly additionalProperties?: boolean;\n readonly items?: JsonSchema;\n readonly enum?: readonly (string | number | boolean | null)[];\n readonly const?: string | number | boolean | null;\n readonly pattern?: string;\n readonly minLength?: number;\n readonly maxLength?: number;\n readonly minimum?: number;\n readonly maximum?: number;\n readonly minItems?: number;\n readonly maxItems?: number;\n readonly default?: unknown;\n readonly examples?: readonly unknown[];\n}\n\n/** An object-typed schema, which is what every tool input and output is. */\nexport interface JsonObjectSchema extends JsonSchema {\n readonly type: \"object\";\n readonly properties: Readonly<Record<string, JsonSchema>>;\n}\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst typeNameOf = (value: unknown): JsonSchemaType => {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"array\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"number\") return Number.isInteger(value) ? \"integer\" : \"number\";\n if (typeof value === \"string\") return \"string\";\n return \"object\";\n};\n\n/**\n * Does a value satisfy one declared type name?\n *\n * `integer` accepts only a safe integer, and `number` accepts an integer too,\n * which is JSON Schema's own rule. A `bigint` matches nothing: an amount in\n * Asset base units crosses this boundary as a decimal string and never as a\n * JavaScript number, so a `bigint` that reached a tool payload is a bug in the\n * mapping and is reported rather than coerced.\n */\nconst matchesType = (value: unknown, declared: JsonSchemaType): boolean => {\n if (typeof value === \"bigint\") return false;\n const actual = typeNameOf(value);\n if (declared === \"number\") return actual === \"number\" || actual === \"integer\";\n if (declared === \"integer\") return actual === \"integer\" && Number.isSafeInteger(value);\n return actual === declared;\n};\n\nconst declaredTypes = (schema: JsonSchema): readonly JsonSchemaType[] | undefined => {\n if (schema.type === undefined) return undefined;\n return typeof schema.type === \"string\" ? [schema.type] : schema.type;\n};\n\n/** A dotted path into the value, so a problem names the field a caller has to fix. */\nconst child = (path: string, segment: string): string => (path === \"\" ? segment : `${path}.${segment}`);\n\n/** Every unsupported keyword found anywhere in a schema, deepest last. */\nfunction unsupportedKeywords(schema: JsonSchema, path: string, found: string[]): void {\n for (const keyword of Object.keys(schema)) {\n if (!(SUPPORTED_KEYWORDS as readonly string[]).includes(keyword)) {\n found.push(`${path === \"\" ? \"<root>\" : path}: ${keyword}`);\n }\n }\n if (schema.properties !== undefined) {\n for (const [name, property] of Object.entries(schema.properties)) {\n unsupportedKeywords(property, child(path, name), found);\n }\n }\n if (schema.items !== undefined) unsupportedKeywords(schema.items, `${path}[]`, found);\n}\n\nfunction collect(schema: JsonSchema, value: unknown, path: string, problems: string[]): void {\n const where = path === \"\" ? \"value\" : path;\n\n const types = declaredTypes(schema);\n if (types !== undefined && !types.some((declared) => matchesType(value, declared))) {\n problems.push(`${where}: expected ${types.join(\" or \")}, received ${typeNameOf(value)}`);\n return;\n }\n\n if (schema.const !== undefined && value !== schema.const) {\n problems.push(`${where}: must equal ${JSON.stringify(schema.const)}`);\n }\n if (schema.enum !== undefined && !schema.enum.includes(value as string)) {\n problems.push(`${where}: must be one of ${schema.enum.map((v) => JSON.stringify(v)).join(\", \")}`);\n }\n\n if (typeof value === \"string\") {\n if (schema.pattern !== undefined && !new RegExp(schema.pattern).test(value)) {\n problems.push(`${where}: must match ${schema.pattern}`);\n }\n if (schema.minLength !== undefined && value.length < schema.minLength) {\n problems.push(`${where}: must be at least ${schema.minLength} characters`);\n }\n if (schema.maxLength !== undefined && value.length > schema.maxLength) {\n problems.push(`${where}: must be at most ${schema.maxLength} characters`);\n }\n }\n\n if (typeof value === \"number\") {\n if (schema.minimum !== undefined && value < schema.minimum) {\n problems.push(`${where}: must be at least ${schema.minimum}`);\n }\n if (schema.maximum !== undefined && value > schema.maximum) {\n problems.push(`${where}: must be at most ${schema.maximum}`);\n }\n }\n\n if (Array.isArray(value)) {\n if (schema.minItems !== undefined && value.length < schema.minItems) {\n problems.push(`${where}: must hold at least ${schema.minItems} items`);\n }\n if (schema.maxItems !== undefined && value.length > schema.maxItems) {\n problems.push(`${where}: must hold at most ${schema.maxItems} items`);\n }\n if (schema.items !== undefined) {\n value.forEach((entry, index) => collect(schema.items!, entry, `${where}[${index}]`, problems));\n }\n }\n\n if (isPlainObject(value)) {\n for (const name of schema.required ?? []) {\n if (value[name] === undefined) problems.push(`${child(where, name)}: required`);\n }\n const properties = schema.properties;\n if (properties !== undefined) {\n for (const [name, property] of Object.entries(properties)) {\n // An absent optional property is absent, not null. `exactOptionalPropertyTypes`\n // holds the same line in the types, so the two agree.\n if (value[name] !== undefined) collect(property, value[name], child(where, name), problems);\n }\n if (schema.additionalProperties === false) {\n for (const name of Object.keys(value)) {\n if (!(name in properties)) problems.push(`${child(where, name)}: not a declared property`);\n }\n }\n }\n }\n}\n\n/**\n * Validates a value against a schema and returns it unchanged when it conforms.\n *\n * `code` names the failure so a caller can tell an input rejection from an\n * output-shape bug without parsing the message.\n */\nexport function validateJsonValue<T>(\n schema: JsonSchema,\n value: unknown,\n label: string,\n code = \"SCHEMA_MISMATCH\",\n): Result<T> {\n const unsupported: string[] = [];\n unsupportedKeywords(schema, \"\", unsupported);\n if (unsupported.length > 0) {\n return fail(\n \"INTERNAL\",\n \"SCHEMA_KEYWORD_UNSUPPORTED\",\n `the ${label} schema uses keywords this validator does not implement, so nothing was checked: ${unsupported.join(\"; \")}`,\n { details: { label, unsupported: unsupported.join(\"; \") } },\n );\n }\n\n const problems: string[] = [];\n collect(schema, value, \"\", problems);\n if (problems.length > 0) {\n return validationError(code, `${label} does not match its declared schema: ${problems.join(\"; \")}`, {\n details: { label, problems: problems.join(\"; \") },\n });\n }\n return ok(value as T);\n}\n\n/**\n * Fills in the declared top-level defaults of an object schema.\n *\n * Only the top level, because that is where every default in this package's\n * schemas sits and a defaulting pass that reached into arrays would be inventing\n * entries a caller did not send. A non-object value is handed back untouched for\n * {@link validateJsonValue} to reject with a type problem, which is a better\n * message than one about defaults.\n */\nexport function applyJsonDefaults(schema: JsonSchema, value: unknown): unknown {\n const properties = schema.properties;\n if (properties === undefined || !isPlainObject(value)) return value;\n const filled: Record<string, unknown> = { ...value };\n for (const [name, property] of Object.entries(properties)) {\n if (filled[name] === undefined && property.default !== undefined) filled[name] = property.default;\n }\n return filled;\n}\n"]}
1
+ {"version":3,"file":"json-schema.js","sourceRoot":"","sources":["../../src/mcp/json-schema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The JSON Schema subset the MCP tools declare, and a validator for it.\n *\n * ## Why this is written here rather than pulled in\n *\n * An MCP tool declares its input and output shape as JSON Schema, and a model\n * reads that declaration to decide what to send. A declaration nothing enforces\n * is a promise, not a contract: the first tool that accepts an out-of-range\n * `limit` or emits an amount as a `number` has broken the schema its caller was\n * reasoning against, and nothing in the process notices. So the four tools in\n * this package validate every input against the schema they publish, and the\n * test suite validates every output against the schema they publish.\n *\n * The validator is 200 lines rather than a dependency because the schemas here\n * use ten keywords between them, this package adds no dependency for the MCP\n * surface, and a validator whose supported keyword set is visible in one file\n * cannot silently ignore a keyword a schema relies on. {@link validateJsonValue}\n * fails loudly on a keyword it does not implement instead of passing the value,\n * which is the property a hand-written validator has to have to be trustworthy.\n *\n * ## Everything returns a Result\n *\n * Nothing here throws, including on a malformed schema. A schema is authored in\n * this package, so a bad one is a bug rather than input, but a bug that surfaces\n * as an `INTERNAL` `Result` at a tool boundary is reported to the model as a\n * failed call, and a bug that surfaces as a thrown value takes the stdio\n * transport down with it.\n */\n\nimport type { Result } from \"../_shared/index.js\";\nimport { ok } from \"../_shared/index.js\";\n\nimport { fail, validationError } from \"../errors.js\";\n\n/** The seven JSON Schema primitive type names. */\nexport type JsonSchemaType =\n | \"object\"\n | \"array\"\n | \"string\"\n | \"number\"\n | \"integer\"\n | \"boolean\"\n | \"null\";\n\n/** Every keyword {@link validateJsonValue} understands. Anything else is a failure, not a pass. */\nexport const SUPPORTED_KEYWORDS = [\n \"type\",\n \"title\",\n \"description\",\n \"properties\",\n \"required\",\n \"additionalProperties\",\n \"items\",\n \"enum\",\n \"const\",\n \"pattern\",\n \"minLength\",\n \"maxLength\",\n \"minimum\",\n \"maximum\",\n \"minItems\",\n \"maxItems\",\n \"default\",\n \"examples\",\n] as const;\n\n/**\n * One node of the schema subset.\n *\n * `type` may be an array, which is how a nullable field is declared here:\n * `{ type: [\"string\", \"null\"] }`. There is no `nullable` keyword, because that\n * one is OpenAPI's rather than JSON Schema's and a model reading the tool\n * declaration would be reading a keyword that does not mean what it says.\n */\nexport interface JsonSchema {\n readonly type?: JsonSchemaType | readonly JsonSchemaType[];\n readonly title?: string;\n readonly description?: string;\n readonly properties?: Readonly<Record<string, JsonSchema>>;\n readonly required?: readonly string[];\n readonly additionalProperties?: boolean;\n readonly items?: JsonSchema;\n readonly enum?: readonly (string | number | boolean | null)[];\n readonly const?: string | number | boolean | null;\n readonly pattern?: string;\n readonly minLength?: number;\n readonly maxLength?: number;\n readonly minimum?: number;\n readonly maximum?: number;\n readonly minItems?: number;\n readonly maxItems?: number;\n readonly default?: unknown;\n readonly examples?: readonly unknown[];\n}\n\n/** An object-typed schema, which is what every tool input and output is. */\nexport interface JsonObjectSchema extends JsonSchema {\n readonly type: \"object\";\n readonly properties: Readonly<Record<string, JsonSchema>>;\n}\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst typeNameOf = (value: unknown): JsonSchemaType => {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"array\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"number\") return Number.isInteger(value) ? \"integer\" : \"number\";\n if (typeof value === \"string\") return \"string\";\n return \"object\";\n};\n\n/**\n * Does a value satisfy one declared type name?\n *\n * `integer` accepts only a safe integer, and `number` accepts an integer too,\n * which is JSON Schema's own rule. A `bigint` matches nothing: an amount in\n * Asset base units crosses this boundary as a decimal string and never as a\n * JavaScript number, so a `bigint` that reached a tool payload is a bug in the\n * mapping and is reported rather than coerced.\n */\nconst matchesType = (value: unknown, declared: JsonSchemaType): boolean => {\n if (typeof value === \"bigint\") return false;\n const actual = typeNameOf(value);\n if (declared === \"number\") return actual === \"number\" || actual === \"integer\";\n if (declared === \"integer\") return actual === \"integer\" && Number.isSafeInteger(value);\n return actual === declared;\n};\n\nconst declaredTypes = (schema: JsonSchema): readonly JsonSchemaType[] | undefined => {\n if (schema.type === undefined) return undefined;\n return typeof schema.type === \"string\" ? [schema.type] : schema.type;\n};\n\n/** A dotted path into the value, so a problem names the field a caller has to fix. */\nconst child = (path: string, segment: string): string => (path === \"\" ? segment : `${path}.${segment}`);\n\n/** Every unsupported keyword found anywhere in a schema, deepest last. */\nfunction unsupportedKeywords(schema: JsonSchema, path: string, found: string[]): void {\n for (const keyword of Object.keys(schema)) {\n if (!(SUPPORTED_KEYWORDS as readonly string[]).includes(keyword)) {\n found.push(`${path === \"\" ? \"<root>\" : path}: ${keyword}`);\n }\n }\n if (schema.properties !== undefined) {\n for (const [name, property] of Object.entries(schema.properties)) {\n unsupportedKeywords(property, child(path, name), found);\n }\n }\n if (schema.items !== undefined) unsupportedKeywords(schema.items, `${path}[]`, found);\n}\n\nfunction collect(schema: JsonSchema, value: unknown, path: string, problems: string[]): void {\n const where = path === \"\" ? \"value\" : path;\n\n const types = declaredTypes(schema);\n if (types !== undefined && !types.some((declared) => matchesType(value, declared))) {\n problems.push(`${where}: expected ${types.join(\" or \")}, received ${typeNameOf(value)}`);\n return;\n }\n\n if (schema.const !== undefined && value !== schema.const) {\n problems.push(`${where}: must equal ${JSON.stringify(schema.const)}`);\n }\n if (schema.enum !== undefined && !schema.enum.includes(value as string)) {\n problems.push(`${where}: must be one of ${schema.enum.map((v) => JSON.stringify(v)).join(\", \")}`);\n }\n\n if (typeof value === \"string\") {\n if (schema.pattern !== undefined && !new RegExp(schema.pattern).test(value)) {\n problems.push(`${where}: must match ${schema.pattern}`);\n }\n if (schema.minLength !== undefined && value.length < schema.minLength) {\n problems.push(`${where}: must be at least ${schema.minLength} characters`);\n }\n if (schema.maxLength !== undefined && value.length > schema.maxLength) {\n problems.push(`${where}: must be at most ${schema.maxLength} characters`);\n }\n }\n\n if (typeof value === \"number\") {\n if (schema.minimum !== undefined && value < schema.minimum) {\n problems.push(`${where}: must be at least ${schema.minimum}`);\n }\n if (schema.maximum !== undefined && value > schema.maximum) {\n problems.push(`${where}: must be at most ${schema.maximum}`);\n }\n }\n\n if (Array.isArray(value)) {\n if (schema.minItems !== undefined && value.length < schema.minItems) {\n problems.push(`${where}: must hold at least ${schema.minItems} items`);\n }\n if (schema.maxItems !== undefined && value.length > schema.maxItems) {\n problems.push(`${where}: must hold at most ${schema.maxItems} items`);\n }\n if (schema.items !== undefined) {\n value.forEach((entry, index) => collect(schema.items!, entry, `${where}[${index}]`, problems));\n }\n }\n\n if (isPlainObject(value)) {\n for (const name of schema.required ?? []) {\n if (value[name] === undefined) problems.push(`${child(where, name)}: required`);\n }\n const properties = schema.properties;\n if (properties !== undefined) {\n for (const [name, property] of Object.entries(properties)) {\n // An absent optional property is absent, not null. `exactOptionalPropertyTypes`\n // holds the same line in the types, so the two agree.\n if (value[name] !== undefined) collect(property, value[name], child(where, name), problems);\n }\n if (schema.additionalProperties === false) {\n for (const name of Object.keys(value)) {\n if (!(name in properties)) problems.push(`${child(where, name)}: not a declared property`);\n }\n }\n }\n }\n}\n\n/**\n * Validates a value against a schema and returns it unchanged when it conforms.\n *\n * `code` names the failure so a caller can tell an input rejection from an\n * output-shape bug without parsing the message.\n */\nexport function validateJsonValue<T>(\n schema: JsonSchema,\n value: unknown,\n label: string,\n code = \"SCHEMA_MISMATCH\",\n): Result<T> {\n const unsupported: string[] = [];\n unsupportedKeywords(schema, \"\", unsupported);\n if (unsupported.length > 0) {\n return fail(\n \"INTERNAL\",\n \"SCHEMA_KEYWORD_UNSUPPORTED\",\n `the ${label} schema uses keywords this validator does not implement, so nothing was checked: ${unsupported.join(\"; \")}`,\n { details: { label, unsupported: unsupported.join(\"; \") } },\n );\n }\n\n const problems: string[] = [];\n collect(schema, value, \"\", problems);\n if (problems.length > 0) {\n return validationError(code, `${label} does not match its declared schema: ${problems.join(\"; \")}`, {\n details: { label, problems: problems.join(\"; \") },\n });\n }\n return ok(value as T);\n}\n\n/**\n * Fills in the declared top-level defaults of an object schema.\n *\n * Only the top level, because that is where every default in this package's\n * schemas sits and a defaulting pass that reached into arrays would be inventing\n * entries a caller did not send. A non-object value is handed back untouched for\n * {@link validateJsonValue} to reject with a type problem, which is a better\n * message than one about defaults.\n */\nexport function applyJsonDefaults(schema: JsonSchema, value: unknown): unknown {\n const properties = schema.properties;\n if (properties === undefined || !isPlainObject(value)) return value;\n const filled: Record<string, unknown> = { ...value };\n for (const [name, property] of Object.entries(properties)) {\n if (filled[name] === undefined && property.default !== undefined) filled[name] = property.default;\n }\n return filled;\n}\n"]}
@@ -11,8 +11,6 @@
11
11
  * a stated fallback and never throws. A field that went missing upstream becomes
12
12
  * a null in the tool's output, which the schema declares as possible, instead of
13
13
  * taking the call down.
14
- *
15
- * Requirements: 25.1, 25.2
16
14
  */
17
15
  /** A JSON object, as far as anything here is concerned. */
18
16
  export type JsonRecord = Readonly<Record<string, unknown>>;
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/mcp/json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,2DAA2D;AAC3D,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE3D,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,UACmB,CAAC;AAEvE,2DAA2D;AAC3D,eAAO,MAAM,KAAK,GAAI,OAAO,OAAO,EAAE,KAAK,MAAM,KAAG,OAAqD,CAAC;AAE1G,oEAAoE;AACpE,eAAO,MAAM,IAAI,GAAI,OAAO,OAAO,EAAE,GAAG,MAAM,SAAS,MAAM,EAAE,KAAG,OACE,CAAC;AAErE,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,UAA4C,CAAC;AAEvF,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,SAAS,OAAO,EAAyC,CAAC;AAEnG,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,EAAE,UAAU,MAAM,KAAG,MACd,CAAC;AAE/C,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,IAAkD,CAAC;AAE5G,gGAAgG;AAChG,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,EAAE,UAAU,MAAM,KAAG,MAO3D,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,OAAO,OAAO,EAAE,UAAU,OAAO,KAAG,OACf,CAAC;AAEhD;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,EAAE,UAAU,MAAM,KAAG,MAK3D,CAAC;AAEF,2CAA2C;AAC3C,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,IAGxD,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,IAMtD,CAAC"}
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/mcp/json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,2DAA2D;AAC3D,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE3D,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,UACmB,CAAC;AAEvE,2DAA2D;AAC3D,eAAO,MAAM,KAAK,GAAI,OAAO,OAAO,EAAE,KAAK,MAAM,KAAG,OAAqD,CAAC;AAE1G,oEAAoE;AACpE,eAAO,MAAM,IAAI,GAAI,OAAO,OAAO,EAAE,GAAG,MAAM,SAAS,MAAM,EAAE,KAAG,OACE,CAAC;AAErE,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,UAA4C,CAAC;AAEvF,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,SAAS,OAAO,EAAyC,CAAC;AAEnG,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,EAAE,UAAU,MAAM,KAAG,MACd,CAAC;AAE/C,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,IAAkD,CAAC;AAE5G,gGAAgG;AAChG,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,EAAE,UAAU,MAAM,KAAG,MAO3D,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,OAAO,OAAO,EAAE,UAAU,OAAO,KAAG,OACf,CAAC;AAEhD;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,EAAE,UAAU,MAAM,KAAG,MAK3D,CAAC;AAEF,2CAA2C;AAC3C,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,IAGxD,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,IAMtD,CAAC"}
package/dist/mcp/json.js CHANGED
@@ -11,8 +11,6 @@
11
11
  * a stated fallback and never throws. A field that went missing upstream becomes
12
12
  * a null in the tool's output, which the schema declares as possible, instead of
13
13
  * taking the call down.
14
- *
15
- * Requirements: 25.1, 25.2
16
14
  */
17
15
  export const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
18
16
  /** The value at `key`, when the container is an object. */
@@ -1 +1 @@
1
- {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/mcp/json.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Defensive readers for JSON this package did not produce.\n *\n * The registry read API is a separate process on a separate release cadence, and\n * a Service endpoint is a third party's code. Neither is a compile-time\n * dependency of this package and neither should be: an interface mirrored here\n * would be a copy that goes stale silently, and the failure it produces is a\n * `TypeError` deep inside a tool handler rather than a message naming the field.\n *\n * So every field is read through one of these, each of which answers a value or\n * a stated fallback and never throws. A field that went missing upstream becomes\n * a null in the tool's output, which the schema declares as possible, instead of\n * taking the call down.\n *\n * Requirements: 25.1, 25.2\n */\n\n/** A JSON object, as far as anything here is concerned. */\nexport type JsonRecord = Readonly<Record<string, unknown>>;\n\nexport const isRecord = (value: unknown): value is JsonRecord =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** The value at `key`, when the container is an object. */\nexport const field = (value: unknown, key: string): unknown => (isRecord(value) ? value[key] : undefined);\n\n/** The value at a dotted path, stopping at the first non-object. */\nexport const path = (value: unknown, ...keys: readonly string[]): unknown =>\n keys.reduce<unknown>((current, key) => field(current, key), value);\n\nexport const asRecord = (value: unknown): JsonRecord => (isRecord(value) ? value : {});\n\nexport const asArray = (value: unknown): readonly unknown[] => (Array.isArray(value) ? value : []);\n\nexport const asString = (value: unknown, fallback: string): string =>\n typeof value === \"string\" ? value : fallback;\n\nexport const asStringOrNull = (value: unknown): string | null => (typeof value === \"string\" ? value : null);\n\n/** A finite number, or the fallback. `NaN` and an infinity are not numbers a schema accepts. */\nexport const asNumber = (value: unknown, fallback: number): number => {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && /^-?[0-9]+$/.test(value)) {\n const parsed = Number(value);\n if (Number.isSafeInteger(parsed)) return parsed;\n }\n return fallback;\n};\n\nexport const asBoolean = (value: unknown, fallback: boolean): boolean =>\n typeof value === \"boolean\" ? value : fallback;\n\n/**\n * A `uint256` as the decimal string every amount crosses this boundary as.\n *\n * A JavaScript number is accepted only when it is a safe integer, because a\n * larger one has already lost digits by the time it arrives and stringifying it\n * would launder a rounded amount into something that looks exact.\n */\nexport const asDigits = (value: unknown, fallback: string): string => {\n if (typeof value === \"string\" && /^[0-9]{1,39}$/.test(value)) return value;\n if (typeof value === \"bigint\" && value >= 0n) return value.toString();\n if (typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0) return String(value);\n return fallback;\n};\n\n/** The same, but absence stays absence. */\nexport const asDigitsOrNull = (value: unknown): string | null => {\n const digits = asDigits(value, \"\");\n return digits === \"\" ? null : digits;\n};\n\n/** Seconds since the epoch, as an ISO instant. Null for anything unreadable. */\nexport const secondsToIso = (value: unknown): string | null => {\n const seconds = asDigits(value, \"\");\n if (seconds === \"\") return null;\n const millis = Number(seconds) * 1000;\n if (!Number.isFinite(millis) || millis <= 0) return null;\n return new Date(millis).toISOString();\n};\n"]}
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/mcp/json.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Defensive readers for JSON this package did not produce.\n *\n * The registry read API is a separate process on a separate release cadence, and\n * a Service endpoint is a third party's code. Neither is a compile-time\n * dependency of this package and neither should be: an interface mirrored here\n * would be a copy that goes stale silently, and the failure it produces is a\n * `TypeError` deep inside a tool handler rather than a message naming the field.\n *\n * So every field is read through one of these, each of which answers a value or\n * a stated fallback and never throws. A field that went missing upstream becomes\n * a null in the tool's output, which the schema declares as possible, instead of\n * taking the call down.\n */\n\n/** A JSON object, as far as anything here is concerned. */\nexport type JsonRecord = Readonly<Record<string, unknown>>;\n\nexport const isRecord = (value: unknown): value is JsonRecord =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** The value at `key`, when the container is an object. */\nexport const field = (value: unknown, key: string): unknown => (isRecord(value) ? value[key] : undefined);\n\n/** The value at a dotted path, stopping at the first non-object. */\nexport const path = (value: unknown, ...keys: readonly string[]): unknown =>\n keys.reduce<unknown>((current, key) => field(current, key), value);\n\nexport const asRecord = (value: unknown): JsonRecord => (isRecord(value) ? value : {});\n\nexport const asArray = (value: unknown): readonly unknown[] => (Array.isArray(value) ? value : []);\n\nexport const asString = (value: unknown, fallback: string): string =>\n typeof value === \"string\" ? value : fallback;\n\nexport const asStringOrNull = (value: unknown): string | null => (typeof value === \"string\" ? value : null);\n\n/** A finite number, or the fallback. `NaN` and an infinity are not numbers a schema accepts. */\nexport const asNumber = (value: unknown, fallback: number): number => {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && /^-?[0-9]+$/.test(value)) {\n const parsed = Number(value);\n if (Number.isSafeInteger(parsed)) return parsed;\n }\n return fallback;\n};\n\nexport const asBoolean = (value: unknown, fallback: boolean): boolean =>\n typeof value === \"boolean\" ? value : fallback;\n\n/**\n * A `uint256` as the decimal string every amount crosses this boundary as.\n *\n * A JavaScript number is accepted only when it is a safe integer, because a\n * larger one has already lost digits by the time it arrives and stringifying it\n * would launder a rounded amount into something that looks exact.\n */\nexport const asDigits = (value: unknown, fallback: string): string => {\n if (typeof value === \"string\" && /^[0-9]{1,39}$/.test(value)) return value;\n if (typeof value === \"bigint\" && value >= 0n) return value.toString();\n if (typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0) return String(value);\n return fallback;\n};\n\n/** The same, but absence stays absence. */\nexport const asDigitsOrNull = (value: unknown): string | null => {\n const digits = asDigits(value, \"\");\n return digits === \"\" ? null : digits;\n};\n\n/** Seconds since the epoch, as an ISO instant. Null for anything unreadable. */\nexport const secondsToIso = (value: unknown): string | null => {\n const seconds = asDigits(value, \"\");\n if (seconds === \"\") return null;\n const millis = Number(seconds) * 1000;\n if (!Number.isFinite(millis) || millis <= 0) return null;\n return new Date(millis).toISOString();\n};\n"]}
@@ -32,8 +32,6 @@
32
32
  * response, and the abort signal are described by the shapes this module
33
33
  * touches, exactly as `client-402.ts` does. A host `fetch`, `undici`, and a test
34
34
  * closure all satisfy {@link RegistryFetch} without any of them being named.
35
- *
36
- * Requirements: 24.1, 24.4, 25.1, 25.3
37
35
  */
38
36
  import type { Result } from "../_shared/index.js";
39
37
  import { type Logger } from "../logger.js";
@@ -1 +1 @@
1
- {"version":3,"file":"registry-client.d.ts","sourceRoot":"","sources":["../../src/mcp/registry-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAY,MAAM,eAAe,CAAC;AAItD,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAG1D,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1B;AAED,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEhH,MAAM,WAAW,yBAAyB;IACxC,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;IACnC,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kFAAkF;IAClF,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACnC,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,6EAA6E;IAC7E,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,mGAAmG;IACnG,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,4EAA4E;IAC5E,WAAW,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9D,qFAAqF;IACrF,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,KAAG,MAAqC,CAAC;AAsDzF;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB,CAsF/F"}
1
+ {"version":3,"file":"registry-client.d.ts","sourceRoot":"","sources":["../../src/mcp/registry-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAY,MAAM,eAAe,CAAC;AAItD,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAG1D,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1B;AAED,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEhH,MAAM,WAAW,yBAAyB;IACxC,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;IACnC,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kFAAkF;IAClF,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACnC,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,6EAA6E;IAC7E,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,mGAAmG;IACnG,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,4EAA4E;IAC5E,WAAW,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9D,qFAAqF;IACrF,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,KAAG,MAAqC,CAAC;AAuDzF;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB,CAsF/F"}
@@ -32,10 +32,8 @@
32
32
  * response, and the abort signal are described by the shapes this module
33
33
  * touches, exactly as `client-402.ts` does. A host `fetch`, `undici`, and a test
34
34
  * closure all satisfy {@link RegistryFetch} without any of them being named.
35
- *
36
- * Requirements: 24.1, 24.4, 25.1, 25.3
37
35
  */
38
- import { ok } from "../_shared/index.js";
36
+ import { HTTP_STATUS_BY_CATEGORY, ok } from "../_shared/index.js";
39
37
  import { tabError, upstreamError, validationError } from "../errors.js";
40
38
  import { defaultLogger } from "../logger.js";
41
39
  import { asString, isRecord } from "./json.js";
@@ -57,7 +55,8 @@ const hostFetch = () => {
57
55
  const candidate = globalThis.fetch;
58
56
  return typeof candidate === "function" ? candidate : undefined;
59
57
  };
60
- const CATEGORIES = ["VALIDATION", "NOT_FOUND", "CONFLICT", "UPSTREAM", "CHAIN", "TIMEOUT", "INTERNAL"];
58
+ /** Every `ErrorCategory`, read off the shared status table so the two cannot disagree. */
59
+ const CATEGORIES = Object.keys(HTTP_STATUS_BY_CATEGORY);
61
60
  /**
62
61
  * Reads the read API's own error body back into a `TabError`.
63
62
  *
@@ -1 +1 @@
1
- {"version":3,"file":"registry-client.js","sourceRoot":"","sources":["../../src/mcp/registry-client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The keyless read client for the Tab registry read API.\n *\n * `tab_discover` and `tab_status` answer questions about chain state -- which\n * Services are registered, what each tool costs, what an Agent owes -- and every\n * one of those answers is a public fact. Nothing here signs, nothing here\n * authenticates, and nothing here writes. That is why `tab connect` can wire a\n * client up without ever touching a key, and why `tab doctor` can check a\n * deployment end to end from a laptop that holds none.\n *\n * ## Why the read API rather than the chain\n *\n * A Credit Limit is not a storage slot. It is `LimitLib` recomputed over an\n * Agent's committed Settlement history and the Bond ledger, cross-checked\n * against `TabBook.creditLimit` at the same block before it is served. A Bond\n * figure is a sum of `Bond`'s own events, checked against `Bond.ledgerOf`. An\n * MCP server that read the chain directly would have to restate both, and a\n * second restatement that drifts from the first is worse than one source of\n * truth: it would put two different Credit Limits in front of the same model.\n * So the figures come from the one process that already computes and checks\n * them, and this client's whole job is to fetch and not to interpret.\n *\n * The consequence is stated rather than hidden: with no registry read API\n * configured, `tab_discover` and `tab_status` fail with `UPSTREAM` and say so.\n * `tab_call` does not go through here at all -- it reads its figures off the\n * Service's own charge headers -- so a model can still call and be metered while\n * the index is down.\n *\n * ## Structural web types\n *\n * This package compiles against ES2023 with no DOM types, so `fetch`, its\n * response, and the abort signal are described by the shapes this module\n * touches, exactly as `client-402.ts` does. A host `fetch`, `undici`, and a test\n * closure all satisfy {@link RegistryFetch} without any of them being named.\n *\n * Requirements: 24.1, 24.4, 25.1, 25.3\n */\n\nimport type { Result, TabError } from \"../_shared/index.js\";\nimport { ok } from \"../_shared/index.js\";\n\nimport { tabError, upstreamError, validationError } from \"../errors.js\";\nimport { defaultLogger, type Logger } from \"../logger.js\";\nimport { asString, isRecord } from \"./json.js\";\n\n/** The two fields of a response this client reads. */\nexport interface RegistryResponse {\n readonly status: number;\n json(): Promise<unknown>;\n}\n\n/** The `fetch` this client calls. A host `fetch` satisfies it as it stands. */\nexport type RegistryFetch = (url: string, init: Readonly<Record<string, unknown>>) => Promise<RegistryResponse>;\n\nexport interface RegistryReadClientOptions {\n /** Absolute base URL of the read API, with or without a trailing slash. */\n readonly baseUrl: string;\n readonly fetchImpl?: RegistryFetch;\n /** How long one read may take. Defaults to 10 seconds. */\n readonly timeoutMs?: number;\n readonly logger?: Logger;\n}\n\n/** Every read `tab_discover` and `tab_status` make, and nothing else. */\nexport interface RegistryReadClient {\n /** The base URL reads go to, normalised, so an error message can name it. */\n readonly baseUrl: string;\n /** `GET /healthz`, which is what `doctor` asks before it trusts anything else. */\n health(): Promise<Result<unknown>>;\n /** A page of registered Services, hydrated with prices, collections and Bond. */\n services(limit: number): Promise<Result<unknown>>;\n /** One Service, or `NOT_FOUND` when nothing ever registered under the id. */\n service(serviceId: string): Promise<Result<unknown>>;\n /** One Agent's credit picture per Asset. An address with no history is a 200 with empty arrays. */\n agent(address: string): Promise<Result<unknown>>;\n /** Settlements, newest first, filtered by Agent and optionally by Asset. */\n settlements(query: SettlementQuery): Promise<Result<unknown>>;\n /** One Settlement with the transaction that paid it, named by its `settlementId`. */\n settlement(settlementId: string): Promise<Result<unknown>>;\n}\n\nexport interface SettlementQuery {\n readonly agent: string;\n /** The token address alone, which is how the index keys an Asset. */\n readonly asset?: string;\n readonly limit: number;\n}\n\n/** Drops a trailing slash so path joining never produces a double one. */\nexport const normaliseBaseUrl = (baseUrl: string): string => baseUrl.replace(/\\/+$/, \"\");\n\n/**\n * An abort signal that fires after `ms`, when the host has one.\n *\n * Described structurally rather than typed as `AbortSignal`, for the same reason\n * `fetch` is: no DOM types here. A host without `AbortSignal.timeout` gets no\n * signal and the read runs to whatever timeout its transport imposes, which is\n * worse than a bounded read but better than a failure to construct one.\n */\nconst timeoutSignal = (ms: number): unknown => {\n const ctor = (globalThis as { AbortSignal?: { timeout?: (ms: number) => unknown } }).AbortSignal;\n return typeof ctor?.timeout === \"function\" ? ctor.timeout(ms) : undefined;\n};\n\nconst hostFetch = (): RegistryFetch | undefined => {\n const candidate = (globalThis as { fetch?: unknown }).fetch;\n return typeof candidate === \"function\" ? (candidate as RegistryFetch) : undefined;\n};\n\nconst CATEGORIES = [\"VALIDATION\", \"NOT_FOUND\", \"CONFLICT\", \"UPSTREAM\", \"CHAIN\", \"TIMEOUT\", \"INTERNAL\"] as const;\n\n/**\n * Reads the read API's own error body back into a `TabError`.\n *\n * Every route there fails with `{ error: { category, code, message } }` in this\n * package's own vocabulary, so a 404 for an unregistered serviceId arrives here\n * as `NOT_FOUND` / `SERVICE_NOT_REGISTERED` and reaches the model unchanged.\n * Rewriting it as a generic upstream failure would throw away the one part a\n * caller can act on.\n */\nfunction upstreamFailure(url: string, status: number, body: unknown): Result<never> {\n const error = isRecord(body) ? body[\"error\"] : undefined;\n if (isRecord(error)) {\n const category = asString(error[\"category\"], \"\");\n if ((CATEGORIES as readonly string[]).includes(category)) {\n return {\n ok: false,\n error: tabError(\n category as TabError[\"category\"],\n asString(error[\"code\"], \"REGISTRY_READ_FAILED\"),\n asString(error[\"message\"], `the registry read API answered ${status} for ${url}`),\n { details: { url, status } },\n ),\n };\n }\n }\n return upstreamError(\n \"REGISTRY_READ_FAILED\",\n `the registry read API answered ${status} for ${url}`,\n { retryable: status >= 500, details: { url, status } },\n );\n}\n\n/**\n * Builds the client.\n *\n * Construction is total, like every other factory in this package: an unusable\n * base URL is reported by the first read, which is the only place a caller can\n * act on it.\n */\nexport function createRegistryReadClient(options: RegistryReadClientOptions): RegistryReadClient {\n const baseUrl = normaliseBaseUrl(options.baseUrl);\n const timeoutMs = options.timeoutMs ?? 10_000;\n const logger = options.logger ?? defaultLogger;\n\n const get = async (pathname: string, query: Readonly<Record<string, string | undefined>> = {}): Promise<Result<unknown>> => {\n if (!/^https?:\\/\\/[^\\s]+$/.test(baseUrl)) {\n return validationError(\n \"REGISTRY_URL_INVALID\",\n `the registry read API base URL must be an absolute http or https URL, received \\`${options.baseUrl}\\``,\n { details: { baseUrl: options.baseUrl } },\n );\n }\n const send = options.fetchImpl ?? hostFetch();\n if (send === undefined) {\n return upstreamError(\n \"FETCH_UNAVAILABLE\",\n \"this host has no global fetch; supply fetchImpl, or run on Node 20.10 or later\",\n );\n }\n\n const search = Object.entries(query)\n .filter((entry): entry is [string, string] => entry[1] !== undefined)\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)\n .join(\"&\");\n const url = `${baseUrl}${pathname}${search === \"\" ? \"\" : `?${search}`}`;\n\n const signal = timeoutSignal(timeoutMs);\n let response: RegistryResponse;\n try {\n response = await send(url, {\n method: \"GET\",\n headers: { accept: \"application/json\" },\n ...(signal === undefined ? {} : { signal }),\n });\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n // A transport failure and a timeout are the same shape here and different\n // things to a caller, so the category splits on the abort name.\n const timedOut = error instanceof Error && (error.name === \"TimeoutError\" || error.name === \"AbortError\");\n logger.warn(\"registry read failed\", { url, reason });\n // Both are UPSTREAM: the category vocabulary has no timeout of its own, and a\n // read API that answered nothing in time is an upstream failure whichever way\n // it failed. The code is what tells the two apart, and both are retryable.\n return timedOut\n ? upstreamError(\n \"REGISTRY_READ_TIMEOUT\",\n `the registry read API at ${url} did not answer within ${timeoutMs}ms`,\n { retryable: true, details: { url, timeoutMs } },\n )\n : upstreamError(\"REGISTRY_UNREACHABLE\", `the registry read API at ${url} could not be reached: ${reason}`, {\n retryable: true,\n details: { url },\n });\n }\n\n let body: unknown;\n try {\n body = await response.json();\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n return upstreamError(\n \"REGISTRY_BODY_UNREADABLE\",\n `the registry read API at ${url} answered ${response.status} with a body that is not JSON: ${reason}`,\n { details: { url, status: response.status } },\n );\n }\n\n if (response.status < 200 || response.status >= 300) return upstreamFailure(url, response.status, body);\n return ok(body);\n };\n\n return {\n baseUrl,\n health: () => get(\"/healthz\"),\n services: (limit) => get(\"/services\", { limit: String(limit) }),\n service: (serviceId) => get(`/services/${encodeURIComponent(serviceId.toLowerCase())}`),\n agent: (address) => get(`/agents/${encodeURIComponent(address.toLowerCase())}`),\n settlements: (query) =>\n get(\"/settlements\", {\n agent: query.agent.toLowerCase(),\n asset: query.asset?.toLowerCase(),\n limit: String(query.limit),\n }),\n settlement: (settlementId) => get(`/settlements/${encodeURIComponent(settlementId.toLowerCase())}`),\n };\n}\n"]}
1
+ {"version":3,"file":"registry-client.js","sourceRoot":"","sources":["../../src/mcp/registry-client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The keyless read client for the Tab registry read API.\n *\n * `tab_discover` and `tab_status` answer questions about chain state -- which\n * Services are registered, what each tool costs, what an Agent owes -- and every\n * one of those answers is a public fact. Nothing here signs, nothing here\n * authenticates, and nothing here writes. That is why `tab connect` can wire a\n * client up without ever touching a key, and why `tab doctor` can check a\n * deployment end to end from a laptop that holds none.\n *\n * ## Why the read API rather than the chain\n *\n * A Credit Limit is not a storage slot. It is `LimitLib` recomputed over an\n * Agent's committed Settlement history and the Bond ledger, cross-checked\n * against `TabBook.creditLimit` at the same block before it is served. A Bond\n * figure is a sum of `Bond`'s own events, checked against `Bond.ledgerOf`. An\n * MCP server that read the chain directly would have to restate both, and a\n * second restatement that drifts from the first is worse than one source of\n * truth: it would put two different Credit Limits in front of the same model.\n * So the figures come from the one process that already computes and checks\n * them, and this client's whole job is to fetch and not to interpret.\n *\n * The consequence is stated rather than hidden: with no registry read API\n * configured, `tab_discover` and `tab_status` fail with `UPSTREAM` and say so.\n * `tab_call` does not go through here at all -- it reads its figures off the\n * Service's own charge headers -- so a model can still call and be metered while\n * the index is down.\n *\n * ## Structural web types\n *\n * This package compiles against ES2023 with no DOM types, so `fetch`, its\n * response, and the abort signal are described by the shapes this module\n * touches, exactly as `client-402.ts` does. A host `fetch`, `undici`, and a test\n * closure all satisfy {@link RegistryFetch} without any of them being named.\n */\n\nimport type { Result, TabError } from \"../_shared/index.js\";\nimport { HTTP_STATUS_BY_CATEGORY, ok } from \"../_shared/index.js\";\n\nimport { tabError, upstreamError, validationError } from \"../errors.js\";\nimport { defaultLogger, type Logger } from \"../logger.js\";\nimport { asString, isRecord } from \"./json.js\";\n\n/** The two fields of a response this client reads. */\nexport interface RegistryResponse {\n readonly status: number;\n json(): Promise<unknown>;\n}\n\n/** The `fetch` this client calls. A host `fetch` satisfies it as it stands. */\nexport type RegistryFetch = (url: string, init: Readonly<Record<string, unknown>>) => Promise<RegistryResponse>;\n\nexport interface RegistryReadClientOptions {\n /** Absolute base URL of the read API, with or without a trailing slash. */\n readonly baseUrl: string;\n readonly fetchImpl?: RegistryFetch;\n /** How long one read may take. Defaults to 10 seconds. */\n readonly timeoutMs?: number;\n readonly logger?: Logger;\n}\n\n/** Every read `tab_discover` and `tab_status` make, and nothing else. */\nexport interface RegistryReadClient {\n /** The base URL reads go to, normalised, so an error message can name it. */\n readonly baseUrl: string;\n /** `GET /healthz`, which is what `doctor` asks before it trusts anything else. */\n health(): Promise<Result<unknown>>;\n /** A page of registered Services, hydrated with prices, collections and Bond. */\n services(limit: number): Promise<Result<unknown>>;\n /** One Service, or `NOT_FOUND` when nothing ever registered under the id. */\n service(serviceId: string): Promise<Result<unknown>>;\n /** One Agent's credit picture per Asset. An address with no history is a 200 with empty arrays. */\n agent(address: string): Promise<Result<unknown>>;\n /** Settlements, newest first, filtered by Agent and optionally by Asset. */\n settlements(query: SettlementQuery): Promise<Result<unknown>>;\n /** One Settlement with the transaction that paid it, named by its `settlementId`. */\n settlement(settlementId: string): Promise<Result<unknown>>;\n}\n\nexport interface SettlementQuery {\n readonly agent: string;\n /** The token address alone, which is how the index keys an Asset. */\n readonly asset?: string;\n readonly limit: number;\n}\n\n/** Drops a trailing slash so path joining never produces a double one. */\nexport const normaliseBaseUrl = (baseUrl: string): string => baseUrl.replace(/\\/+$/, \"\");\n\n/**\n * An abort signal that fires after `ms`, when the host has one.\n *\n * Described structurally rather than typed as `AbortSignal`, for the same reason\n * `fetch` is: no DOM types here. A host without `AbortSignal.timeout` gets no\n * signal and the read runs to whatever timeout its transport imposes, which is\n * worse than a bounded read but better than a failure to construct one.\n */\nconst timeoutSignal = (ms: number): unknown => {\n const ctor = (globalThis as { AbortSignal?: { timeout?: (ms: number) => unknown } }).AbortSignal;\n return typeof ctor?.timeout === \"function\" ? ctor.timeout(ms) : undefined;\n};\n\nconst hostFetch = (): RegistryFetch | undefined => {\n const candidate = (globalThis as { fetch?: unknown }).fetch;\n return typeof candidate === \"function\" ? (candidate as RegistryFetch) : undefined;\n};\n\n/** Every `ErrorCategory`, read off the shared status table so the two cannot disagree. */\nconst CATEGORIES: readonly string[] = Object.keys(HTTP_STATUS_BY_CATEGORY);\n\n/**\n * Reads the read API's own error body back into a `TabError`.\n *\n * Every route there fails with `{ error: { category, code, message } }` in this\n * package's own vocabulary, so a 404 for an unregistered serviceId arrives here\n * as `NOT_FOUND` / `SERVICE_NOT_REGISTERED` and reaches the model unchanged.\n * Rewriting it as a generic upstream failure would throw away the one part a\n * caller can act on.\n */\nfunction upstreamFailure(url: string, status: number, body: unknown): Result<never> {\n const error = isRecord(body) ? body[\"error\"] : undefined;\n if (isRecord(error)) {\n const category = asString(error[\"category\"], \"\");\n if (CATEGORIES.includes(category)) {\n return {\n ok: false,\n error: tabError(\n category as TabError[\"category\"],\n asString(error[\"code\"], \"REGISTRY_READ_FAILED\"),\n asString(error[\"message\"], `the registry read API answered ${status} for ${url}`),\n { details: { url, status } },\n ),\n };\n }\n }\n return upstreamError(\n \"REGISTRY_READ_FAILED\",\n `the registry read API answered ${status} for ${url}`,\n { retryable: status >= 500, details: { url, status } },\n );\n}\n\n/**\n * Builds the client.\n *\n * Construction is total, like every other factory in this package: an unusable\n * base URL is reported by the first read, which is the only place a caller can\n * act on it.\n */\nexport function createRegistryReadClient(options: RegistryReadClientOptions): RegistryReadClient {\n const baseUrl = normaliseBaseUrl(options.baseUrl);\n const timeoutMs = options.timeoutMs ?? 10_000;\n const logger = options.logger ?? defaultLogger;\n\n const get = async (pathname: string, query: Readonly<Record<string, string | undefined>> = {}): Promise<Result<unknown>> => {\n if (!/^https?:\\/\\/[^\\s]+$/.test(baseUrl)) {\n return validationError(\n \"REGISTRY_URL_INVALID\",\n `the registry read API base URL must be an absolute http or https URL, received \\`${options.baseUrl}\\``,\n { details: { baseUrl: options.baseUrl } },\n );\n }\n const send = options.fetchImpl ?? hostFetch();\n if (send === undefined) {\n return upstreamError(\n \"FETCH_UNAVAILABLE\",\n \"this host has no global fetch; supply fetchImpl, or run on Node 20.10 or later\",\n );\n }\n\n const search = Object.entries(query)\n .filter((entry): entry is [string, string] => entry[1] !== undefined)\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)\n .join(\"&\");\n const url = `${baseUrl}${pathname}${search === \"\" ? \"\" : `?${search}`}`;\n\n const signal = timeoutSignal(timeoutMs);\n let response: RegistryResponse;\n try {\n response = await send(url, {\n method: \"GET\",\n headers: { accept: \"application/json\" },\n ...(signal === undefined ? {} : { signal }),\n });\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n // A transport failure and a timeout are the same shape here and different\n // things to a caller, so the category splits on the abort name.\n const timedOut = error instanceof Error && (error.name === \"TimeoutError\" || error.name === \"AbortError\");\n logger.warn(\"registry read failed\", { url, reason });\n // Both are UPSTREAM: the category vocabulary has no timeout of its own, and a\n // read API that answered nothing in time is an upstream failure whichever way\n // it failed. The code is what tells the two apart, and both are retryable.\n return timedOut\n ? upstreamError(\n \"REGISTRY_READ_TIMEOUT\",\n `the registry read API at ${url} did not answer within ${timeoutMs}ms`,\n { retryable: true, details: { url, timeoutMs } },\n )\n : upstreamError(\"REGISTRY_UNREACHABLE\", `the registry read API at ${url} could not be reached: ${reason}`, {\n retryable: true,\n details: { url },\n });\n }\n\n let body: unknown;\n try {\n body = await response.json();\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n return upstreamError(\n \"REGISTRY_BODY_UNREADABLE\",\n `the registry read API at ${url} answered ${response.status} with a body that is not JSON: ${reason}`,\n { details: { url, status: response.status } },\n );\n }\n\n if (response.status < 200 || response.status >= 300) return upstreamFailure(url, response.status, body);\n return ok(body);\n };\n\n return {\n baseUrl,\n health: () => get(\"/healthz\"),\n services: (limit) => get(\"/services\", { limit: String(limit) }),\n service: (serviceId) => get(`/services/${encodeURIComponent(serviceId.toLowerCase())}`),\n agent: (address) => get(`/agents/${encodeURIComponent(address.toLowerCase())}`),\n settlements: (query) =>\n get(\"/settlements\", {\n agent: query.agent.toLowerCase(),\n asset: query.asset?.toLowerCase(),\n limit: String(query.limit),\n }),\n settlement: (settlementId) => get(`/settlements/${encodeURIComponent(settlementId.toLowerCase())}`),\n };\n}\n"]}
@@ -1,10 +1,10 @@
1
1
  /**
2
- * The four MCP tools, declared. (R25.1, R25.2)
2
+ * The four MCP tools, declared.
3
3
  *
4
4
  * This file is the contract. A model reads these schemas to decide what to send
5
5
  * and what it will get back, `tools/list` serves them verbatim, every tool
6
6
  * validates its input against the schema here before doing any work, and the
7
- * task 16.3 tests validate every output against it. There is one declaration of
7
+ * test suite validates every output against it. There is one declaration of
8
8
  * each shape and it lives here, so the published contract and the enforced
9
9
  * contract cannot drift apart.
10
10
  *
@@ -27,12 +27,10 @@
27
27
  * `tab_settle` additionally carry a required `ok` boolean, because those two
28
28
  * change state and "did it happen" is the first thing a caller must read.
29
29
  *
30
- * `LIMIT_EXCEEDED` is the case the design singles out: the Agent has no headroom
30
+ * `LIMIT_EXCEEDED` is the case that matters most: the Agent has no headroom
31
31
  * for the Asset, so the tool answers `ok: false` with both `requiredBaseUnits`
32
32
  * and `headroomBaseUnits` populated. That is the difference between a model that
33
- * decides to settle and a model that guesses. (R21.5)
34
- *
35
- * Requirements: 21.5, 25.1, 25.2, 25.3, 25.4
33
+ * decides to settle and a model that guesses.
36
34
  */
37
35
  import type { JsonObjectSchema, JsonSchema } from "./json-schema.js";
38
36
  /** A `uint256` in decimal, as a string. 78 digits is the ceiling; 39 covers every real amount. */
@@ -46,7 +44,7 @@ export declare const ASSET_PATTERN = "^[0-9]+:0x[a-fA-F0-9]{40}$";
46
44
  /**
47
45
  * The failure block every tool can return.
48
46
  *
49
- * `category` is the same seven-value vocabulary `../_shared/index.js` uses everywhere
47
+ * `category` is the same nine-value vocabulary `../_shared/index.js` uses everywhere
50
48
  * else, so a code path that maps a category to an HTTP status on one surface
51
49
  * maps it the same way here. `requiredBaseUnits` and `headroomBaseUnits` are
52
50
  * present only on `LIMIT_EXCEEDED`.
@@ -67,7 +65,11 @@ export interface TabToolDeclaration {
67
65
  readonly description: string;
68
66
  readonly inputSchema: JsonObjectSchema;
69
67
  readonly outputSchema: JsonObjectSchema;
70
- /** MCP behaviour hints. `tab_settle` is the only tool that is not read-only. */
68
+ /**
69
+ * MCP behaviour hints. `tab_discover` and `tab_status` are read-only;
70
+ * `tab_call` meters onto an Open Tab, and `tab_settle`, which spends funds,
71
+ * is the one destructive tool.
72
+ */
71
73
  readonly annotations: {
72
74
  readonly readOnlyHint: boolean;
73
75
  readonly destructiveHint: boolean;