permdock 0.3.0 → 0.4.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.
- package/dist/a2a/index.d.ts +7 -5
- package/dist/a2a/index.js +14 -21
- package/dist/ai-sdk/index.d.ts +18 -6
- package/dist/ai-sdk/index.js +8 -5
- package/dist/app-data-CV8pqqMi.js +59 -0
- package/dist/approvals/index.d.ts +2 -2
- package/dist/approvals/index.js +3 -3
- package/dist/{approvals-DbYAuNkG.js → approvals-DZL76lKW.js} +35 -19
- package/dist/{approvers-DT4AVyIg.js → approvers-CqPOLD-o.js} +1 -1
- package/dist/{arazzo-BAgw9ap6.js → arazzo-C4BcskbX.js} +6 -5
- package/dist/{arazzo-CKwwqFWz.js → arazzo-CD8d0WLb.js} +2 -2
- package/dist/authzen/index.d.ts +3 -3
- package/dist/authzen/index.js +161 -23
- package/dist/better-auth/index.d.ts +2 -2
- package/dist/better-auth/index.js +6 -19
- package/dist/better-supabase/index.d.ts +112 -87
- package/dist/better-supabase/index.js +257 -156
- package/dist/{bind-C_ob3LKa.d.ts → bind-BzqG8SMb.d.ts} +16 -5
- package/dist/{bind-CuzGytRf.js → bind-kk-uyCwH.js} +14 -6
- package/dist/build-instance-DIIV9DbC.js +51 -0
- package/dist/{canonical-json-DemiK4Vp.js → canonical-json-CFKyFOke.js} +1 -3
- package/dist/{capability-2kYTMWDv.d.ts → capability-CksyCWDH.d.ts} +2 -2
- package/dist/{capability-4qPdO9gk.js → capability-txATtKV4.js} +3 -2
- package/dist/catalog/index.d.ts +1 -1
- package/dist/catalog/index.js +1 -1
- package/dist/catalog-6yWAX6Ps.js +2 -0
- package/dist/{catalog-dFZdWCGN.js → catalog-WHG4kBq7.js} +2 -2
- package/dist/{catalog-doc-DdGm82XT.js → catalog-doc-DWtg0gGR.js} +17 -10
- package/dist/{catalog-DWF5cYqB.js → catalog-tZUyOfOv.js} +4 -4
- package/dist/claude-agent/index.d.ts +4 -4
- package/dist/claude-agent/index.js +4 -5
- package/dist/clerk/index.d.ts +3 -3
- package/dist/clerk/index.js +8 -33
- package/dist/cli/bin.js +1 -1
- package/dist/cli/index.d.ts +3 -3
- package/dist/cli/index.js +3 -3
- package/dist/cloud/index.d.ts +4 -4
- package/dist/cloud/index.js +9 -15
- package/dist/{cloud-CB_ELYuX.js → cloud-C7bSDgX4.js} +2 -2
- package/dist/{cloud-DWLXO2af.js → cloud-PeBN3IEn.js} +5 -5
- package/dist/{collect-DSTE3ml9.js → collect-2SrB7lfk.js} +6 -8
- package/dist/collect-Bw2Jdq1q.js +2 -0
- package/dist/{collect-DClJsheJ.js → collect-CQx4hX4N.js} +3 -3
- package/dist/{commands-DSxCqG9A.js → commands-Cpip4ULx.js} +13 -13
- package/dist/{compact-y6vfu2_f.js → compact-CAf557ZB.js} +11 -1
- package/dist/compile/index.d.ts +1 -1
- package/dist/compile/index.js +1 -1
- package/dist/{compile-Ba4blROR.js → compile-CcEu2ZQ1.js} +6 -5
- package/dist/{compile-BkRyBDBw.d.ts → compile-CtTP7eur.d.ts} +2 -2
- package/dist/{config-BJiM-xAm.js → config-BIjREpyE.js} +4 -4
- package/dist/{config-Dnszbv-6.js → config-DLt6BvV7.js} +2 -2
- package/dist/{config-report-DMvWKUxL.js → config-report--Xb9APJS.js} +5 -6
- package/dist/{connection-CU8MQxRi.js → connection-DUsczpB5.js} +3 -2
- package/dist/constants-DcJABbLW.js +20 -0
- package/dist/{context-Bcy85rGi.js → context-DieZVJXu.js} +16 -5
- package/dist/convex/index.d.ts +4 -4
- package/dist/convex/index.js +14 -16
- package/dist/create-BBO24vj6.js +324 -0
- package/dist/{credential-8P81ff4u.js → credential-kue0qpbm.js} +4 -4
- package/dist/deciding-columns-Sq12mEA6.js +123 -0
- package/dist/delegation-input-FTxMK3ji.js +19 -0
- package/dist/denied-text-DfRk15uw.d.ts +19 -0
- package/dist/{describe-DMYiCjJV.d.ts → describe-C3ldA9os.d.ts} +11 -2
- package/dist/{describe-B_gVZEVi.js → describe-Cgkp7eP0.js} +5 -4
- package/dist/{diff-DBIhz_f2.js → diff-Cnghh_Ip.js} +2 -2
- package/dist/{diff-2VaeChzX.js → diff-rpqoe7Ag.js} +9 -9
- package/dist/{doctor-BwCAMLx_.js → doctor-CuVJvhV_.js} +2 -2
- package/dist/doctor-owlv6ddh.js +2166 -0
- package/dist/drizzle/index.d.ts +2 -2
- package/dist/drizzle/index.js +6 -14
- package/dist/elysia/index.d.ts +4 -4
- package/dist/elysia/index.js +7 -7
- package/dist/{errors-DPdqI50v.d.ts → errors-OD_4c__H.d.ts} +2 -2
- package/dist/{errors-Cz5zn0mW.js → errors-drnfxYtm.js} +3 -3
- package/dist/{evaluations-tz6D9A2G.js → evaluations-C9YJsv3H.js} +45 -7
- package/dist/eve/index.d.ts +5 -5
- package/dist/eve/index.js +5 -5
- package/dist/express/index.d.ts +4 -4
- package/dist/express/index.js +8 -7
- package/dist/fastify/index.d.ts +15 -5
- package/dist/fastify/index.js +9 -29
- package/dist/fields-fwfXN01n.js +1016 -0
- package/dist/{files-DWUN9PzI.js → files-D_JIzUrP.js} +12 -3
- package/dist/{fixtures-ht28Cplr.js → fixtures-h2eG2OZV.js} +5 -6
- package/dist/{from-snapshot-CTLijvdm.js → from-snapshot-7REQZe0H.js} +32 -22
- package/dist/{generate-CH7ThJ14.js → generate-CNU1pIOo.js} +1 -3
- package/dist/grantee-CXY-0C87.js +254 -0
- package/dist/grantee-conditions-MQs5NIuX.js +270 -0
- package/dist/grantee-match-fxYp9Nmt.js +141 -0
- package/dist/{helpers-H_KARDVC.js → helpers-C9YmyBIa.js} +7 -6
- package/dist/hono/index.d.ts +4 -4
- package/dist/hono/index.js +8 -8
- package/dist/hosted-CBHgkzDl.js +655 -0
- package/dist/{http-SO7qGttf.js → http-BBoKefeS.js} +35 -2
- package/dist/{http-BIhmLfCc.js → http-BEbdMZ05.js} +2 -24
- package/dist/{rls-import-BTFVk3jH.js → import-BFIlhetD.js} +86 -80
- package/dist/{openapi-import-cmwetbzw.js → import-UHk1N7vH.js} +6 -7
- package/dist/{index-C8z4RjQV.d.ts → index-BwMe94Xq.d.ts} +1 -1
- package/dist/{index-0nNqHspG.d.ts → index-DgM7w96K.d.ts} +2 -2
- package/dist/{index-C7Tw7MaW.d.ts → index-Dz7didpC.d.ts} +3 -3
- package/dist/{index-BNc7Wer_.d.ts → index-UdVQgq48.d.ts} +105 -38
- package/dist/index.d.ts +15 -13
- package/dist/index.js +22 -22
- package/dist/{instance-options-jARdwfPN.d.ts → instance-options-plkrsAog.d.ts} +1 -1
- package/dist/{json-schema-BsAflUGW.js → json-schema-DBkdUuCO.js} +1 -3
- package/dist/jwt/index.d.ts +4 -4
- package/dist/jwt/index.js +13 -13
- package/dist/{kernel-_RRHmnMY.js → kernel-CB3LrIJM.js} +81 -32
- package/dist/kysely/index.d.ts +2 -2
- package/dist/kysely/index.js +6 -14
- package/dist/{live-options-H2eG8dfJ.js → live-options-BaAFCIiu.js} +16 -3
- package/dist/load-B_shHrmd.js +2 -0
- package/dist/{load-DhWEB9P1.js → load-C3_oTHyO.js} +11 -3
- package/dist/{manifest-iaoBXHKc.js → manifest-BngWKryL.js} +5 -18
- package/dist/{map-claims-Z4QZOiFF.js → map-claims-AVlBaJoa.js} +7 -16
- package/dist/{map-error-pgUWNNtG.js → map-error-BE27PYkE.js} +5 -5
- package/dist/{markers-B0hYLvMD.js → markers-CiwAOZtO.js} +6 -2
- package/dist/{may-use-DndQ5tQ-.js → may-use-BOn_dR_w.js} +4 -3
- package/dist/mcp/index.d.ts +26 -9
- package/dist/mcp/index.js +456 -446
- package/dist/{rls-migrate-CN4TlJTO.js → migrate-BwlB5-Wk.js} +75 -15
- package/dist/nest/index.d.ts +4 -4
- package/dist/nest/index.js +11 -12
- package/dist/next/index.d.ts +31 -8
- package/dist/next/index.js +29 -33
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +1 -1
- package/dist/node/index.d.ts +4 -4
- package/dist/node/index.js +4 -4
- package/dist/ocsf-uYFXGVw2.js +115 -0
- package/dist/openai/index.d.ts +5 -5
- package/dist/openai/index.js +4 -4
- package/dist/openapi/index.d.ts +3 -3
- package/dist/openapi/index.js +1 -1
- package/dist/{openapi-CqqEbHsL.js → openapi-BJdBmYDG.js} +19 -23
- package/dist/{openapi-BqRBvAng.js → openapi-DBzHQbov.js} +2 -5
- package/dist/{openapi-l0xYHkcv.js → openapi-DcQ1Kg40.js} +3 -3
- package/dist/orpc/index.d.ts +5 -5
- package/dist/orpc/index.js +9 -11
- package/dist/otel/index.d.ts +2 -2
- package/dist/otel/index.js +26 -23
- package/dist/output-files-CgIsvdSa.js +140 -0
- package/dist/{rls-ownership-RuFqaqGz.js → ownership-BgnbOJFT.js} +387 -379
- package/dist/{package-root-zGkxO3oN.js → package-root-DtNnpmUd.js} +3 -2
- package/dist/{parse-BuyjxhZV.js → parse-CVFQHk6c.js} +3 -3
- package/dist/{parse-local-manifest-BrMozJCo.js → parse-local-manifest-C-q02Pal.js} +2 -4
- package/dist/{parse-local-manifest-JLnaBH0S.d.ts → parse-local-manifest-DvYUR0QL.d.ts} +1 -1
- package/dist/pdp/index.d.ts +2 -2
- package/dist/pdp/index.js +8 -9
- package/dist/{permdock-CbgvLnuo.js → permdock-L26udHSk.js} +1387 -1220
- package/dist/{rls-fields-74oZPXWL.js → permission-keys-B26jeyUv.js} +1289 -2066
- package/dist/permission-registry-BsblpB-z.js +385 -0
- package/dist/{permissions-DttnqqxC.d.ts → permission-types-Dg39z9Kv.d.ts} +59 -31
- package/dist/{permissions-RJrovw98.js → permissions-CCJ6lmxB.js} +3 -3
- package/dist/{permissions-CDgvKG5L.d.ts → permissions-COH-KRgT.d.ts} +2 -2
- package/dist/{policy-IB6yxpC3.d.ts → policy-types-sp9P1iIV.d.ts} +661 -582
- package/dist/{policy-D2W7uv19.d.ts → policy-ur0BBK_e.d.ts} +2 -2
- package/dist/{powersync-DliZK_-y.js → powersync-BFSbSElQ.js} +2 -2
- package/dist/powersync-DCDgZgfZ.js +2 -0
- package/dist/{powersync-BylawD_U.js → powersync-cZe4kzhL.js} +26 -27
- package/dist/prisma/index.d.ts +1 -1
- package/dist/prisma/index.js +6 -12
- package/dist/{problem-B43ztKIm.js → problem-CnWraVhC.js} +8 -7
- package/dist/{problem-details-_AMOYz-w.js → problem-details-DECUnXAn.js} +2 -2
- package/dist/{problem-details-DX075l9V.d.ts → problem-details-rIIo8B2C.d.ts} +1 -1
- package/dist/{protected-BdnIEFkY.d.ts → protected-B3_P7xuw.d.ts} +9 -6
- package/dist/{protected-utU-SLuy.js → protected-BN7BUdY1.js} +20 -8
- package/dist/{provider-CSQd7m92.js → provider-Bi-0HNL3.js} +23 -10
- package/dist/{provider-DZY281jO.d.ts → provider-kwHC9CZD.d.ts} +1 -1
- package/dist/{provider-store-Cfbvo4DE.js → provider-store-Ctt-81Ni.js} +6 -3
- package/dist/react/index.d.ts +7 -7
- package/dist/react/index.js +5 -5
- package/dist/react/provider-client.d.ts +1 -1
- package/dist/react/provider-client.js +1 -1
- package/dist/react-native/index.d.ts +20 -11
- package/dist/react-native/index.js +33 -17
- package/dist/{refs-KYb4ntaz.js → refs-f3imMjNW.js} +1 -1
- package/dist/relation-walk-BXnlLtg2.js +186 -0
- package/dist/relations-CvmSpG67.js +163 -0
- package/dist/{replay-CYyiLiwO.d.ts → replay-COpmZUfx.d.ts} +1 -1
- package/dist/request-context-BVM7aWNs.js +71 -0
- package/dist/request-context-C2_1lchh.d.ts +5 -0
- package/dist/resource-relations-D1Un3pcz.js +243 -0
- package/dist/retire-BFcxL6un.js +240 -0
- package/dist/{rls-B4ur8nKQ.js → rls-55DQTuf5.js} +14 -2
- package/dist/{rls-DQtdgTHB.js → rls-BEktUaqg.js} +884 -484
- package/dist/{policy-xyWg7QdR.js → role-builder-B4GbKM-i.js} +235 -541
- package/dist/{rpc-DL9Sqvb1.js → rpc-nYC3cR_u.js} +15 -5
- package/dist/{run-DKDbM4Du.js → run-DMcNW_8T.js} +3 -3
- package/dist/{runtime-CB8gQFvS.js → runtime-D_tMXnsq.js} +1 -1
- package/dist/{saas-DhpJjnBA.js → saas-DtaA4JuW.js} +9 -8
- package/dist/{openapi-schema--ARjyfQr.js → schema-Bol6gqXA.js} +3 -5
- package/dist/{schema-D3MnDMxa.js → schema-DjQBcAP9.js} +4 -2
- package/dist/scim/index.d.ts +1 -1
- package/dist/scim/index.js +1 -1
- package/dist/{scim-BFGBBAqR.js → scim-vFR1O5oG.js} +6 -12
- package/dist/{scopes-CVdPrWN-.js → scopes-tvpa34ep.js} +10 -2
- package/dist/server/index.d.ts +18 -8
- package/dist/server/index.js +8 -8
- package/dist/{server-DlZ28Oz5.js → server-ZYEHtNHS.js} +4 -4
- package/dist/{create-B1d519XK.d.ts → server-types-DdqBwZN_.d.ts} +69 -33
- package/dist/shared-1uhI-e-b.js +294 -0
- package/dist/{shared-DRu8VFVM.js → shared-JhMeOsDF.js} +1 -1
- package/dist/{sink-Bd9PSyfq.js → sink-BTtmfPnE.js} +1 -1
- package/dist/{sink-B1fZzIMf.d.ts → sink-CPN4zLfs.d.ts} +1 -1
- package/dist/{skills-Biq5uup3.js → skills-BZdRQkxs.js} +4 -3
- package/dist/{skills-Bx-U0irG.js → skills-Dle4ZoLF.js} +2 -2
- package/dist/{snapshot-Enxd0n-s.js → snapshot-Dc9CHPME.js} +116 -13
- package/dist/{snapshot-cache-D6jLelln.js → snapshot-cache-Cod40D-X.js} +1 -1
- package/dist/{snapshot-cache-B-LxUahj.d.ts → snapshot-cache-J5T7t_vD.d.ts} +1 -1
- package/dist/solid/index.d.ts +22 -22
- package/dist/solid/index.js +40 -10
- package/dist/{sources-4p0-4xQm.js → sources-B1-b-R0t.js} +25 -26
- package/dist/{sources-BdOYLw9j.d.ts → sources-Dljy06zL.d.ts} +6 -2
- package/dist/{sql-xdg5f4Fi.js → sql-t8teWOfo.js} +19 -3
- package/dist/{src-BDwEZWrZ.js → src-Bb7no-hS.js} +194 -123
- package/dist/ssf/index.d.ts +3 -3
- package/dist/ssf/index.js +58 -61
- package/dist/store-9KO4BlkB.d.ts +147 -0
- package/dist/{store-DMAWwAJV.js → store-ILkLUj_o.js} +4 -4
- package/dist/{store-options-ByhZq1jV.js → store-options-CbUHDoSe.js} +16 -7
- package/dist/{stores-BT2pSCub.js → stores-BHXdSbw9.js} +7 -3
- package/dist/stores-Cxz_80g-.d.ts +31 -0
- package/dist/{stream-DDpvynSB.js → stream-BMui98KK.js} +2 -1
- package/dist/{stream-D1RXubw2.d.ts → stream-Dwsg6WwE.d.ts} +2 -2
- package/dist/{subject-settings-BwBmCdTR.js → subject-settings-CkEwrw3W.js} +23 -5
- package/dist/supabase/index.d.ts +4 -4
- package/dist/supabase/index.js +3 -3
- package/dist/supabase/middleware.d.ts +11 -5
- package/dist/supabase/middleware.js +10 -7
- package/dist/{supabase-C0qESOa-.js → supabase-C1TWlnYt.js} +36 -52
- package/dist/{supabase-Boqb9a_Y.js → supabase-Dbqr141s.js} +2 -2
- package/dist/supabase-hook-BULg7Ut_.js +2 -0
- package/dist/{supabase-hook-CSLQ4zJ3.js → supabase-hook-Cva3ikXz.js} +731 -782
- package/dist/{supautils-Cn4R4cJ4.js → supautils-B6HYL9eL.js} +2 -2
- package/dist/svelte/Protected.svelte +9 -2
- package/dist/svelte/index.d.ts +6 -6
- package/dist/svelte/index.js +25 -11
- package/dist/svelte/runtime.d.ts +10 -3
- package/dist/svelte/runtime.js +4 -3
- package/dist/svelte/source.d.ts +6 -6
- package/dist/svelte/source.js +4 -4
- package/dist/{validity-uOp8UpYn.js → tenancy-DshjqY0n.js} +3 -39
- package/dist/terminal/index.d.ts +5 -5
- package/dist/terminal/index.js +18 -34
- package/dist/testing/index.d.ts +89 -77
- package/dist/testing/index.js +1276 -1262
- package/dist/testing/saas/index.d.ts +4 -4
- package/dist/testing/saas/index.js +2 -2
- package/dist/testing/saas/permissions.d.ts +1 -1
- package/dist/testing/saas/permissions.js +1 -1
- package/dist/thenable-QSmvt-vZ.js +65 -0
- package/dist/{token-CIWxJOCh.js → token-Bx9qy30p.js} +1 -1
- package/dist/tool-refusal-CQ7OEia9.js +41 -0
- package/dist/trpc/index.d.ts +5 -5
- package/dist/trpc/index.js +7 -8
- package/dist/{types-DbjRN55U.d.ts → types-7rJVLWe5.d.ts} +82 -4
- package/dist/{types-aqOHTHcp.d.ts → types-Br9IXUBG.d.ts} +1 -1
- package/dist/types-ByUo56un.d.ts +88 -0
- package/dist/{types-Cq_H8N_X.d.ts → types-C0dee0Qf.d.ts} +2 -2
- package/dist/{types-DxiCGmmd.d.ts → types-CIlTojhq.d.ts} +2 -2
- package/dist/{types-QCuYJh4q.d.ts → types-CUcFZfpU.d.ts} +1 -1
- package/dist/{types-C97IskBw.d.ts → types-CqrjB9o6.d.ts} +1 -1
- package/dist/{types-BnIEz1sU.d.ts → types-DHrmBJLH.d.ts} +24 -6
- package/dist/{types-Dnemwd77.d.ts → types-DpWfQdSh.d.ts} +5 -1
- package/dist/{types-Bvc4oWT6.d.ts → types-DvupZE0w.d.ts} +1 -1
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +1 -1
- package/dist/{usage-GTixvDDU.js → usage-4G6t643F.js} +2 -2
- package/dist/{usage-ItS7nLcl.js → usage-CG9nOYZw.js} +7 -7
- package/dist/usage-iFLJ0-rr.js +2 -0
- package/dist/{validation-error-Ba264OaJ.d.ts → validation-error-BSSw0l_8.d.ts} +1 -1
- package/dist/{validation-error-CnjZ4t92.js → validation-error-ZfUHbKvf.js} +1 -1
- package/dist/validity-dVrUEPJt.js +62 -0
- package/dist/{verifier-BxBo6I44.js → verifier-DMr_Zmb_.js} +51 -15
- package/dist/{rls-verify-CVQTHTh5.js → verify-BztUB3GX.js} +668 -239
- package/dist/{version-BvacVTZR.js → version-BEogC-rB.js} +1 -1
- package/dist/{views-Soe7bGSC.js → views-CJsrBS1f.js} +23 -1
- package/dist/{vocabulary-DA8a8zif.d.ts → vocabulary-hhEFLFoL.d.ts} +15 -6
- package/dist/vue/index.d.ts +24 -19
- package/dist/vue/index.js +24 -8
- package/dist/watch-C8TpsAl4.js +2 -0
- package/dist/{watch-DVBJ9cBq.js → watch-zeucB7eF.js} +6 -6
- package/dist/webmcp/index.d.ts +2 -2
- package/dist/webmcp/index.js +13 -37
- package/dist/wrap-Cr93qqdP.d.ts +14 -0
- package/dist/wrap-DWsU_5Bd.js +26 -0
- package/package.json +8 -8
- package/schemas/catalog-v1.json +6 -0
- package/skills/permdock-credentials/SKILL.md +1 -1
- package/skills/permdock-credentials/references/tokens-keys-links.md +7 -2
- package/skills/permdock-data/references/queries-and-rls.md +4 -0
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +1 -0
- package/skills/permdock-wire/SKILL.md +2 -2
- package/skills/permdock-wire/references/adapters.md +9 -1
- package/skills/permdock-wire/references/policy-options.md +10 -1
- package/dist/catalog-CRW5Mx_B.js +0 -2
- package/dist/collect-DSWOk19R.js +0 -2
- package/dist/create-CMwIl-w2.js +0 -253
- package/dist/delegation-D8pUEuce.js +0 -103
- package/dist/doctor-collect-CHk6XWOA.js +0 -665
- package/dist/doctor-igIiM_Hl.js +0 -1194
- package/dist/evaluation-items-CfCCejQd.js +0 -40
- package/dist/fields-BMl1RxoX.js +0 -26
- package/dist/grantee-Cv5Sogfv.js +0 -602
- package/dist/hosted-DJQw46hn.js +0 -266
- package/dist/instance-options-Dhhcnm3y.js +0 -22
- package/dist/load-DwJUafYN.js +0 -2
- package/dist/map-AcxK9o_D.js +0 -147
- package/dist/permissions-DVoF6dMt.js +0 -662
- package/dist/powersync-e_AZrKbg.js +0 -2
- package/dist/protected-xAr24FEC.d.ts +0 -40
- package/dist/relations-cjOL01hZ.js +0 -505
- package/dist/rls-graph-CHwx0V8q.js +0 -435
- package/dist/rls-shared-Cewd_-zI.js +0 -495
- package/dist/store-jOBYpiXC.d.ts +0 -36
- package/dist/stores-C0IMQrGI.d.ts +0 -52
- package/dist/supabase-config-BpPXt3x1.js +0 -472
- package/dist/supabase-hook-6sYLXg_u.js +0 -2
- package/dist/thenable-okc2xDsj.js +0 -10
- package/dist/types-CKNSyIf_.d.ts +0 -52
- package/dist/usage-DorAJiU_.js +0 -2
- package/dist/watch-BwxLTYMZ.js +0 -2
package/dist/mcp/index.js
CHANGED
|
@@ -1,83 +1,28 @@
|
|
|
1
|
-
import { t as compact } from "../compact-
|
|
1
|
+
import { r as isRecord, t as compact } from "../compact-CAf557ZB.js";
|
|
2
2
|
import { n as freezeDeep } from "../freeze-CrU8nQeX.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { i as stepUpOf } from "../problem-details-DECUnXAn.js";
|
|
4
|
+
import { t as describe } from "../describe-Cgkp7eP0.js";
|
|
5
|
+
import { m as annotationsFor } from "../permission-registry-BsblpB-z.js";
|
|
6
|
+
import { a as scopesReaching, i as hasScope, r as challengeScope } from "../permdock-L26udHSk.js";
|
|
7
7
|
import { n as resourceRef } from "../resource-ref-CaS9yVPi.js";
|
|
8
8
|
import { t as anonymousSubject } from "../subject-C6dLxqHU.js";
|
|
9
|
-
import { t as
|
|
10
|
-
import {
|
|
9
|
+
import { t as composeWraps } from "../wrap-DWsU_5Bd.js";
|
|
10
|
+
import { t as mayUse } from "../may-use-BOn_dR_w.js";
|
|
11
|
+
import { l as protectedResourceMetadataUrl, r as bearerChallenge } from "../problem-CnWraVhC.js";
|
|
11
12
|
import { t as clientNameOf } from "../clients-DjU5ABMy.js";
|
|
12
|
-
import { n as mapClaimsToSubject } from "../map-claims-
|
|
13
|
-
import {
|
|
14
|
-
import { n as createAgentKernel } from "../kernel-
|
|
13
|
+
import { n as mapClaimsToSubject } from "../map-claims-AVlBaJoa.js";
|
|
14
|
+
import { r as instanceOptions } from "../build-instance-DIIV9DbC.js";
|
|
15
|
+
import { n as createAgentKernel, r as deniedText } from "../kernel-CB3LrIJM.js";
|
|
15
16
|
import { t as boundedMap } from "../lru-8q4WZjBV.js";
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function mcpActorKind(kind) {
|
|
20
|
-
if (kind === void 0) return DEFAULT_MCP_ACTOR_KIND;
|
|
21
|
-
return typeof kind === "string" && kind !== "" ? kind : void 0;
|
|
22
|
-
}
|
|
23
|
-
function isRecord$1(value) {
|
|
24
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Maps the `AuthInfo` the MCP SDK's bearer verification produced to a
|
|
28
|
-
* `Subject`: the principal from the verified claims in `extra`, the client
|
|
29
|
-
* as an actor of kind `actorKind` (`mcp-client` by default), the granted
|
|
30
|
-
* scopes as the delegation. Never verifies and never throws; anything
|
|
31
|
-
* unusable, including an `actorKind` that is not a non-empty string, is
|
|
32
|
-
* the anonymous subject.
|
|
33
|
-
*/
|
|
34
|
-
function subjectFromMcp(authInfo, options = {}) {
|
|
35
|
-
try {
|
|
36
|
-
const kind = mcpActorKind(options.actorKind);
|
|
37
|
-
if (authInfo === void 0 || !isRecord$1(authInfo.extra) || kind === void 0) return anonymousSubject();
|
|
38
|
-
const claims = compact({
|
|
39
|
-
...authInfo.extra,
|
|
40
|
-
authorization_details: authInfo.extra["authorizationDetails"] ?? authInfo.extra["authorization_details"],
|
|
41
|
-
scope: authInfo.scopes?.join(" "),
|
|
42
|
-
client_id: authInfo.clientId,
|
|
43
|
-
exp: authInfo.expiresAt
|
|
44
|
-
});
|
|
45
|
-
const mapped = mapClaimsToSubject(claims, options);
|
|
46
|
-
if (mapped.invalidClaims || mapped.invalidChain || mapped.subject.principal === null) return anonymousSubject();
|
|
47
|
-
const clientId = authInfo.clientId;
|
|
48
|
-
return freezeDeep(compact({
|
|
49
|
-
...mapped.subject,
|
|
50
|
-
actor: typeof clientId === "string" && clientId !== "" ? compact({
|
|
51
|
-
id: clientId,
|
|
52
|
-
kind,
|
|
53
|
-
client: clientNameOf(options.clients, clientId)
|
|
54
|
-
}) : mapped.subject.actor
|
|
55
|
-
}));
|
|
56
|
-
} catch {
|
|
57
|
-
return anonymousSubject();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
//#endregion
|
|
61
|
-
//#region src/mcp/create.ts
|
|
17
|
+
import { t as delegationFrom } from "../delegation-input-FTxMK3ji.js";
|
|
18
|
+
import { n as deniedToolRefusal, r as toolErrorResult, t as approvalToolRefusal } from "../tool-refusal-CQ7OEia9.js";
|
|
19
|
+
//#region src/mcp/mcp-auth.ts
|
|
62
20
|
/** `_meta` key on `tools/call`, `resources/read` and `prompts/get` carrying an approval token. */
|
|
63
21
|
const APPROVAL_META_KEY = "dev.permdock/approval";
|
|
64
|
-
const SESSIONS_PER_SERVER = 1e3;
|
|
65
22
|
const CLIENT_CAPABILITIES = "io.modelcontextprotocol/clientCapabilities";
|
|
66
23
|
const PROTOCOL_VERSION = "io.modelcontextprotocol/protocolVersion";
|
|
67
24
|
/** The first protocol revision whose list results define `ttlMs` and `cacheScope`. */
|
|
68
25
|
const CACHEABLE_LISTS_SINCE = "2026-07-28";
|
|
69
|
-
/** What a completion re-check still accepts after the handler ran. */
|
|
70
|
-
function stillAllowed(decision, approved) {
|
|
71
|
-
switch (decision.outcome) {
|
|
72
|
-
case "granted": return true;
|
|
73
|
-
case "approval-required": return approved !== void 0 && decision.token === approved;
|
|
74
|
-
case "denied": return decision.denials.length > 0 && decision.denials.every((denial) => denial.reason === "limit");
|
|
75
|
-
default: return decision;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
function isRecord(value) {
|
|
79
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
80
|
-
}
|
|
81
26
|
function contextOf(value) {
|
|
82
27
|
return isRecord(value) ? value : {};
|
|
83
28
|
}
|
|
@@ -94,19 +39,6 @@ function approvalTokenOf(context) {
|
|
|
94
39
|
const fromAuth = context.http?.authInfo?.extra?.["approval"];
|
|
95
40
|
return typeof fromAuth === "string" && fromAuth !== "" ? fromAuth : void 0;
|
|
96
41
|
}
|
|
97
|
-
function authorizationDetailsOf(authInfo) {
|
|
98
|
-
const details = authInfo.extra?.["authorizationDetails"] ?? authInfo.extra?.["authorization_details"];
|
|
99
|
-
return Array.isArray(details) ? details : void 0;
|
|
100
|
-
}
|
|
101
|
-
function delegationOf(authInfo) {
|
|
102
|
-
return compact({
|
|
103
|
-
scopes: authInfo.scopes,
|
|
104
|
-
authorizationDetails: authorizationDetailsOf(authInfo)
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
function hasScope(authInfo, scopes) {
|
|
108
|
-
return scopes.some((scope) => (authInfo.scopes ?? []).includes(scope));
|
|
109
|
-
}
|
|
110
42
|
function resourceMetadataOf(authInfo) {
|
|
111
43
|
if (authInfo.resourceMetadataUrl !== void 0) return authInfo.resourceMetadataUrl;
|
|
112
44
|
const resource = authInfo.resource;
|
|
@@ -129,6 +61,41 @@ function definesCacheScope(context) {
|
|
|
129
61
|
const version = context.mcpReq?.envelope?.[PROTOCOL_VERSION];
|
|
130
62
|
return typeof version === "string" && version >= CACHEABLE_LISTS_SINCE;
|
|
131
63
|
}
|
|
64
|
+
function tenantOf(tenant) {
|
|
65
|
+
if (typeof tenant !== "function") return tenant;
|
|
66
|
+
return (call) => tenant(call.authInfo ?? {});
|
|
67
|
+
}
|
|
68
|
+
function sessionKey(context) {
|
|
69
|
+
return context.sessionId ?? "";
|
|
70
|
+
}
|
|
71
|
+
function declaredScopes(name, value) {
|
|
72
|
+
if (value === void 0) return;
|
|
73
|
+
const scopes = Array.isArray(value) ? value : [];
|
|
74
|
+
const names = scopes.filter((scope) => typeof scope === "string" && scope !== "");
|
|
75
|
+
const [first, ...rest] = names;
|
|
76
|
+
if (first === void 0 || names.length !== scopes.length) throw new TypeError(`permdock/mcp: tool ${name} sets oauthScopes, which must be a non-empty list of scope names.`);
|
|
77
|
+
return Object.freeze([first, ...rest]);
|
|
78
|
+
}
|
|
79
|
+
function challengeFor(own, reaching, challenged) {
|
|
80
|
+
return async (context) => {
|
|
81
|
+
const first = await own?.(context);
|
|
82
|
+
if (first !== void 0) return first;
|
|
83
|
+
const authInfo = context.authInfo;
|
|
84
|
+
if (authInfo === void 0 || hasScope(authInfo.scopes, reaching)) return;
|
|
85
|
+
return { scopes: [challenged] };
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/mcp/refusals.ts
|
|
90
|
+
/** What a completion re-check still accepts after the handler ran. */
|
|
91
|
+
function stillAllowed(decision, approved) {
|
|
92
|
+
switch (decision.outcome) {
|
|
93
|
+
case "granted": return true;
|
|
94
|
+
case "approval-required": return approved !== void 0 && decision.token === approved;
|
|
95
|
+
case "denied": return decision.denials.length > 0 && decision.denials.every((denial) => denial.reason === "limit");
|
|
96
|
+
default: return decision;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
132
99
|
function acceptsUrlElicitation(context, fallback) {
|
|
133
100
|
const fromEnvelope = context.mcpReq?.envelope?.[CLIENT_CAPABILITIES];
|
|
134
101
|
const capabilities = isRecord(fromEnvelope) ? fromEnvelope : fallback?.();
|
|
@@ -160,30 +127,10 @@ function urlElicitation(key, message, url, requestState) {
|
|
|
160
127
|
});
|
|
161
128
|
}
|
|
162
129
|
function deniedRefusal(decision, permission, data) {
|
|
163
|
-
|
|
164
|
-
const resource = resourceRef(permission, data);
|
|
165
|
-
return {
|
|
166
|
-
text: `Denied: ${resource.id === void 0 ? permission.key : `${permission.key} on ${resource.id}`}.${alternatives.length === 0 ? "" : ` You may: ${alternatives.join(", ")}.`}`,
|
|
167
|
-
structured: {
|
|
168
|
-
outcome: "denied",
|
|
169
|
-
permission: permission.key,
|
|
170
|
-
resource,
|
|
171
|
-
denials: wireDenials(decision.denials),
|
|
172
|
-
alternatives,
|
|
173
|
-
detail: describe(decision).detail
|
|
174
|
-
}
|
|
175
|
-
};
|
|
130
|
+
return deniedToolRefusal(decision, permission, resourceRef(permission, data));
|
|
176
131
|
}
|
|
177
132
|
function approvalRefusal(decision, permission, data) {
|
|
178
|
-
return
|
|
179
|
-
text: describe(decision).detail,
|
|
180
|
-
structured: {
|
|
181
|
-
outcome: "approval-required",
|
|
182
|
-
permission: permission.key,
|
|
183
|
-
resource: resourceRef(permission, data),
|
|
184
|
-
token: decision.token
|
|
185
|
-
}
|
|
186
|
-
};
|
|
133
|
+
return approvalToolRefusal(decision, permission, resourceRef(permission, data));
|
|
187
134
|
}
|
|
188
135
|
function plainRefusal(permission, text, extra) {
|
|
189
136
|
return {
|
|
@@ -197,10 +144,6 @@ function plainRefusal(permission, text, extra) {
|
|
|
197
144
|
}
|
|
198
145
|
};
|
|
199
146
|
}
|
|
200
|
-
function tenantOf(tenant) {
|
|
201
|
-
if (typeof tenant !== "function") return tenant;
|
|
202
|
-
return (call) => tenant(call.authInfo ?? {});
|
|
203
|
-
}
|
|
204
147
|
function failedRefusal(permission, failure) {
|
|
205
148
|
switch (failure) {
|
|
206
149
|
case "load-failed": return plainRefusal(permission, `Denied: ${permission.key} failed closed.`, { denials: [{
|
|
@@ -216,388 +159,455 @@ function failedRefusal(permission, failure) {
|
|
|
216
159
|
}
|
|
217
160
|
}
|
|
218
161
|
function toolRefusal(refusal) {
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
isError: true,
|
|
222
|
-
content: [{
|
|
223
|
-
type: "text",
|
|
224
|
-
text: refusal.text
|
|
225
|
-
}],
|
|
226
|
-
structuredContent: refusal.structured
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
function requirePermission(kind, name, config) {
|
|
230
|
-
const permission = isRecord(config) ? config["permission"] : void 0;
|
|
231
|
-
if (!isRecord(permission) || typeof permission["key"] !== "string") throw new TypeError(`permdock/mcp: ${kind} ${name} has no permission; every guarded registration needs one.`);
|
|
232
|
-
return permission;
|
|
233
|
-
}
|
|
234
|
-
function sessionKey(context) {
|
|
235
|
-
return context.sessionId ?? "";
|
|
162
|
+
return refusal.inputRequired ?? toolErrorResult(refusal);
|
|
236
163
|
}
|
|
237
164
|
function throwRefusal(refusal) {
|
|
238
165
|
if (refusal.inputRequired !== void 0) return refusal.inputRequired;
|
|
239
166
|
throw new Error(refusal.text);
|
|
240
167
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
if (
|
|
247
|
-
|
|
168
|
+
/** The MRTR URL elicitation that sends the user to `options.approval.at`, when the client accepts one. */
|
|
169
|
+
async function approvalInput(options, decision, handlerContext, context, capabilities) {
|
|
170
|
+
const at = options.approval?.at;
|
|
171
|
+
if (at === void 0 || !acceptsUrlElicitation(context, capabilities)) return;
|
|
172
|
+
const url = withParams(at, { token: decision.token });
|
|
173
|
+
if (url === void 0) return;
|
|
174
|
+
const requestState = options.requestState === void 0 ? decision.token : await options.requestState.mint(decision.token, handlerContext);
|
|
175
|
+
return urlElicitation("approval", options.approval?.hint ?? describe(decision).detail, url, requestState);
|
|
248
176
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
177
|
+
/** A step-up denial: the RFC 9470 challenge, and a URL elicitation to `stepUpAt` when the client accepts one. */
|
|
178
|
+
function stepUpRefusal(stepUpAt, decision, permission, data, context, capabilities) {
|
|
179
|
+
const base = deniedRefusal(decision, permission, data);
|
|
180
|
+
const { acrValues, maxAge } = stepUpOf(decision);
|
|
181
|
+
const url = stepUpAt === void 0 || !acceptsUrlElicitation(context, capabilities) ? void 0 : withParams(stepUpAt, {
|
|
182
|
+
acr_values: acrValues?.join(" "),
|
|
183
|
+
max_age: maxAge === void 0 ? void 0 : String(maxAge)
|
|
184
|
+
});
|
|
185
|
+
return compact({
|
|
186
|
+
text: base.text,
|
|
187
|
+
structured: compact({
|
|
188
|
+
...base.structured,
|
|
189
|
+
error: "insufficient_user_authentication",
|
|
190
|
+
acr_values: acrValues?.join(" "),
|
|
191
|
+
max_age: maxAge,
|
|
192
|
+
www_authenticate: bearerChallenge(compact({
|
|
193
|
+
error: "insufficient_user_authentication",
|
|
194
|
+
acrValues,
|
|
195
|
+
maxAge
|
|
196
|
+
}))
|
|
197
|
+
}),
|
|
198
|
+
inputRequired: url === void 0 ? void 0 : urlElicitation("step_up", `Sign in again to use ${permission.key}.`, url, void 0)
|
|
199
|
+
});
|
|
269
200
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
subject: (call) => options.subject(call.authInfo ?? {}),
|
|
275
|
-
actor: (call) => {
|
|
276
|
-
const clientId = call.authInfo?.clientId;
|
|
277
|
-
return typeof clientId === "string" && clientId !== "" ? compact({
|
|
278
|
-
id: clientId,
|
|
279
|
-
kind: actorKind,
|
|
280
|
-
client: clientNameOf(options.clients, clientId)
|
|
281
|
-
}) : void 0;
|
|
282
|
-
},
|
|
283
|
-
delegation: (call) => call.authInfo === void 0 ? void 0 : delegationOf(call.authInfo),
|
|
284
|
-
tenant: tenantOf(options.tenant),
|
|
285
|
-
store: options.store,
|
|
286
|
-
adapter: "mcp",
|
|
287
|
-
boundary: "mcp-args",
|
|
288
|
-
wrap: options.otel,
|
|
289
|
-
...instanceOptions(options)
|
|
290
|
-
}));
|
|
291
|
-
const forThisServer = (authInfo) => options.resource === void 0 || sameResource(options.resource, authInfo.resource);
|
|
292
|
-
const reachOf = (permission, declared) => declared === void 0 ? {
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/mcp/call-check.ts
|
|
203
|
+
function reachOf(policy, permission, declared) {
|
|
204
|
+
return declared === void 0 ? {
|
|
293
205
|
scopes: scopesReaching(policy, permission),
|
|
294
206
|
challenge: challengeScope(policy, permission)
|
|
295
207
|
} : {
|
|
296
208
|
scopes: declared,
|
|
297
209
|
challenge: declared[0]
|
|
298
210
|
};
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
211
|
+
}
|
|
212
|
+
function forThisServer(checks, authInfo) {
|
|
213
|
+
return checks.options.resource === void 0 || sameResource(checks.options.resource, authInfo.resource);
|
|
214
|
+
}
|
|
215
|
+
function reachable(checks, authInfo, reach) {
|
|
216
|
+
return authInfo === void 0 ? checks.options.requireAuthInfo !== true : forThisServer(checks, authInfo) && hasScope(authInfo.scopes, reach.scopes);
|
|
217
|
+
}
|
|
218
|
+
/** The refusal a token earns before any decision: none, the wrong resource, or a missing scope. */
|
|
219
|
+
function credentialRefusal(checks, permission, reach, authInfo) {
|
|
220
|
+
if (authInfo === void 0) return checks.options.requireAuthInfo === true ? plainRefusal(permission, "Denied: no verified credentials.", { error: "invalid_token" }) : void 0;
|
|
221
|
+
if (!forThisServer(checks, authInfo)) return plainRefusal(permission, "Denied: the token was not issued for this server.", { error: "invalid_token" });
|
|
222
|
+
if (hasScope(authInfo.scopes, reach.scopes)) return;
|
|
223
|
+
const resourceMetadata = resourceMetadataOf(authInfo);
|
|
224
|
+
const challenged = reach.challenge;
|
|
225
|
+
return plainRefusal(permission, `Denied: ${permission.key} needs the ${challenged} scope.`, compact({
|
|
226
|
+
error: "insufficient_scope",
|
|
227
|
+
scope: challenged,
|
|
228
|
+
resource_metadata: resourceMetadata,
|
|
229
|
+
www_authenticate: bearerChallenge(compact({
|
|
230
|
+
error: "insufficient_scope",
|
|
231
|
+
scopes: [challenged],
|
|
232
|
+
resourceMetadata
|
|
233
|
+
}))
|
|
234
|
+
}));
|
|
235
|
+
}
|
|
236
|
+
/** The completion re-check of a long-running tool: the call must still be allowed, or approved with the same token. */
|
|
237
|
+
async function recheck(checks, binding, call, permission, approved) {
|
|
238
|
+
const rechecked = await checks.kernel.check(binding, void 0, call, { simulate: true });
|
|
239
|
+
if (!rechecked.ok) return {
|
|
240
|
+
ok: false,
|
|
241
|
+
refusal: failedRefusal(permission, rechecked.failure)
|
|
307
242
|
};
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
})
|
|
316
|
-
return compact({
|
|
317
|
-
text: base.text,
|
|
318
|
-
structured: compact({
|
|
319
|
-
...base.structured,
|
|
320
|
-
error: "insufficient_user_authentication",
|
|
321
|
-
acr_values: acrValues?.join(" "),
|
|
322
|
-
max_age: maxAge,
|
|
323
|
-
www_authenticate: bearerChallenge(compact({
|
|
324
|
-
error: "insufficient_user_authentication",
|
|
325
|
-
acrValues,
|
|
326
|
-
maxAge
|
|
327
|
-
}))
|
|
328
|
-
}),
|
|
329
|
-
inputRequired: url === void 0 ? void 0 : urlElicitation("step_up", `Sign in again to use ${permission.key}.`, url, void 0)
|
|
330
|
-
});
|
|
243
|
+
if (stillAllowed(rechecked.raw, approved)) return { ok: true };
|
|
244
|
+
return {
|
|
245
|
+
ok: false,
|
|
246
|
+
refusal: rechecked.raw.outcome === "denied" ? {
|
|
247
|
+
...deniedRefusal(rechecked.raw, permission, rechecked.data),
|
|
248
|
+
decision: rechecked.raw,
|
|
249
|
+
permdock: rechecked.permdock
|
|
250
|
+
} : plainRefusal(permission, `Denied: ${permission.key} was revoked before the call completed.`, {})
|
|
331
251
|
};
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
252
|
+
}
|
|
253
|
+
async function decideCall(checks, permission, reach, load, context, handlerContext, capabilities, completion) {
|
|
254
|
+
const authInfo = context.http?.authInfo;
|
|
255
|
+
const refused = credentialRefusal(checks, permission, reach, authInfo);
|
|
256
|
+
if (refused !== void 0) return {
|
|
257
|
+
ok: false,
|
|
258
|
+
refusal: refused
|
|
259
|
+
};
|
|
260
|
+
const binding = compact({
|
|
261
|
+
permission,
|
|
262
|
+
data: load === void 0 ? void 0 : () => load()
|
|
263
|
+
});
|
|
264
|
+
const call = compact({ authInfo });
|
|
265
|
+
if (completion !== void 0) return recheck(checks, binding, call, permission, completion.approved);
|
|
266
|
+
const checked = await checks.kernel.check(binding, void 0, call, compact({ resumeToken: approvalTokenOf(context) }));
|
|
267
|
+
if (!checked.ok) return {
|
|
268
|
+
ok: false,
|
|
269
|
+
refusal: failedRefusal(permission, checked.failure)
|
|
270
|
+
};
|
|
271
|
+
const { raw, decision, data } = checked;
|
|
272
|
+
switch (decision.outcome) {
|
|
273
|
+
case "granted": return raw.outcome === "approval-required" ? {
|
|
274
|
+
ok: true,
|
|
275
|
+
approved: raw.token
|
|
276
|
+
} : { ok: true };
|
|
277
|
+
case "denied": return {
|
|
339
278
|
ok: false,
|
|
340
|
-
refusal:
|
|
279
|
+
refusal: {
|
|
280
|
+
...decision.denials.some((denial) => denial.reason === "insufficient-user-authentication") ? stepUpRefusal(checks.options.stepUp?.at, decision, permission, data, context, capabilities) : deniedRefusal(decision, permission, data),
|
|
281
|
+
decision,
|
|
282
|
+
permdock: checked.permdock
|
|
283
|
+
}
|
|
341
284
|
};
|
|
342
|
-
|
|
343
|
-
const resourceMetadata = resourceMetadataOf(authInfo);
|
|
344
|
-
const challenged = reach.challenge;
|
|
285
|
+
case "approval-required": try {
|
|
345
286
|
return {
|
|
346
287
|
ok: false,
|
|
347
|
-
refusal:
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
scopes: [challenged],
|
|
354
|
-
resourceMetadata
|
|
355
|
-
}))
|
|
356
|
-
}))
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
const binding = compact({
|
|
360
|
-
permission,
|
|
361
|
-
data: load === void 0 ? void 0 : () => load()
|
|
362
|
-
});
|
|
363
|
-
const call = compact({ authInfo });
|
|
364
|
-
if (completion !== void 0) {
|
|
365
|
-
const rechecked = await kernel.check(binding, void 0, call, { simulate: true });
|
|
366
|
-
if (!rechecked.ok) return {
|
|
367
|
-
ok: false,
|
|
368
|
-
refusal: failedRefusal(permission, rechecked.failure)
|
|
288
|
+
refusal: compact({
|
|
289
|
+
...approvalRefusal(decision, permission, data),
|
|
290
|
+
decision,
|
|
291
|
+
permdock: checked.permdock,
|
|
292
|
+
inputRequired: await approvalInput(checks.options, decision, handlerContext, context, capabilities)
|
|
293
|
+
})
|
|
369
294
|
};
|
|
370
|
-
|
|
295
|
+
} catch {
|
|
371
296
|
return {
|
|
372
297
|
ok: false,
|
|
373
|
-
refusal:
|
|
298
|
+
refusal: failedRefusal(permission, "failed")
|
|
374
299
|
};
|
|
375
300
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
inputRequired: await approvalInput(decision, handlerContext, context, capabilities)
|
|
397
|
-
})
|
|
398
|
-
};
|
|
399
|
-
} catch {
|
|
400
|
-
return {
|
|
401
|
-
ok: false,
|
|
402
|
-
refusal: failedRefusal(permission, "failed")
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
default: return decision;
|
|
301
|
+
default: return decision;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/** `decideCall` with the refusal text replaced by `onDenied`'s. */
|
|
305
|
+
async function checkCall(checks, permission, reach, load, context, handlerContext, capabilities, completion) {
|
|
306
|
+
const checked = await decideCall(checks, permission, reach, load, context, handlerContext, capabilities, completion);
|
|
307
|
+
const { onDenied } = checks.options;
|
|
308
|
+
if (checked.ok || onDenied === void 0) return checked;
|
|
309
|
+
const { refusal } = checked;
|
|
310
|
+
return {
|
|
311
|
+
ok: false,
|
|
312
|
+
refusal: {
|
|
313
|
+
...refusal,
|
|
314
|
+
text: deniedText(onDenied, {
|
|
315
|
+
decision: refusal.decision ?? null,
|
|
316
|
+
permission,
|
|
317
|
+
text: refusal.text
|
|
318
|
+
}, (error) => {
|
|
319
|
+
checks.kernel.report(refusal.permdock, error);
|
|
320
|
+
})
|
|
406
321
|
}
|
|
407
322
|
};
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
for (const entry of guarded) if (mayUse(permdock, entry.permission)) shown.add(entry.key);
|
|
323
|
+
}
|
|
324
|
+
/** The keys a list shows: unguarded ones, and guarded ones the token reaches and the subject may use. */
|
|
325
|
+
async function visible(checks, context, permissionOf, keys, declaredOf = () => void 0) {
|
|
326
|
+
const authInfo = context.http?.authInfo;
|
|
327
|
+
const shown = /* @__PURE__ */ new Set();
|
|
328
|
+
const guarded = [];
|
|
329
|
+
for (const key of keys) {
|
|
330
|
+
const permission = permissionOf(key);
|
|
331
|
+
if (permission === void 0) shown.add(key);
|
|
332
|
+
else if (reachable(checks, authInfo, reachOf(checks.policy, permission, declaredOf(key)))) guarded.push({
|
|
333
|
+
key,
|
|
334
|
+
permission
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
if (guarded.length === 0) return shown;
|
|
338
|
+
let permdock;
|
|
339
|
+
try {
|
|
340
|
+
permdock = await checks.kernel.instance(compact({ authInfo }));
|
|
341
|
+
} catch {
|
|
428
342
|
return shown;
|
|
429
|
-
}
|
|
430
|
-
const
|
|
343
|
+
}
|
|
344
|
+
for (const entry of guarded) if (mayUse(permdock, entry.permission)) shown.add(entry.key);
|
|
345
|
+
return shown;
|
|
346
|
+
}
|
|
347
|
+
/** `handler` behind the call check; a refusal goes to `onRefusal` instead. */
|
|
348
|
+
function guard(checks, permission, load, handler, dataArgs, onRefusal, extra = {}) {
|
|
349
|
+
return async (...params) => {
|
|
431
350
|
const raw = params.at(-1);
|
|
432
351
|
const context = contextOf(raw);
|
|
433
352
|
const loader = load === void 0 ? void 0 : () => load(...dataArgs(params));
|
|
434
|
-
const reach = extra.reach ?? reachOf(permission, void 0);
|
|
435
|
-
const checked = await
|
|
353
|
+
const reach = extra.reach ?? reachOf(checks.policy, permission, void 0);
|
|
354
|
+
const checked = await checkCall(checks, permission, reach, loader, context, raw, extra.capabilities);
|
|
436
355
|
let result;
|
|
437
356
|
if (checked.ok) {
|
|
438
357
|
result = await handler(...params);
|
|
439
358
|
if (extra.longRunning === true) {
|
|
440
|
-
const again = await
|
|
359
|
+
const again = await checkCall(checks, permission, reach, loader, context, raw, extra.capabilities, { approved: checked.approved });
|
|
441
360
|
if (!again.ok) result = onRefusal(again.refusal);
|
|
442
361
|
}
|
|
443
362
|
} else result = onRefusal(checked.refusal);
|
|
444
363
|
await extra.after?.(context);
|
|
445
364
|
return result;
|
|
446
365
|
};
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
if (!isRecord(result) || !Array.isArray(result[field])) return result;
|
|
465
|
-
const context = contextOf(rawContext);
|
|
466
|
-
const entries = result[field];
|
|
467
|
-
const shown = await visible(context, permissionOf, entries.map(keyOf), remember ? toolDeclared : void 0);
|
|
468
|
-
const kept = entries.filter((entry) => shown.has(keyOf(entry)));
|
|
469
|
-
if (remember) lastListed.set(sessionKey(context), [...shown].toSorted().join(","));
|
|
470
|
-
return definesCacheScope(context) ? {
|
|
471
|
-
...result,
|
|
472
|
-
[field]: kept,
|
|
473
|
-
cacheScope: "private"
|
|
474
|
-
} : {
|
|
475
|
-
...result,
|
|
476
|
-
[field]: kept
|
|
477
|
-
};
|
|
478
|
-
};
|
|
479
|
-
const filters = {
|
|
480
|
-
"tools/list": listFilter("tools", (entry) => entry.name, toolPermission, true),
|
|
481
|
-
"prompts/list": listFilter("prompts", (entry) => entry.name, promptPermission, false),
|
|
482
|
-
"resources/list": listFilter("resources", (entry) => entry.uri, resourcePermission, false),
|
|
483
|
-
"resources/templates/list": listFilter("resourceTemplates", (entry) => entry.uriTemplate, templatePermission, false)
|
|
484
|
-
};
|
|
485
|
-
const inner = server.server;
|
|
486
|
-
const capabilities = () => inner.getClientCapabilities?.();
|
|
487
|
-
const set = inner.setRequestHandler.bind(inner);
|
|
488
|
-
inner.setRequestHandler = (method, ...rest) => {
|
|
489
|
-
const filter = Object.hasOwn(filters, method) ? filters[method] : void 0;
|
|
490
|
-
const [handler] = rest;
|
|
491
|
-
if (filter !== void 0 && rest.length === 1 && typeof handler === "function") {
|
|
492
|
-
set(method, async (request, context) => filter(await handler(request, context), context));
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
set(method, ...rest);
|
|
496
|
-
};
|
|
497
|
-
for (const method of Object.keys(filters)) {
|
|
498
|
-
const existing = inner._getRequestHandler?.(method);
|
|
499
|
-
if (existing !== void 0) {
|
|
500
|
-
inner.removeRequestHandler(method);
|
|
501
|
-
inner.setRequestHandler(method, (request, context) => existing(request, context));
|
|
502
|
-
}
|
|
366
|
+
}
|
|
367
|
+
//#endregion
|
|
368
|
+
//#region src/mcp/protect-server.ts
|
|
369
|
+
const SESSIONS_PER_SERVER = 1e3;
|
|
370
|
+
function requirePermission(kind, name, config) {
|
|
371
|
+
const permission = isRecord(config) ? config["permission"] : void 0;
|
|
372
|
+
if (!isRecord(permission) || typeof permission["key"] !== "string") throw new TypeError(`permdock/mcp: ${kind} ${name} has no permission; every guarded registration needs one.`);
|
|
373
|
+
return permission;
|
|
374
|
+
}
|
|
375
|
+
function guardUpdates(registered, wrap, rename, currentName) {
|
|
376
|
+
const update = registered.update.bind(registered);
|
|
377
|
+
registered.update = (updates) => {
|
|
378
|
+
const next = { ...updates };
|
|
379
|
+
if (typeof updates["callback"] === "function") next["callback"] = wrap(updates["callback"]);
|
|
380
|
+
if (rename !== void 0 && currentName !== void 0) {
|
|
381
|
+
const to = updates["name"];
|
|
382
|
+
if (typeof to === "string" || to === null) rename(currentName(), to);
|
|
503
383
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
384
|
+
update(next);
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function protectServer(checks, server, enforcement) {
|
|
388
|
+
const tools = /* @__PURE__ */ new Map();
|
|
389
|
+
const toolScopes = /* @__PURE__ */ new Map();
|
|
390
|
+
const prompts = /* @__PURE__ */ new Map();
|
|
391
|
+
const resources = /* @__PURE__ */ new Map();
|
|
392
|
+
const templates = [];
|
|
393
|
+
const lastListed = boundedMap(SESSIONS_PER_SERVER);
|
|
394
|
+
const toolPermission = (name) => tools.get(name);
|
|
395
|
+
const toolDeclared = (name) => toolScopes.get(name);
|
|
396
|
+
const promptPermission = (name) => prompts.get(name);
|
|
397
|
+
const resourcePermission = (uri) => {
|
|
398
|
+
const direct = resources.get(uri);
|
|
399
|
+
if (direct !== void 0) return direct;
|
|
400
|
+
return templates.find((entry) => entry.template.uriTemplate.match(uri) !== null)?.permission;
|
|
401
|
+
};
|
|
402
|
+
const templatePermission = (uriTemplate) => templates.find((entry) => entry.template.uriTemplate.toString() === uriTemplate)?.permission;
|
|
403
|
+
const listFilter = (field, keyOf, permissionOf, remember) => async (result, rawContext) => {
|
|
404
|
+
if (!isRecord(result) || !Array.isArray(result[field])) return result;
|
|
405
|
+
const context = contextOf(rawContext);
|
|
406
|
+
const entries = result[field];
|
|
407
|
+
const shown = await visible(checks, context, permissionOf, entries.map(keyOf), remember ? toolDeclared : void 0);
|
|
408
|
+
const kept = entries.filter((entry) => shown.has(keyOf(entry)));
|
|
409
|
+
if (remember) lastListed.set(sessionKey(context), [...shown].toSorted().join(","));
|
|
410
|
+
return definesCacheScope(context) ? {
|
|
411
|
+
...result,
|
|
412
|
+
[field]: kept,
|
|
413
|
+
cacheScope: "private"
|
|
414
|
+
} : {
|
|
415
|
+
...result,
|
|
416
|
+
[field]: kept
|
|
514
417
|
};
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
418
|
+
};
|
|
419
|
+
const filters = {
|
|
420
|
+
"tools/list": listFilter("tools", (entry) => entry.name, toolPermission, true),
|
|
421
|
+
"prompts/list": listFilter("prompts", (entry) => entry.name, promptPermission, false),
|
|
422
|
+
"resources/list": listFilter("resources", (entry) => entry.uri, resourcePermission, false),
|
|
423
|
+
"resources/templates/list": listFilter("resourceTemplates", (entry) => entry.uriTemplate, templatePermission, false)
|
|
424
|
+
};
|
|
425
|
+
const inner = server.server;
|
|
426
|
+
const capabilities = () => inner.getClientCapabilities?.();
|
|
427
|
+
const set = inner.setRequestHandler.bind(inner);
|
|
428
|
+
inner.setRequestHandler = (method, ...rest) => {
|
|
429
|
+
const filter = Object.hasOwn(filters, method) ? filters[method] : void 0;
|
|
430
|
+
const [handler] = rest;
|
|
431
|
+
if (filter !== void 0 && rest.length === 1 && typeof handler === "function") {
|
|
432
|
+
set(method, async (request, context) => filter(await handler(request, context), context));
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
set(method, ...rest);
|
|
436
|
+
};
|
|
437
|
+
for (const method of Object.keys(filters)) {
|
|
438
|
+
const existing = inner._getRequestHandler?.(method);
|
|
439
|
+
if (existing !== void 0) {
|
|
440
|
+
inner.removeRequestHandler(method);
|
|
441
|
+
inner.setRequestHandler(method, (request, context) => existing(request, context));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const announce = async (context) => {
|
|
445
|
+
const key = sessionKey(context);
|
|
446
|
+
const before = lastListed.get(key);
|
|
447
|
+
if (before === void 0) return;
|
|
448
|
+
const after = [...await visible(checks, context, toolPermission, [...tools.keys()], toolDeclared)].toSorted().join(",");
|
|
449
|
+
if (after === before) return;
|
|
450
|
+
lastListed.set(key, after);
|
|
451
|
+
try {
|
|
452
|
+
await (context.mcpReq?.notify?.({ method: "notifications/tools/list_changed" }) ?? inner.sendToolListChanged?.());
|
|
453
|
+
} catch {}
|
|
454
|
+
};
|
|
455
|
+
const guardTool = (permission, reach, load, handler, longRunning) => guard(checks, permission, load, handler, (params) => params.length >= 2 ? [params[0]] : [void 0], toolRefusal, {
|
|
456
|
+
after: announce,
|
|
457
|
+
longRunning,
|
|
458
|
+
capabilities,
|
|
459
|
+
reach
|
|
460
|
+
});
|
|
461
|
+
const originalTool = server.registerTool.bind(server);
|
|
462
|
+
const toolPermissionOf = (name, config) => {
|
|
463
|
+
if (enforcement === void 0 || config["permission"] !== void 0) return requirePermission("tool", name, config);
|
|
464
|
+
for (const field of ["data", "longRunning"]) if (config[field] !== void 0) throw new TypeError(`permdock/mcp: tool ${name} sets ${field}, but with enforce: 'procedure' its procedure decides.`);
|
|
465
|
+
return requirePermission("tool", name, { permission: enforcement.permissionFor(name) });
|
|
466
|
+
};
|
|
467
|
+
/** With `enforce: 'procedure'` the handler runs undecided: the procedure it calls decides. */
|
|
468
|
+
const wrapTool = (permission, reach, load, handler, longRunning) => enforcement === void 0 ? guardTool(permission, reach, load, handler, longRunning) : async (...params) => {
|
|
469
|
+
const result = await handler(...params);
|
|
470
|
+
await announce(contextOf(params.at(-1)));
|
|
471
|
+
return result;
|
|
472
|
+
};
|
|
473
|
+
const registerTool = (name, config, handler) => {
|
|
474
|
+
const permission = toolPermissionOf(name, config);
|
|
475
|
+
const { permission: _permission, data, scopeChallenge, longRunning: rawLongRunning, oauthScopes: rawScopes, ...passthrough } = config;
|
|
476
|
+
const load = data;
|
|
477
|
+
const longRunning = rawLongRunning === true;
|
|
478
|
+
const declared = declaredScopes(name, rawScopes === void 0 ? enforcement?.oauthScopesFor?.(name) : rawScopes);
|
|
479
|
+
const reach = reachOf(checks.policy, permission, declared);
|
|
480
|
+
const registered = originalTool(name, {
|
|
481
|
+
...passthrough,
|
|
482
|
+
annotations: {
|
|
483
|
+
...annotationsFor(permission),
|
|
484
|
+
...passthrough["annotations"]
|
|
485
|
+
},
|
|
486
|
+
scopeChallenge: challengeFor(scopeChallenge, reach.scopes, reach.challenge)
|
|
487
|
+
}, wrapTool(permission, reach, load, handler, longRunning));
|
|
488
|
+
tools.set(name, permission);
|
|
489
|
+
if (declared !== void 0) toolScopes.set(name, declared);
|
|
490
|
+
let current = name;
|
|
491
|
+
guardUpdates(registered, (callback) => wrapTool(permission, reach, load, callback, longRunning), (from, to) => {
|
|
492
|
+
tools.delete(from);
|
|
493
|
+
toolScopes.delete(from);
|
|
494
|
+
if (to !== null) {
|
|
495
|
+
tools.set(to, permission);
|
|
496
|
+
if (declared !== void 0) toolScopes.set(to, declared);
|
|
497
|
+
current = to;
|
|
498
|
+
}
|
|
499
|
+
}, () => current);
|
|
500
|
+
return registered;
|
|
501
|
+
};
|
|
502
|
+
const originalPrompt = server.registerPrompt.bind(server);
|
|
503
|
+
const registerPrompt = (name, config, handler) => {
|
|
504
|
+
const permission = requirePermission("prompt", name, config);
|
|
505
|
+
const { permission: _permission, data, scopeChallenge, ...passthrough } = config;
|
|
506
|
+
const load = data;
|
|
507
|
+
const wrap = (callback) => guard(checks, permission, load, callback, (params) => params.length >= 2 ? [params[0]] : [void 0], throwRefusal, { capabilities });
|
|
508
|
+
const registered = originalPrompt(name, {
|
|
509
|
+
...passthrough,
|
|
510
|
+
scopeChallenge: challengeFor(scopeChallenge, scopesReaching(checks.policy, permission), challengeScope(checks.policy, permission))
|
|
511
|
+
}, wrap(handler));
|
|
512
|
+
prompts.set(name, permission);
|
|
513
|
+
guardUpdates(registered, wrap);
|
|
514
|
+
return registered;
|
|
515
|
+
};
|
|
516
|
+
const originalResource = server.registerResource.bind(server);
|
|
517
|
+
const registerResource = (name, uriOrTemplate, config, read) => {
|
|
518
|
+
const permission = requirePermission("resource", name, config);
|
|
519
|
+
const { permission: _permission, data, scopeChallenge, ...passthrough } = config;
|
|
520
|
+
const load = data;
|
|
521
|
+
const wrap = (callback) => guard(checks, permission, load, callback, (params) => params.slice(0, -1), throwRefusal, { capabilities });
|
|
522
|
+
const registered = originalResource(name, uriOrTemplate, {
|
|
523
|
+
...passthrough,
|
|
524
|
+
scopeChallenge: challengeFor(scopeChallenge, scopesReaching(checks.policy, permission), challengeScope(checks.policy, permission))
|
|
525
|
+
}, wrap(read));
|
|
526
|
+
if (typeof uriOrTemplate === "string") resources.set(uriOrTemplate, permission);
|
|
527
|
+
else templates.push({
|
|
528
|
+
template: uriOrTemplate,
|
|
529
|
+
permission
|
|
520
530
|
});
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
531
|
+
guardUpdates(registered, wrap);
|
|
532
|
+
return registered;
|
|
533
|
+
};
|
|
534
|
+
const guarded = server;
|
|
535
|
+
guarded["registerTool"] = registerTool;
|
|
536
|
+
guarded["registerPrompt"] = registerPrompt;
|
|
537
|
+
guarded["registerResource"] = registerResource;
|
|
538
|
+
return server;
|
|
539
|
+
}
|
|
540
|
+
//#endregion
|
|
541
|
+
//#region src/mcp/subject.ts
|
|
542
|
+
const DEFAULT_MCP_ACTOR_KIND = "mcp-client";
|
|
543
|
+
/** `kind`, or the default when absent; `undefined` for a kind that is not a non-empty string. */
|
|
544
|
+
function mcpActorKind(kind) {
|
|
545
|
+
if (kind === void 0) return DEFAULT_MCP_ACTOR_KIND;
|
|
546
|
+
return typeof kind === "string" && kind !== "" ? kind : void 0;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Maps the `AuthInfo` the MCP SDK's bearer verification produced to a
|
|
550
|
+
* `Subject`: the principal from the verified claims in `extra`, the client
|
|
551
|
+
* as an actor of kind `actorKind` (`mcp-client` by default), the granted
|
|
552
|
+
* scopes as the delegation. Never verifies and never throws; anything
|
|
553
|
+
* unusable, including an `actorKind` that is not a non-empty string, is
|
|
554
|
+
* the anonymous subject.
|
|
555
|
+
*/
|
|
556
|
+
function subjectFromMcp(authInfo, options = {}) {
|
|
557
|
+
try {
|
|
558
|
+
const kind = mcpActorKind(options.actorKind);
|
|
559
|
+
if (authInfo === void 0 || !isRecord(authInfo.extra) || kind === void 0) return anonymousSubject();
|
|
560
|
+
const claims = compact({
|
|
561
|
+
...authInfo.extra,
|
|
562
|
+
authorization_details: authInfo.extra["authorizationDetails"] ?? authInfo.extra["authorization_details"],
|
|
563
|
+
scope: authInfo.scopes?.join(" "),
|
|
564
|
+
client_id: authInfo.clientId,
|
|
565
|
+
exp: authInfo.expiresAt
|
|
566
|
+
});
|
|
567
|
+
const mapped = mapClaimsToSubject(claims, options);
|
|
568
|
+
if (mapped.invalidClaims || mapped.invalidChain || mapped.subject.principal === null) return anonymousSubject();
|
|
569
|
+
const clientId = authInfo.clientId;
|
|
570
|
+
return freezeDeep(compact({
|
|
571
|
+
...mapped.subject,
|
|
572
|
+
actor: typeof clientId === "string" && clientId !== "" ? compact({
|
|
573
|
+
id: clientId,
|
|
574
|
+
kind,
|
|
575
|
+
client: clientNameOf(options.clients, clientId)
|
|
576
|
+
}) : mapped.subject.actor
|
|
577
|
+
}));
|
|
578
|
+
} catch {
|
|
579
|
+
return anonymousSubject();
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
//#endregion
|
|
583
|
+
//#region src/mcp/create.ts
|
|
584
|
+
function createPermDock(policy, options) {
|
|
585
|
+
const actorKind = mcpActorKind(options.actorKind);
|
|
586
|
+
if (actorKind === void 0) throw new TypeError("permdock/mcp: actorKind must be a non-empty string");
|
|
587
|
+
const checks = {
|
|
588
|
+
policy,
|
|
589
|
+
options,
|
|
590
|
+
kernel: createAgentKernel(policy, compact({
|
|
591
|
+
subject: (call) => options.subject(call.authInfo ?? {}),
|
|
592
|
+
actor: (call) => {
|
|
593
|
+
const clientId = call.authInfo?.clientId;
|
|
594
|
+
return typeof clientId === "string" && clientId !== "" ? compact({
|
|
595
|
+
id: clientId,
|
|
596
|
+
kind: actorKind,
|
|
597
|
+
client: clientNameOf(options.clients, clientId)
|
|
598
|
+
}) : void 0;
|
|
599
|
+
},
|
|
600
|
+
delegation: (call) => call.authInfo === void 0 ? void 0 : delegationFrom(call.authInfo.scopes, call.authInfo.extra),
|
|
601
|
+
tenant: tenantOf(options.tenant),
|
|
602
|
+
store: options.store,
|
|
603
|
+
adapter: "mcp",
|
|
604
|
+
boundary: "mcp-args",
|
|
605
|
+
wrap: composeWraps(options.otel, options.wrap),
|
|
606
|
+
context: options.context === void 0 ? void 0 : (call) => options.context?.(call.authInfo ?? {}),
|
|
607
|
+
...instanceOptions(options)
|
|
608
|
+
}))
|
|
599
609
|
};
|
|
600
|
-
return { protectServer };
|
|
610
|
+
return { protectServer: ((server, enforcement) => protectServer(checks, server, enforcement)) };
|
|
601
611
|
}
|
|
602
612
|
//#endregion
|
|
603
613
|
export { APPROVAL_META_KEY, createPermDock, subjectFromMcp };
|