@classytic/arc 2.41.1 → 2.44.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 (248) hide show
  1. package/dist/{EventTransport-CbUKDzZZ.d.mts → EventTransport-CiicKz2Z.d.mts} +1 -1
  2. package/dist/{EventTransport-DSqD_Xsg.mjs → EventTransport-DZWDsaPa.mjs} +22 -17
  3. package/dist/{HookSystem-CMF94SEA.mjs → HookSystem-yjEgn7Cm.mjs} +2 -2
  4. package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
  5. package/dist/{ResourceRegistry-MGpXoxHZ.mjs → ResourceRegistry-DJxHUpE4.mjs} +44 -13
  6. package/dist/acquire-BozJ-9yt.mjs +35 -0
  7. package/dist/actionPermissions-5N04zBL4.mjs +772 -0
  8. package/dist/audit/index.d.mts +46 -8
  9. package/dist/audit/index.mjs +48 -26
  10. package/dist/auth/audit.d.mts +8 -8
  11. package/dist/auth/audit.mjs +1 -1
  12. package/dist/auth/index.d.mts +46 -16
  13. package/dist/auth/index.mjs +134 -156
  14. package/dist/auth/redis-session.d.mts +4 -5
  15. package/dist/auth/redis-session.mjs +1 -1
  16. package/dist/{authorizationDecision-DawKGoXb.mjs → authorizationDecision-wX3SA9w_.mjs} +7 -102
  17. package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
  18. package/dist/boundedMap-DWiKW7lP.mjs +57 -0
  19. package/dist/{buildHandler-CvxKOc0m.mjs → buildHandler-Bswu_vNb.mjs} +4 -4
  20. package/dist/cache/index.d.mts +31 -43
  21. package/dist/cache/index.mjs +4 -4
  22. package/dist/{caching-DbCBxlgw.mjs → caching-Drp68DIS.mjs} +3 -3
  23. package/dist/claimed-DYvQoZLv.mjs +8 -0
  24. package/dist/cleanup/index.d.mts +18 -22
  25. package/dist/cleanup/index.mjs +27 -73
  26. package/dist/cli/commands/describe.d.mts +7 -5
  27. package/dist/cli/commands/describe.mjs +7 -4
  28. package/dist/cli/commands/docs.d.mts +2 -3
  29. package/dist/cli/commands/docs.mjs +3 -3
  30. package/dist/cli/commands/doctor.d.mts +2 -3
  31. package/dist/cli/commands/doctor.mjs +1 -1
  32. package/dist/cli/commands/generate.mjs +2 -3
  33. package/dist/cli/commands/init.mjs +2 -3
  34. package/dist/cli/commands/introspect.d.mts +2 -3
  35. package/dist/cli/commands/introspect.mjs +2 -2
  36. package/dist/cli/index.mjs +3 -3
  37. package/dist/{constants-D5Ubn1fe.mjs → constants-B2QHb6u6.mjs} +4 -2
  38. package/dist/context/index.d.mts +11 -14
  39. package/dist/context/index.mjs +18 -38
  40. package/dist/core/index.d.mts +3 -3
  41. package/dist/core/index.mjs +8 -8
  42. package/dist/{core-5lazhva9.mjs → core-COcqo0Cp.mjs} +15 -5
  43. package/dist/{createActionRouter-DNqoaK81.mjs → createActionRouter-zG13rpPJ.mjs} +97 -39
  44. package/dist/{createAggregationRouter-BcbQJf10.mjs → createAggregationRouter-CWRz96Vr.mjs} +11 -8
  45. package/dist/{preloadResources-f3boenVZ.mjs → createApp-BGLYtErt.mjs} +305 -394
  46. package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-DSztofa9.d.mts} +1 -1
  47. package/dist/{defineAggregation-D_eJjm5A.mjs → defineAggregation--3xguLkk.mjs} +1 -1
  48. package/dist/{defineResource-DxkMTjRn.mjs → defineResource-gUp9ug66.mjs} +118 -1024
  49. package/dist/discovery/index.d.mts +6 -7
  50. package/dist/discovery/index.mjs +1 -1
  51. package/dist/docs/index.d.mts +11 -5
  52. package/dist/docs/index.mjs +4 -4
  53. package/dist/{elevation-CkvG0LG2.mjs → elevation-BG4gnMsJ.mjs} +41 -12
  54. package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
  55. package/dist/encryption/index.d.mts +5 -5
  56. package/dist/encryption/index.mjs +3 -3
  57. package/dist/{errorHandler-D1GHKGlV.mjs → errorHandler-COHw3N1b.mjs} +26 -5
  58. package/dist/{errors-BH7YU_9O.mjs → errors-CW4anJBy.mjs} +58 -23
  59. package/dist/{errors-BGdVwP25.d.mts → errors-JYc9vwqE.d.mts} +9 -0
  60. package/dist/{eventPlugin-B52abJ8W.d.mts → eventPlugin-BtmA4-3P.d.mts} +65 -9
  61. package/dist/{eventPlugin-zZY-unhr.mjs → eventPlugin-BweHAVws.mjs} +82 -21
  62. package/dist/{subscribe-helpers-DatDkcuG.mjs → eventTypes-N4-omQFR.mjs} +151 -17
  63. package/dist/events/index.d.mts +43 -18
  64. package/dist/events/index.mjs +49 -53
  65. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  66. package/dist/events/transports/redis-stream-entry.mjs +139 -157
  67. package/dist/events/transports/redis.d.mts +5 -6
  68. package/dist/events/transports/redis.mjs +2 -2
  69. package/dist/factory/index.d.mts +42 -27
  70. package/dist/factory/index.mjs +213 -32
  71. package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DRlCIWE9.d.mts} +11 -25
  72. package/dist/{fields-BHrkoF5y.mjs → fields-A5crhsJb.mjs} +1 -1
  73. package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
  74. package/dist/filter-merge-DGM-GQ1J.mjs +99 -0
  75. package/dist/{generate-BLRf2m3y.mjs → generate-BXKo2Gc8.mjs} +2 -2
  76. package/dist/{headers-BIXWKrzQ.mjs → headers-DKZQKL-Z.mjs} +2 -2
  77. package/dist/health-BYAwA6YS.mjs +22 -0
  78. package/dist/{health-ZRHeDHXH.mjs → health-CrqUp3sz.mjs} +115 -55
  79. package/dist/hooks/index.d.mts +2 -2
  80. package/dist/hooks/index.mjs +2 -3
  81. package/dist/idempotency/index.d.mts +36 -10
  82. package/dist/idempotency/index.mjs +230 -111
  83. package/dist/idempotency/redis.d.mts +1 -1
  84. package/dist/idempotency/redis.mjs +77 -12
  85. package/dist/{index-D4u76eQW.d.mts → index-Beg3aNnK.d.mts} +23 -101
  86. package/dist/{preloadResources-CjS2OfpU.d.mts → index-BkV55WlV.d.mts} +37 -38
  87. package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
  88. package/dist/index-CUJvoyw3.d.mts +510 -0
  89. package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
  90. package/dist/{index-DHbyPo5j.d.mts → index-WH7xgvxS.d.mts} +304 -254
  91. package/dist/index.d.mts +9 -9
  92. package/dist/index.mjs +11 -11
  93. package/dist/{init-CpLkjU18.mjs → init-BPzK5Lnr.mjs} +1 -1
  94. package/dist/integrations/event-gateway.d.mts +11 -7
  95. package/dist/integrations/event-gateway.mjs +4 -4
  96. package/dist/integrations/index.d.mts +3 -3
  97. package/dist/integrations/index.mjs +1 -1
  98. package/dist/integrations/jobs/index.d.mts +1 -1
  99. package/dist/integrations/jobs/index.mjs +18 -5
  100. package/dist/integrations/mcp/index.d.mts +102 -89
  101. package/dist/integrations/mcp/index.mjs +19 -4
  102. package/dist/integrations/mcp/testing.d.mts +8 -9
  103. package/dist/integrations/mcp/testing.mjs +2 -2
  104. package/dist/integrations/streamline.d.mts +85 -144
  105. package/dist/integrations/streamline.mjs +131 -55
  106. package/dist/integrations/webhooks.d.mts +83 -143
  107. package/dist/integrations/webhooks.mjs +36 -80
  108. package/dist/integrations/websocket-pushref-redis.d.mts +5 -6
  109. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  110. package/dist/integrations/websocket-redis.d.mts +5 -6
  111. package/dist/integrations/websocket-redis.mjs +1 -1
  112. package/dist/integrations/websocket.d.mts +3 -3
  113. package/dist/integrations/websocket.mjs +2 -2
  114. package/dist/{interface-DEWNOMza.d.mts → interface-JJzwH4Ro.d.mts} +34 -2
  115. package/dist/{jsonBody-ELR9lfeU.mjs → jsonBody-YIq-vGLv.mjs} +2 -2
  116. package/dist/{keys-cpZCtcIE.mjs → keys-CpoiZ8hU.mjs} +7 -18
  117. package/dist/{loadResources-BPfWiI5s.mjs → loadResources-6jO52-RR.mjs} +3 -3
  118. package/dist/{loadResourcesFromEntry-ixtfL3J2.mjs → loadResourcesFromEntry-MP8xEppu.mjs} +2 -2
  119. package/dist/logger/index.d.mts +9 -17
  120. package/dist/logger/index.mjs +2 -153
  121. package/dist/logger-D4V1zGLI.mjs +160 -0
  122. package/dist/{mcpPlugin-BX5PujI0.mjs → mcpPlugin-srPc7ItN.mjs} +446 -170
  123. package/dist/{memory-Iotz0cCx.mjs → memory-CTa5pOCW.mjs} +36 -34
  124. package/dist/{metrics-NMsmpRmY.mjs → metrics-DsZrvnpB.mjs} +39 -72
  125. package/dist/middleware/index.d.mts +5 -5
  126. package/dist/middleware/index.mjs +3 -3
  127. package/dist/migrations/index.d.mts +13 -13
  128. package/dist/migrations/index.mjs +2 -2
  129. package/dist/{module-DpLJG2_N.mjs → module--L0iBIZz.mjs} +8 -8
  130. package/dist/mongodb-memory-server-CHhYSq5y.mjs +35539 -0
  131. package/dist/mongoose-CYJCLyi-.mjs +67451 -0
  132. package/dist/multiTenant-CAtYlW6H.d.mts +104 -0
  133. package/dist/{multipartBody-DtRWM6_U.mjs → multipartBody-Cc7eZ9cq.mjs} +46 -7
  134. package/dist/node-D8UZv_8c.mjs +41236 -0
  135. package/dist/{openapi-CmFf3yq_.mjs → openapi-CMegfsm8.mjs} +48 -10
  136. package/dist/outbox-admin/index.d.mts +5 -5
  137. package/dist/outbox-admin/index.mjs +6 -18
  138. package/dist/{outbox-module-D2z5WkDY.mjs → outbox-module-Ryk270jd.mjs} +203 -33
  139. package/dist/{outbox-module-BHdUnuuK.d.mts → outbox-module-gZCHoE4H.d.mts} +79 -6
  140. package/dist/permissions/index.d.mts +5 -4
  141. package/dist/permissions/index.mjs +8 -6
  142. package/dist/{permissions-D8TBH44E.mjs → permissions-C5ijIz5Q.mjs} +149 -33
  143. package/dist/{pipe-FMWNheSO.mjs → pipe-D81zMtWs.mjs} +2 -2
  144. package/dist/pipeline/index.d.mts +7 -7
  145. package/dist/pipeline/index.mjs +2 -2
  146. package/dist/plan-CJGMKoui.mjs +565 -0
  147. package/dist/{BaseController-CYEwjyjN.mjs → plugin-DALd9dcF.mjs} +1269 -346
  148. package/dist/plugins/index.d.mts +17 -14
  149. package/dist/plugins/index.mjs +44 -22
  150. package/dist/plugins/response-cache.d.mts +5 -6
  151. package/dist/plugins/response-cache.mjs +8 -14
  152. package/dist/plugins/tracing-entry.mjs +2 -2
  153. package/dist/{pluralize-CXWZIzTv.mjs → pluralize-COUNYo3d.mjs} +1 -1
  154. package/dist/presets/filesUpload.d.mts +8 -8
  155. package/dist/presets/filesUpload.mjs +5 -5
  156. package/dist/presets/index.d.mts +35 -40
  157. package/dist/presets/index.mjs +4 -4
  158. package/dist/presets/multiTenant.d.mts +2 -107
  159. package/dist/presets/multiTenant.mjs +22 -162
  160. package/dist/presets/search.d.mts +9 -10
  161. package/dist/presets/search.mjs +2 -2
  162. package/dist/{presets-ZJSPgmT2.mjs → presets-CONf2Qem.mjs} +99 -107
  163. package/dist/{projection-BzIgzoKZ.mjs → projection-wFoLqdss.mjs} +2 -2
  164. package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
  165. package/dist/{queryCachePlugin-DZHPOm9p.mjs → queryCachePlugin-BATECpyh.mjs} +12 -14
  166. package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
  167. package/dist/{rateLimitKey-BvOvcBDi.mjs → rateLimitKey-DVEZ_UpV.mjs} +1 -1
  168. package/dist/{realtime-8tIQWZz4.mjs → realtime-xyubMKSB.mjs} +25 -21
  169. package/dist/{redis-Bzi1y3F9.d.mts → redis-BDgv9Glw.d.mts} +17 -24
  170. package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
  171. package/dist/redisShapes-CQksY67q.d.mts +55 -0
  172. package/dist/registry/index.d.mts +15 -10
  173. package/dist/registry/index.mjs +4 -4
  174. package/dist/{registry-DNkgGQcK.mjs → registry-Bdzg-hdm.mjs} +13 -10
  175. package/dist/{renewingLease-yF8BdW4b.mjs → renewingLease-BFJxGPuh.mjs} +1 -1
  176. package/dist/{replyHelpers-CNdvMfM4.mjs → replyHelpers-Bnc6lJyy.mjs} +2 -2
  177. package/dist/{requestContext-cDkAUr-P.mjs → requestContext-C10UeFe5.mjs} +1 -1
  178. package/dist/requirement-Bu50QALH.mjs +35 -0
  179. package/dist/requirement-CbiLdQ1n.d.mts +22 -0
  180. package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
  181. package/dist/resourceRateLimit-urWPJxrt.mjs +180 -0
  182. package/dist/rolldown-runtime-MYrLNGnq.mjs +45 -0
  183. package/dist/routeValidator-DHtpzrzk.mjs +70 -0
  184. package/dist/{runtimeCapabilities-CkhS6jn6.mjs → runtimeCapabilities-DIMKMV2G.mjs} +1 -1
  185. package/dist/{schedules-j6uXXAeJ.mjs → schedules-CKTs5ys8.mjs} +46 -37
  186. package/dist/{schemaConverter-BVA7cFRG.mjs → schemaConverter-BVjkQ5DD.mjs} +2 -2
  187. package/dist/{schemaIR-CTvPDoPB.mjs → schemaIR-BA0Hb21Y.mjs} +2 -2
  188. package/dist/schemas/index.d.mts +20 -10
  189. package/dist/schemas/index.mjs +8 -3
  190. package/dist/scim/index.d.mts +13 -13
  191. package/dist/scim/index.mjs +2 -2
  192. package/dist/scope/index.d.mts +4 -4
  193. package/dist/scope/index.mjs +6 -6
  194. package/dist/{scope-BUi1xV1I.mjs → scope-DINaj7Ax.mjs} +4 -4
  195. package/dist/shutdown-LpLMH8T1.mjs +59 -0
  196. package/dist/{simpleEqualityMatcher-C9bzq2gO.mjs → simpleEqualityMatcher-DbwIM1f1.mjs} +1 -1
  197. package/dist/{sse-BeqVKQKu.mjs → sse-BzJFPX_1.mjs} +11 -15
  198. package/dist/{sseStream-Ppzo-WUH.mjs → sseStream-DQChzGTs.mjs} +2 -2
  199. package/dist/{store-helpers-dW71XVWL.mjs → store-helpers-Coj9RhOZ.mjs} +1 -1
  200. package/dist/{streaming-IcRFjWQU.mjs → streaming-JzB5CAga.mjs} +1 -1
  201. package/dist/subscriberIdentity-DeyppIif.mjs +28 -0
  202. package/dist/tenancy-DxV8OuX2.mjs +43 -0
  203. package/dist/tenantPreparation-CBvvAcAj.mjs +67 -0
  204. package/dist/testing/cleanupStoreContract.d.mts +4 -5
  205. package/dist/testing/cleanupStoreContract.mjs +1 -1
  206. package/dist/testing/index.d.mts +215 -197
  207. package/dist/testing/index.mjs +289 -214
  208. package/dist/testing/outboxStoreContract.d.mts +4 -5
  209. package/dist/testing/outboxStoreContract.mjs +137 -2
  210. package/dist/testing/requiredPorts.d.mts +16 -0
  211. package/dist/testing/requiredPorts.mjs +65 -0
  212. package/dist/testing/storageContract.d.mts +4 -5
  213. package/dist/testing/storageContract.mjs +1 -1
  214. package/dist/timing-CntavTlR.mjs +46 -0
  215. package/dist/{transactionContext-CPbdlnVw.mjs → transactionContext-orvnGu4F.mjs} +1 -1
  216. package/dist/{typeGuards-BLjPgVBm.mjs → typeGuards-kEaP_WmO.mjs} +1 -1
  217. package/dist/types/index.d.mts +4 -4
  218. package/dist/types/index.mjs +1 -1
  219. package/dist/types/storage.mjs +1 -1
  220. package/dist/{types-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
  221. package/dist/{types-CWQn9-IC.d.mts → types-D3Ixsh00.d.mts} +22 -9
  222. package/dist/{types-CgenB1gv.mjs → types-fvO6Ovzx.mjs} +4 -4
  223. package/dist/usage/index.d.mts +5 -6
  224. package/dist/usage/index.mjs +5 -5
  225. package/dist/{userHelpers-ChRvkuSZ.mjs → userHelpers-C76zD5pa.mjs} +1 -1
  226. package/dist/utils/index.d.mts +125 -264
  227. package/dist/utils/index.mjs +13 -12
  228. package/dist/{utils-CxcctrAh.mjs → utils-BpkVZ8rB.mjs} +315 -404
  229. package/dist/{validate-DrYWnOzI.mjs → validate-CWp-lsrm.mjs} +6 -14
  230. package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
  231. package/dist/{versioning-DKkT4hNJ.mjs → versioning-CHhL9P3o.mjs} +4 -4
  232. package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
  233. package/dist/{workScope-CxKqoIFn.mjs → workScope-n9a7d_lZ.mjs} +2 -2
  234. package/package.json +57 -59
  235. package/skills/arc/SKILL.md +13 -4
  236. package/skills/arc/references/auth.md +8 -5
  237. package/skills/arc/references/integrations.md +1 -1
  238. package/skills/arc/references/mcp.md +8 -6
  239. package/skills/arc/references/multi-tenancy.md +19 -14
  240. package/skills/arc/references/production.md +27 -10
  241. package/dist/actionPermissions-BoPGQ5-4.mjs +0 -350
  242. package/dist/index-CFfLKMH4.d.mts +0 -290
  243. package/dist/interface-YzIbF7Ii.mjs +0 -17
  244. package/dist/redis-stream-BPKscsTB.d.mts +0 -260
  245. package/dist/registerRoute-Bsnk6NiZ.mjs +0 -364
  246. package/dist/rolldown-runtime-eysyzFAF.mjs +0 -15
  247. package/dist/sync/changelog.d.mts +0 -3
  248. package/dist/sync/changelog.mjs +0 -3
@@ -1,21 +1,29 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
- import { r as evaluatePermissionDecision } from "./authorizationDecision-DawKGoXb.mjs";
3
- import { p as isArcError, t as ArcError } from "./errors-BH7YU_9O.mjs";
4
- import { n as requireSingleHeaderValue } from "./headers-BIXWKrzQ.mjs";
5
- import { b as isElevated, c as getOrgId, g as getUserId, x as isMember } from "./types-CgenB1gv.mjs";
6
- import { t as BaseController } from "./BaseController-CYEwjyjN.mjs";
7
- import { a as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-BHrkoF5y.mjs";
8
- import { t as buildRequestScopeProjection } from "./projection-BzIgzoKZ.mjs";
9
- import { i as resolvePipelineSteps, t as resolveActionPermission } from "./actionPermissions-BoPGQ5-4.mjs";
10
- import { t as executePipeline } from "./pipe-FMWNheSO.mjs";
11
- import { t as pluralize } from "./pluralize-CXWZIzTv.mjs";
12
- import { t as createIdempotencyResult } from "./interface-YzIbF7Ii.mjs";
13
- import { r as shouldRejectAdditionalProperties, t as normalizeSchemaIR } from "./schemaIR-CTvPDoPB.mjs";
14
- import { n as validateAggregations } from "./validate-DrYWnOzI.mjs";
15
- import { n as executeAggregation } from "./buildHandler-CvxKOc0m.mjs";
16
- import { isHttpError, toErrorContract } from "@classytic/repo-core/errors";
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
+ import { t as arcLog } from "./logger-D4V1zGLI.mjs";
3
+ import { i as NotFoundError, p as isArcError, t as ArcError } from "./errors-CW4anJBy.mjs";
4
+ import { c as BaseController, n as normalizeActionsToRouterConfig, r as resolveActionTargetFence } from "./plugin-DALd9dcF.mjs";
5
+ import { n as requireSingleHeaderValue } from "./headers-DKZQKL-Z.mjs";
6
+ import { b as isElevated, c as getOrgId, g as getUserId, x as isMember } from "./types-fvO6Ovzx.mjs";
7
+ import { a as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-A5crhsJb.mjs";
8
+ import { o as toJsonSchema } from "./schemaConverter-BVjkQ5DD.mjs";
9
+ import { t as buildRequestScopeProjection } from "./projection-wFoLqdss.mjs";
10
+ import { l as resolvePipelineSteps, t as resolveActionPermission } from "./actionPermissions-5N04zBL4.mjs";
11
+ import { a as withoutTenantInjection, r as prepareTenantContext } from "./tenantPreparation-CBvvAcAj.mjs";
12
+ import { r as evaluatePermissionDecision } from "./authorizationDecision-wX3SA9w_.mjs";
13
+ import { n as declareRuntimeCapability } from "./runtimeCapabilities-DIMKMV2G.mjs";
14
+ import { t as executePipeline } from "./pipe-D81zMtWs.mjs";
15
+ import { a as resourceRateLimitWindowMs, i as resolveResourceRateLimiter, t as createMemoryResourceRateLimiter } from "./resourceRateLimit-urWPJxrt.mjs";
16
+ import { n as evictToSize, r as touch, t as assertCapacity } from "./boundedMap-DWiKW7lP.mjs";
17
+ import { t as pluralize } from "./pluralize-COUNYo3d.mjs";
18
+ import { n as createIdempotencyResult, r as operationIdentity, t as resolveIdempotencyTiming } from "./timing-CntavTlR.mjs";
19
+ import { n as acquireIdempotencyKey, r as releaseIdempotencyLock } from "./acquire-BozJ-9yt.mjs";
20
+ import { r as shouldRejectAdditionalProperties, t as normalizeSchemaIR } from "./schemaIR-BA0Hb21Y.mjs";
21
+ import { n as validateAggregations } from "./validate-CWp-lsrm.mjs";
22
+ import { n as executeAggregation } from "./buildHandler-Bswu_vNb.mjs";
23
+ import { ARC_ERROR_CODES, isHttpError, toErrorContract } from "@classytic/repo-core/errors";
17
24
  import { createHash, randomUUID } from "node:crypto";
18
25
  import fp from "fastify-plugin";
26
+ import { IdempotencyError, fingerprintRequest } from "@classytic/repo-core/idempotency";
19
27
  import { z } from "zod";
20
28
  //#region src/integrations/mcp/createMcpServer.ts
21
29
  /**
@@ -222,51 +230,55 @@ function buildRequestContext(input, auth, operation, policyFilters, scopeOverrid
222
230
  },
223
231
  ...extras?.server ? { server: extras.server } : {}
224
232
  };
233
+ const prepared = (ctx) => {
234
+ prepareTenantContext(ctx, extras?.tenantMiddlewares);
235
+ return ctx;
236
+ };
225
237
  switch (operation) {
226
- case "list": return {
238
+ case "list": return prepared({
227
239
  ...base,
228
240
  params: {},
229
241
  query: expandOperatorKeys(input),
230
242
  body: void 0
231
- };
243
+ });
232
244
  case "get": {
233
245
  const { id, ...rest } = input;
234
- return {
246
+ return prepared({
235
247
  ...base,
236
248
  params: { id: String(id ?? "") },
237
249
  query: expandOperatorKeys(rest),
238
250
  body: void 0
239
- };
251
+ });
240
252
  }
241
- case "create": return {
253
+ case "create": return prepared({
242
254
  ...base,
243
255
  params: {},
244
256
  query: {},
245
257
  body: { ...input }
246
- };
258
+ });
247
259
  case "update": {
248
260
  const { id: _id, ...body } = input;
249
- return {
261
+ return prepared({
250
262
  ...base,
251
263
  params: { id: String(_id ?? "") },
252
264
  query: {},
253
265
  body
254
- };
266
+ });
255
267
  }
256
- case "delete": return {
268
+ case "delete": return prepared({
257
269
  ...base,
258
270
  params: { id: String(input.id ?? "") },
259
271
  query: {},
260
272
  body: void 0
261
- };
273
+ });
262
274
  case "action": {
263
275
  const { id: _actionId, ...actionBody } = input;
264
- return {
276
+ return prepared({
265
277
  ...base,
266
278
  params: { id: String(_actionId ?? "") },
267
279
  query: {},
268
280
  body: actionBody
269
- };
281
+ });
270
282
  }
271
283
  }
272
284
  }
@@ -563,36 +575,137 @@ function createMcpController(resource) {
563
575
  });
564
576
  }
565
577
  //#endregion
566
- //#region src/integrations/mcp/invokeController.ts
578
+ //#region src/integrations/mcp/http-parity.ts
567
579
  /**
568
- * @classytic/arc — Non-HTTP controller invocation for MCP / jobs / workflows.
569
- *
570
- * Internal callers (MCP tools, scheduled jobs, workflow steps) need to invoke
571
- * controller methods without going through Fastify's preHandler chain.
572
- * Before this module landed, every call site reinvented the same shim:
573
- * fabricate `params` / `body` / `headers`, project the session into a
574
- * `RequestScope`, run the permission check by hand, translate thrown errors
575
- * to the MCP `CallToolResult` shape. The drift across those copies is what
576
- * the mentora review flagged as the "buildCtx synthetic-context hack."
577
- *
578
- * Two public surfaces, layered so callers reach for the smallest piece that
579
- * does the job:
580
- *
581
- * - {@link invokeController} full pipeline: permission → context →
582
- * dispatch → canonical envelope/error
583
- * conversion. Returns `CallToolResult`.
584
- * - {@link mcpHandlerAdapter} thin try/catch wrapper that turns any
585
- * async function into a `ToolDefinition`
586
- * handler with canonical error shape. Use
587
- * when you want to compose multiple
588
- * controller calls or weave in custom logic
589
- * between them.
590
- *
591
- * Both honor the same {@link buildRequestContext} primitive — re-exported
592
- * from this module so authors who want the synthetic context as data (e.g.
593
- * to pass into `executePipeline`) can grab it without depending on an
594
- * internal subpath.
580
+ * The gates an HTTP route applies that an MCP tool must apply too, because a tool calls the
581
+ * controller or action handler directly and skips the route: idempotent execution, the
582
+ * key-required refusal, the per-caller rate limit and the action audit record.
595
583
  */
584
+ function isIdempotencyStore(value) {
585
+ if (typeof value !== "object" || value === null) return false;
586
+ const s = value;
587
+ return typeof s.get === "function" && typeof s.set === "function" && typeof s.tryLock === "function" && typeof s.unlock === "function";
588
+ }
589
+ /** The wired store, when it is one. */
590
+ function idempotencyStoreOf(store) {
591
+ return isIdempotencyStore(store) ? store : void 0;
592
+ }
593
+ function isFencedStore(store) {
594
+ return typeof store.setIfLocked === "function" && store.supportsFencedCompletion !== false;
595
+ }
596
+ /** Refuse an unsafe standalone store; an explicit migration escape is logged at wiring. */
597
+ function assertMcpIdempotencyFencing(wiring) {
598
+ resolveIdempotencyTiming(wiring?.idempotencyTtlMs, wiring?.idempotencyLockTimeoutMs);
599
+ const store = idempotencyStoreOf(wiring?.idempotencyStore);
600
+ if (!store || isFencedStore(store)) return;
601
+ const message = "[Arc/MCP] Idempotency store must implement atomic setIfLocked completion fencing. Upgrade the store or explicitly allowUnfencedCompletion during migration.";
602
+ if (wiring?.allowUnfencedCompletion !== true) throw new Error(message);
603
+ const logger = wiring.log && typeof wiring.log === "object" ? wiring.log : {};
604
+ const warning = "[Arc/MCP] allowUnfencedCompletion permits an expired worker to overwrite a newer result; migrate the idempotency store to atomic setIfLocked.";
605
+ if (typeof logger.warn === "function") logger.warn.call(wiring.log, warning);
606
+ else arcLog("mcp:idempotency").warn(warning);
607
+ }
608
+ /** Same protocol as the HTTP plugin: cached → replay; locked → 409; else lock → run → record. */
609
+ async function runIdempotentTool(args) {
610
+ const { store, dispatch } = args;
611
+ const { ttlMs, lockTimeoutMs } = resolveIdempotencyTiming(args.idempotencyTtlMs, args.idempotencyLockTimeoutMs);
612
+ if (!isFencedStore(store) && args.allowUnfencedCompletion !== true) return toCallToolError({
613
+ code: ARC_ERROR_CODES.INTERNAL_ERROR,
614
+ message: "Idempotency store requires atomic setIfLocked completion fencing",
615
+ status: 500
616
+ });
617
+ const fullKey = `${args.key}:${operationIdentity(`${args.resourceName}:${args.operation}`, args.scope ?? buildScope(args.session), void 0, "mcp")}`;
618
+ let fingerprint;
619
+ try {
620
+ fingerprint = fingerprintRequest(args.input);
621
+ } catch (error) {
622
+ if (error instanceof IdempotencyError) return toCallToolError({
623
+ code: ARC_ERROR_CODES.IDEMPOTENCY_BODY_UNFINGERPRINTABLE,
624
+ message: error.message,
625
+ status: 400
626
+ });
627
+ throw error;
628
+ }
629
+ const replay = (cached) => {
630
+ if (cached) {
631
+ if (cached.requestFingerprint && cached.requestFingerprint !== fingerprint) return toCallToolError({
632
+ code: ARC_ERROR_CODES.IDEMPOTENCY_KEY_REUSED,
633
+ message: "Idempotency key was already used for a different request body. Use a new key for a new request.",
634
+ status: 422
635
+ });
636
+ return cached.body;
637
+ }
638
+ };
639
+ const lockId = randomUUID();
640
+ const acquisition = await acquireIdempotencyKey(store, fullKey, lockId, lockTimeoutMs);
641
+ if (acquisition.status === "completed") return replay(acquisition.result);
642
+ if (acquisition.status === "locked") return toCallToolError({
643
+ code: ARC_ERROR_CODES.IDEMPOTENCY_CONFLICT,
644
+ message: "A call with this idempotency key is already in progress",
645
+ status: 409
646
+ });
647
+ let fencedCompletion = false;
648
+ try {
649
+ const result = JSON.parse(JSON.stringify(await dispatch()));
650
+ if (!result.isError) {
651
+ const completion = createIdempotencyResult(200, result, {}, ttlMs, fingerprint);
652
+ if (store.setIfLocked && store.supportsFencedCompletion !== false) {
653
+ if (!await store.setIfLocked(fullKey, lockId, completion)) return toCallToolError({
654
+ code: ARC_ERROR_CODES.IDEMPOTENCY_CONFLICT,
655
+ message: "The idempotency completion lease was lost; success could not be recorded",
656
+ status: 409
657
+ });
658
+ fencedCompletion = true;
659
+ } else await store.set(fullKey, completion);
660
+ }
661
+ return result;
662
+ } finally {
663
+ await releaseIdempotencyLock(store, fullKey, lockId, fencedCompletion);
664
+ }
665
+ }
666
+ /** The 400 a keyless call to a key-required route gets over HTTP. */
667
+ function idempotencyKeyRequiredResult(toolName) {
668
+ return toCallToolError({
669
+ code: ARC_ERROR_CODES.IDEMPOTENCY_KEY_REQUIRED,
670
+ message: `'${toolName}' moves money or stock: pass \`_idempotencyKey\` and reuse it on a retry.`,
671
+ status: 400
672
+ });
673
+ }
674
+ function createToolRateLimiter(config, resourceName, limiter = createMemoryResourceRateLimiter()) {
675
+ if (!config) return void 0;
676
+ resourceRateLimitWindowMs(config.timeWindow);
677
+ return async (session, scope) => (await limiter.consume(resourceName, config, { scope: scope ?? buildScope(session) })).allowed;
678
+ }
679
+ function rateLimitedResult(toolName) {
680
+ return toCallToolError({
681
+ code: "arc.rate_limited",
682
+ message: `Too many calls to '${toolName}' — retry later.`,
683
+ status: 429
684
+ });
685
+ }
686
+ /** `audit.custom(resource, id, action, {}, context)` after a successful action, like the route. */
687
+ async function auditToolAction(audit, args) {
688
+ if (typeof audit !== "object" || audit === null) return;
689
+ const logger = audit;
690
+ if (typeof logger.custom !== "function" || logger._noop === true) return;
691
+ const { session } = args;
692
+ const actorId = session?.userId ?? session?.clientId;
693
+ try {
694
+ await logger.custom.call(audit, args.resourceName, args.id, args.action, { channel: "mcp" }, {
695
+ ...actorId !== void 0 ? { actorId } : {},
696
+ ...session?.organizationId !== void 0 ? { organizationId: session.organizationId } : {},
697
+ endpoint: `MCP ${args.toolName}`
698
+ });
699
+ } catch (err) {
700
+ const log = typeof args.log === "object" && args.log !== null ? args.log : void 0;
701
+ if (typeof log?.error === "function") log.error.call(log, {
702
+ err,
703
+ tool: args.toolName
704
+ }, "[arc/mcp] action audit write failed");
705
+ }
706
+ }
707
+ //#endregion
708
+ //#region src/integrations/mcp/invokeController.ts
596
709
  /**
597
710
  * Assemble the {@link McpContextExtras} for a controller invocation from
598
711
  * resource-scoped metadata + app-level wiring. Mirrors what
@@ -601,8 +714,9 @@ function createMcpController(resource) {
601
714
  */
602
715
  function buildContextExtras(options) {
603
716
  const { wiring, resourceName, permissions, fields, schemaOptions, idField } = options;
604
- if (!wiring) return void 0;
717
+ if (!wiring) return { tenantMiddlewares: options.tenantMiddlewares };
605
718
  return {
719
+ tenantMiddlewares: options.tenantMiddlewares,
606
720
  arc: {
607
721
  resourceName,
608
722
  schemaOptions,
@@ -620,23 +734,6 @@ function buildContextExtras(options) {
620
734
  };
621
735
  }
622
736
  /**
623
- * Stable fingerprint of a tool invocation — sorted-key JSON, hashed.
624
- * Scopes an idempotency key to (resource, action, input, caller) so a
625
- * reused key with DIFFERENT input is a fresh execution, matching the HTTP
626
- * plugin's method+url+bodyHash+user fingerprint semantics.
627
- */
628
- function invocationFingerprint(resourceName, actionName, input, session) {
629
- const sortKeys = (value) => {
630
- if (Array.isArray(value)) return value.map(sortKeys);
631
- if (value !== null && typeof value === "object") return Object.fromEntries(Object.keys(value).sort().map((k) => [k, sortKeys(value[k])]));
632
- return value;
633
- };
634
- return `mcp:${resourceName}:${actionName}:${createHash("sha256").update(JSON.stringify(sortKeys(input))).digest("hex").substring(0, 16)}:u=${session?.userId ?? session?.clientId ?? "anon"}`;
635
- }
636
- /** TTLs matching the HTTP idempotency plugin's defaults. */
637
- const MCP_IDEMPOTENCY_TTL_MS = 864e5;
638
- const MCP_IDEMPOTENCY_LOCK_MS = 1e4;
639
- /**
640
737
  * Invoke a controller method through the MCP synthetic-context path.
641
738
  *
642
739
  * Runs the same chain CRUD MCP tools run:
@@ -685,8 +782,9 @@ async function invokeController(controller, op, input, options) {
685
782
  reason: permResult.reason,
686
783
  session
687
784
  });
785
+ const reqCtx = buildRequestContext(input, session, op, permResult?.policy, permResult?.scope, buildContextExtras(options));
786
+ if (options.allow && !await options.allow(session, reqCtx.metadata._scope)) return rateLimitedResult(actionName);
688
787
  const dispatch = async () => {
689
- const reqCtx = buildRequestContext(input, session, op, permResult?.policy, permResult?.scope, buildContextExtras(options));
690
788
  const response = await method(reqCtx);
691
789
  const data = applyMcpReadMasking(response.data, {
692
790
  fields,
@@ -698,25 +796,20 @@ async function invokeController(controller, op, input, options) {
698
796
  data
699
797
  });
700
798
  };
701
- const store = wiring?.idempotencyStore;
702
- if (idempotencyKey && store) {
703
- const fullKey = `${idempotencyKey}:${invocationFingerprint(resourceName, actionName, input, session)}`;
704
- const cached = await store.get(fullKey);
705
- if (cached) return cached.body;
706
- const lockId = randomUUID();
707
- if (!await store.tryLock(fullKey, lockId, MCP_IDEMPOTENCY_LOCK_MS)) return toCallToolError({
708
- code: "arc.idempotency_conflict",
709
- message: "A call with this idempotency key is already in progress",
710
- status: 409
711
- });
712
- try {
713
- const result = await dispatch();
714
- if (!result.isError) await store.set(fullKey, createIdempotencyResult(200, result, {}, MCP_IDEMPOTENCY_TTL_MS));
715
- return result;
716
- } finally {
717
- await store.unlock(fullKey, lockId);
718
- }
719
- }
799
+ const store = idempotencyStoreOf(wiring?.idempotencyStore);
800
+ if (idempotencyKey && store) return await runIdempotentTool({
801
+ store,
802
+ allowUnfencedCompletion: wiring?.allowUnfencedCompletion,
803
+ idempotencyTtlMs: wiring?.idempotencyTtlMs,
804
+ idempotencyLockTimeoutMs: wiring?.idempotencyLockTimeoutMs,
805
+ key: idempotencyKey,
806
+ resourceName,
807
+ operation: actionName,
808
+ input,
809
+ session,
810
+ scope: reqCtx.metadata._scope,
811
+ dispatch
812
+ });
720
813
  return await dispatch();
721
814
  } catch (err) {
722
815
  return toCallToolError(err instanceof Error ? err : new Error(String(err)));
@@ -806,7 +899,9 @@ const MUTATING_CRUD_OPS = /* @__PURE__ */ new Set([
806
899
  ]);
807
900
  function createCrudHandler(op, controller, resourceName, permissions, fields, extras) {
808
901
  const permission = permissions?.[op];
809
- return (input, ctx) => {
902
+ const toolName = extras?.toolName ?? `${op}_${resourceName}`;
903
+ return async (input, ctx) => {
904
+ if (extras?.keyRequired && (typeof input._idempotencyKey !== "string" || input._idempotencyKey === "")) return idempotencyKeyRequiredResult(toolName);
810
905
  let idempotencyKey;
811
906
  let payload = input;
812
907
  if ("_idempotencyKey" in input) {
@@ -816,10 +911,12 @@ function createCrudHandler(op, controller, resourceName, permissions, fields, ex
816
911
  }
817
912
  return invokeController(controller, op, payload, {
818
913
  session: ctx.session,
914
+ allow: extras?.allow,
819
915
  resourceName,
820
916
  permissions: permission,
821
917
  fields,
822
918
  wiring: extras?.wiring,
919
+ tenantMiddlewares: extras?.tenantMiddlewares,
823
920
  schemaOptions: extras?.schemaOptions,
824
921
  idField: extras?.idField,
825
922
  idempotencyKey
@@ -1082,7 +1179,7 @@ var McpAuthCache = class {
1082
1179
  maxEntries;
1083
1180
  constructor(opts) {
1084
1181
  this.ttlMs = opts?.ttlMs ?? DEFAULT_AUTH_CACHE_TTL_MS;
1085
- this.maxEntries = opts?.maxEntries ?? DEFAULT_AUTH_CACHE_MAX;
1182
+ this.maxEntries = assertCapacity("McpAuthCache maxEntries", opts?.maxEntries ?? DEFAULT_AUTH_CACHE_MAX);
1086
1183
  }
1087
1184
  get(key) {
1088
1185
  const entry = this.cache.get(key);
@@ -1097,15 +1194,12 @@ var McpAuthCache = class {
1097
1194
  if (this.cache.size >= this.maxEntries) {
1098
1195
  const now = Date.now();
1099
1196
  for (const [k, v] of this.cache) if (now > v.expires) this.cache.delete(k);
1100
- if (this.cache.size >= this.maxEntries) {
1101
- const firstKey = this.cache.keys().next().value;
1102
- if (firstKey) this.cache.delete(firstKey);
1103
- }
1104
1197
  }
1105
- this.cache.set(key, {
1198
+ touch(this.cache, key, {
1106
1199
  result,
1107
1200
  expires: Date.now() + this.ttlMs
1108
1201
  });
1202
+ evictToSize(this.cache, this.maxEntries);
1109
1203
  }
1110
1204
  };
1111
1205
  /**
@@ -1229,7 +1323,10 @@ function convertActionSchemaToZod(raw) {
1229
1323
  */
1230
1324
  function createActionToolHandler(actionName, handler, permissions, resourceName, _resourcePermissions, rawSchema, idLess = false, fields, extras) {
1231
1325
  const wiring = extras?.wiring;
1326
+ const parity = extras?.parity;
1327
+ const toolName = parity?.toolName ?? `${actionName}_${resourceName}`;
1232
1328
  const contextExtras = wiring ? {
1329
+ tenantMiddlewares: extras?.tenantMiddlewares,
1233
1330
  arc: {
1234
1331
  resourceName,
1235
1332
  schemaOptions: extras?.schemaOptions,
@@ -1244,11 +1341,16 @@ function createActionToolHandler(actionName, handler, permissions, resourceName,
1244
1341
  audit: wiring.audit,
1245
1342
  log: wiring.log
1246
1343
  }
1247
- } : void 0;
1344
+ } : { tenantMiddlewares: extras?.tenantMiddlewares };
1248
1345
  const ir = rawSchema ? normalizeSchemaIR(rawSchema) : void 0;
1249
- const allowedKeys = (ir ? shouldRejectAdditionalProperties(ir) : false) && ir ? /* @__PURE__ */ new Set([...idLess ? [] : ["id"], ...Object.keys(ir.properties)]) : void 0;
1250
- return async (input, ctx) => {
1346
+ const strict = ir ? shouldRejectAdditionalProperties(ir) : false;
1347
+ const allowedBaseKeys = [...idLess ? [] : ["id"], ...parity?.keyRequired ? ["_idempotencyKey"] : []];
1348
+ const allowedKeys = strict && ir ? /* @__PURE__ */ new Set([...allowedBaseKeys, ...Object.keys(ir.properties)]) : void 0;
1349
+ return async (rawInput, ctx) => {
1251
1350
  const session = ctx.session;
1351
+ const { _idempotencyKey: rawKey, ...input } = rawInput;
1352
+ const idempotencyKey = typeof rawKey === "string" && rawKey !== "" ? rawKey : void 0;
1353
+ if (parity?.keyRequired && idempotencyKey === void 0) return idempotencyKeyRequiredResult(toolName);
1252
1354
  if (allowedKeys) {
1253
1355
  const extras = Object.keys(input).filter((k) => !allowedKeys.has(k));
1254
1356
  if (extras.length > 0) return toCallToolError({
@@ -1269,18 +1371,62 @@ function createActionToolHandler(actionName, handler, permissions, resourceName,
1269
1371
  reason: permResult.reason,
1270
1372
  session
1271
1373
  });
1272
- const reqCtx = buildRequestContext({
1273
- ...input,
1274
- action: actionName
1275
- }, session, "action", permResult?.policy, permResult?.scope, contextExtras);
1276
- const id = idLess ? "" : typeof input.id === "string" ? input.id : "";
1277
- const { id: _discardId, ...data } = input;
1278
1374
  try {
1279
- return toCallToolSuccess(applyMcpReadMasking(await handler(id, data, reqCtx), {
1280
- fields,
1375
+ const reqCtx = buildRequestContext({
1376
+ ...input,
1377
+ action: actionName
1378
+ }, session, "action", permResult?.policy, permResult?.scope, contextExtras);
1379
+ const id = idLess ? "" : typeof input.id === "string" ? input.id : "";
1380
+ const { id: _discardId, ...data } = input;
1381
+ let target;
1382
+ if (!idLess && parity?.fence) {
1383
+ const check = await parity.fence(id, reqCtx);
1384
+ if (!check.visible) throw new NotFoundError(resourceName);
1385
+ target = check.doc;
1386
+ }
1387
+ if (parity?.allow && !await parity.allow(session, reqCtx.metadata._scope)) return rateLimitedResult(toolName);
1388
+ const run = async () => {
1389
+ const withTarget = target === void 0 ? reqCtx : {
1390
+ ...reqCtx,
1391
+ arc: {
1392
+ ...reqCtx.arc,
1393
+ actionTarget: target
1394
+ }
1395
+ };
1396
+ const req = idempotencyKey === void 0 ? withTarget : {
1397
+ ...withTarget,
1398
+ idempotencyKey
1399
+ };
1400
+ const result = await handler(id, data, req);
1401
+ if (parity?.audit) await auditToolAction(wiring?.audit, {
1402
+ resourceName,
1403
+ id,
1404
+ action: actionName,
1405
+ toolName,
1406
+ session,
1407
+ log: wiring?.log
1408
+ });
1409
+ return toCallToolSuccess(applyMcpReadMasking(result, {
1410
+ fields,
1411
+ session,
1412
+ scopeOverride: permResult?.scope
1413
+ }));
1414
+ };
1415
+ const store = idempotencyStoreOf(wiring?.idempotencyStore);
1416
+ if (idempotencyKey !== void 0 && store && !parity?.handlerDedup) return await runIdempotentTool({
1417
+ store,
1418
+ allowUnfencedCompletion: wiring?.allowUnfencedCompletion,
1419
+ idempotencyTtlMs: wiring?.idempotencyTtlMs,
1420
+ idempotencyLockTimeoutMs: wiring?.idempotencyLockTimeoutMs,
1421
+ key: idempotencyKey,
1422
+ resourceName,
1423
+ operation: `action.${actionName}`,
1424
+ input,
1281
1425
  session,
1282
- scopeOverride: permResult?.scope
1283
- }));
1426
+ scope: reqCtx.metadata._scope,
1427
+ dispatch: run
1428
+ });
1429
+ return await run();
1284
1430
  } catch (err) {
1285
1431
  return toCallToolError(err instanceof Error ? err : new Error(String(err)));
1286
1432
  }
@@ -1320,7 +1466,8 @@ function buildAggregationTools(args) {
1320
1466
  resourceName,
1321
1467
  repo,
1322
1468
  buildOptionsFromSession,
1323
- fields
1469
+ fields,
1470
+ tenantMiddlewares: args.tenantMiddlewares
1324
1471
  })
1325
1472
  });
1326
1473
  }
@@ -1372,14 +1519,24 @@ function createAggregationToolHandler(args) {
1372
1519
  session: ctx.session
1373
1520
  });
1374
1521
  const filterInput = input.filter ?? {};
1375
- const tenantOptions = buildOptionsFromSession(ctx.session);
1522
+ let prepared;
1523
+ try {
1524
+ prepared = buildRequestContext(filterInput, ctx.session, "list", permResult?.policy, permResult?.scope, { tenantMiddlewares: args.tenantMiddlewares });
1525
+ } catch (error) {
1526
+ return toCallToolError(error instanceof Error ? error : new Error(String(error)));
1527
+ }
1528
+ const meta = prepared.metadata;
1529
+ const tenantOptions = {
1530
+ ...buildOptionsFromSession(ctx.session),
1531
+ ...meta._tenantFields
1532
+ };
1376
1533
  const result = await executeAggregation(normalized, {
1377
1534
  repo,
1378
1535
  buildOptions: () => tenantOptions
1379
1536
  }, {
1380
1537
  query: filterInput,
1381
1538
  tenantOptions,
1382
- policyFilter: permResult?.policy
1539
+ policyFilter: meta._policyFilters
1383
1540
  });
1384
1541
  if (result.status === 200) {
1385
1542
  const body = result.body;
@@ -1677,6 +1834,73 @@ function mapJsonSchemaTypeToArcType(jsonType) {
1677
1834
  }
1678
1835
  }
1679
1836
  //#endregion
1837
+ //#region src/integrations/mcp/route-input-schema.ts
1838
+ /**
1839
+ * Custom-route → MCP tool INPUT schema. The route's own schema is the contract:
1840
+ * an input the REST route would refuse must be refused on MCP, never stripped.
1841
+ *
1842
+ * - A Zod schema that is not a plain object (a union of strict arms) is registered
1843
+ * WHOLE — the MCP SDK validates and advertises a Standard Schema as-is. Flattening
1844
+ * it yields `{}`, and the SDK's `z.object({})` then strips every key the caller sent.
1845
+ * - A flat schema declaring `additionalProperties: false` registers as `z.strictObject`,
1846
+ * so an unknown key is an input error instead of a silently dropped filter. So
1847
+ * does a route with NO schema: a key it was sent could never reach the handler.
1848
+ * - Any other non-object schema (a JSON Schema `anyOf`, or a Zod union beside `:id`
1849
+ * or a second source) throws at tool build: there is no lossless flat form, so
1850
+ * bridge it with `mcpHandler` or opt out with `mcp: false`.
1851
+ */
1852
+ /** A Zod v4 schema (carries the `_zod` marker and the Standard Schema slot). */
1853
+ function isZodTypeSchema(value) {
1854
+ return value !== null && typeof value === "object" && "_zod" in value && "~standard" in value;
1855
+ }
1856
+ function isRecord(value) {
1857
+ return value !== null && typeof value === "object";
1858
+ }
1859
+ /** The JSON Schema HTTP validates this request slot with (`input` io, as the router converts it). */
1860
+ function requestJsonSchema(schema) {
1861
+ return toJsonSchema(schema, "draft-7", "throw", "input");
1862
+ }
1863
+ /** Does this JSON Schema have a single top-level object a flat shape can carry? */
1864
+ function flattensLosslessly(json) {
1865
+ if (!json) return true;
1866
+ if ("anyOf" in json || "oneOf" in json || "allOf" in json) return false;
1867
+ return json.type === "object" || "properties" in json;
1868
+ }
1869
+ function buildRouteInputSchema(route, hasId) {
1870
+ const routeSchema = isRecord(route.schema) ? route.schema : void 0;
1871
+ const sources = [routeSchema?.body, routeSchema?.querystring].filter(isRecord);
1872
+ const whole = [];
1873
+ let unflattenable = 0;
1874
+ for (const source of sources) {
1875
+ if (flattensLosslessly(requestJsonSchema(source))) continue;
1876
+ unflattenable++;
1877
+ if (isZodTypeSchema(source)) whole.push(source);
1878
+ }
1879
+ const [only] = whole;
1880
+ if (unflattenable > 0) {
1881
+ if (only && sources.length === 1 && !hasId) return only;
1882
+ throw new Error(`[Arc/MCP] Route '${route.method} ${route.path}': a non-object input schema (union) is bridged only as a Zod schema that is the route's sole input with no ':id' — anything else loses validation. Declare \`mcpHandler\` for this route or set \`mcp: false\`.`);
1883
+ }
1884
+ const shape = {};
1885
+ if (hasId) shape.id = z.string().describe("Resource ID");
1886
+ let strict = sources.length === 0;
1887
+ for (const source of sources) {
1888
+ if (requestJsonSchema(source)?.additionalProperties === false) strict = true;
1889
+ const ir = normalizeSchemaIR(source);
1890
+ for (const [key, val] of Object.entries(schemaIRToZodShape(ir))) if (!(key in shape)) shape[key] = val;
1891
+ }
1892
+ return strict ? z.strictObject(shape) : shape;
1893
+ }
1894
+ /**
1895
+ * The field shape a tool advertises — the flat record itself, or a whole object
1896
+ * schema's `.shape`. `undefined` for a union, which has no single shape.
1897
+ */
1898
+ function toolInputShape(schema) {
1899
+ if (schema === void 0) return void 0;
1900
+ if (!isZodTypeSchema(schema)) return schema;
1901
+ return schema instanceof z.ZodObject ? schema.shape : void 0;
1902
+ }
1903
+ //#endregion
1680
1904
  //#region src/integrations/mcp/route-tools.ts
1681
1905
  /**
1682
1906
  * Custom-route → MCP tool generation.
@@ -1737,6 +1961,7 @@ function createCustomRouteHandler(route, controller, hasId, options) {
1737
1961
  const { resourceName, operationName, permissions, pipeline, fields, wiring } = options;
1738
1962
  const pipelineSteps = resolvePipelineSteps(pipeline, operationName);
1739
1963
  const contextExtras = wiring ? {
1964
+ tenantMiddlewares: options.tenantMiddlewares,
1740
1965
  arc: {
1741
1966
  resourceName,
1742
1967
  schemaOptions: options.schemaOptions,
@@ -1751,7 +1976,7 @@ function createCustomRouteHandler(route, controller, hasId, options) {
1751
1976
  audit: wiring.audit,
1752
1977
  log: wiring.log
1753
1978
  }
1754
- } : void 0;
1979
+ } : { tenantMiddlewares: options.tenantMiddlewares };
1755
1980
  return async (input, _ctx) => {
1756
1981
  const session = _ctx.session;
1757
1982
  const permResult = await evaluatePermission(permissions, session, resourceName, operationName, input);
@@ -1763,6 +1988,7 @@ function createCustomRouteHandler(route, controller, hasId, options) {
1763
1988
  });
1764
1989
  try {
1765
1990
  const reqCtx = buildRequestContext(input, session, operationKindForRoute(route.method, hasId), permResult?.policy, permResult?.scope, contextExtras);
1991
+ if (options.allow && !await options.allow(session, reqCtx.metadata._scope)) return rateLimitedResult(operationName);
1766
1992
  const emit = (envelope) => {
1767
1993
  const data = applyMcpReadMasking(envelope.data, {
1768
1994
  fields,
@@ -1881,10 +2107,18 @@ function resourceToTools(resource, config = {}) {
1881
2107
  const tools = [];
1882
2108
  const prefix = config.toolNamePrefix;
1883
2109
  const wiring = config.wiring;
2110
+ assertMcpIdempotencyFencing(wiring);
2111
+ const hasIdempotencyStore = idempotencyStoreOf(wiring?.idempotencyStore) !== void 0;
2112
+ const keyRequiredOps = new Set(resource.idempotency?.required ?? []);
2113
+ const rateLimit = resource.rateLimit;
2114
+ const rateLimiter = config.wiring?.rateLimiter ?? createMemoryResourceRateLimiter();
1884
2115
  if (controller) {
1885
2116
  let ops = ALL_CRUD_OPS.filter((op) => !resource.disabledRoutes?.includes(op));
1886
2117
  if (config.operations) ops = ops.filter((op) => config.operations?.includes(op));
1887
2118
  for (const op of ops) {
2119
+ if (!resource.permissions?.[op]) continue;
2120
+ const keyRequired = keyRequiredOps.has(op);
2121
+ if (keyRequired && !hasIdempotencyStore) throw new Error(`[Arc/MCP] Resource '${resource.name}': '${op}' declares idempotency.required but no idempotency store is wired — register idempotencyPlugin before mcpPlugin, or the key would be demanded and never deduplicated.`);
1888
2122
  const name = config.names?.[op] ?? (op === "list" ? `${prefix ? `${prefix}_` : ""}list_${pluralize(resource.name)}` : `${prefix ? `${prefix}_` : ""}${op}_${resource.name}`);
1889
2123
  const defaultDescription = defaultCrudDescription(op, resource.displayName, hasSoftDelete, {
1890
2124
  filterableFields,
@@ -1900,7 +2134,8 @@ function resourceToTools(resource, config = {}) {
1900
2134
  maxLimit,
1901
2135
  adapterBodies
1902
2136
  });
1903
- if (wiring?.idempotencyStore && op !== "list" && op !== "get") inputSchema._idempotencyKey = z.string().optional().describe("Optional idempotency key — retrying with the same key and input replays the first successful result instead of re-executing.");
2137
+ if (keyRequired) inputSchema._idempotencyKey = z.string().min(1).describe("Required idempotency key — reuse it on a retry; the first result replays.");
2138
+ else if (hasIdempotencyStore && op !== "list" && op !== "get") inputSchema._idempotencyKey = z.string().optional().describe("Optional idempotency key — retrying with the same key and input replays the first successful result instead of re-executing.");
1904
2139
  tools.push({
1905
2140
  name,
1906
2141
  description: resolveCrudDescription(config.descriptions?.[op], {
@@ -1913,11 +2148,15 @@ function resourceToTools(resource, config = {}) {
1913
2148
  sortableFields
1914
2149
  }),
1915
2150
  annotations: CRUD_ANNOTATIONS[op],
1916
- inputSchema,
2151
+ inputSchema: op === "list" || op === "get" ? z.strictObject(inputSchema) : inputSchema,
1917
2152
  handler: createCrudHandler(op, controller, resource.name, resource.permissions, resource.fields, {
1918
2153
  wiring,
2154
+ tenantMiddlewares: resource.middlewares?.[op],
1919
2155
  schemaOptions: resource.schemaOptions,
1920
- idField: resource.idField
2156
+ idField: resource.idField,
2157
+ keyRequired,
2158
+ allow: createToolRateLimiter(rateLimit, resource.name, rateLimiter),
2159
+ toolName: name
1921
2160
  }),
1922
2161
  source: `crud:${resource.name}:${op}`
1923
2162
  });
@@ -1941,27 +2180,26 @@ function resourceToTools(resource, config = {}) {
1941
2180
  const hasId = route.path.includes(":id");
1942
2181
  const mcpConfig = typeof route.mcp === "object" && route.mcp !== null ? route.mcp : void 0;
1943
2182
  const toolDescription = mcpConfig?.description ?? route.summary ?? route.description ?? `${opName} on ${resource.displayName}`;
1944
- const toolAnnotations = mcpConfig?.annotations ? { ...mcpConfig.annotations } : { openWorldHint: true };
1945
- const inputShape = {};
1946
- if (hasId) inputShape.id = z.string().describe("Resource ID");
1947
- const routeSchema = route.schema;
1948
- if (routeSchema?.body) {
1949
- const ir = normalizeSchemaIR(routeSchema.body);
1950
- for (const [key, val] of Object.entries(schemaIRToZodShape(ir))) inputShape[key] = val;
1951
- }
1952
- if (routeSchema?.querystring) {
1953
- const ir = normalizeSchemaIR(routeSchema.querystring);
1954
- for (const [key, val] of Object.entries(schemaIRToZodShape(ir))) if (!(key in inputShape)) inputShape[key] = val;
1955
- }
2183
+ const toolAnnotations = mcpConfig?.annotations ? { ...mcpConfig.annotations } : route.method.toUpperCase() === "GET" ? { readOnlyHint: true } : { openWorldHint: true };
2184
+ const inputShape = buildRouteInputSchema(route, hasId);
1956
2185
  const toolName = prefix ? `${prefix}_${opName}_${resource.name}` : `${opName}_${resource.name}`;
1957
2186
  const routeSource = route._presetSource ? `preset:${route._presetSource}:${resource.name}:${opName}` : `route:${resource.name}:${route.method} ${route.path}`;
2187
+ const routePolicy = route.rateLimit !== void 0 ? route.rateLimit : rateLimit;
2188
+ const allowRoute = createToolRateLimiter(routePolicy && typeof routePolicy === "object" ? {
2189
+ ...routePolicy,
2190
+ ...route.rateLimit ? { bucket: `route:${route.method}:${route.path}` } : {}
2191
+ } : routePolicy, resource.name, rateLimiter);
2192
+ const passthrough = mcpHandler ? createMcpHandlerPassthrough(mcpHandler) : void 0;
1958
2193
  tools.push({
1959
2194
  name: toolName,
1960
2195
  description: toolDescription,
1961
2196
  annotations: toolAnnotations,
1962
2197
  inputSchema: inputShape,
1963
2198
  source: routeSource,
1964
- handler: mcpHandler ? createMcpHandlerPassthrough(mcpHandler) : createCustomRouteHandler({
2199
+ handler: passthrough ? async (input, ctx) => {
2200
+ if (allowRoute && !await allowRoute(ctx.session)) return rateLimitedResult(toolName);
2201
+ return passthrough(input, ctx);
2202
+ } : createCustomRouteHandler({
1965
2203
  handler: resolvedRouteHandler,
1966
2204
  operation: route.operation,
1967
2205
  method: route.method,
@@ -1969,48 +2207,70 @@ function resourceToTools(resource, config = {}) {
1969
2207
  }, controller, hasId, {
1970
2208
  resourceName: resource.name,
1971
2209
  operationName: opName,
2210
+ allow: allowRoute,
1972
2211
  permissions: route.permissions,
1973
2212
  pipeline: resource.pipe,
1974
2213
  fields: resource.fields,
1975
2214
  wiring,
2215
+ tenantMiddlewares: route.tenantScope ? resource.middlewares?.tenantScope : void 0,
1976
2216
  schemaOptions: resource.schemaOptions,
1977
2217
  idField: resource.idField
1978
2218
  })
1979
2219
  });
1980
2220
  }
1981
- if (resource.actions) for (const [actionName, entry] of Object.entries(resource.actions)) {
1982
- const def = typeof entry === "function" ? { handler: entry } : entry;
1983
- if (typeof def !== "function" && "mcp" in def && def.mcp === false) continue;
1984
- const mcpCfg = typeof def !== "function" && typeof def.mcp === "object" ? def.mcp : void 0;
1985
- const description = mcpCfg?.description ?? (typeof def !== "function" ? def.description : void 0) ?? `${actionName} action on ${resource.displayName}`;
1986
- const annotations = mcpCfg?.annotations ? { ...mcpCfg.annotations } : { destructiveHint: true };
1987
- const idLess = typeof def !== "function" && def.id === false;
1988
- const inputShape = idLess ? {} : { id: z.string().describe("Resource ID") };
1989
- const rawSchema = typeof def !== "function" ? def.schema : void 0;
1990
- if (rawSchema && typeof rawSchema === "object") {
1991
- const converted = convertActionSchemaToZod(rawSchema);
1992
- for (const [key, val] of Object.entries(converted)) inputShape[key] = val;
2221
+ if (resource.actions) {
2222
+ const actionConfig = normalizeActionsToRouterConfig(resource.actions, resource.actionPermissions, resource.tag, resource.permissions, resource.name, void 0);
2223
+ const fence = resolveActionTargetFence(resource, actionConfig);
2224
+ const handlerScoped = new Set(actionConfig.handlerScopedActionNames);
2225
+ const keyRequiredActions = new Set(actionConfig.idempotentActionNames);
2226
+ const allow = createToolRateLimiter(rateLimit, resource.name, rateLimiter);
2227
+ for (const [actionName, entry] of Object.entries(resource.actions)) {
2228
+ const def = typeof entry === "function" ? { handler: entry } : entry;
2229
+ if (typeof def !== "function" && "mcp" in def && def.mcp === false) continue;
2230
+ const mcpCfg = typeof def !== "function" && typeof def.mcp === "object" ? def.mcp : void 0;
2231
+ const description = mcpCfg?.description ?? (typeof def !== "function" ? def.description : void 0) ?? `${actionName} action on ${resource.displayName}`;
2232
+ const annotations = mcpCfg?.annotations ? { ...mcpCfg.annotations } : { destructiveHint: true };
2233
+ const idLess = typeof def !== "function" && def.id === false;
2234
+ const inputShape = idLess ? {} : { id: z.string().describe("Resource ID") };
2235
+ const rawSchema = typeof def !== "function" ? def.schema : void 0;
2236
+ if (rawSchema && typeof rawSchema === "object") {
2237
+ const converted = convertActionSchemaToZod(rawSchema);
2238
+ for (const [key, val] of Object.entries(converted)) inputShape[key] = val;
2239
+ }
2240
+ const toolName = prefix ? `${prefix}_${actionName}_${resource.name}` : `${actionName}_${resource.name}`;
2241
+ const keyRequired = keyRequiredActions.has(actionName);
2242
+ const handlerDedup = actionConfig.handlerDedupOwners[actionName] !== void 0;
2243
+ if (keyRequired && !handlerDedup && !hasIdempotencyStore) throw new Error(`[Arc/MCP] Resource '${resource.name}': action '${actionName}' declares idempotency.required but no idempotency store is wired — register idempotencyPlugin before mcpPlugin, or the key would be demanded and never deduplicated.`);
2244
+ if (keyRequired) inputShape._idempotencyKey = z.string().min(1).describe("Required idempotency key — reuse it on a retry; the first result replays.");
2245
+ const handler = typeof entry === "function" ? entry : def.handler;
2246
+ const actionPerms = resolveActionPermission({
2247
+ action: entry,
2248
+ resourcePermissions: resource.permissions,
2249
+ resourceActionPermissions: resource.actionPermissions
2250
+ });
2251
+ if (!actionPerms) throw new Error(`[Arc/MCP] Resource '${resource.name}': action '${actionName}' has no permission gate and the resource defines no \`permissions.update\` fallback. Declare one of:\n - \`actions.${actionName}.permissions: <PermissionCheck>\` (per-action)\n - \`actionPermissions: <PermissionCheck>\` (resource-wide)\n - \`permissions.update: <PermissionCheck>\` (inherited by actions)\nUse \`allowPublic()\` if you genuinely want the action unauthenticated.`);
2252
+ tools.push({
2253
+ name: toolName,
2254
+ description: String(description),
2255
+ annotations,
2256
+ inputSchema: inputShape,
2257
+ source: `action:${resource.name}:${actionName}`,
2258
+ handler: createActionToolHandler(actionName, handler, actionPerms, resource.name, resource.permissions, rawSchema, idLess, resource.fields, {
2259
+ wiring,
2260
+ tenantMiddlewares: withoutTenantInjection(resource.middlewares?.tenantScope),
2261
+ schemaOptions: resource.schemaOptions,
2262
+ idField: resource.idField,
2263
+ parity: {
2264
+ toolName,
2265
+ keyRequired,
2266
+ handlerDedup,
2267
+ ...fence && !idLess && !handlerScoped.has(actionName) ? { fence } : {},
2268
+ audit: Boolean(resource.audit),
2269
+ ...allow ? { allow } : {}
2270
+ }
2271
+ })
2272
+ });
1993
2273
  }
1994
- const toolName = prefix ? `${prefix}_${actionName}_${resource.name}` : `${actionName}_${resource.name}`;
1995
- const handler = typeof entry === "function" ? entry : def.handler;
1996
- const actionPerms = resolveActionPermission({
1997
- action: entry,
1998
- resourcePermissions: resource.permissions,
1999
- resourceActionPermissions: resource.actionPermissions
2000
- });
2001
- if (!actionPerms) throw new Error(`[Arc/MCP] Resource '${resource.name}': action '${actionName}' has no permission gate and the resource defines no \`permissions.update\` fallback. Declare one of:\n - \`actions.${actionName}.permissions: <PermissionCheck>\` (per-action)\n - \`actionPermissions: <PermissionCheck>\` (resource-wide)\n - \`permissions.update: <PermissionCheck>\` (inherited by actions)\nUse \`allowPublic()\` if you genuinely want the action unauthenticated.`);
2002
- tools.push({
2003
- name: toolName,
2004
- description: String(description),
2005
- annotations,
2006
- inputSchema: inputShape,
2007
- source: `action:${resource.name}:${actionName}`,
2008
- handler: createActionToolHandler(actionName, handler, actionPerms, resource.name, resource.permissions, rawSchema, idLess, resource.fields, {
2009
- wiring,
2010
- schemaOptions: resource.schemaOptions,
2011
- idField: resource.idField
2012
- })
2013
- });
2014
2274
  }
2015
2275
  if (resource.aggregations && Object.keys(resource.aggregations).length > 0) {
2016
2276
  const repoForAgg = resource.controller?.repository;
@@ -2036,6 +2296,7 @@ function resourceToTools(resource, config = {}) {
2036
2296
  schemaOptions: resource.schemaOptions,
2037
2297
  repo: repoForAgg,
2038
2298
  buildOptionsFromSession,
2299
+ tenantMiddlewares: resource.middlewares?.aggregations,
2039
2300
  prefix,
2040
2301
  fields: resource.fields
2041
2302
  }));
@@ -2247,7 +2508,14 @@ const mcpPluginImpl = async (fastify, options) => {
2247
2508
  exclude: options.exclude
2248
2509
  });
2249
2510
  const fastifyAny = fastify;
2511
+ const rateLimiter = resolveResourceRateLimiter(fastify) ?? createMemoryResourceRateLimiter();
2512
+ if (enabledResources.some((resource) => resource.rateLimit)) declareRuntimeCapability(fastify, {
2513
+ subsystem: "mcp.resource-rate-limit",
2514
+ durability: rateLimiter.durability,
2515
+ detail: "REST and MCP resource budgets require a shared rate-limit backend across replicas."
2516
+ });
2250
2517
  const wiring = {
2518
+ rateLimiter,
2251
2519
  get hooks() {
2252
2520
  return fastifyAny.arc?.hooks;
2253
2521
  },
@@ -2260,6 +2528,15 @@ const mcpPluginImpl = async (fastify, options) => {
2260
2528
  get log() {
2261
2529
  return fastify.log;
2262
2530
  },
2531
+ get allowUnfencedCompletion() {
2532
+ return fastifyAny.idempotency?.allowUnfencedCompletion;
2533
+ },
2534
+ get idempotencyTtlMs() {
2535
+ return fastifyAny.idempotency?.ttlMs;
2536
+ },
2537
+ get idempotencyLockTimeoutMs() {
2538
+ return fastifyAny.idempotency?.lockTimeoutMs;
2539
+ },
2263
2540
  get idempotencyStore() {
2264
2541
  return fastifyAny.idempotency?.store;
2265
2542
  }
@@ -2526,11 +2803,10 @@ function registerStatefulRoutes(fastify, prefix, options, cache, createServer, T
2526
2803
  });
2527
2804
  }
2528
2805
  /**
2529
- * Resolve `expose` / `include` / `exclude` into the filtered resource list
2530
- * `mcpPlugin` should surface. Three mutually exclusive intents:
2806
+ * Resolve `expose` / `exclude` into the filtered resource list `mcpPlugin`
2807
+ * should surface. Two mutually exclusive intents:
2531
2808
  *
2532
2809
  * - `expose`: default-deny allowlist (preferred — new resources auto-deny).
2533
- * - `include`: legacy alias for `expose`, kept for back-compat.
2534
2810
  * - `exclude`: default-allow opt-out (drift-prone — new resources auto-leak).
2535
2811
  *
2536
2812
  * Conflicting combinations throw with an actionable message so a host that
@@ -2564,4 +2840,4 @@ const mcpPlugin = fp(mcpPluginImpl, {
2564
2840
  fastify: "5.x"
2565
2841
  });
2566
2842
  //#endregion
2567
- export { defaultCrudDescription as a, mcpHandlerAdapter as c, toCallToolResult as d, toCallToolSuccess as f, resolveToolCollisions as h, fieldRulesToZod as i, permissionDeniedResult as l, createMcpServer as m, mcpPlugin as n, resolveCrudDescription as o, buildRequestContext as p, resourceToTools as r, invokeController as s, filterResourcesForMcp as t, toCallToolError as u };
2843
+ export { resolveToolCollisions as _, toolInputShape as a, resolveCrudDescription as c, permissionDeniedResult as d, toCallToolError as f, createMcpServer as g, buildRequestContext as h, isZodTypeSchema as i, invokeController as l, toCallToolSuccess as m, mcpPlugin as n, fieldRulesToZod as o, toCallToolResult as p, resourceToTools as r, defaultCrudDescription as s, filterResourcesForMcp as t, mcpHandlerAdapter as u };