effect 4.1.0 → 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (445) hide show
  1. package/README.md +30 -0
  2. package/THIRD_PARTY_NOTICES.md +62 -0
  3. package/ai-docs/package.json +2 -2
  4. package/dist/BigDecimal.d.ts +139 -7
  5. package/dist/BigDecimal.d.ts.map +1 -1
  6. package/dist/BigDecimal.js +83 -14
  7. package/dist/BigDecimal.js.map +1 -1
  8. package/dist/ByteSize.d.ts.map +1 -1
  9. package/dist/ByteSize.js +27 -14
  10. package/dist/ByteSize.js.map +1 -1
  11. package/dist/Equal.d.ts +2 -2
  12. package/dist/Equal.d.ts.map +1 -1
  13. package/dist/Equal.js +3 -3
  14. package/dist/Equal.js.map +1 -1
  15. package/dist/Hash.d.ts +3 -2
  16. package/dist/Hash.d.ts.map +1 -1
  17. package/dist/Hash.js +5 -4
  18. package/dist/Hash.js.map +1 -1
  19. package/dist/Iterable.d.ts.map +1 -1
  20. package/dist/Iterable.js +38 -32
  21. package/dist/Iterable.js.map +1 -1
  22. package/dist/JsonSchema.d.ts.map +1 -1
  23. package/dist/JsonSchema.js +27 -6
  24. package/dist/JsonSchema.js.map +1 -1
  25. package/dist/MutableRef.d.ts.map +1 -1
  26. package/dist/MutableRef.js +0 -7
  27. package/dist/MutableRef.js.map +1 -1
  28. package/dist/Path.d.ts.map +1 -1
  29. package/dist/Path.js +5 -1
  30. package/dist/Path.js.map +1 -1
  31. package/dist/Pool.d.ts.map +1 -1
  32. package/dist/Pool.js +2 -1
  33. package/dist/Pool.js.map +1 -1
  34. package/dist/Ref.d.ts.map +1 -1
  35. package/dist/Ref.js +0 -8
  36. package/dist/Ref.js.map +1 -1
  37. package/dist/Schedule.d.ts.map +1 -1
  38. package/dist/Schedule.js +8 -3
  39. package/dist/Schedule.js.map +1 -1
  40. package/dist/Schema.d.ts +2 -1
  41. package/dist/Schema.d.ts.map +1 -1
  42. package/dist/Schema.js +2 -1
  43. package/dist/Schema.js.map +1 -1
  44. package/dist/SchemaRepresentation.d.ts +4 -2
  45. package/dist/SchemaRepresentation.d.ts.map +1 -1
  46. package/dist/SchemaRepresentation.js +4 -2
  47. package/dist/SchemaRepresentation.js.map +1 -1
  48. package/dist/SchemaTransformation.d.ts +2 -2
  49. package/dist/SchemaTransformation.js +2 -2
  50. package/dist/Stream.d.ts.map +1 -1
  51. package/dist/Stream.js +6 -5
  52. package/dist/Stream.js.map +1 -1
  53. package/dist/ai/McpClient.d.ts +171 -19
  54. package/dist/ai/McpClient.d.ts.map +1 -1
  55. package/dist/ai/McpClient.js +277 -66
  56. package/dist/ai/McpClient.js.map +1 -1
  57. package/dist/ai/McpSchema.d.ts +108 -0
  58. package/dist/ai/McpSchema.d.ts.map +1 -1
  59. package/dist/ai/McpSchema.js +99 -0
  60. package/dist/ai/McpSchema.js.map +1 -1
  61. package/dist/ai/McpServer.d.ts +120 -6
  62. package/dist/ai/McpServer.d.ts.map +1 -1
  63. package/dist/ai/McpServer.js +258 -49
  64. package/dist/ai/McpServer.js.map +1 -1
  65. package/dist/ai/Prompt.d.ts.map +1 -1
  66. package/dist/ai/Prompt.js +20 -11
  67. package/dist/ai/Prompt.js.map +1 -1
  68. package/dist/ai/Tool.d.ts.map +1 -1
  69. package/dist/ai/Tool.js +5 -7
  70. package/dist/ai/Tool.js.map +1 -1
  71. package/dist/ai/internal/mcpCore.js +72 -3
  72. package/dist/ai/internal/mcpCore.js.map +1 -1
  73. package/dist/ai/internal/mcpProtocol/v2024_11_05.js +3 -1
  74. package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
  75. package/dist/ai/internal/mcpProtocol/v2025_03_26.js +3 -1
  76. package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
  77. package/dist/ai/internal/mcpProtocol/v2025_06_18.js +3 -1
  78. package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
  79. package/dist/ai/internal/mcpProtocol/v2025_11_25.js +3 -1
  80. package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
  81. package/dist/ai/internal/mcpProtocol/v2026_07_28.js +44 -8
  82. package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
  83. package/dist/ai/internal/mcpProtocol.js +12 -0
  84. package/dist/ai/internal/mcpProtocol.js.map +1 -1
  85. package/dist/ai/internal/mcpRuntime.js +3 -3
  86. package/dist/ai/internal/mcpRuntime.js.map +1 -1
  87. package/dist/ai/internal/mcpSkills.d.ts +2 -0
  88. package/dist/ai/internal/mcpSkills.d.ts.map +1 -0
  89. package/dist/ai/internal/mcpSkills.js +428 -0
  90. package/dist/ai/internal/mcpSkills.js.map +1 -0
  91. package/dist/ai/internal/structured-output.js +11 -2
  92. package/dist/ai/internal/structured-output.js.map +1 -1
  93. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  94. package/dist/cluster/ClusterWorkflowEngine.js +30 -11
  95. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  96. package/dist/cluster/Entity.d.ts +26 -0
  97. package/dist/cluster/Entity.d.ts.map +1 -1
  98. package/dist/cluster/Entity.js.map +1 -1
  99. package/dist/cluster/MessageStorage.d.ts +6 -6
  100. package/dist/cluster/MessageStorage.d.ts.map +1 -1
  101. package/dist/cluster/MessageStorage.js +1 -3
  102. package/dist/cluster/MessageStorage.js.map +1 -1
  103. package/dist/cluster/Reply.d.ts +1 -1
  104. package/dist/cluster/Reply.d.ts.map +1 -1
  105. package/dist/cluster/Reply.js.map +1 -1
  106. package/dist/cluster/Runners.d.ts.map +1 -1
  107. package/dist/cluster/Runners.js +13 -2
  108. package/dist/cluster/Runners.js.map +1 -1
  109. package/dist/cluster/Sharding.d.ts +13 -0
  110. package/dist/cluster/Sharding.d.ts.map +1 -1
  111. package/dist/cluster/Sharding.js +4 -1
  112. package/dist/cluster/Sharding.js.map +1 -1
  113. package/dist/cluster/internal/clusterAbandon.d.ts.map +1 -1
  114. package/dist/cluster/internal/clusterAbandon.js +6 -4
  115. package/dist/cluster/internal/clusterAbandon.js.map +1 -1
  116. package/dist/cluster/internal/entityManager.js +63 -11
  117. package/dist/cluster/internal/entityManager.js.map +1 -1
  118. package/dist/cluster/internal/transactionNotifications.d.ts +5 -0
  119. package/dist/cluster/internal/transactionNotifications.d.ts.map +1 -0
  120. package/dist/cluster/internal/transactionNotifications.js +5 -0
  121. package/dist/cluster/internal/transactionNotifications.js.map +1 -0
  122. package/dist/crypto/Aead.d.ts +2 -1
  123. package/dist/crypto/Aead.d.ts.map +1 -1
  124. package/dist/crypto/Aead.js +2 -2
  125. package/dist/crypto/Aead.js.map +1 -1
  126. package/dist/crypto/Cipher.d.ts +2 -1
  127. package/dist/crypto/Cipher.d.ts.map +1 -1
  128. package/dist/crypto/Cipher.js +2 -2
  129. package/dist/crypto/Cipher.js.map +1 -1
  130. package/dist/crypto/KeyAgreement.d.ts +3 -2
  131. package/dist/crypto/KeyAgreement.d.ts.map +1 -1
  132. package/dist/crypto/KeyAgreement.js +2 -2
  133. package/dist/crypto/KeyAgreement.js.map +1 -1
  134. package/dist/crypto/KeyFormat.d.ts +33 -1
  135. package/dist/crypto/KeyFormat.d.ts.map +1 -1
  136. package/dist/crypto/KeyFormat.js.map +1 -1
  137. package/dist/crypto/KeyWrap.d.ts +4 -3
  138. package/dist/crypto/KeyWrap.d.ts.map +1 -1
  139. package/dist/crypto/KeyWrap.js +2 -2
  140. package/dist/crypto/KeyWrap.js.map +1 -1
  141. package/dist/crypto/Mac.d.ts +2 -1
  142. package/dist/crypto/Mac.d.ts.map +1 -1
  143. package/dist/crypto/Mac.js +2 -2
  144. package/dist/crypto/Mac.js.map +1 -1
  145. package/dist/crypto/Signature.d.ts +3 -2
  146. package/dist/crypto/Signature.d.ts.map +1 -1
  147. package/dist/crypto/Signature.js +2 -2
  148. package/dist/crypto/Signature.js.map +1 -1
  149. package/dist/crypto/WebCrypto.d.ts +19 -0
  150. package/dist/crypto/WebCrypto.d.ts.map +1 -1
  151. package/dist/crypto/WebCrypto.js.map +1 -1
  152. package/dist/crypto/internal/asymmetric.js.map +1 -1
  153. package/dist/crypto/internal/key.js +3 -2
  154. package/dist/crypto/internal/key.js.map +1 -1
  155. package/dist/encoding/Base64Url.d.ts.map +1 -1
  156. package/dist/encoding/Base64Url.js +26 -3
  157. package/dist/encoding/Base64Url.js.map +1 -1
  158. package/dist/encoding/Json5.d.ts +44 -0
  159. package/dist/encoding/Json5.d.ts.map +1 -0
  160. package/dist/encoding/Json5.js +56 -0
  161. package/dist/encoding/Json5.js.map +1 -0
  162. package/dist/encoding/Jsonc.d.ts +47 -0
  163. package/dist/encoding/Jsonc.d.ts.map +1 -0
  164. package/dist/encoding/Jsonc.js +120 -0
  165. package/dist/encoding/Jsonc.js.map +1 -0
  166. package/dist/encoding/Ndjson.d.ts +5 -4
  167. package/dist/encoding/Ndjson.d.ts.map +1 -1
  168. package/dist/encoding/Ndjson.js +6 -5
  169. package/dist/encoding/Ndjson.js.map +1 -1
  170. package/dist/encoding/SchemaBinary.d.ts +94 -0
  171. package/dist/encoding/SchemaBinary.d.ts.map +1 -1
  172. package/dist/encoding/SchemaBinary.js +219 -6
  173. package/dist/encoding/SchemaBinary.js.map +1 -1
  174. package/dist/encoding/Sse.d.ts +3 -1
  175. package/dist/encoding/Sse.d.ts.map +1 -1
  176. package/dist/encoding/Sse.js +5 -3
  177. package/dist/encoding/Sse.js.map +1 -1
  178. package/dist/encoding/index.d.ts +10 -0
  179. package/dist/encoding/index.d.ts.map +1 -1
  180. package/dist/encoding/index.js +10 -0
  181. package/dist/encoding/index.js.map +1 -1
  182. package/dist/eventlog/EventLog.d.ts +7 -0
  183. package/dist/eventlog/EventLog.d.ts.map +1 -1
  184. package/dist/eventlog/EventLog.js +7 -0
  185. package/dist/eventlog/EventLog.js.map +1 -1
  186. package/dist/eventlog/EventLogServer.d.ts +7 -1
  187. package/dist/eventlog/EventLogServer.d.ts.map +1 -1
  188. package/dist/eventlog/EventLogServer.js +55 -24
  189. package/dist/eventlog/EventLogServer.js.map +1 -1
  190. package/dist/eventlog/EventLogServerEncrypted.d.ts +10 -0
  191. package/dist/eventlog/EventLogServerEncrypted.d.ts.map +1 -1
  192. package/dist/eventlog/EventLogServerEncrypted.js.map +1 -1
  193. package/dist/eventlog/EventLogServerUnencrypted.d.ts +17 -0
  194. package/dist/eventlog/EventLogServerUnencrypted.d.ts.map +1 -1
  195. package/dist/eventlog/EventLogServerUnencrypted.js +14 -2
  196. package/dist/eventlog/EventLogServerUnencrypted.js.map +1 -1
  197. package/dist/eventlog/EventLogSessionAuth.d.ts +21 -0
  198. package/dist/eventlog/EventLogSessionAuth.d.ts.map +1 -1
  199. package/dist/eventlog/EventLogSessionAuth.js +22 -0
  200. package/dist/eventlog/EventLogSessionAuth.js.map +1 -1
  201. package/dist/graphql/GraphQLClient.d.ts +3 -1
  202. package/dist/graphql/GraphQLClient.d.ts.map +1 -1
  203. package/dist/graphql/GraphQLClient.js.map +1 -1
  204. package/dist/graphql/GraphQLClientError.d.ts +14 -9
  205. package/dist/graphql/GraphQLClientError.d.ts.map +1 -1
  206. package/dist/graphql/GraphQLClientError.js +14 -21
  207. package/dist/graphql/GraphQLClientError.js.map +1 -1
  208. package/dist/graphql/GraphQLProtocol.d.ts +12 -3
  209. package/dist/graphql/GraphQLProtocol.d.ts.map +1 -1
  210. package/dist/graphql/GraphQLProtocol.js +4 -1
  211. package/dist/graphql/GraphQLProtocol.js.map +1 -1
  212. package/dist/http/HttpClient.d.ts +204 -16
  213. package/dist/http/HttpClient.d.ts.map +1 -1
  214. package/dist/http/HttpClient.js +93 -13
  215. package/dist/http/HttpClient.js.map +1 -1
  216. package/dist/http/HttpServerRequest.d.ts.map +1 -1
  217. package/dist/http/HttpServerRequest.js +38 -22
  218. package/dist/http/HttpServerRequest.js.map +1 -1
  219. package/dist/http/internal/mimeTypes.d.ts +4 -1
  220. package/dist/http/internal/mimeTypes.d.ts.map +1 -1
  221. package/dist/http/internal/mimeTypes.js +4 -1
  222. package/dist/http/internal/mimeTypes.js.map +1 -1
  223. package/dist/http-api/HttpApiSchema.js +1 -1
  224. package/dist/http-api/HttpApiSchema.js.map +1 -1
  225. package/dist/internal/dateTime.js +66 -27
  226. package/dist/internal/dateTime.js.map +1 -1
  227. package/dist/internal/effect.js +10 -0
  228. package/dist/internal/effect.js.map +1 -1
  229. package/dist/internal/glob.js +234 -56
  230. package/dist/internal/glob.js.map +1 -1
  231. package/dist/internal/graphql.js +21 -7
  232. package/dist/internal/graphql.js.map +1 -1
  233. package/dist/internal/graphqlHeaders.d.ts +7 -0
  234. package/dist/internal/graphqlHeaders.d.ts.map +1 -1
  235. package/dist/internal/graphqlHeaders.js +11 -0
  236. package/dist/internal/graphqlHeaders.js.map +1 -1
  237. package/dist/internal/jsonParser.d.ts +5 -0
  238. package/dist/internal/jsonParser.d.ts.map +1 -0
  239. package/dist/internal/jsonParser.js +280 -0
  240. package/dist/internal/jsonParser.js.map +1 -0
  241. package/dist/internal/persistence.js +8 -1
  242. package/dist/internal/persistence.js.map +1 -1
  243. package/dist/internal/request.js +29 -42
  244. package/dist/internal/request.js.map +1 -1
  245. package/dist/internal/schema/fromJsonSchemaDocument.js +7 -8
  246. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  247. package/dist/internal/sql.js +16 -0
  248. package/dist/internal/sql.js.map +1 -1
  249. package/dist/internal/version.js +1 -1
  250. package/dist/jose/JoseError.d.ts +285 -0
  251. package/dist/jose/JoseError.d.ts.map +1 -0
  252. package/dist/jose/JoseError.js +269 -0
  253. package/dist/jose/JoseError.js.map +1 -0
  254. package/dist/jose/Jwe.d.ts +128 -0
  255. package/dist/jose/Jwe.d.ts.map +1 -0
  256. package/dist/jose/Jwe.js +134 -0
  257. package/dist/jose/Jwe.js.map +1 -0
  258. package/dist/jose/Jwk.d.ts +597 -0
  259. package/dist/jose/Jwk.d.ts.map +1 -0
  260. package/dist/jose/Jwk.js +503 -0
  261. package/dist/jose/Jwk.js.map +1 -0
  262. package/dist/jose/Jwks.d.ts +236 -0
  263. package/dist/jose/Jwks.d.ts.map +1 -0
  264. package/dist/jose/Jwks.js +306 -0
  265. package/dist/jose/Jwks.js.map +1 -0
  266. package/dist/jose/Jws.d.ts +177 -0
  267. package/dist/jose/Jws.d.ts.map +1 -0
  268. package/dist/jose/Jws.js +183 -0
  269. package/dist/jose/Jws.js.map +1 -0
  270. package/dist/jose/Jwt.d.ts +270 -0
  271. package/dist/jose/Jwt.d.ts.map +1 -0
  272. package/dist/jose/Jwt.js +293 -0
  273. package/dist/jose/Jwt.js.map +1 -0
  274. package/dist/jose/index.d.ts +51 -0
  275. package/dist/jose/index.d.ts.map +1 -0
  276. package/dist/jose/index.js +52 -0
  277. package/dist/jose/index.js.map +1 -0
  278. package/dist/jose/internal/compact.d.ts +2 -0
  279. package/dist/jose/internal/compact.d.ts.map +1 -0
  280. package/dist/jose/internal/compact.js +179 -0
  281. package/dist/jose/internal/compact.js.map +1 -0
  282. package/dist/net/Host.d.ts.map +1 -1
  283. package/dist/net/Host.js +5 -10
  284. package/dist/net/Host.js.map +1 -1
  285. package/dist/observability/OtlpExporter.d.ts +4 -0
  286. package/dist/observability/OtlpExporter.d.ts.map +1 -1
  287. package/dist/observability/OtlpExporter.js +40 -12
  288. package/dist/observability/OtlpExporter.js.map +1 -1
  289. package/dist/observability/OtlpSerialization.d.ts +1 -1
  290. package/dist/observability/OtlpSerialization.d.ts.map +1 -1
  291. package/dist/observability/OtlpSerialization.js +10 -4
  292. package/dist/observability/OtlpSerialization.js.map +1 -1
  293. package/dist/observability/internal/otlpProtobuf.js +3 -3
  294. package/dist/observability/internal/otlpProtobuf.js.map +1 -1
  295. package/dist/observability/internal/protobuf.js +6 -0
  296. package/dist/observability/internal/protobuf.js.map +1 -1
  297. package/dist/persistence/CircuitBreaker.d.ts +98 -38
  298. package/dist/persistence/CircuitBreaker.d.ts.map +1 -1
  299. package/dist/persistence/CircuitBreaker.js +67 -29
  300. package/dist/persistence/CircuitBreaker.js.map +1 -1
  301. package/dist/persistence/RateLimiter.d.ts +15 -0
  302. package/dist/persistence/RateLimiter.d.ts.map +1 -1
  303. package/dist/persistence/RateLimiter.js +76 -16
  304. package/dist/persistence/RateLimiter.js.map +1 -1
  305. package/dist/persistence/index.d.ts +1 -1
  306. package/dist/persistence/index.js +1 -1
  307. package/dist/reactivity/AtomRegistry.js +17 -7
  308. package/dist/reactivity/AtomRegistry.js.map +1 -1
  309. package/dist/rpc/Rpc.d.ts +1 -1
  310. package/dist/rpc/Rpc.d.ts.map +1 -1
  311. package/dist/rpc/Rpc.js.map +1 -1
  312. package/dist/rpc/RpcClient.d.ts +14 -2
  313. package/dist/rpc/RpcClient.d.ts.map +1 -1
  314. package/dist/rpc/RpcClient.js +38 -21
  315. package/dist/rpc/RpcClient.js.map +1 -1
  316. package/dist/rpc/RpcMessage.d.ts +3 -2
  317. package/dist/rpc/RpcMessage.d.ts.map +1 -1
  318. package/dist/rpc/RpcMessage.js.map +1 -1
  319. package/dist/rpc/RpcServer.d.ts.map +1 -1
  320. package/dist/rpc/RpcServer.js +10 -1
  321. package/dist/rpc/RpcServer.js.map +1 -1
  322. package/dist/sql/Migrator.d.ts +9 -1
  323. package/dist/sql/Migrator.d.ts.map +1 -1
  324. package/dist/sql/Migrator.js +18 -6
  325. package/dist/sql/Migrator.js.map +1 -1
  326. package/dist/sql/SqlClient.d.ts +46 -3
  327. package/dist/sql/SqlClient.d.ts.map +1 -1
  328. package/dist/sql/SqlClient.js +40 -15
  329. package/dist/sql/SqlClient.js.map +1 -1
  330. package/dist/sql/Statement.d.ts +46 -3
  331. package/dist/sql/Statement.d.ts.map +1 -1
  332. package/dist/sql/Statement.js +32 -26
  333. package/dist/sql/Statement.js.map +1 -1
  334. package/dist/workflow/Workflow.d.ts +4 -0
  335. package/dist/workflow/Workflow.d.ts.map +1 -1
  336. package/dist/workflow/Workflow.js.map +1 -1
  337. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  338. package/dist/workflow/WorkflowEngine.js +5 -0
  339. package/dist/workflow/WorkflowEngine.js.map +1 -1
  340. package/package.json +7 -3
  341. package/src/BigDecimal.ts +170 -16
  342. package/src/ByteSize.ts +29 -14
  343. package/src/Equal.ts +3 -3
  344. package/src/Hash.ts +5 -4
  345. package/src/Iterable.ts +28 -19
  346. package/src/JsonSchema.ts +45 -7
  347. package/src/MutableRef.ts +1 -8
  348. package/src/Path.ts +6 -1
  349. package/src/Pool.ts +2 -1
  350. package/src/Ref.ts +0 -8
  351. package/src/Schedule.ts +8 -6
  352. package/src/Schema.ts +3 -2
  353. package/src/SchemaRepresentation.ts +4 -2
  354. package/src/SchemaTransformation.ts +2 -2
  355. package/src/Stream.ts +6 -5
  356. package/src/ai/McpClient.ts +488 -91
  357. package/src/ai/McpSchema.ts +119 -0
  358. package/src/ai/McpServer.ts +302 -56
  359. package/src/ai/Prompt.ts +16 -11
  360. package/src/ai/Tool.ts +5 -10
  361. package/src/ai/internal/mcpCore.ts +165 -12
  362. package/src/ai/internal/mcpProtocol/v2024_11_05.ts +3 -1
  363. package/src/ai/internal/mcpProtocol/v2025_03_26.ts +3 -1
  364. package/src/ai/internal/mcpProtocol/v2025_06_18.ts +3 -1
  365. package/src/ai/internal/mcpProtocol/v2025_11_25.ts +3 -1
  366. package/src/ai/internal/mcpProtocol/v2026_07_28.ts +42 -9
  367. package/src/ai/internal/mcpProtocol.ts +15 -0
  368. package/src/ai/internal/mcpRuntime.ts +6 -3
  369. package/src/ai/internal/mcpSkills.ts +480 -0
  370. package/src/ai/internal/structured-output.ts +10 -4
  371. package/src/cluster/ClusterWorkflowEngine.ts +48 -33
  372. package/src/cluster/Entity.ts +52 -0
  373. package/src/cluster/MessageStorage.ts +3 -6
  374. package/src/cluster/Reply.ts +1 -1
  375. package/src/cluster/Runners.ts +16 -2
  376. package/src/cluster/Sharding.ts +17 -1
  377. package/src/cluster/internal/clusterAbandon.ts +7 -7
  378. package/src/cluster/internal/entityManager.ts +89 -15
  379. package/src/cluster/internal/transactionNotifications.ts +7 -0
  380. package/src/crypto/Aead.ts +3 -3
  381. package/src/crypto/Cipher.ts +3 -3
  382. package/src/crypto/KeyAgreement.ts +4 -4
  383. package/src/crypto/KeyFormat.ts +30 -1
  384. package/src/crypto/KeyWrap.ts +6 -6
  385. package/src/crypto/Mac.ts +3 -3
  386. package/src/crypto/Signature.ts +4 -4
  387. package/src/crypto/WebCrypto.ts +20 -0
  388. package/src/crypto/internal/asymmetric.ts +2 -1
  389. package/src/crypto/internal/key.ts +12 -5
  390. package/src/encoding/Base64Url.ts +27 -3
  391. package/src/encoding/Json5.ts +59 -0
  392. package/src/encoding/Jsonc.ts +126 -0
  393. package/src/encoding/Ndjson.ts +6 -5
  394. package/src/encoding/SchemaBinary.ts +228 -7
  395. package/src/encoding/Sse.ts +5 -3
  396. package/src/encoding/index.ts +12 -0
  397. package/src/eventlog/EventLog.ts +7 -0
  398. package/src/eventlog/EventLogServer.ts +67 -24
  399. package/src/eventlog/EventLogServerEncrypted.ts +5 -0
  400. package/src/eventlog/EventLogServerUnencrypted.ts +19 -2
  401. package/src/eventlog/EventLogSessionAuth.ts +26 -0
  402. package/src/graphql/GraphQLClient.ts +3 -1
  403. package/src/graphql/GraphQLClientError.ts +15 -22
  404. package/src/graphql/GraphQLProtocol.ts +12 -3
  405. package/src/http/HttpClient.ts +244 -33
  406. package/src/http/HttpServerRequest.ts +61 -23
  407. package/src/http/internal/mimeTypes.ts +4 -1
  408. package/src/http-api/HttpApiSchema.ts +1 -1
  409. package/src/internal/dateTime.ts +80 -27
  410. package/src/internal/effect.ts +6 -0
  411. package/src/internal/glob.ts +279 -63
  412. package/src/internal/graphql.ts +27 -9
  413. package/src/internal/graphqlHeaders.ts +13 -0
  414. package/src/internal/jsonParser.ts +307 -0
  415. package/src/internal/persistence.ts +9 -1
  416. package/src/internal/request.ts +51 -58
  417. package/src/internal/schema/fromJsonSchemaDocument.ts +11 -10
  418. package/src/internal/sql.ts +25 -0
  419. package/src/internal/version.ts +1 -1
  420. package/src/jose/JoseError.ts +329 -0
  421. package/src/jose/Jwe.ts +196 -0
  422. package/src/jose/Jwk.ts +777 -0
  423. package/src/jose/Jwks.ts +418 -0
  424. package/src/jose/Jws.ts +312 -0
  425. package/src/jose/Jwt.ts +493 -0
  426. package/src/jose/index.ts +58 -0
  427. package/src/jose/internal/compact.ts +195 -0
  428. package/src/net/Host.ts +5 -10
  429. package/src/observability/OtlpExporter.ts +42 -18
  430. package/src/observability/OtlpSerialization.ts +13 -5
  431. package/src/observability/internal/otlpProtobuf.ts +6 -6
  432. package/src/observability/internal/protobuf.ts +10 -0
  433. package/src/persistence/CircuitBreaker.ts +113 -48
  434. package/src/persistence/RateLimiter.ts +97 -19
  435. package/src/persistence/index.ts +1 -1
  436. package/src/reactivity/AtomRegistry.ts +19 -8
  437. package/src/rpc/Rpc.ts +3 -1
  438. package/src/rpc/RpcClient.ts +44 -24
  439. package/src/rpc/RpcMessage.ts +3 -2
  440. package/src/rpc/RpcServer.ts +12 -1
  441. package/src/sql/Migrator.ts +35 -18
  442. package/src/sql/SqlClient.ts +91 -22
  443. package/src/sql/Statement.ts +60 -35
  444. package/src/workflow/Workflow.ts +4 -0
  445. package/src/workflow/WorkflowEngine.ts +5 -0
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Connects to MCP servers with scoped Effect clients.
3
3
  *
4
- * Clients discover tools, prompts and resources using the protocol revision selected by
5
- * their transport. `callTool` returns tool-reported errors as results; supplying a schema
4
+ * Clients discover tools, prompts, resources and Agent Skills using the protocol revision
5
+ * selected by their transport. `callTool` returns tool-reported errors as results; supplying a schema
6
6
  * turns them into a `ToolError` reason that retains the original result. Transport and
7
7
  * protocol failures fail the Effect. Interactive server requests, subscriptions, OAuth
8
8
  * and retries are not included.
@@ -13,11 +13,13 @@
13
13
  import * as ByteSize from "../ByteSize.ts"
14
14
  import type * as Cause from "../Cause.ts"
15
15
  import * as Context from "../Context.ts"
16
+ import * as Digest from "../crypto/Digest.ts"
16
17
  import * as Deferred from "../Deferred.ts"
17
18
  import * as Duration from "../Duration.ts"
18
19
  import * as Effect from "../Effect.ts"
19
20
  import * as Base64 from "../encoding/Base64.ts"
20
21
  import * as Sse from "../encoding/Sse.ts"
22
+ import * as Equal from "../Equal.ts"
21
23
  import * as HttpClient from "../http/HttpClient.ts"
22
24
  import * as HttpClientRequest from "../http/HttpClientRequest.ts"
23
25
  import type * as HttpClientResponse from "../http/HttpClientResponse.ts"
@@ -32,6 +34,7 @@ import * as Schema from "../Schema.ts"
32
34
  import type * as Scope from "../Scope.ts"
33
35
  import * as Stream from "../Stream.ts"
34
36
  import { BASE64_SENTINEL_PREFIX, BASE64_SENTINEL_SUFFIX } from "./internal/mcpProtocol.ts"
37
+ import * as McpSkills from "./internal/mcpSkills.ts"
35
38
  import {
36
39
  ClosedError,
37
40
  ConfigurationError,
@@ -49,6 +52,7 @@ import {
49
52
  } from "./McpClientError.ts"
50
53
  import type * as McpProtocol from "./McpProtocol.ts"
51
54
  import * as McpSchema from "./McpSchema.ts"
55
+ import * as Prompt from "./Prompt.ts"
52
56
  import * as Tool from "./Tool.ts"
53
57
  import * as Toolkit from "./Toolkit.ts"
54
58
 
@@ -84,18 +88,33 @@ export const McpClient: Context.Service<McpClient, McpClient> = Context.Service<
84
88
  export interface McpClient {
85
89
  readonly [TypeId]: typeof TypeId
86
90
  readonly protocolVersion: ProtocolVersion
91
+ /** Features advertised by the remote server. */
92
+ readonly capabilities: McpSchema.ServerCapabilities
87
93
  readonly serverInfo: McpSchema.Implementation | undefined
88
94
  readonly instructions?: string | undefined
89
95
  /**
90
- * Returns every tool page as one snapshot, bounded by the deadline, 100 pages and 10,000 tools.
91
- * `timeout` overrides the client's discovery deadline.
96
+ * Returns every tool page as one snapshot within the client's discovery bounds.
97
+ * `timeout` and `limits` (nonnegative integers) override those bounds, and each
98
+ * page stays within the transport's message size limit.
92
99
  */
93
100
  readonly listTools: (
94
- options?: { readonly timeout?: Duration.Input | undefined }
101
+ options?: {
102
+ readonly timeout?: Duration.Input | undefined
103
+ readonly limits?: {
104
+ readonly maxPages?: number | undefined
105
+ readonly maxItems?: number | undefined
106
+ } | undefined
107
+ }
95
108
  ) => Effect.Effect<ReadonlyArray<McpSchema.Tool>, McpClientError>
96
109
  /** Returns every prompt page as one snapshot with the same bounds as `listTools`. */
97
110
  readonly listPrompts: (
98
- options?: { readonly timeout?: Duration.Input | undefined }
111
+ options?: {
112
+ readonly timeout?: Duration.Input | undefined
113
+ readonly limits?: {
114
+ readonly maxPages?: number | undefined
115
+ readonly maxItems?: number | undefined
116
+ } | undefined
117
+ }
99
118
  ) => Effect.Effect<ReadonlyArray<McpSchema.Prompt>, McpClientError>
100
119
  /** Gets the messages for a discovered prompt. Unbounded unless `timeout` is supplied. */
101
120
  readonly getPrompt: (
@@ -107,13 +126,71 @@ export interface McpClient {
107
126
  ) => Effect.Effect<McpSchema.GetPromptResult, McpClientError>
108
127
  /** Returns every resource page as one snapshot with the same bounds as `listTools`. */
109
128
  readonly listResources: (
110
- options?: { readonly timeout?: Duration.Input | undefined }
129
+ options?: {
130
+ readonly timeout?: Duration.Input | undefined
131
+ readonly limits?: {
132
+ readonly maxPages?: number | undefined
133
+ readonly maxItems?: number | undefined
134
+ } | undefined
135
+ }
111
136
  ) => Effect.Effect<ReadonlyArray<McpSchema.Resource>, McpClientError>
112
137
  /** Reads the complete contents of a resource URI. Unbounded unless `timeout` is supplied. */
113
138
  readonly readResource: (
114
139
  params: { readonly uri: string },
115
140
  options?: { readonly timeout?: Duration.Input | undefined }
116
141
  ) => Effect.Effect<McpSchema.ReadResourceResult, McpClientError>
142
+ /**
143
+ * Returns every skill page as one snapshot with the same bounds as `listTools`, or none
144
+ * when the server does not declare the Skills extension. Fails on an invalid entry, unless
145
+ * `onInvalidSkill` is supplied: invalid entries are then left out and each is passed to it.
146
+ * Requires `extensions: { skills: true }`, as do the other skill methods.
147
+ */
148
+ readonly listSkills: (
149
+ options?: {
150
+ readonly timeout?: Duration.Input | undefined
151
+ readonly limits?: {
152
+ readonly maxPages?: number | undefined
153
+ readonly maxItems?: number | undefined
154
+ } | undefined
155
+ readonly onInvalidSkill?:
156
+ | ((invalid: { readonly entry: unknown; readonly error: McpClientError }) => Effect.Effect<void>)
157
+ | undefined
158
+ }
159
+ ) => Effect.Effect<ReadonlyArray<McpSchema.Skill>, McpClientError>
160
+ /**
161
+ * Gets a skill by the URI of its `SKILL.md`, whether or not `listSkills` returns it.
162
+ * Unbounded unless `timeout` is supplied.
163
+ */
164
+ readonly getSkill: (
165
+ params: { readonly uri: string },
166
+ options?: { readonly timeout?: Duration.Input | undefined }
167
+ ) => Effect.Effect<McpSchema.Skill, McpClientError>
168
+ /**
169
+ * Lists the direct children of a directory in a skill namespace, with the same bounds as
170
+ * `listTools`. Requires the server's `directoryRead` setting.
171
+ */
172
+ readonly readResourceDirectory: (
173
+ params: { readonly uri: string },
174
+ options?: {
175
+ readonly timeout?: Duration.Input | undefined
176
+ readonly limits?: {
177
+ readonly maxPages?: number | undefined
178
+ readonly maxItems?: number | undefined
179
+ } | undefined
180
+ }
181
+ ) => Effect.Effect<ReadonlyArray<McpSchema.Resource>, McpClientError>
182
+ /**
183
+ * Reads a file of a skill and fails with a `ValidationError` unless its bytes match the
184
+ * SHA-256 digest and size listed in the skill's manifest, and, for `SKILL.md`, unless its
185
+ * YAML frontmatter equals the listed frontmatter. A skill whose `resources` is `"dynamic"`
186
+ * has no digests: any file inside its directory can be read, and `SKILL.md` is still
187
+ * checked. Files outside the manifest or directory are never requested. Unbounded unless
188
+ * `timeout` is supplied.
189
+ */
190
+ readonly readSkillResource: (
191
+ params: { readonly skill: McpSchema.Skill; readonly uri: string },
192
+ options?: { readonly timeout?: Duration.Input | undefined }
193
+ ) => Effect.Effect<McpSchema.ReadResourceResult, McpClientError>
117
194
  /**
118
195
  * Calls a discovered tool, optionally decoding with a schema. Errors retain the full result.
119
196
  * Unbounded unless `timeout` is supplied.
@@ -166,9 +243,20 @@ export const isMcpClient = (value: unknown): value is McpClient => Predicate.has
166
243
  *
167
244
  * `name`, `version` and the optional `title`, `description`, `websiteUrl` and
168
245
  * `icons` identify the client to the server. `timeout` defaults to 60 seconds
169
- * and bounds initialization and each complete `listTools`, `listPrompts` or
170
- * `listResources` scan. `callTool`, `getPrompt` and `readResource` have no
171
- * deadline unless their own `timeout` option is supplied.
246
+ * and bounds initialization and each complete `listTools`, `listPrompts`,
247
+ * `listResources`, `listSkills` or `readResourceDirectory` scan.
248
+ * `discoveryLimits` sets the default scan limits of 100 pages and 10,000
249
+ * items. `callTool`, `getPrompt`, `readResource`, `getSkill` and
250
+ * `readSkillResource` have no deadline unless their own `timeout` option is
251
+ * supplied.
252
+ *
253
+ * `extensions` selects the protocol extensions the client declares. With
254
+ * `skills: true` it declares the Skills extension (SEP-2640) and enables
255
+ * `listSkills`, `getSkill`, `readResourceDirectory` and `readSkillResource`;
256
+ * without it they fail with a `ConfigurationError`. They run only against a
257
+ * server that declares the extension too, and skill entries are checked
258
+ * against its rules. `readSkillResource` checks digests with SHA-256 on
259
+ * `crypto.subtle`.
172
260
  * Schema decoding requires the schema's services from the caller.
173
261
  *
174
262
  * @stability experimental
@@ -183,6 +271,13 @@ export const make: (options: {
183
271
  readonly websiteUrl?: string | undefined
184
272
  readonly icons?: ReadonlyArray<McpSchema.Icon> | undefined
185
273
  readonly timeout?: Duration.Input | undefined
274
+ readonly discoveryLimits?: {
275
+ readonly maxPages?: number | undefined
276
+ readonly maxItems?: number | undefined
277
+ } | undefined
278
+ readonly extensions?: {
279
+ readonly skills?: boolean | undefined
280
+ } | undefined
186
281
  }) => Effect.Effect<
187
282
  McpClient,
188
283
  McpClientError,
@@ -197,6 +292,7 @@ export const make: (options: {
197
292
  const version = protocol.protocolVersion
198
293
  const modern = version === "2026-07-28"
199
294
  const timeout = options.timeout ?? DEFAULT_TIMEOUT
295
+ const discoveryLimits = yield* resolveDiscoveryLimits(options.discoveryLimits, DEFAULT_DISCOVERY_LIMITS)
200
296
  const closed = yield* Deferred.make<never, McpClientError>()
201
297
  yield* Effect.addFinalizer(() =>
202
298
  Deferred.fail(closed, fail(new ClosedError({ description: "MCP client scope closed" })))
@@ -214,10 +310,12 @@ export const make: (options: {
214
310
 
215
311
  // Modern requests carry the client identity in every request.
216
312
  // https://modelcontextprotocol.io/specification/2026-07-28/basic/index
313
+ const skillsEnabled = options.extensions?.skills === true
314
+ const clientCapabilities = skillsEnabled ? { extensions: { [McpSkills.EXTENSION_ID]: {} } } : {}
217
315
  const meta = {
218
316
  "io.modelcontextprotocol/protocolVersion": version,
219
317
  "io.modelcontextprotocol/clientInfo": clientInfo,
220
- "io.modelcontextprotocol/clientCapabilities": {}
318
+ "io.modelcontextprotocol/clientCapabilities": clientCapabilities
221
319
  }
222
320
 
223
321
  const request = Effect.fnUntraced(function*<A>(
@@ -284,7 +382,7 @@ export const make: (options: {
284
382
  if (!modern) {
285
383
  return yield* request<InitializeResult>("initialize", {
286
384
  protocolVersion: version,
287
- capabilities: {},
385
+ capabilities: clientCapabilities,
288
386
  clientInfo
289
387
  })
290
388
  }
@@ -334,30 +432,160 @@ export const make: (options: {
334
432
  ? Effect.fail(fail(new UnsupportedError({ description: `Server does not advertise ${kind}` })))
335
433
  : request<A>(method, params, tool)
336
434
 
435
+ const paginate = Effect.fnUntraced(function*<K extends string, A>(
436
+ label: string,
437
+ method: string,
438
+ key: K,
439
+ params: Record<string, unknown>,
440
+ limits: typeof DEFAULT_DISCOVERY_LIMITS
441
+ ) {
442
+ const items: Array<A> = []
443
+ const cursors = new Set<string>()
444
+ let cursor: string | undefined
445
+ for (let page = 0; page < limits.maxPages; page++) {
446
+ const result = yield* request<ListResult<K, A>>(method, cursor === undefined ? params : { ...params, cursor })
447
+ if (items.length + result[key].length > limits.maxItems) {
448
+ return yield* limitError(`MCP ${label} discovery exceeded its item limit`)
449
+ }
450
+ for (const item of result[key]) items.push(item)
451
+ if (result.nextCursor === undefined) return items
452
+ if (cursors.has(result.nextCursor)) {
453
+ return yield* limitError(`MCP ${label} discovery repeated a cursor`)
454
+ }
455
+ cursors.add(cursor = result.nextCursor)
456
+ }
457
+ return yield* limitError(`MCP ${label} discovery exceeded its page limit`)
458
+ })
459
+
337
460
  const list = <K extends Capability, A>(kind: K) =>
338
461
  Effect.fnUntraced(
339
- function*(_options?: CallOptions) {
462
+ function*(callOptions?: DiscoveryOptions) {
463
+ const limits = yield* resolveDiscoveryLimits(callOptions?.limits, discoveryLimits)
340
464
  if (capabilities[kind] === undefined) return []
341
- const items: Array<A> = []
342
- const cursors = new Set<string>()
343
- let cursor: string | undefined
344
- for (let page = 0; page < MAX_DISCOVERY_PAGES; page++) {
345
- const result = yield* request<ListResult<K, A>>(`${kind}/list`, cursor === undefined ? {} : { cursor })
346
- if (items.length + result[kind].length > MAX_DISCOVERY_ITEMS) {
347
- return yield* limitError(`MCP ${kind} discovery exceeded its item limit`)
348
- }
349
- items.push(...result[kind])
350
- if (result.nextCursor === undefined) return items
351
- if (cursors.has(result.nextCursor)) {
352
- return yield* limitError(`MCP ${kind} discovery repeated a cursor`)
353
- }
354
- cursors.add(cursor = result.nextCursor)
355
- }
356
- return yield* limitError(`MCP ${kind} discovery exceeded its page limit`)
465
+ return yield* paginate<K, A>(kind, `${kind}/list`, kind, {}, limits)
357
466
  },
358
467
  (effect, callOptions) => withDeadline(effect, `${kind} discovery`, callOptions?.timeout ?? timeout)
359
468
  )
360
469
 
470
+ // Hosts must not call the extension's methods unless the server declares it,
471
+ // and the extension requires the resources capability.
472
+ const skillsSettings = capabilities.extensions?.[McpSkills.EXTENSION_ID]
473
+ const servesSkills = isRecord(skillsSettings) && capabilities.resources !== undefined
474
+ const servesDirectories = servesSkills && isRecord(skillsSettings) && skillsSettings.directoryRead === true
475
+ const digest = yield* Effect.provide(Digest.Digest, Digest.layerWebCrypto)
476
+
477
+ const invalid = (description: string) => Effect.fail(fail(new ValidationError({ description })))
478
+ const unsupported = (description: string) => Effect.fail(fail(new UnsupportedError({ description })))
479
+ const requireSkillsOption = skillsEnabled ? Effect.void : Effect.fail(
480
+ fail(new ConfigurationError({ description: "MCP skills methods require extensions: { skills: true }" }))
481
+ )
482
+
483
+ const decodeSkill = Effect.fnUntraced(function*(entry: unknown) {
484
+ const skill = yield* decodeUnknown(McpSchema.Skill, entry) as Effect.Effect<McpSchema.Skill, McpClientError>
485
+ const problem = McpSkills.entryProblem(skill)
486
+ return problem === undefined ? skill : yield* invalid(`Invalid MCP skill ${skill.uri}: ${problem}`)
487
+ })
488
+
489
+ const listSkills = Effect.fnUntraced(
490
+ function*(options?: Parameters<McpClient["listSkills"]>[0]) {
491
+ yield* requireSkillsOption
492
+ const limits = yield* resolveDiscoveryLimits(options?.limits, discoveryLimits)
493
+ if (!servesSkills) return []
494
+ const entries = yield* paginate<"skills", unknown>("skills", "skills/list", "skills", {}, limits)
495
+ // A catalog that changes between pages can list a skill twice; the first entry wins.
496
+ const skills = new Map<string, McpSchema.Skill>()
497
+ for (const entry of entries) {
498
+ const decoded = yield* Effect.result(decodeSkill(entry))
499
+ if (decoded._tag === "Failure") {
500
+ if (options?.onInvalidSkill === undefined) return yield* Effect.fail(decoded.failure)
501
+ yield* options.onInvalidSkill({ entry, error: decoded.failure })
502
+ continue
503
+ }
504
+ if (!skills.has(decoded.success.uri)) skills.set(decoded.success.uri, decoded.success)
505
+ }
506
+ return Array.from(skills.values())
507
+ },
508
+ (effect, callOptions) => withDeadline(effect, "skills discovery", callOptions?.timeout ?? timeout)
509
+ )
510
+
511
+ const getSkill = Effect.fnUntraced(
512
+ function*(params: { readonly uri: string }, _options?: CallOptions) {
513
+ yield* requireSkillsOption
514
+ if (!servesSkills) return yield* unsupported("Server does not declare the MCP Skills extension")
515
+ const result = yield* request<{ readonly skill: unknown }>("skills/get", { uri: params.uri })
516
+ const skill = yield* decodeSkill(result.skill)
517
+ return skill.uri === params.uri
518
+ ? skill
519
+ : yield* invalid(`MCP server returned skill ${skill.uri} for ${params.uri}`)
520
+ },
521
+ (effect, _params, callOptions) => withDeadline(effect, "skills/get", callOptions?.timeout)
522
+ )
523
+
524
+ const readResourceDirectory = Effect.fnUntraced(
525
+ function*(params: { readonly uri: string }, options?: DiscoveryOptions) {
526
+ yield* requireSkillsOption
527
+ const limits = yield* resolveDiscoveryLimits(options?.limits, discoveryLimits)
528
+ if (!servesDirectories) {
529
+ return yield* unsupported("Server does not declare directory reads for the MCP Skills extension")
530
+ }
531
+ const entries = yield* paginate<"resources", unknown>(
532
+ "directory",
533
+ "resources/directory/read",
534
+ "resources",
535
+ { uri: params.uri },
536
+ limits
537
+ )
538
+ // A directory that changes between pages can list a child twice; the first entry wins.
539
+ const children = new Map<string, McpSchema.Resource>()
540
+ for (const entry of entries) {
541
+ const child = yield* decodeUnknown(McpSchema.Resource, entry) as Effect.Effect<
542
+ McpSchema.Resource,
543
+ McpClientError
544
+ >
545
+ if (!McpSkills.isDirectChild(params.uri, child.uri)) {
546
+ return yield* invalid(`${child.uri} is not a child of the MCP directory ${params.uri}`)
547
+ }
548
+ if (!children.has(child.uri)) children.set(child.uri, child)
549
+ }
550
+ return Array.from(children.values())
551
+ },
552
+ (effect, _params, callOptions) => withDeadline(effect, "directory discovery", callOptions?.timeout ?? timeout)
553
+ )
554
+
555
+ const readSkillResource = Effect.fnUntraced(
556
+ function*(params: { readonly skill: McpSchema.Skill; readonly uri: string }, _options?: CallOptions) {
557
+ yield* requireSkillsOption
558
+ const { skill, uri } = params
559
+ // A file missing from the manifest is a change to the skill, not a file to
560
+ // trust. A dynamic skill has no manifest, so any file in its directory is read.
561
+ const manifest = skill.resources === "dynamic" ? undefined : skill.resources
562
+ const listed = manifest?.find((resource) => resource.uri === uri)
563
+ if (manifest === undefined ? !McpSkills.isSkillFile(skill.uri, uri) : listed === undefined) {
564
+ return yield* invalid(`${uri} is not a file of the MCP skill ${skill.uri}`)
565
+ }
566
+ const result = yield* call<McpSchema.ReadResourceResult>("resources", "resources/read", { uri })
567
+ const [content, ...rest] = result.contents
568
+ if (content === undefined || rest.length > 0 || content.uri !== uri) {
569
+ return yield* invalid(`MCP resources/read returned other contents than ${uri}`)
570
+ }
571
+ const bytes = "text" in content ? encoder.encode(content.text) : content.blob
572
+ if (listed !== undefined) {
573
+ const actual = `sha256:${yield* digest.digestHex(Digest.sha256, bytes)}`
574
+ if (bytes.byteLength !== listed.size || actual !== listed.digest) {
575
+ return yield* invalid(`${uri} does not match the digest and size listed by the MCP skill ${skill.uri}`)
576
+ }
577
+ }
578
+ if (uri !== skill.uri) return result
579
+ // Verified bytes can still carry other frontmatter than the listing, and
580
+ // hosts must not load a skill whose two descriptions differ.
581
+ const frontmatter = McpSkills.parseFrontmatter("text" in content ? content.text : decoder.decode(bytes))
582
+ return frontmatter !== undefined && Equal.equals(frontmatter, skill.frontmatter)
583
+ ? result
584
+ : yield* invalid(`The frontmatter of ${uri} differs from the frontmatter listed for the MCP skill`)
585
+ },
586
+ (effect, _params, callOptions) => withDeadline(effect, "resources/read", callOptions?.timeout)
587
+ )
588
+
361
589
  const listTools = list<"tools", McpSchema.Tool>("tools")
362
590
 
363
591
  const callTool = Effect.fnUntraced(
@@ -391,6 +619,7 @@ export const make: (options: {
391
619
  return {
392
620
  [TypeId]: TypeId,
393
621
  protocolVersion: version,
622
+ capabilities,
394
623
  serverInfo,
395
624
  instructions,
396
625
  listTools: (callOptions) =>
@@ -412,6 +641,10 @@ export const make: (options: {
412
641
  "resources/read",
413
642
  callOptions?.timeout
414
643
  ),
644
+ listSkills,
645
+ getSkill,
646
+ readResourceDirectory,
647
+ readSkillResource,
415
648
  callTool: callTool as McpClient["callTool"]
416
649
  }
417
650
  })
@@ -421,75 +654,159 @@ export const make: (options: {
421
654
  // =============================================================================
422
655
 
423
656
  /**
424
- * Discovers remote tools and binds their handlers using the standard Toolkit implementation.
657
+ * Annotation containing the remote descriptor used to bind an MCP tool.
658
+ *
659
+ * **Details**
660
+ *
661
+ * The descriptor is server-provided and should be treated as untrusted.
662
+ *
663
+ * @stability experimental
664
+ * @category services
665
+ * @since 4.1.1
666
+ */
667
+ export class ToolDescriptor
668
+ extends Context.Service<ToolDescriptor, McpSchema.Tool>()("effect/ai/McpClient/ToolDescriptor")
669
+ {}
670
+
671
+ /**
672
+ * Native tool bound to a remote MCP descriptor with configurable result and failure modes.
673
+ *
674
+ * @stability experimental
675
+ * @category models
676
+ * @since 4.1.1
677
+ */
678
+ export type RemoteTool<
679
+ ResultMode extends "text" | "complete" = "text",
680
+ FailureMode extends Tool.FailureMode = "error",
681
+ Requirements = never
682
+ > = FailureMode extends Tool.FailureMode ? Tool.Tool<string, {
683
+ readonly parameters: typeof Schema.Unknown
684
+ readonly success: {
685
+ readonly text: typeof Schema.String
686
+ readonly complete: typeof McpSchema.CallToolResult
687
+ }[ResultMode]
688
+ readonly failure: typeof McpClientError
689
+ readonly failureMode: FailureMode
690
+ }, Requirements>
691
+ : never
692
+
693
+ /**
694
+ * Binds remote tools using the standard Toolkit implementation.
425
695
  *
426
696
  * **Details**
427
697
  *
428
- * Pass an optional prefix such as `"reports__"` to qualify model tool names.
429
- * Remote JSON Schemas are advertised without local schema import. Results become
430
- * text; tool-reported and operational failures fail the Effect. Use `Tool.make`
431
- * or `Tool.dynamic` with `Toolkit.make` for custom validation, approval or results.
432
- * Build a new toolkit to discover changes between model turns.
698
+ * Pass a prefix such as `"reports__"` or an options object. Tools are discovered
699
+ * unless an approved `tools` snapshot is supplied. Remote JSON Schemas are
700
+ * advertised as-is, and model tool names must be 1–64 ASCII letters, digits,
701
+ * underscores or hyphens. Build a new toolkit to pick up remote changes.
702
+ *
703
+ * By default results are text and failures fail the Effect.
704
+ * `resultMode: "complete"` returns the full `CallToolResult` and sends its
705
+ * content to the model as text and file parts. `failureMode: "return"` returns
706
+ * typed failures, including transport errors, to the model; `ToolError` keeps
707
+ * the failed result. Type arguments that select a non-default mode require the
708
+ * matching option.
709
+ *
710
+ * `timeout` bounds each tool call. `trustAnnotations` copies server hints into
711
+ * tool annotations but never sets approval. `transformTool` receives each
712
+ * annotated tool before binding and must return a tool derived from it, e.g.
713
+ * with `setNeedsApproval`, `annotate` or `addDependency`, keeping its name,
714
+ * schemas and failure mode.
433
715
  *
434
716
  * @stability experimental
435
717
  * @category constructors
436
718
  * @since 4.0.6
437
719
  */
438
- export const toolkit: (client: McpClient, prefix?: string) => Effect.Effect<
439
- Toolkit.WithHandler<
440
- Record<
441
- string,
442
- Tool.Tool<string, {
443
- readonly parameters: typeof Schema.Unknown
444
- readonly success: typeof Schema.String
445
- readonly failure: typeof McpClientError
446
- readonly failureMode: "error"
447
- }>
448
- >
449
- >,
450
- McpClientError
451
- > = Effect.fnUntraced(function*(client: McpClient, prefix = "") {
452
- const descriptors = yield* client.listTools()
453
- const tools: Array<
454
- Tool.Tool<string, {
455
- readonly parameters: typeof Schema.Unknown
456
- readonly success: typeof Schema.String
457
- readonly failure: typeof McpClientError
458
- readonly failureMode: "error"
459
- }>
460
- > = []
461
- const handlers: Record<string, (params: unknown) => Effect.Effect<string, McpClientError>> = Object.create(null)
462
-
463
- for (const descriptor of descriptors) {
464
- const name = `${prefix}${descriptor.name}`
465
- if (!/^[a-zA-Z0-9_-]{1,64}$/.test(name) || Object.hasOwn(handlers, name)) {
466
- return yield* fail(new ConfigurationError({ description: `Invalid or duplicate MCP model tool name: ${name}` }))
720
+ export function toolkit(
721
+ client: McpClient,
722
+ prefix?: string
723
+ ): Effect.Effect<Toolkit.WithHandler<Record<string, RemoteTool>>, McpClientError>
724
+ export function toolkit<
725
+ ResultMode extends "text" | "complete" = "text",
726
+ FailureMode extends Tool.FailureMode = "error",
727
+ Requirements = never
728
+ >(
729
+ client: McpClient,
730
+ options:
731
+ & {
732
+ readonly prefix?: string | undefined
733
+ readonly tools?: ReadonlyArray<McpSchema.Tool> | undefined
734
+ readonly trustAnnotations?: boolean | undefined
735
+ readonly timeout?: Duration.Input | undefined
736
+ readonly transformTool?:
737
+ | ((
738
+ tool: RemoteTool<ResultMode, FailureMode>,
739
+ descriptor: McpSchema.Tool
740
+ ) => RemoteTool<ResultMode, FailureMode, Requirements>)
741
+ | undefined
467
742
  }
468
- tools.push(
469
- Tool.dynamic(name, {
743
+ // A mode may be omitted only when its type is exactly the default.
744
+ & ([ResultMode] extends ["text"] ? { readonly resultMode?: "text" | undefined }
745
+ : { readonly resultMode: ResultMode })
746
+ & ([FailureMode] extends ["error"] ? { readonly failureMode?: "error" | undefined }
747
+ : { readonly failureMode: FailureMode })
748
+ ): Effect.Effect<
749
+ Toolkit.WithHandler<Record<string, RemoteTool<ResultMode, FailureMode, Requirements>>>,
750
+ McpClientError
751
+ >
752
+ export function toolkit(
753
+ client: McpClient,
754
+ options: string | ToolkitConfig = ""
755
+ ): Effect.Effect<Toolkit.WithHandler<any>, McpClientError> {
756
+ return Effect.gen(function*() {
757
+ const config: ToolkitConfig = typeof options === "string" ? { prefix: options } : options
758
+ const descriptors = config.tools ?? (yield* client.listTools())
759
+ const complete = config.resultMode === "complete"
760
+ const success = complete ? McpSchema.CallToolResult : Schema.String
761
+ const failureMode = config.failureMode ?? "error"
762
+ const tools: Array<Tool.Any> = []
763
+ const handlers: Record<
764
+ string,
765
+ (params: unknown) => Effect.Effect<string | McpSchema.CallToolResult, McpClientError>
766
+ > = Object.create(null)
767
+
768
+ for (const descriptor of descriptors) {
769
+ const name = `${config.prefix ?? ""}${descriptor.name}`
770
+ if (!/^[a-zA-Z0-9_-]{1,64}$/.test(name) || Object.hasOwn(handlers, name)) {
771
+ return yield* fail(new ConfigurationError({ description: `Invalid or duplicate MCP model tool name: ${name}` }))
772
+ }
773
+ const spec = {
470
774
  parameters: descriptor.inputSchema,
471
775
  description: descriptor.description,
472
- success: Schema.String,
473
- failure: McpClientError
474
- }).annotate(Tool.Strict, false)
475
- )
476
- handlers[name] = Effect.fnUntraced(function*(params: unknown) {
477
- const args = yield* decodeToolArguments(params).pipe(
478
- Effect.mapError((cause) =>
479
- fail(new ValidationError({ description: "MCP tool arguments must be a JSON object", cause }))
480
- )
481
- )
482
- const result = yield* client.callTool({ tool: descriptor, arguments: args })
483
- if (result.isError === true) {
484
- return yield* fail(new ToolError({ result }))
776
+ success,
777
+ failure: McpClientError,
778
+ toModelOutput: complete ? toModelOutput : undefined
485
779
  }
486
- return yield* toText(result)
487
- })
488
- }
780
+ // The identical branches narrow `failureMode` to a single `RemoteTool` member.
781
+ let tool: RemoteTool<"text" | "complete", Tool.FailureMode> = (
782
+ failureMode === "return"
783
+ ? Tool.dynamic(name, { ...spec, failureMode })
784
+ : Tool.dynamic(name, { ...spec, failureMode })
785
+ ).annotate(Tool.Strict, false).annotate(ToolDescriptor, descriptor)
786
+ if (config.trustAnnotations && descriptor.annotations !== undefined) {
787
+ tool = tool.annotate(Tool.Readonly, descriptor.annotations.readOnlyHint)
788
+ .annotate(Tool.Destructive, descriptor.annotations.destructiveHint)
789
+ .annotate(Tool.Idempotent, descriptor.annotations.idempotentHint)
790
+ .annotate(Tool.OpenWorld, descriptor.annotations.openWorldHint)
791
+ if (descriptor.annotations.title !== undefined) tool = tool.annotate(Tool.Title, descriptor.annotations.title)
792
+ }
793
+ tools.push(config.transformTool === undefined ? tool : config.transformTool(tool, descriptor))
794
+ handlers[name] = Effect.fnUntraced(function*(params: unknown) {
795
+ const args = yield* decodeToolArguments(params).pipe(
796
+ Effect.mapError((cause) =>
797
+ fail(new ValidationError({ description: "MCP tool arguments must be a JSON object", cause }))
798
+ )
799
+ )
800
+ const result = yield* client.callTool({ tool: descriptor, arguments: args }, { timeout: config.timeout })
801
+ if (result.isError === true) return yield* fail(new ToolError({ result }))
802
+ return complete ? result : yield* toText(result)
803
+ })
804
+ }
489
805
 
490
- const toolkit = Toolkit.make(...tools)
491
- return yield* toolkit.pipe(Effect.provideContext(yield* toolkit.toHandlers(handlers)))
492
- })
806
+ const toolkit = Toolkit.make(...tools)
807
+ return yield* toolkit.pipe(Effect.provideContext(yield* toolkit.toHandlers(handlers)))
808
+ });
809
+ }
493
810
 
494
811
  // =============================================================================
495
812
  // Transports
@@ -687,6 +1004,13 @@ export const layerStdio: (options: {
687
1004
  readonly websiteUrl?: string | undefined
688
1005
  readonly icons?: ReadonlyArray<McpSchema.Icon> | undefined
689
1006
  readonly timeout?: Duration.Input | undefined
1007
+ readonly discoveryLimits?: {
1008
+ readonly maxPages?: number | undefined
1009
+ readonly maxItems?: number | undefined
1010
+ } | undefined
1011
+ readonly extensions?: {
1012
+ readonly skills?: boolean | undefined
1013
+ } | undefined
690
1014
  readonly command: ChildProcess.Command
691
1015
  readonly protocol: McpProtocol.ProtocolAdapter<ProtocolVersion>
692
1016
  readonly maxMessageBytes?: ByteSize.Input | undefined
@@ -921,6 +1245,13 @@ export const layerHttp: (options: {
921
1245
  readonly websiteUrl?: string | undefined
922
1246
  readonly icons?: ReadonlyArray<McpSchema.Icon> | undefined
923
1247
  readonly timeout?: Duration.Input | undefined
1248
+ readonly discoveryLimits?: {
1249
+ readonly maxPages?: number | undefined
1250
+ readonly maxItems?: number | undefined
1251
+ } | undefined
1252
+ readonly extensions?: {
1253
+ readonly skills?: boolean | undefined
1254
+ } | undefined
924
1255
  readonly url: string | URL
925
1256
  readonly protocol: McpProtocol.ProtocolAdapter<ProtocolVersion>
926
1257
  readonly headers?: Readonly<Record<string, string>> | undefined
@@ -940,8 +1271,7 @@ const TransportTypeId = "~effect/ai/McpClient/Transport" as const
940
1271
  const DEFAULT_MAX_MESSAGE_BYTES = ByteSize.mebibytes(16)
941
1272
  const DEFAULT_TIMEOUT = Duration.seconds(60)
942
1273
  const CLOSE_TIMEOUT = Duration.seconds(5)
943
- const MAX_DISCOVERY_PAGES = 100
944
- const MAX_DISCOVERY_ITEMS = 10_000
1274
+ const DEFAULT_DISCOVERY_LIMITS = { maxPages: 100, maxItems: 10_000 }
945
1275
  const HEADER_TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/
946
1276
  const encoder = new TextEncoder()
947
1277
  const decoder = new TextDecoder()
@@ -952,6 +1282,10 @@ type Options = Parameters<typeof make>[0]
952
1282
 
953
1283
  type Capability = "tools" | "prompts" | "resources"
954
1284
 
1285
+ type DiscoveryOptions = NonNullable<Parameters<McpClient["listTools"]>[0]>
1286
+
1287
+ type DiscoveryLimits = DiscoveryOptions["limits"]
1288
+
955
1289
  interface CallOptions {
956
1290
  readonly timeout?: Duration.Input | undefined
957
1291
  }
@@ -969,6 +1303,19 @@ type ListResult<K extends string, A> = Record<K, ReadonlyArray<A>> & { readonly
969
1303
  const fail = (reason: McpClientErrorReason) => new McpClientError({ reason })
970
1304
  const limitError = (description: string) => fail(new LimitError({ description }))
971
1305
 
1306
+ const resolveDiscoveryLimits = (
1307
+ limits: DiscoveryLimits,
1308
+ defaults: typeof DEFAULT_DISCOVERY_LIMITS
1309
+ ): Effect.Effect<typeof DEFAULT_DISCOVERY_LIMITS, McpClientError> => {
1310
+ const maxPages = limits?.maxPages ?? defaults.maxPages
1311
+ const maxItems = limits?.maxItems ?? defaults.maxItems
1312
+ return Number.isSafeInteger(maxPages) && maxPages >= 0 && Number.isSafeInteger(maxItems) && maxItems >= 0
1313
+ ? Effect.succeed({ maxPages, maxItems })
1314
+ : Effect.fail(
1315
+ fail(new ConfigurationError({ description: "MCP discovery limits must be nonnegative safe integers" }))
1316
+ )
1317
+ }
1318
+
972
1319
  const validationError = (cause: Schema.SchemaError) =>
973
1320
  fail(new ValidationError({ description: "Invalid MCP payload", cause }))
974
1321
 
@@ -1035,7 +1382,8 @@ const isRequest = (message: McpSchema.JsonRpcMessage): message is McpSchema.Json
1035
1382
 
1036
1383
  const isResponse = (message: McpSchema.JsonRpcMessage): message is McpSchema.JsonRpcResponse => !("method" in message)
1037
1384
 
1038
- // The client advertises no capabilities, so it answers pings and declines every other server request.
1385
+ // The client advertises no capability that lets a server send it requests, so it answers pings and
1386
+ // declines every other server request.
1039
1387
  const replyToServer = (request: McpSchema.JsonRpcRequest): McpSchema.JsonRpcResponse =>
1040
1388
  request.method === "ping"
1041
1389
  ? { jsonrpc: "2.0", id: request.id, result: {} }
@@ -1175,6 +1523,57 @@ const routingHeaders = Effect.fnUntraced(function*(message: McpSchema.JsonRpcMes
1175
1523
  // Internal: Toolkit
1176
1524
  // =============================================================================
1177
1525
 
1526
+ interface ToolkitConfig {
1527
+ readonly prefix?: string | undefined
1528
+ readonly tools?: ReadonlyArray<McpSchema.Tool> | undefined
1529
+ readonly resultMode?: "text" | "complete" | undefined
1530
+ readonly failureMode?: Tool.FailureMode | undefined
1531
+ readonly trustAnnotations?: boolean | undefined
1532
+ readonly timeout?: Duration.Input | undefined
1533
+ readonly transformTool?:
1534
+ | ((tool: RemoteTool<"text" | "complete", Tool.FailureMode>, descriptor: McpSchema.Tool) => Tool.Any)
1535
+ | undefined
1536
+ }
1537
+
1538
+ const toModelOutput = (result: McpSchema.CallToolResult): Array<Prompt.ToolModelOutputPart> => {
1539
+ const parts: Array<Prompt.ToolModelOutputPart> = []
1540
+ for (const block of result.content) {
1541
+ switch (block.type) {
1542
+ case "text": {
1543
+ parts.push(Prompt.textPart({ text: block.text }))
1544
+ break
1545
+ }
1546
+ case "image":
1547
+ case "audio": {
1548
+ parts.push(Prompt.filePart({ mediaType: block.mimeType, data: block.data }))
1549
+ break
1550
+ }
1551
+ case "resource_link": {
1552
+ parts.push(Prompt.textPart({ text: resourceLinkText(block) }))
1553
+ break
1554
+ }
1555
+ case "resource": {
1556
+ parts.push(
1557
+ "text" in block.resource
1558
+ ? Prompt.textPart({ text: block.resource.text })
1559
+ : Prompt.filePart({
1560
+ mediaType: block.resource.mimeType ?? "application/octet-stream",
1561
+ data: block.resource.blob
1562
+ })
1563
+ )
1564
+ break
1565
+ }
1566
+ }
1567
+ }
1568
+ if (parts.length === 0 && result.structuredContent !== undefined) {
1569
+ parts.push(Prompt.textPart({ text: JSON.stringify(result.structuredContent) }))
1570
+ }
1571
+ return parts
1572
+ }
1573
+
1574
+ const resourceLinkText = (link: McpSchema.ResourceLink) =>
1575
+ `${link.title ?? link.name}: ${link.uri}${link.description === undefined ? "" : `\n${link.description}`}`
1576
+
1178
1577
  const toText = (result: McpSchema.CallToolResult): Effect.Effect<string, McpClientError> => {
1179
1578
  const unsupported = (content: string) =>
1180
1579
  Effect.fail(
@@ -1188,9 +1587,7 @@ const toText = (result: McpSchema.CallToolResult): Effect.Effect<string, McpClie
1188
1587
  break
1189
1588
  }
1190
1589
  case "resource_link": {
1191
- parts.push(
1192
- `${block.title ?? block.name}: ${block.uri}${block.description === undefined ? "" : `\n${block.description}`}`
1193
- )
1590
+ parts.push(resourceLinkText(block))
1194
1591
  break
1195
1592
  }
1196
1593
  case "resource": {