@tabai/sdk 0.2.0

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 (204) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +401 -0
  3. package/bin/tab.mjs +23 -0
  4. package/dist/_shared/abi.d.ts +150 -0
  5. package/dist/_shared/abi.d.ts.map +1 -0
  6. package/dist/_shared/abi.js +197 -0
  7. package/dist/_shared/abi.js.map +1 -0
  8. package/dist/_shared/chains.d.ts +118 -0
  9. package/dist/_shared/chains.d.ts.map +1 -0
  10. package/dist/_shared/chains.js +89 -0
  11. package/dist/_shared/chains.js.map +1 -0
  12. package/dist/_shared/hex.d.ts +35 -0
  13. package/dist/_shared/hex.d.ts.map +1 -0
  14. package/dist/_shared/hex.js +40 -0
  15. package/dist/_shared/hex.js.map +1 -0
  16. package/dist/_shared/index.d.ts +14 -0
  17. package/dist/_shared/index.d.ts.map +1 -0
  18. package/dist/_shared/index.js +14 -0
  19. package/dist/_shared/index.js.map +1 -0
  20. package/dist/_shared/keccak256.d.ts +29 -0
  21. package/dist/_shared/keccak256.d.ts.map +1 -0
  22. package/dist/_shared/keccak256.js +145 -0
  23. package/dist/_shared/keccak256.js.map +1 -0
  24. package/dist/_shared/result.d.ts +78 -0
  25. package/dist/_shared/result.d.ts.map +1 -0
  26. package/dist/_shared/result.js +61 -0
  27. package/dist/_shared/result.js.map +1 -0
  28. package/dist/cli/client-config.d.ts +155 -0
  29. package/dist/cli/client-config.d.ts.map +1 -0
  30. package/dist/cli/client-config.js +382 -0
  31. package/dist/cli/client-config.js.map +1 -0
  32. package/dist/cli/connect.d.ts +76 -0
  33. package/dist/cli/connect.d.ts.map +1 -0
  34. package/dist/cli/connect.js +158 -0
  35. package/dist/cli/connect.js.map +1 -0
  36. package/dist/cli/doctor.d.ts +57 -0
  37. package/dist/cli/doctor.d.ts.map +1 -0
  38. package/dist/cli/doctor.js +253 -0
  39. package/dist/cli/doctor.js.map +1 -0
  40. package/dist/cli/index.d.ts +13 -0
  41. package/dist/cli/index.d.ts.map +1 -0
  42. package/dist/cli/index.js +13 -0
  43. package/dist/cli/index.js.map +1 -0
  44. package/dist/cli/main.d.ts +45 -0
  45. package/dist/cli/main.d.ts.map +1 -0
  46. package/dist/cli/main.js +371 -0
  47. package/dist/cli/main.js.map +1 -0
  48. package/dist/errors.d.ts +29 -0
  49. package/dist/errors.d.ts.map +1 -0
  50. package/dist/errors.js +37 -0
  51. package/dist/errors.js.map +1 -0
  52. package/dist/http/client-402.d.ts +243 -0
  53. package/dist/http/client-402.d.ts.map +1 -0
  54. package/dist/http/client-402.js +515 -0
  55. package/dist/http/client-402.js.map +1 -0
  56. package/dist/http/headers.d.ts +173 -0
  57. package/dist/http/headers.d.ts.map +1 -0
  58. package/dist/http/headers.js +284 -0
  59. package/dist/http/headers.js.map +1 -0
  60. package/dist/http/index.d.ts +15 -0
  61. package/dist/http/index.d.ts.map +1 -0
  62. package/dist/http/index.js +15 -0
  63. package/dist/http/index.js.map +1 -0
  64. package/dist/http/metering-claim.d.ts +82 -0
  65. package/dist/http/metering-claim.d.ts.map +1 -0
  66. package/dist/http/metering-claim.js +99 -0
  67. package/dist/http/metering-claim.js.map +1 -0
  68. package/dist/index.d.ts +48 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +51 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/logger.d.ts +40 -0
  73. package/dist/logger.d.ts.map +1 -0
  74. package/dist/logger.js +50 -0
  75. package/dist/logger.js.map +1 -0
  76. package/dist/mcp/assets.d.ts +31 -0
  77. package/dist/mcp/assets.d.ts.map +1 -0
  78. package/dist/mcp/assets.js +78 -0
  79. package/dist/mcp/assets.js.map +1 -0
  80. package/dist/mcp/index.d.ts +19 -0
  81. package/dist/mcp/index.d.ts.map +1 -0
  82. package/dist/mcp/index.js +19 -0
  83. package/dist/mcp/index.js.map +1 -0
  84. package/dist/mcp/json-schema.d.ts +86 -0
  85. package/dist/mcp/json-schema.d.ts.map +1 -0
  86. package/dist/mcp/json-schema.js +215 -0
  87. package/dist/mcp/json-schema.js.map +1 -0
  88. package/dist/mcp/json.d.ts +43 -0
  89. package/dist/mcp/json.d.ts.map +1 -0
  90. package/dist/mcp/json.js +69 -0
  91. package/dist/mcp/json.js.map +1 -0
  92. package/dist/mcp/registry-client.d.ts +88 -0
  93. package/dist/mcp/registry-client.d.ts.map +1 -0
  94. package/dist/mcp/registry-client.js +158 -0
  95. package/dist/mcp/registry-client.js.map +1 -0
  96. package/dist/mcp/schemas.d.ts +82 -0
  97. package/dist/mcp/schemas.d.ts.map +1 -0
  98. package/dist/mcp/schemas.js +493 -0
  99. package/dist/mcp/schemas.js.map +1 -0
  100. package/dist/mcp/server.d.ts +97 -0
  101. package/dist/mcp/server.d.ts.map +1 -0
  102. package/dist/mcp/server.js +285 -0
  103. package/dist/mcp/server.js.map +1 -0
  104. package/dist/mcp/settings.d.ts +90 -0
  105. package/dist/mcp/settings.d.ts.map +1 -0
  106. package/dist/mcp/settings.js +160 -0
  107. package/dist/mcp/settings.js.map +1 -0
  108. package/dist/mcp/toolset.d.ts +231 -0
  109. package/dist/mcp/toolset.d.ts.map +1 -0
  110. package/dist/mcp/toolset.js +760 -0
  111. package/dist/mcp/toolset.js.map +1 -0
  112. package/dist/payments/abi.d.ts +9 -0
  113. package/dist/payments/abi.d.ts.map +1 -0
  114. package/dist/payments/abi.js +17 -0
  115. package/dist/payments/abi.js.map +1 -0
  116. package/dist/payments/config.d.ts +199 -0
  117. package/dist/payments/config.d.ts.map +1 -0
  118. package/dist/payments/config.js +259 -0
  119. package/dist/payments/config.js.map +1 -0
  120. package/dist/payments/index.d.ts +13 -0
  121. package/dist/payments/index.d.ts.map +1 -0
  122. package/dist/payments/index.js +13 -0
  123. package/dist/payments/index.js.map +1 -0
  124. package/dist/payments/kuru.d.ts +191 -0
  125. package/dist/payments/kuru.d.ts.map +1 -0
  126. package/dist/payments/kuru.js +377 -0
  127. package/dist/payments/kuru.js.map +1 -0
  128. package/dist/payments/monad.d.ts +69 -0
  129. package/dist/payments/monad.d.ts.map +1 -0
  130. package/dist/payments/monad.js +306 -0
  131. package/dist/payments/monad.js.map +1 -0
  132. package/dist/payments/permit2.d.ts +118 -0
  133. package/dist/payments/permit2.d.ts.map +1 -0
  134. package/dist/payments/permit2.js +366 -0
  135. package/dist/payments/permit2.js.map +1 -0
  136. package/dist/payments/registry.d.ts +119 -0
  137. package/dist/payments/registry.d.ts.map +1 -0
  138. package/dist/payments/registry.js +199 -0
  139. package/dist/payments/registry.js.map +1 -0
  140. package/dist/payments/strategy.d.ts +80 -0
  141. package/dist/payments/strategy.d.ts.map +1 -0
  142. package/dist/payments/strategy.js +103 -0
  143. package/dist/payments/strategy.js.map +1 -0
  144. package/dist/proxy/hooks.d.ts +90 -0
  145. package/dist/proxy/hooks.d.ts.map +1 -0
  146. package/dist/proxy/hooks.js +35 -0
  147. package/dist/proxy/hooks.js.map +1 -0
  148. package/dist/proxy/index.d.ts +9 -0
  149. package/dist/proxy/index.d.ts.map +1 -0
  150. package/dist/proxy/index.js +9 -0
  151. package/dist/proxy/index.js.map +1 -0
  152. package/dist/proxy/proxy.d.ts +156 -0
  153. package/dist/proxy/proxy.d.ts.map +1 -0
  154. package/dist/proxy/proxy.js +366 -0
  155. package/dist/proxy/proxy.js.map +1 -0
  156. package/dist/server/adapters/express.d.ts +89 -0
  157. package/dist/server/adapters/express.d.ts.map +1 -0
  158. package/dist/server/adapters/express.js +215 -0
  159. package/dist/server/adapters/express.js.map +1 -0
  160. package/dist/server/adapters/hono.d.ts +52 -0
  161. package/dist/server/adapters/hono.d.ts.map +1 -0
  162. package/dist/server/adapters/hono.js +61 -0
  163. package/dist/server/adapters/hono.js.map +1 -0
  164. package/dist/server/adapters/next.d.ts +52 -0
  165. package/dist/server/adapters/next.d.ts.map +1 -0
  166. package/dist/server/adapters/next.js +56 -0
  167. package/dist/server/adapters/next.js.map +1 -0
  168. package/dist/server/index.d.ts +30 -0
  169. package/dist/server/index.d.ts.map +1 -0
  170. package/dist/server/index.js +30 -0
  171. package/dist/server/index.js.map +1 -0
  172. package/dist/server/metering.d.ts +209 -0
  173. package/dist/server/metering.d.ts.map +1 -0
  174. package/dist/server/metering.js +365 -0
  175. package/dist/server/metering.js.map +1 -0
  176. package/dist/server/post-paid.d.ts +355 -0
  177. package/dist/server/post-paid.d.ts.map +1 -0
  178. package/dist/server/post-paid.js +512 -0
  179. package/dist/server/post-paid.js.map +1 -0
  180. package/dist/x402/client.d.ts +203 -0
  181. package/dist/x402/client.d.ts.map +1 -0
  182. package/dist/x402/client.js +337 -0
  183. package/dist/x402/client.js.map +1 -0
  184. package/dist/x402/hub.d.ts +79 -0
  185. package/dist/x402/hub.d.ts.map +1 -0
  186. package/dist/x402/hub.js +164 -0
  187. package/dist/x402/hub.js.map +1 -0
  188. package/dist/x402/index.d.ts +27 -0
  189. package/dist/x402/index.d.ts.map +1 -0
  190. package/dist/x402/index.js +27 -0
  191. package/dist/x402/index.js.map +1 -0
  192. package/dist/x402/proxy.d.ts +162 -0
  193. package/dist/x402/proxy.d.ts.map +1 -0
  194. package/dist/x402/proxy.js +198 -0
  195. package/dist/x402/proxy.js.map +1 -0
  196. package/dist/x402/server.d.ts +162 -0
  197. package/dist/x402/server.d.ts.map +1 -0
  198. package/dist/x402/server.js +306 -0
  199. package/dist/x402/server.js.map +1 -0
  200. package/dist/x402/wire.d.ts +104 -0
  201. package/dist/x402/wire.d.ts.map +1 -0
  202. package/dist/x402/wire.js +265 -0
  203. package/dist/x402/wire.js.map +1 -0
  204. package/package.json +61 -0
@@ -0,0 +1,99 @@
1
+ /**
2
+ * The metering claim: what a signed metered request binds, and who may sign it.
3
+ *
4
+ * ## Two signers, one digest
5
+ *
6
+ * A metered call records a delivery on chain and spends the Service operator's
7
+ * gas, so a metering endpoint anyone can reach must know the call was meant.
8
+ * Two parties can say so, and the digest they sign is the same string:
9
+ *
10
+ * - **The operator**, from the Service's own front, which has authenticated
11
+ * its caller however it likes. `Tab-Operator-Signature` carries it.
12
+ * - **The Agent**, whose Open Tab the charge lands on. `Tab-Agent-Signature`
13
+ * carries it, recovered against the address in `Tab-Agent`, so the one
14
+ * party that pays for the call is the one that asked for it, and a
15
+ * stranger who knows an Agent's address can put nothing on its tab.
16
+ *
17
+ * The digest binds the method, the path, the Agent, the tool, the unit count
18
+ * and a timestamp. Binding the Agent and the units is the point: a signature
19
+ * over the path alone would be a bearer token that replays against a
20
+ * different Agent for a different amount. The timestamp bounds replay to a
21
+ * window the gateway checks.
22
+ *
23
+ * ## Why it is written out and not JSON
24
+ *
25
+ * Newline-separated and fully ordered, so two different claims can never
26
+ * produce one digest. JSON key order is not guaranteed across
27
+ * implementations, and a signature over a reordered object would fail for a
28
+ * caller who did nothing wrong.
29
+ *
30
+ * Requirements: 12.1, 12.2, 21.5
31
+ */
32
+ import { encodeBytes32String } from "ethers";
33
+ /** The headers a signed metering request carries, by who signed it. */
34
+ export const METERING_HEADER = {
35
+ operatorSignature: "Tab-Operator-Signature",
36
+ operatorIssuedAt: "Tab-Operator-Issued-At",
37
+ agentSignature: "Tab-Agent-Signature",
38
+ agentIssuedAt: "Tab-Agent-Issued-At",
39
+ };
40
+ /** The exact string a signer signs, with EIP-191 `personal_sign`. */
41
+ export function meteringDigest(claim) {
42
+ return [
43
+ "tab-metering-request",
44
+ claim.method.toUpperCase(),
45
+ claim.path,
46
+ claim.agent.toLowerCase(),
47
+ claim.tool.toLowerCase(),
48
+ String(claim.units),
49
+ String(claim.issuedAt),
50
+ ].join("\n");
51
+ }
52
+ /** A tool name as the caller gave it, packed to the key the price list holds, unless it already is one. */
53
+ export function toolKeyOf(tool) {
54
+ return (/^0x[0-9a-fA-F]{64}$/.test(tool) ? tool : encodeBytes32String(tool)).toLowerCase();
55
+ }
56
+ /**
57
+ * A header provider that signs every metered call as the Agent.
58
+ *
59
+ * Put it on a Service entry in `tab.config` and `tab_call` sends
60
+ * `Tab-Agent-Signature` and `Tab-Agent-Issued-At` with each call, signed by
61
+ * the key the factory returns. A factory, like the strategies, so the key is
62
+ * built only when a call is made and every read stays keyless; one that
63
+ * returns nothing sends the call unsigned, and a gateway that requires a
64
+ * signature says so in its refusal.
65
+ *
66
+ * It signs only when the key it is given is the Agent the call is metered
67
+ * against, because that is what the gateway recovers the signature against.
68
+ * A key for anyone else adds nothing and the Service decides: one that
69
+ * requires a signature refuses by name, and one that does not is unaffected.
70
+ * That is the case where the Agent comes from somewhere other than this
71
+ * config, such as a wallet the MetaMask Agent Wallet plugin reads, and it is
72
+ * an honest "this key is not that Agent" rather than a signature that could
73
+ * only be rejected.
74
+ */
75
+ export function agentSignedMetering(signer, options = {}) {
76
+ const now = options.now ?? (() => Date.now());
77
+ return async (request) => {
78
+ const wallet = signer();
79
+ if (wallet === undefined)
80
+ return {};
81
+ const address = (await wallet.getAddress()).toLowerCase();
82
+ if (address !== request.agent.toLowerCase())
83
+ return {};
84
+ const issuedAt = now();
85
+ const digest = meteringDigest({
86
+ method: request.method,
87
+ path: new URL(request.url).pathname,
88
+ agent: address,
89
+ tool: toolKeyOf(request.tool),
90
+ units: 1,
91
+ issuedAt,
92
+ });
93
+ return {
94
+ [METERING_HEADER.agentSignature]: await wallet.signMessage(digest),
95
+ [METERING_HEADER.agentIssuedAt]: String(issuedAt),
96
+ };
97
+ };
98
+ }
99
+ //# sourceMappingURL=metering-claim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metering-claim.js","sourceRoot":"","sources":["../../src/http/metering-claim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAI7C,uEAAuE;AACvE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,iBAAiB,EAAE,wBAAwB;IAC3C,gBAAgB,EAAE,wBAAwB;IAC1C,cAAc,EAAE,qBAAqB;IACrC,aAAa,EAAE,qBAAqB;CAC5B,CAAC;AAcX,qEAAqE;AACrE,MAAM,UAAU,cAAc,CAAC,KAA2B;IACxD,OAAO;QACL,sBAAsB;QACtB,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE;QAC1B,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE;QACzB,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE;QACxB,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QACnB,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;KACvB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAQD,2GAA2G;AAC3G,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AAC7F,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAwC,EACxC,UAA2C,EAAE;IAE7C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9C,OAAO,KAAK,EAAE,OAA6B,EAAE,EAAE;QAC7C,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;QACxB,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1D,IAAI,OAAO,KAAK,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,OAAO,EAAE,CAAC;QACvD,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,cAAc,CAAC;YAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,IAAI,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ;YACnC,KAAK,EAAE,OAAO;YACd,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;YAC7B,KAAK,EAAE,CAAC;YACR,QAAQ;SACT,CAAC,CAAC;QACH,OAAO;YACL,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC;YAClE,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;SAClD,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The metering claim: what a signed metered request binds, and who may sign it.\n *\n * ## Two signers, one digest\n *\n * A metered call records a delivery on chain and spends the Service operator's\n * gas, so a metering endpoint anyone can reach must know the call was meant.\n * Two parties can say so, and the digest they sign is the same string:\n *\n * - **The operator**, from the Service's own front, which has authenticated\n * its caller however it likes. `Tab-Operator-Signature` carries it.\n * - **The Agent**, whose Open Tab the charge lands on. `Tab-Agent-Signature`\n * carries it, recovered against the address in `Tab-Agent`, so the one\n * party that pays for the call is the one that asked for it, and a\n * stranger who knows an Agent's address can put nothing on its tab.\n *\n * The digest binds the method, the path, the Agent, the tool, the unit count\n * and a timestamp. Binding the Agent and the units is the point: a signature\n * over the path alone would be a bearer token that replays against a\n * different Agent for a different amount. The timestamp bounds replay to a\n * window the gateway checks.\n *\n * ## Why it is written out and not JSON\n *\n * Newline-separated and fully ordered, so two different claims can never\n * produce one digest. JSON key order is not guaranteed across\n * implementations, and a signature over a reordered object would fail for a\n * caller who did nothing wrong.\n *\n * Requirements: 12.1, 12.2, 21.5\n */\n\nimport { encodeBytes32String } from \"ethers\";\n\nimport type { ServiceHeaderProvider, ServiceHeaderRequest } from \"../payments/config.js\";\n\n/** The headers a signed metering request carries, by who signed it. */\nexport const METERING_HEADER = {\n operatorSignature: \"Tab-Operator-Signature\",\n operatorIssuedAt: \"Tab-Operator-Issued-At\",\n agentSignature: \"Tab-Agent-Signature\",\n agentIssuedAt: \"Tab-Agent-Issued-At\",\n} as const;\n\n/** The fields a metering request signature binds. */\nexport interface MeteringRequestClaim {\n readonly method: string;\n readonly path: string;\n readonly agent: string;\n /** The 32-byte tool key the price list is keyed by, never the label. */\n readonly tool: string;\n readonly units: number;\n /** Milliseconds since the epoch, as the caller stated it. */\n readonly issuedAt: number;\n}\n\n/** The exact string a signer signs, with EIP-191 `personal_sign`. */\nexport function meteringDigest(claim: MeteringRequestClaim): string {\n return [\n \"tab-metering-request\",\n claim.method.toUpperCase(),\n claim.path,\n claim.agent.toLowerCase(),\n claim.tool.toLowerCase(),\n String(claim.units),\n String(claim.issuedAt),\n ].join(\"\\n\");\n}\n\n/** A signer that can `personal_sign`. An ethers `Wallet` satisfies it. */\nexport interface MeteringSigner {\n getAddress(): Promise<string>;\n signMessage(message: string): Promise<string>;\n}\n\n/** A tool name as the caller gave it, packed to the key the price list holds, unless it already is one. */\nexport function toolKeyOf(tool: string): string {\n return (/^0x[0-9a-fA-F]{64}$/.test(tool) ? tool : encodeBytes32String(tool)).toLowerCase();\n}\n\n/**\n * A header provider that signs every metered call as the Agent.\n *\n * Put it on a Service entry in `tab.config` and `tab_call` sends\n * `Tab-Agent-Signature` and `Tab-Agent-Issued-At` with each call, signed by\n * the key the factory returns. A factory, like the strategies, so the key is\n * built only when a call is made and every read stays keyless; one that\n * returns nothing sends the call unsigned, and a gateway that requires a\n * signature says so in its refusal.\n *\n * It signs only when the key it is given is the Agent the call is metered\n * against, because that is what the gateway recovers the signature against.\n * A key for anyone else adds nothing and the Service decides: one that\n * requires a signature refuses by name, and one that does not is unaffected.\n * That is the case where the Agent comes from somewhere other than this\n * config, such as a wallet the MetaMask Agent Wallet plugin reads, and it is\n * an honest \"this key is not that Agent\" rather than a signature that could\n * only be rejected.\n */\nexport function agentSignedMetering(\n signer: () => MeteringSigner | undefined,\n options: { readonly now?: () => number } = {},\n): ServiceHeaderProvider {\n const now = options.now ?? (() => Date.now());\n return async (request: ServiceHeaderRequest) => {\n const wallet = signer();\n if (wallet === undefined) return {};\n const address = (await wallet.getAddress()).toLowerCase();\n if (address !== request.agent.toLowerCase()) return {};\n const issuedAt = now();\n const digest = meteringDigest({\n method: request.method,\n path: new URL(request.url).pathname,\n agent: address,\n tool: toolKeyOf(request.tool),\n units: 1,\n issuedAt,\n });\n return {\n [METERING_HEADER.agentSignature]: await wallet.signMessage(digest),\n [METERING_HEADER.agentIssuedAt]: String(issuedAt),\n };\n };\n}\n"]}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * `@tabai/sdk` is the published client surface. It may draw on `./_shared/index.js`
3
+ * and on nothing else inside the workspace.
4
+ *
5
+ * Three surfaces, and the shape of the product is visible in how they relate:
6
+ *
7
+ * - **`payments/`** - the strategy seam. The interface a Settlement goes through,
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
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).
16
+ * - **`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).
19
+ * - **`x402/`** - the prepaid protocol beside the credit one. A Tab `402` can
20
+ * offer an x402 payment for the one call it refused, an Agent with an x402
21
+ * signer can take it, and a Service can front an x402 upstream and meter the
22
+ * Agent for what it paid.
23
+ *
24
+ * **`http/headers.ts` is deliberately the only place the wire format exists.** The
25
+ * client parses with it and the server formats with it, so the two halves cannot
26
+ * drift apart without a test failing in one file, rather than mis-parsing silently
27
+ * between two.
28
+ *
29
+ * 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
32
+ * 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.
34
+ */
35
+ export declare const WORKSPACE_ID_SDK: "@tabai/sdk";
36
+ export declare const SHARED_WORKSPACE_ID: "./_shared/index.js";
37
+ export { ok, err, wrap, causeOf } from "./_shared/index.js";
38
+ export type { Result, TabError, ErrorCategory, Address, Bytes32, Hex } from "./_shared/index.js";
39
+ export * from "./logger.js";
40
+ export * from "./errors.js";
41
+ export * from "./payments/index.js";
42
+ export * from "./http/index.js";
43
+ export * from "./server/index.js";
44
+ export * from "./proxy/index.js";
45
+ export * from "./x402/index.js";
46
+ export * from "./mcp/index.js";
47
+ export * from "./cli/index.js";
48
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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;AAE5F,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 ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * `@tabai/sdk` is the published client surface. It may draw on `./_shared/index.js`
3
+ * and on nothing else inside the workspace.
4
+ *
5
+ * Three surfaces, and the shape of the product is visible in how they relate:
6
+ *
7
+ * - **`payments/`** - the strategy seam. The interface a Settlement goes through,
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
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).
16
+ * - **`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).
19
+ * - **`x402/`** - the prepaid protocol beside the credit one. A Tab `402` can
20
+ * offer an x402 payment for the one call it refused, an Agent with an x402
21
+ * signer can take it, and a Service can front an x402 upstream and meter the
22
+ * Agent for what it paid.
23
+ *
24
+ * **`http/headers.ts` is deliberately the only place the wire format exists.** The
25
+ * client parses with it and the server formats with it, so the two halves cannot
26
+ * drift apart without a test failing in one file, rather than mis-parsing silently
27
+ * between two.
28
+ *
29
+ * 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
32
+ * 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.
34
+ */
35
+ import { WORKSPACE_ID } from "./_shared/index.js";
36
+ export const WORKSPACE_ID_SDK = "@tabai/sdk";
37
+ export const SHARED_WORKSPACE_ID = WORKSPACE_ID;
38
+ // Every fallible call returns a `Result`, so a consumer needs its type and its
39
+ // constructors from the package it installed; `./_shared/index.js` is inlined at pack
40
+ // time and has no name on npm to import them from.
41
+ export { ok, err, wrap, causeOf } from "./_shared/index.js";
42
+ export * from "./logger.js";
43
+ export * from "./errors.js";
44
+ export * from "./payments/index.js";
45
+ export * from "./http/index.js";
46
+ export * from "./server/index.js";
47
+ export * from "./proxy/index.js";
48
+ export * from "./x402/index.js";
49
+ export * from "./mcp/index.js";
50
+ export * from "./cli/index.js";
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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,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\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"]}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The SDK logger.
3
+ *
4
+ * The seam needs somewhere to put a warning that is not an error: a duplicate
5
+ * strategy id replaces the earlier registration rather than throwing, and a
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.
9
+ *
10
+ * The interface is four methods and one optional field bag, so a consumer can
11
+ * hand in `pino`, `winston`, or a closure over an array in a test without an
12
+ * adapter. Nothing here formats, filters, or buffers.
13
+ *
14
+ * Requirements: 23.6
15
+ */
16
+ /** Structured fields attached to one log line. Values are rendered by the sink. */
17
+ export type LogFields = Record<string, unknown>;
18
+ export interface Logger {
19
+ debug(message: string, fields?: LogFields): void;
20
+ info(message: string, fields?: LogFields): void;
21
+ warn(message: string, fields?: LogFields): void;
22
+ error(message: string, fields?: LogFields): void;
23
+ }
24
+ /** Discards every line. The right default inside a library test. */
25
+ export declare const silentLogger: Logger;
26
+ /**
27
+ * Writes to the host console, prefixing every line with `tab:` so a Service
28
+ * operator can tell an SDK line from their own.
29
+ */
30
+ export declare const consoleLogger: Logger;
31
+ /**
32
+ * The logger used when a caller supplies none.
33
+ *
34
+ * The console rather than silence, because the one thing this sink exists to
35
+ * carry, a strategy registration that replaced another, is invisible
36
+ * otherwise, and a silently swapped payment strategy is the kind of surprise
37
+ * that costs money.
38
+ */
39
+ export declare const defaultLogger: Logger;
40
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +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"}
package/dist/logger.js ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The SDK logger.
3
+ *
4
+ * The seam needs somewhere to put a warning that is not an error: a duplicate
5
+ * strategy id replaces the earlier registration rather than throwing, and a
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.
9
+ *
10
+ * The interface is four methods and one optional field bag, so a consumer can
11
+ * hand in `pino`, `winston`, or a closure over an array in a test without an
12
+ * adapter. Nothing here formats, filters, or buffers.
13
+ *
14
+ * Requirements: 23.6
15
+ */
16
+ /** Discards every line. The right default inside a library test. */
17
+ export const silentLogger = {
18
+ debug: () => { },
19
+ info: () => { },
20
+ warn: () => { },
21
+ error: () => { },
22
+ };
23
+ /**
24
+ * Writes to the host console, prefixing every line with `tab:` so a Service
25
+ * operator can tell an SDK line from their own.
26
+ */
27
+ export const consoleLogger = {
28
+ debug: (message, fields) => emit("debug", message, fields),
29
+ info: (message, fields) => emit("info", message, fields),
30
+ warn: (message, fields) => emit("warn", message, fields),
31
+ error: (message, fields) => emit("error", message, fields),
32
+ };
33
+ /**
34
+ * The logger used when a caller supplies none.
35
+ *
36
+ * The console rather than silence, because the one thing this sink exists to
37
+ * carry, a strategy registration that replaced another, is invisible
38
+ * otherwise, and a silently swapped payment strategy is the kind of surprise
39
+ * that costs money.
40
+ */
41
+ export const defaultLogger = consoleLogger;
42
+ function emit(level, message, fields) {
43
+ const line = `tab: ${message}`;
44
+ if (fields === undefined) {
45
+ console[level](line);
46
+ return;
47
+ }
48
+ console[level](line, fields);
49
+ }
50
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +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"]}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Assets at the MCP boundary.
3
+ *
4
+ * An Asset is named to a model as `<chainId>:<address>`, the same shape the
5
+ * `Tab-Charge-Asset` header carries, so the string a tool result shows is the
6
+ * string a tool argument accepts. `TabBook` keys an Asset by token address alone;
7
+ * the chain id travels with it here because the SDK can be pointed at Monad
8
+ * Mainnet or Monad Testnet and a bare address is ambiguous between the two.
9
+ *
10
+ * The canonical stablecoins are known by symbol and decimals. Any other address
11
+ * is still a valid Asset, it is just one this SDK has no facts about, and the
12
+ * tool results say so rather than guessing.
13
+ */
14
+ import type { Address, Result } from "../_shared/index.js";
15
+ import type { AssetRef } from "../payments/strategy.js";
16
+ export type AssetString = string;
17
+ export interface AssetFacts {
18
+ readonly chainId: number;
19
+ readonly address: Address;
20
+ readonly symbol: string | null;
21
+ readonly decimals: number | null;
22
+ /** True when the address is a canonical stablecoin on that chain, or a configured test token. */
23
+ readonly curatedAsset: boolean;
24
+ }
25
+ export declare const formatAsset: (chainId: bigint | number, address: string) => AssetString;
26
+ export declare const assetStringOf: (asset: AssetRef) => AssetString;
27
+ /** The chain ids this SDK knows how to describe. */
28
+ export declare const KNOWN_CHAIN_IDS: readonly [143, 10143];
29
+ export declare function parseAsset(value: unknown, label?: string, env?: NodeJS.ProcessEnv): Result<AssetRef>;
30
+ export declare function assetFacts(chainId: bigint | number, address: string, env?: NodeJS.ProcessEnv): AssetFacts;
31
+ //# sourceMappingURL=assets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/mcp/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAGrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAExD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iGAAiG;IACjG,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AAED,eAAO,MAAM,WAAW,GAAI,SAAS,MAAM,GAAG,MAAM,EAAE,SAAS,MAAM,KAAG,WACzB,CAAC;AAEhD,eAAO,MAAM,aAAa,GAAI,OAAO,QAAQ,KAAG,WAAwD,CAAC;AAEzG,oDAAoD;AACpD,eAAO,MAAM,eAAe,uBAA0D,CAAC;AA6BvF,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAU,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAAC,QAAQ,CAAC,CA2ClH;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,UAAU,CAQtH"}
@@ -0,0 +1,78 @@
1
+ import { MAINNET_ASSETS, MONAD_MAINNET, MONAD_TESTNET, TESTNET_ASSETS, isAddress, ok } from "../_shared/index.js";
2
+ import { validationError } from "../errors.js";
3
+ export const formatAsset = (chainId, address) => `${String(chainId)}:${address.toLowerCase()}`;
4
+ export const assetStringOf = (asset) => formatAsset(asset.chainId, asset.address);
5
+ /** The chain ids this SDK knows how to describe. */
6
+ export const KNOWN_CHAIN_IDS = [MONAD_MAINNET.chainId, MONAD_TESTNET.chainId];
7
+ /**
8
+ * The stablecoins this SDK can name, per chain. On Testnet that is Circle's
9
+ * USDC from the shared table plus the mock token the deployment shipped, read
10
+ * from `MOCK_USDC_ADDRESS`. The mock is named `mUSDC` here although its own
11
+ * `symbol()` says `USDC`, because a Service that accepts both must show two
12
+ * Assets and not one word twice.
13
+ */
14
+ function knownAssets(chainId, env) {
15
+ if (chainId === MONAD_MAINNET.chainId) {
16
+ return Object.values(MAINNET_ASSETS).map((asset) => ({
17
+ address: asset.address.toLowerCase(),
18
+ symbol: asset.symbol,
19
+ decimals: asset.decimals,
20
+ }));
21
+ }
22
+ if (chainId === MONAD_TESTNET.chainId) {
23
+ const known = Object.values(TESTNET_ASSETS).map((asset) => ({
24
+ address: asset.address.toLowerCase(),
25
+ symbol: asset.symbol,
26
+ decimals: asset.decimals,
27
+ }));
28
+ const mock = env["MOCK_USDC_ADDRESS"];
29
+ return mock !== undefined && isAddress(mock) ? [...known, { address: mock.toLowerCase(), symbol: "mUSDC", decimals: 6 }] : known;
30
+ }
31
+ return [];
32
+ }
33
+ export function parseAsset(value, label = "asset", env = process.env) {
34
+ if (typeof value !== "string") {
35
+ return validationError("ASSET_MALFORMED", `${label} must be a string of the form chainId:tokenAddress, received ${typeof value}`, {
36
+ details: { label },
37
+ });
38
+ }
39
+ const colon = value.indexOf(":");
40
+ if (colon <= 0) {
41
+ return validationError("ASSET_MALFORMED", `${label} must be chainId:tokenAddress, for example 143:${MAINNET_ASSETS.USDC.address.toLowerCase()}, received \`${value}\``, { details: { label, value } });
42
+ }
43
+ const rawChainId = value.slice(0, colon);
44
+ const rawAddress = value.slice(colon + 1);
45
+ if (!/^[0-9]+$/.test(rawChainId)) {
46
+ return validationError("ASSET_CHAIN_ID_MALFORMED", `${label} must start with a decimal chain id, received \`${rawChainId}\``, {
47
+ details: { label, chainId: rawChainId },
48
+ });
49
+ }
50
+ if (!isAddress(rawAddress)) {
51
+ return validationError("ASSET_ADDRESS_MALFORMED", `${label} must end in a 20-byte 0x address, received \`${rawAddress}\``, {
52
+ details: { label, address: rawAddress },
53
+ });
54
+ }
55
+ const chainId = Number(rawChainId);
56
+ if (!KNOWN_CHAIN_IDS.includes(chainId)) {
57
+ return validationError("ASSET_CHAIN_UNSUPPORTED", `chain id ${rawChainId} is not a Monad network; the supported ids are ${KNOWN_CHAIN_IDS.join(" and ")}`, { details: { label, chainId: rawChainId, supported: KNOWN_CHAIN_IDS.join(", ") } });
58
+ }
59
+ const facts = assetFacts(chainId, rawAddress, env);
60
+ return ok({
61
+ chainId: BigInt(chainId),
62
+ address: rawAddress.toLowerCase(),
63
+ // An unknown token is assumed to be a six-decimal stablecoin, which is what
64
+ // every Asset Tab settles in. The facts say when that is a guess.
65
+ decimals: facts.decimals ?? 6,
66
+ symbol: facts.symbol ?? "TOKEN",
67
+ });
68
+ }
69
+ export function assetFacts(chainId, address, env = process.env) {
70
+ const lowered = address.toLowerCase();
71
+ const numeric = typeof chainId === "bigint" ? Number(chainId) : chainId;
72
+ const known = knownAssets(numeric, env).find((asset) => asset.address === lowered);
73
+ if (known !== undefined) {
74
+ return { chainId: numeric, address: lowered, symbol: known.symbol, decimals: known.decimals, curatedAsset: true };
75
+ }
76
+ return { chainId: numeric, address: lowered, symbol: null, decimals: null, curatedAsset: false };
77
+ }
78
+ //# sourceMappingURL=assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/mcp/assets.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAC5G,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAc/C,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAwB,EAAE,OAAe,EAAe,EAAE,CACpF,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;AAEhD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAe,EAAe,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;AAEzG,oDAAoD;AACpD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,aAAa,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAU,CAAC;AAEvF;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,OAAe,EAAE,GAAsB;IAC1D,IAAI,OAAO,KAAK,aAAa,CAAC,OAAO,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACnD,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE;YACpC,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC,CAAC,CAAC;IACN,CAAC;IACD,IAAI,OAAO,KAAK,aAAa,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC1D,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE;YACpC,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC,CAAC,CAAC;QACJ,MAAM,IAAI,GAAG,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACtC,OAAO,IAAI,KAAK,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACnI,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAc,EAAE,KAAK,GAAG,OAAO,EAAE,MAAyB,OAAO,CAAC,GAAG;IAC9F,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,eAAe,CAAC,iBAAiB,EAAE,GAAG,KAAK,gEAAgE,OAAO,KAAK,EAAE,EAAE;YAChI,OAAO,EAAE,EAAE,KAAK,EAAE;SACnB,CAAC,CAAC;IACL,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACf,OAAO,eAAe,CACpB,iBAAiB,EACjB,GAAG,KAAK,kDAAkD,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,gBAAgB,KAAK,IAAI,EAC5H,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAC9B,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACzC,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,OAAO,eAAe,CAAC,0BAA0B,EAAE,GAAG,KAAK,mDAAmD,UAAU,IAAI,EAAE;YAC5H,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE;SACxC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,OAAO,eAAe,CAAC,yBAAyB,EAAE,GAAG,KAAK,iDAAiD,UAAU,IAAI,EAAE;YACzH,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE;SACxC,CAAC,CAAC;IACL,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IACnC,IAAI,CAAE,eAAqC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9D,OAAO,eAAe,CACpB,yBAAyB,EACzB,YAAY,UAAU,kDAAkD,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EACvG,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CACnF,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;IACnD,OAAO,EAAE,CAAC;QACR,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC;QACxB,OAAO,EAAE,UAAU,CAAC,WAAW,EAAa;QAC5C,4EAA4E;QAC5E,kEAAkE;QAClE,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,CAAC;QAC7B,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,OAAO;KAChC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAwB,EAAE,OAAe,EAAE,MAAyB,OAAO,CAAC,GAAG;IACxG,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,EAAa,CAAC;IACjD,MAAM,OAAO,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACxE,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IACnF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;IACpH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;AACnG,CAAC","sourcesContent":["/**\n * Assets at the MCP boundary.\n *\n * An Asset is named to a model as `<chainId>:<address>`, the same shape the\n * `Tab-Charge-Asset` header carries, so the string a tool result shows is the\n * string a tool argument accepts. `TabBook` keys an Asset by token address alone;\n * the chain id travels with it here because the SDK can be pointed at Monad\n * Mainnet or Monad Testnet and a bare address is ambiguous between the two.\n *\n * The canonical stablecoins are known by symbol and decimals. Any other address\n * is still a valid Asset, it is just one this SDK has no facts about, and the\n * tool results say so rather than guessing.\n */\nimport type { Address, Result } from \"../_shared/index.js\";\nimport { MAINNET_ASSETS, MONAD_MAINNET, MONAD_TESTNET, TESTNET_ASSETS, isAddress, ok } from \"../_shared/index.js\";\nimport { validationError } from \"../errors.js\";\nimport type { AssetRef } from \"../payments/strategy.js\";\n\nexport type AssetString = string;\n\nexport interface AssetFacts {\n readonly chainId: number;\n readonly address: Address;\n readonly symbol: string | null;\n readonly decimals: number | null;\n /** True when the address is a canonical stablecoin on that chain, or a configured test token. */\n readonly curatedAsset: boolean;\n}\n\nexport const formatAsset = (chainId: bigint | number, address: string): AssetString =>\n `${String(chainId)}:${address.toLowerCase()}`;\n\nexport const assetStringOf = (asset: AssetRef): AssetString => formatAsset(asset.chainId, asset.address);\n\n/** The chain ids this SDK knows how to describe. */\nexport const KNOWN_CHAIN_IDS = [MONAD_MAINNET.chainId, MONAD_TESTNET.chainId] as const;\n\n/**\n * The stablecoins this SDK can name, per chain. On Testnet that is Circle's\n * USDC from the shared table plus the mock token the deployment shipped, read\n * from `MOCK_USDC_ADDRESS`. The mock is named `mUSDC` here although its own\n * `symbol()` says `USDC`, because a Service that accepts both must show two\n * Assets and not one word twice.\n */\nfunction knownAssets(chainId: number, env: NodeJS.ProcessEnv): readonly { address: string; symbol: string; decimals: number }[] {\n if (chainId === MONAD_MAINNET.chainId) {\n return Object.values(MAINNET_ASSETS).map((asset) => ({\n address: asset.address.toLowerCase(),\n symbol: asset.symbol,\n decimals: asset.decimals,\n }));\n }\n if (chainId === MONAD_TESTNET.chainId) {\n const known = Object.values(TESTNET_ASSETS).map((asset) => ({\n address: asset.address.toLowerCase(),\n symbol: asset.symbol,\n decimals: asset.decimals,\n }));\n const mock = env[\"MOCK_USDC_ADDRESS\"];\n return mock !== undefined && isAddress(mock) ? [...known, { address: mock.toLowerCase(), symbol: \"mUSDC\", decimals: 6 }] : known;\n }\n return [];\n}\n\nexport function parseAsset(value: unknown, label = \"asset\", env: NodeJS.ProcessEnv = process.env): Result<AssetRef> {\n if (typeof value !== \"string\") {\n return validationError(\"ASSET_MALFORMED\", `${label} must be a string of the form chainId:tokenAddress, received ${typeof value}`, {\n details: { label },\n });\n }\n const colon = value.indexOf(\":\");\n if (colon <= 0) {\n return validationError(\n \"ASSET_MALFORMED\",\n `${label} must be chainId:tokenAddress, for example 143:${MAINNET_ASSETS.USDC.address.toLowerCase()}, received \\`${value}\\``,\n { details: { label, value } },\n );\n }\n const rawChainId = value.slice(0, colon);\n const rawAddress = value.slice(colon + 1);\n if (!/^[0-9]+$/.test(rawChainId)) {\n return validationError(\"ASSET_CHAIN_ID_MALFORMED\", `${label} must start with a decimal chain id, received \\`${rawChainId}\\``, {\n details: { label, chainId: rawChainId },\n });\n }\n if (!isAddress(rawAddress)) {\n return validationError(\"ASSET_ADDRESS_MALFORMED\", `${label} must end in a 20-byte 0x address, received \\`${rawAddress}\\``, {\n details: { label, address: rawAddress },\n });\n }\n const chainId = Number(rawChainId);\n if (!(KNOWN_CHAIN_IDS as readonly number[]).includes(chainId)) {\n return validationError(\n \"ASSET_CHAIN_UNSUPPORTED\",\n `chain id ${rawChainId} is not a Monad network; the supported ids are ${KNOWN_CHAIN_IDS.join(\" and \")}`,\n { details: { label, chainId: rawChainId, supported: KNOWN_CHAIN_IDS.join(\", \") } },\n );\n }\n const facts = assetFacts(chainId, rawAddress, env);\n return ok({\n chainId: BigInt(chainId),\n address: rawAddress.toLowerCase() as Address,\n // An unknown token is assumed to be a six-decimal stablecoin, which is what\n // every Asset Tab settles in. The facts say when that is a guess.\n decimals: facts.decimals ?? 6,\n symbol: facts.symbol ?? \"TOKEN\",\n });\n}\n\nexport function assetFacts(chainId: bigint | number, address: string, env: NodeJS.ProcessEnv = process.env): AssetFacts {\n const lowered = address.toLowerCase() as Address;\n const numeric = typeof chainId === \"bigint\" ? Number(chainId) : chainId;\n const known = knownAssets(numeric, env).find((asset) => asset.address === lowered);\n if (known !== undefined) {\n return { chainId: numeric, address: lowered, symbol: known.symbol, decimals: known.decimals, curatedAsset: true };\n }\n return { chainId: numeric, address: lowered, symbol: null, decimals: null, curatedAsset: false };\n}\n"]}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The MCP surface: four tools, their declared schemas, and the server that
3
+ * serves them. (R25.1, R25.2, R25.3)
4
+ *
5
+ * `tab_discover` finds Services, `tab_call` uses one and is metered onto the
6
+ * Agent's Open Tab, `tab_status` reports what the Agent owes and may still
7
+ * spend, and `tab_settle` pays it down. Three of the four are keyless reads.
8
+ * Only `tab_settle` signs, and it signs through the same payment-strategy seam
9
+ * the rest of this package settles through.
10
+ */
11
+ export * from "./json-schema.js";
12
+ export * from "./schemas.js";
13
+ export * from "./assets.js";
14
+ export * from "./json.js";
15
+ export * from "./registry-client.js";
16
+ export * from "./settings.js";
17
+ export * from "./toolset.js";
18
+ export * from "./server.js";
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The MCP surface: four tools, their declared schemas, and the server that
3
+ * serves them. (R25.1, R25.2, R25.3)
4
+ *
5
+ * `tab_discover` finds Services, `tab_call` uses one and is metered onto the
6
+ * Agent's Open Tab, `tab_status` reports what the Agent owes and may still
7
+ * spend, and `tab_settle` pays it down. Three of the four are keyless reads.
8
+ * Only `tab_settle` signs, and it signs through the same payment-strategy seam
9
+ * the rest of this package settles through.
10
+ */
11
+ export * from "./json-schema.js";
12
+ export * from "./schemas.js";
13
+ export * from "./assets.js";
14
+ export * from "./json.js";
15
+ export * from "./registry-client.js";
16
+ export * from "./settings.js";
17
+ export * from "./toolset.js";
18
+ export * from "./server.js";
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"]}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The JSON Schema subset the MCP tools declare, and a validator for it.
3
+ *
4
+ * ## Why this is written here rather than pulled in
5
+ *
6
+ * An MCP tool declares its input and output shape as JSON Schema, and a model
7
+ * reads that declaration to decide what to send. A declaration nothing enforces
8
+ * is a promise, not a contract: the first tool that accepts an out-of-range
9
+ * `limit` or emits an amount as a `number` has broken the schema its caller was
10
+ * reasoning against, and nothing in the process notices. So the four tools in
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).
13
+ *
14
+ * The validator is 200 lines rather than a dependency because the schemas here
15
+ * use ten keywords between them, this package adds no dependency for the MCP
16
+ * surface, and a validator whose supported keyword set is visible in one file
17
+ * cannot silently ignore a keyword a schema relies on. {@link validateJsonValue}
18
+ * fails loudly on a keyword it does not implement instead of passing the value,
19
+ * which is the property a hand-written validator has to have to be trustworthy.
20
+ *
21
+ * ## Everything returns a Result
22
+ *
23
+ * Nothing here throws, including on a malformed schema. A schema is authored in
24
+ * this package, so a bad one is a bug rather than input, but a bug that surfaces
25
+ * as an `INTERNAL` `Result` at a tool boundary is reported to the model as a
26
+ * failed call, and a bug that surfaces as a thrown value takes the stdio
27
+ * transport down with it.
28
+ *
29
+ * Requirements: 21.5, 25.1, 25.2
30
+ */
31
+ import type { Result } from "../_shared/index.js";
32
+ /** The seven JSON Schema primitive type names. */
33
+ export type JsonSchemaType = "object" | "array" | "string" | "number" | "integer" | "boolean" | "null";
34
+ /** Every keyword {@link validateJsonValue} understands. Anything else is a failure, not a pass. */
35
+ export declare const SUPPORTED_KEYWORDS: readonly ["type", "title", "description", "properties", "required", "additionalProperties", "items", "enum", "const", "pattern", "minLength", "maxLength", "minimum", "maximum", "minItems", "maxItems", "default", "examples"];
36
+ /**
37
+ * One node of the schema subset.
38
+ *
39
+ * `type` may be an array, which is how a nullable field is declared here:
40
+ * `{ type: ["string", "null"] }`. There is no `nullable` keyword, because that
41
+ * one is OpenAPI's rather than JSON Schema's and a model reading the tool
42
+ * declaration would be reading a keyword that does not mean what it says.
43
+ */
44
+ export interface JsonSchema {
45
+ readonly type?: JsonSchemaType | readonly JsonSchemaType[];
46
+ readonly title?: string;
47
+ readonly description?: string;
48
+ readonly properties?: Readonly<Record<string, JsonSchema>>;
49
+ readonly required?: readonly string[];
50
+ readonly additionalProperties?: boolean;
51
+ readonly items?: JsonSchema;
52
+ readonly enum?: readonly (string | number | boolean | null)[];
53
+ readonly const?: string | number | boolean | null;
54
+ readonly pattern?: string;
55
+ readonly minLength?: number;
56
+ readonly maxLength?: number;
57
+ readonly minimum?: number;
58
+ readonly maximum?: number;
59
+ readonly minItems?: number;
60
+ readonly maxItems?: number;
61
+ readonly default?: unknown;
62
+ readonly examples?: readonly unknown[];
63
+ }
64
+ /** An object-typed schema, which is what every tool input and output is. */
65
+ export interface JsonObjectSchema extends JsonSchema {
66
+ readonly type: "object";
67
+ readonly properties: Readonly<Record<string, JsonSchema>>;
68
+ }
69
+ /**
70
+ * Validates a value against a schema and returns it unchanged when it conforms.
71
+ *
72
+ * `code` names the failure so a caller can tell an input rejection from an
73
+ * output-shape bug without parsing the message.
74
+ */
75
+ export declare function validateJsonValue<T>(schema: JsonSchema, value: unknown, label: string, code?: string): Result<T>;
76
+ /**
77
+ * Fills in the declared top-level defaults of an object schema.
78
+ *
79
+ * Only the top level, because that is where every default in this package's
80
+ * schemas sits and a defaulting pass that reached into arrays would be inventing
81
+ * entries a caller did not send. A non-object value is handed back untouched for
82
+ * {@link validateJsonValue} to reject with a type problem, which is a better
83
+ * message than one about defaults.
84
+ */
85
+ export declare function applyJsonDefaults(schema: JsonSchema, value: unknown): unknown;
86
+ //# sourceMappingURL=json-schema.d.ts.map
@@ -0,0 +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"}