uigraph 1.0.0 → 1.0.1
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/bin.mjs +134 -56911
- package/dist/viewer/assets/abnfDiagram-VCTEODGH-D7zWJuPJ.js +1 -0
- package/dist/viewer/assets/actor-avatar-Bu-mKKXB.js +1 -0
- package/dist/viewer/assets/{api-adapters-CnQpLiWI.js → api-adapters-tfr51BsS.js} +1 -1
- package/dist/viewer/assets/{api-endpoints-DCA5GNpC.js → api-endpoints-D-Whnnwd.js} +9 -9
- package/dist/viewer/assets/{api-group-endpoints-page-Di42W69y.js → api-group-endpoints-page-B9R1EI4i.js} +9 -9
- package/dist/viewer/assets/{arc-Cn8owoDw.js → arc-DauzQXFu.js} +1 -1
- package/dist/viewer/assets/architectureDiagram-5GKGNRK7-CXzgHd-c.js +36 -0
- package/dist/viewer/assets/{arrow-left-DWcMgM2U.js → arrow-left-DpQqmf17.js} +1 -1
- package/dist/viewer/assets/avatar-DAFUyk1i.js +43 -0
- package/dist/viewer/assets/badge-hR3QvPWv.js +1 -0
- package/dist/viewer/assets/better-delete-confirmation-modal-CGEQ5O4X.js +1 -0
- package/dist/viewer/assets/better-dialog-DcIxFWwk.js +1 -0
- package/dist/viewer/assets/{blockDiagram-I7D4REHJ-CbYzOsL_.js → blockDiagram-I7D4REHJ-CFHWUw4z.js} +2 -2
- package/dist/viewer/assets/{brands-Bd4RklRx.js → brands-DtmA1AhP.js} +1 -1
- package/dist/viewer/assets/button-DZj74qS7.js +1 -0
- package/dist/viewer/assets/{c4Diagram-7LVT6UL2-CLkVbphC.js → c4Diagram-7LVT6UL2-BHSggPKI.js} +6 -6
- package/dist/viewer/assets/{calendar-C0xdI_ag.js → calendar-Bolgc8E5.js} +1 -1
- package/dist/viewer/assets/channel-CNRSofFP.js +1 -0
- package/dist/viewer/assets/{check-XjanpAFg.js → check-BpmCQNbj.js} +1 -1
- package/dist/viewer/assets/checkbox-CFJfXL6R.js +1 -0
- package/dist/viewer/assets/{chevron-down-R7pQzyLD.js → chevron-down-DDsUE_xz.js} +1 -1
- package/dist/viewer/assets/{chevron-up-CRPypfOs.js → chevron-up-DVznKMGv.js} +1 -1
- package/dist/viewer/assets/{chunk-2Q5K7J3B-CcvtugCu.js → chunk-2Q5K7J3B-CNfz5YDQ.js} +1 -1
- package/dist/viewer/assets/{chunk-5VM5RSS4-CpZHHr12.js → chunk-5VM5RSS4-CwIhjvMe.js} +1 -1
- package/dist/viewer/assets/{chunk-F27PBJKO-DeAl4iOc.js → chunk-F27PBJKO-YFexFJof.js} +1 -1
- package/dist/viewer/assets/{chunk-IMKFNOWR-BfrPe7pS.js → chunk-IMKFNOWR-GY2yrvuo.js} +1 -1
- package/dist/viewer/assets/{chunk-JWPE2WC7-DDjyvUmZ.js → chunk-JWPE2WC7-xd0JGvne.js} +1 -1
- package/dist/viewer/assets/chunk-OB3PAWPO-DJIP1M4r.js +12 -0
- package/dist/viewer/assets/{chunk-POPQ4Y6H-DkDb0mzz.js → chunk-POPQ4Y6H-Bh6rizEy.js} +1 -1
- package/dist/viewer/assets/{chunk-SVP7TREG-D-K2XIGs.js → chunk-SVP7TREG-DtNrjvpq.js} +1 -1
- package/dist/viewer/assets/{chunk-TICWLB2K-DWMQQx6z.js → chunk-TICWLB2K-DrERwtw8.js} +1 -1
- package/dist/viewer/assets/{chunk-XXDRQBXY-DfkljAyM.js → chunk-XXDRQBXY-fYo04iwY.js} +1 -1
- package/dist/viewer/assets/{circle-alert-pm13M-Ef.js → circle-alert-Bl7DXYD_.js} +1 -1
- package/dist/viewer/assets/classDiagram-ZZMXUADV-D7PO-ME9.js +1 -0
- package/dist/viewer/assets/classDiagram-v2-VYDZK3BY-D7PO-ME9.js +1 -0
- package/dist/viewer/assets/color-input-ClkkG_kZ.js +11 -0
- package/dist/viewer/assets/{common-Y7ySZBdh.js → common-DLCsGQ-t.js} +1 -1
- package/dist/viewer/assets/{component-icons-D1BDAuEt.js → component-icons-MJ61E9nt.js} +2 -2
- package/dist/viewer/assets/{copy-CN4K2f_R.js → copy-DMJJonQF.js} +1 -1
- package/dist/viewer/assets/cose-bilkent-JH36ORCC-Dplz8nVT.js +1 -0
- package/dist/viewer/assets/cross-button-D5qst1tj.js +1 -0
- package/dist/viewer/assets/{cynefin-OW5HDTMX-yz-uw9Kz.js → cynefin-OW5HDTMX-DiBs4CI8.js} +1 -1
- package/dist/viewer/assets/cynefinDiagram-5FMLGOSQ-COXS8-dI.js +62 -0
- package/dist/viewer/assets/{dagre-GXQ25YYZ-zjZDwXOe.js → dagre-GXQ25YYZ-K8qvfG8D.js} +2 -2
- package/dist/viewer/assets/dashboard-diagrams-page-Cb0xNr4W.js +2 -0
- package/dist/viewer/assets/dashboard-diagrams-page-inner-CD--zr5q.js +1 -0
- package/dist/viewer/assets/dashboard-docs-page-BfWR9Ai9.js +2 -0
- package/dist/viewer/assets/dashboard-docs-page-inner-BLbX-YwF.js +67 -0
- package/dist/viewer/assets/dashboard-layout-sTeeHNsZ.js +1 -0
- package/dist/viewer/assets/dashboard-lens-layout-D-tu_4kn.js +1 -0
- package/dist/viewer/assets/dashboard-map-frame-page-7pDmbTSe.js +1 -0
- package/dist/viewer/assets/dashboard-plans-page-CfwOZ34A.js +1 -0
- package/dist/viewer/assets/dashboard-service-apis-k93fjW51.js +1 -0
- package/dist/viewer/assets/{dashboard-service-diagrams-CIjk95Jk.js → dashboard-service-diagrams-BIWUxX-Y.js} +3 -3
- package/dist/viewer/assets/dashboard-service-docs-CnniTD6Q.js +1 -0
- package/dist/viewer/assets/{dashboard-service-overview-CzePVPjl.js → dashboard-service-overview-CGBu4omY.js} +5 -5
- package/dist/viewer/assets/dashboard-services-DEscIO2O.js +1 -0
- package/dist/viewer/assets/{db-type-badge-DOgu1Pyc.js → db-type-badge-UbPgpwQf.js} +2 -2
- package/dist/viewer/assets/debounce-Cp8eREM4-D7o-Pzb-.js +2 -0
- package/dist/viewer/assets/{dependencies-CYdEztHw.js → dependencies-DK2GDlhQ.js} +1 -1
- package/dist/viewer/assets/{dependency-graph-DbyfN-rC.js → dependency-graph-xBEnCS0J.js} +3 -3
- package/dist/viewer/assets/{diagram-S7CK7UJ4-q-NHkUCx.js → diagram-S7CK7UJ4-D-liadVe.js} +2 -2
- package/dist/viewer/assets/diagram-UQ7AKVKN-CnA8mgXp.js +41 -0
- package/dist/viewer/assets/{diagram-VSXAHHWV-DZc1kHGx.js → diagram-VSXAHHWV-Ck7dgRFR.js} +2 -2
- package/dist/viewer/assets/diagram-VX7I27RA-Cm1LHbu3.js +24 -0
- package/dist/viewer/assets/diagram-Z3DM3KII-DV3yBUCB.js +24 -0
- package/dist/viewer/assets/diagram-live-preview-DzJ7Nxp1.js +2 -0
- package/dist/viewer/assets/{diagram-portal-page-CWxuok9E.js → diagram-portal-page-gguGLULH.js} +24 -24
- package/dist/viewer/assets/{diagrams-C1xkRPqT.js → diagrams-BqBSv2eb.js} +4 -4
- package/dist/viewer/assets/dialog-D7wzTt0L.js +6 -0
- package/dist/viewer/assets/{doc-file-content-B06roi8L.js → doc-file-content-Be0au9Vo.js} +4 -4
- package/dist/viewer/assets/{download-CLdCXVsO.js → download-DhyZ3g4F.js} +1 -1
- package/dist/viewer/assets/ebnfDiagram-PWID7BFC-BXbHVQvG.js +1 -0
- package/dist/viewer/assets/{editor-vqK4qAnB.js → editor-F9o4zoeB.js} +2 -2
- package/dist/viewer/assets/{erDiagram-RLTQ6QDP-BWuA-aqV.js → erDiagram-RLTQ6QDP-DMfiWput.js} +2 -2
- package/dist/viewer/assets/{file-text-BvszilVL.js → file-text-BFqJQ67P.js} +1 -1
- package/dist/viewer/assets/flow-diagram-preview-B83UU1n_.js +1 -0
- package/dist/viewer/assets/{flowDiagram-HODETNUW-Db0QtNj4.js → flowDiagram-HODETNUW-BNFS2Mkr.js} +1 -1
- package/dist/viewer/assets/ganttDiagram-EL5Y4UJY-DFDsYJNp.js +292 -0
- package/dist/viewer/assets/gitGraphDiagram-WWUBYQGX-BVHabPDp.js +106 -0
- package/dist/viewer/assets/global-loader-XJO9_e0l.js +1 -0
- package/dist/viewer/assets/{globe-Jl6eIZmG.js → globe-CJ35KsQW.js} +1 -1
- package/dist/viewer/assets/gql-FFIj8zdp.js +1 -0
- package/dist/viewer/assets/{grid-scroll-body-CPqSxU6-.js → grid-scroll-body-DC5pYDPJ.js} +2 -2
- package/dist/viewer/assets/iconBase-DtqdW2XR.js +1 -0
- package/dist/viewer/assets/index-B6MxYIzs.js +1 -0
- package/dist/viewer/assets/index-BC5xdBQU.js +48 -0
- package/dist/viewer/assets/index-BIrx3oY4.js +1 -0
- package/dist/viewer/assets/{index-jm22RWpY.js → index-BZGsDmUx.js} +5 -5
- package/dist/viewer/assets/{index-Poftp9f1.js → index-BZXhpoWr.js} +4 -4
- package/dist/viewer/assets/index-Bn2bqqMd.js +1 -0
- package/dist/viewer/assets/{index-HU5pz3kj.js → index-BpCVYTvP.js} +36 -36
- package/dist/viewer/assets/{index-Bt2YHgxl.js → index-CBOYhBKJ.js} +1 -1
- package/dist/viewer/assets/index-CH7cEAxj.js +17 -0
- package/dist/viewer/assets/index-CIf0GId3.js +74 -0
- package/dist/viewer/assets/{index-D7MEHpJf.js → index-Ck66Ic_p.js} +4 -4
- package/dist/viewer/assets/index-Cp9mIQ8F.js +1 -0
- package/dist/viewer/assets/{index-c6Bxcoqm.js → index-DDmBuMKI.js} +1 -1
- package/dist/viewer/assets/{index-BSyJbFDG.js → index-DY3BFyb6.js} +1 -1
- package/dist/viewer/assets/{index-BS94XVnd.js → index-DkzZ-ZvR.js} +1 -1
- package/dist/viewer/assets/{index-C_43VC19.js → index-PglMbhys.js} +1 -1
- package/dist/viewer/assets/infoDiagram-27XIBGKW-CG-wwxBf.js +2 -0
- package/dist/viewer/assets/input--C59pq6j.js +1 -0
- package/dist/viewer/assets/{ishikawaDiagram-5VMMS53U-C4PzFnZ4.js → ishikawaDiagram-5VMMS53U-BlgV7ceJ.js} +2 -2
- package/dist/viewer/assets/{journeyDiagram-3NMN7TZE-Dpn3bBNo.js → journeyDiagram-3NMN7TZE-Cn03_Xl6.js} +2 -2
- package/dist/viewer/assets/{kanban-definition-UXKFOSKX-CJ99H4G6.js → kanban-definition-UXKFOSKX-CJogvDPY.js} +6 -6
- package/dist/viewer/assets/{layout-Bfvxt_D5.js → layout-B26eC4yQ.js} +1 -1
- package/dist/viewer/assets/{lens-run-page-DZneics-.js → lens-run-page-Ba1ppzJt.js} +3 -3
- package/dist/viewer/assets/lens-runs-page-BnUS57LM.js +1 -0
- package/dist/viewer/assets/{link-2-C75goIOt.js → link-2-D1e0xznG.js} +1 -1
- package/dist/viewer/assets/linked-project-dot-BY1gEES9.js +543 -0
- package/dist/viewer/assets/{loader-circle-C5jZoM7X.js → loader-circle-CiqyoJF7.js} +1 -1
- package/dist/viewer/assets/{merge-Cy7YkrN7.js → merge-BhOMvhW1.js} +1 -1
- package/dist/viewer/assets/mermaid.core-BDgZg3kW.js +312 -0
- package/dist/viewer/assets/method-badge-DErZP7y5.js +1 -0
- package/dist/viewer/assets/{mindmap-definition-YA3MSWOX-yQCKA-K4.js → mindmap-definition-YA3MSWOX-DFkLu0ac.js} +2 -2
- package/dist/viewer/assets/nav-links-DZWMpwMR.js +61 -0
- package/dist/viewer/assets/{network-COWNXGMM.js → network-B1apzT1s.js} +1 -1
- package/dist/viewer/assets/{nosql-editor-modal-DTiWkc_r.js → nosql-editor-modal-CvH0SWAi.js} +5 -5
- package/dist/viewer/assets/not-found-page-C-fn8QeM.js +1 -0
- package/dist/viewer/assets/organization-dependency-graph-page-BryP-iJL.js +1 -0
- package/dist/viewer/assets/pegDiagram-XKGWAZYB-g1cM14-L.js +1 -0
- package/dist/viewer/assets/{pieDiagram-E7YTZNPT-DqnkpCX4.js → pieDiagram-E7YTZNPT-WEnDsMY3.js} +2 -2
- package/dist/viewer/assets/{plus-DQakmTjA.js → plus-DbyuOgKB.js} +1 -1
- package/dist/viewer/assets/popover-476u3TN0.js +1 -0
- package/dist/viewer/assets/preload-helper-BlTxHScW.js +1 -0
- package/dist/viewer/assets/{quadrantDiagram-AXDQQJYC-iQjJfars.js → quadrantDiagram-AXDQQJYC-NdfQCpzF.js} +2 -2
- package/dist/viewer/assets/query-D9oj6LGx.js +50 -0
- package/dist/viewer/assets/{quill-rCHj4fCw.js → quill-Cde5JMRD.js} +1 -1
- package/dist/viewer/assets/railroadDiagram-O6MQD6OU-BYhlXlMY.js +1 -0
- package/dist/viewer/assets/{react-BNzYaZuG.js → react-Bx48XWN4.js} +1 -1
- package/dist/viewer/assets/react-flow-wrapper-BcbvHgxz.js +277 -0
- package/dist/viewer/assets/{requirementDiagram-BXWQKSXE-Bv-Vh1dW.js → requirementDiagram-BXWQKSXE-C25Bcm2L.js} +2 -2
- package/dist/viewer/assets/{sankeyDiagram-P5KCCOFB-C__uD07f.js → sankeyDiagram-P5KCCOFB-h8LiCsgk.js} +2 -2
- package/dist/viewer/assets/scroll-area-RagrStGj.js +6 -0
- package/dist/viewer/assets/{search-DW-qhXLk.js → search-vw7ucoOs.js} +1 -1
- package/dist/viewer/assets/section-loader-C4hOfhQS.js +1 -0
- package/dist/viewer/assets/section-not-found-BnrXiPog.js +1 -0
- package/dist/viewer/assets/select-D9fbrr7O.js +1 -0
- package/dist/viewer/assets/{sequenceDiagram-WJ2MYXX4-1RbvcHwO.js → sequenceDiagram-WJ2MYXX4-CDRVvD30.js} +2 -2
- package/dist/viewer/assets/{service-context-C17u_Fw9.js → service-context-D5nBtU18.js} +1 -1
- package/dist/viewer/assets/{service-database-list-page-cYuDHIls.js → service-database-list-page-DBjaDLVy.js} +2 -2
- package/dist/viewer/assets/service-database-page-CenNCgwf.js +2 -0
- package/dist/viewer/assets/service-database-page-inner-gS51TQTZ.js +85 -0
- package/dist/viewer/assets/{service-doc-Dir8g83J.js → service-doc-C-jHqT1U.js} +4 -4
- package/dist/viewer/assets/{service-doc-preview-modal-bJ6tJ5YR.js → service-doc-preview-modal-5sX6FOu1.js} +2 -2
- package/dist/viewer/assets/service-layout-Bf0vZBCH.js +1 -0
- package/dist/viewer/assets/service-pages-CFikvX4t.js +1 -0
- package/dist/viewer/assets/{services-Crqkffr5.js → services-Ivav34WC.js} +4 -4
- package/dist/viewer/assets/{share-2-BmYmqN4k.js → share-2-CQEaWbfH.js} +1 -1
- package/dist/viewer/assets/sizeCapture-INFHLROL-BJ6ndnPM.js +1 -0
- package/dist/viewer/assets/stateDiagram-D77RDMKH-B_tNCX3o.js +1 -0
- package/dist/viewer/assets/stateDiagram-v2-MP3YSRHH-Dl9wqdli.js +1 -0
- package/dist/viewer/assets/{style-DgSU0bQl.js → style-BPesJvJr.js} +8 -8
- package/dist/viewer/assets/super-circle-loader-DSkos6uP.js +1 -0
- package/dist/viewer/assets/super-logo-loader-Cxlbn6MB.js +1 -0
- package/dist/viewer/assets/swimlanes-42K2YHIH-CzURzYwf.js +1 -0
- package/dist/viewer/assets/swimlanesDiagram-VR7AAH4N-DrIlwFtk.js +8 -0
- package/dist/viewer/assets/table-B4pFND-o.js +1 -0
- package/dist/viewer/assets/textarea-Clgdv3Oi.js +1 -0
- package/dist/viewer/assets/{timeline-definition-24CTP7MA-C4yuLwUH.js → timeline-definition-24CTP7MA-D0YXQE1U.js} +2 -2
- package/dist/viewer/assets/ui-graph-logo-gHowu5gP.js +1 -0
- package/dist/viewer/assets/{upload-CRsS43j9.js → upload-CYb3nN_5.js} +1 -1
- package/dist/viewer/assets/{uploads-BOQHX9l4.js → uploads-CX2kdXbM.js} +8 -8
- package/dist/viewer/assets/{use-debounced-value-Ba-kkCUY.js → use-debounced-value-BpNFJd3C.js} +1 -1
- package/dist/viewer/assets/{use-localstorage-DIisTx-J.js → use-localstorage-D233xVb0.js} +1 -1
- package/dist/viewer/assets/use-permissions-BvmxJJ5r.js +1 -0
- package/dist/viewer/assets/{use-scoped-storage-C4RzED-E.js → use-scoped-storage-DL1XVp1j.js} +1 -1
- package/dist/viewer/assets/useMutation-D5ZHwHsx.js +1 -0
- package/dist/viewer/assets/useQuery-Dhe6uxrL.js +1 -0
- package/dist/viewer/assets/{vennDiagram-4TSXK5OY-BCtqXZqe.js → vennDiagram-4TSXK5OY-SSjHlqCQ.js} +2 -2
- package/dist/viewer/assets/{wardleyDiagram-VM6X3IG4-D3OtZJoT.js → wardleyDiagram-VM6X3IG4-CZWNdQxH.js} +2 -2
- package/dist/viewer/assets/{xychartDiagram-S5SC5T6Z-DxLL2CHH.js → xychartDiagram-S5SC5T6Z-B2qAGeb0.js} +2 -2
- package/dist/viewer/assets/zod-BMU4UTQt.js +1 -0
- package/dist/viewer/index.html +10 -1
- package/package.json +56 -56
- package/dist/abort-signals-B4fc3ahg.mjs +0 -34
- package/dist/anthropic-messages-KPxCHqBN.mjs +0 -14327
- package/dist/auth-storage-BvNH97p9.mjs +0 -2180
- package/dist/azure-openai-config-BbeCSg4y.mjs +0 -55
- package/dist/azure-openai-responses-BRzy8RfL.mjs +0 -182
- package/dist/classifier-shared-DBUHiTGw.mjs +0 -105
- package/dist/cli-2R6QQFp1.mjs +0 -451
- package/dist/cloudflare-workers-ai-system-one-BfqwmRQf.mjs +0 -44
- package/dist/config-D8DMGzIq.mjs +0 -647
- package/dist/cross-spawn-B2jIQPlu.mjs +0 -404
- package/dist/diagnostics-xdSgQR8t.mjs +0 -131
- package/dist/dist-BRyghrKX.mjs +0 -404
- package/dist/dist-D0EzrblG.mjs +0 -12161
- package/dist/dist-EKhnHWHG.mjs +0 -140221
- package/dist/dist-pQtuusFv.mjs +0 -1243
- package/dist/easter-egg-3d-C9_btlWn.mjs +0 -1225
- package/dist/env-api-keys-uytNub6O.mjs +0 -122
- package/dist/error-body-FhmCtR7m.mjs +0 -101
- package/dist/esm-D5isprgl.mjs +0 -1533
- package/dist/esm-DnqE4pfM.mjs +0 -104876
- package/dist/execute-BoTKl80U.mjs +0 -1715
- package/dist/github-copilot-headers-DWcTgoXr.mjs +0 -22
- package/dist/google-generative-ai-Brs5YpJo.mjs +0 -332
- package/dist/google-shared-2CyYa4VE.mjs +0 -33438
- package/dist/google-vertex-DZDUQPKa.mjs +0 -378
- package/dist/hash-DN7ukSfd.mjs +0 -16
- package/dist/headers-GhQHLk-3.mjs +0 -17
- package/dist/jiti-9kWALm4Y.mjs +0 -5439
- package/dist/jiti-loader-f21DrY1K.mjs +0 -27
- package/dist/jiti-static-loader-BI_w28l7.mjs +0 -41704
- package/dist/json-parse-B2mHg7o7.mjs +0 -381
- package/dist/keybinding-hints-D0nLU3ZH.mjs +0 -17899
- package/dist/lib-Dj1gKQJc.mjs +0 -24379
- package/dist/llama-cpp-classify-C73BoMYd.mjs +0 -344
- package/dist/mistral-conversations-DXVR7znL.mjs +0 -698
- package/dist/models-DBdii9gQ.mjs +0 -1255
- package/dist/multipart-parser-DLw9Z0uJ.mjs +0 -298
- package/dist/node-B0NU8X55.mjs +0 -2479
- package/dist/node-B8gaKm9g.mjs +0 -23
- package/dist/openai-I3_AqKDX.mjs +0 -17357
- package/dist/openai-codex-responses-PcZHDuBD.mjs +0 -1198
- package/dist/openai-completions-a_mH01ii.mjs +0 -994
- package/dist/openai-decisions-Du8y4mQG.mjs +0 -142
- package/dist/openai-prompt-cache-Bc6qNWt0.mjs +0 -8
- package/dist/openai-responses-a9lV_U7O.mjs +0 -270
- package/dist/openai-responses-shared-CDs_PBiH.mjs +0 -598
- package/dist/openrouter-images-D0Zc8Cjs.mjs +0 -125
- package/dist/photon_rs-DCvMOMu0.mjs +0 -4252
- package/dist/pi-messages-B5ietIBq.mjs +0 -306
- package/dist/provider-env-BJhgA66g.mjs +0 -37
- package/dist/provider-retry-DXYM7rVE.mjs +0 -79
- package/dist/runtime-DTkBxiaM.mjs +0 -2822
- package/dist/sanitize-unicode-CqChtoM6.mjs +0 -26
- package/dist/shell-836_JFnI.mjs +0 -162
- package/dist/source-CKPndhKl.mjs +0 -478
- package/dist/src-DNm2QRm3.mjs +0 -5028
- package/dist/system-one-shared-DUAFtNa8.mjs +0 -75
- package/dist/token-DEUkymby.mjs +0 -56
- package/dist/token-util-Cx5S4eKt.mjs +0 -3
- package/dist/tool-DHDYqsyG.mjs +0 -6673
- package/dist/transform-messages-BClk99Vn.mjs +0 -477
- package/dist/typesafe-system-one-D2d8j8hZ.mjs +0 -24
- package/dist/typescript-Br-vsRJV.mjs +0 -6737
- package/dist/uuid-CNCZqNBb.mjs +0 -49
- package/dist/v2-1z8ijsD3.mjs +0 -6120
- package/dist/viewer/assets/abnfDiagram-VCTEODGH-j5YaKnl9.js +0 -1
- package/dist/viewer/assets/actor-avatar-DqmoY8Xc.js +0 -1
- package/dist/viewer/assets/architectureDiagram-5GKGNRK7-D80cJKz_.js +0 -36
- package/dist/viewer/assets/avatar-BI7D-Aia.js +0 -43
- package/dist/viewer/assets/badge-870-A4Y6.js +0 -1
- package/dist/viewer/assets/better-delete-confirmation-modal-Dib31LVb.js +0 -1
- package/dist/viewer/assets/better-dialog-D3AAiahr.js +0 -1
- package/dist/viewer/assets/button-DadqadpO.js +0 -1
- package/dist/viewer/assets/channel-DRkt3LIn.js +0 -1
- package/dist/viewer/assets/checkbox-BXpR079x.js +0 -1
- package/dist/viewer/assets/classDiagram-ZZMXUADV-G5CebNaC.js +0 -1
- package/dist/viewer/assets/classDiagram-v2-VYDZK3BY-G5CebNaC.js +0 -1
- package/dist/viewer/assets/color-input-Efm-lxDy.js +0 -11
- package/dist/viewer/assets/cose-bilkent-JH36ORCC-CrSHFmnp.js +0 -1
- package/dist/viewer/assets/cross-button-5Sr5rWVP.js +0 -1
- package/dist/viewer/assets/cynefinDiagram-5FMLGOSQ-Bdl_eBdu.js +0 -62
- package/dist/viewer/assets/dashboard-diagrams-page-DMupOe1m.js +0 -2
- package/dist/viewer/assets/dashboard-diagrams-page-inner-Dh0yKh2A.js +0 -1
- package/dist/viewer/assets/dashboard-docs-page-DWJ1qOQz.js +0 -2
- package/dist/viewer/assets/dashboard-docs-page-inner-Fs6G3oGo.js +0 -67
- package/dist/viewer/assets/dashboard-layout-D36InAaB.js +0 -1
- package/dist/viewer/assets/dashboard-lens-layout-Cv1Xx3hY.js +0 -1
- package/dist/viewer/assets/dashboard-map-frame-page-CTn61NUO.js +0 -1
- package/dist/viewer/assets/dashboard-plans-page-DxFJkrgK.js +0 -1
- package/dist/viewer/assets/dashboard-service-apis-BHtD9s5z.js +0 -1
- package/dist/viewer/assets/dashboard-service-docs-Uv_1uxxO.js +0 -1
- package/dist/viewer/assets/dashboard-services-CUP-L4Jg.js +0 -1
- package/dist/viewer/assets/diagram-UQ7AKVKN-DhFcurW6.js +0 -41
- package/dist/viewer/assets/diagram-VX7I27RA-DE1nREE8.js +0 -24
- package/dist/viewer/assets/diagram-Z3DM3KII-CSrYcCbc.js +0 -24
- package/dist/viewer/assets/diagram-live-preview-i5doxXLH.js +0 -2
- package/dist/viewer/assets/dialog-D_HtNS17.js +0 -6
- package/dist/viewer/assets/ebnfDiagram-PWID7BFC-BfL8m_sf.js +0 -1
- package/dist/viewer/assets/flow-diagram-preview-T0Z4iMVU.js +0 -1
- package/dist/viewer/assets/ganttDiagram-EL5Y4UJY-CsITpniw.js +0 -292
- package/dist/viewer/assets/gitGraphDiagram-WWUBYQGX-B4eCBGoe.js +0 -106
- package/dist/viewer/assets/iconBase-BoDZfJgC.js +0 -1
- package/dist/viewer/assets/index-5E1tJ7yK.js +0 -125
- package/dist/viewer/assets/index-7XpV2DRw.js +0 -1
- package/dist/viewer/assets/index-C0Rs3s9Z.js +0 -1
- package/dist/viewer/assets/index-CzHjJWeV.js +0 -1
- package/dist/viewer/assets/index-DcEA6SIV.js +0 -74
- package/dist/viewer/assets/infoDiagram-27XIBGKW-lTebN5Ix.js +0 -2
- package/dist/viewer/assets/input-YvHTVS0E.js +0 -1
- package/dist/viewer/assets/lens-runs-page-DoAOObxx.js +0 -1
- package/dist/viewer/assets/linked-project-dot-Ds2uEmJ9.js +0 -543
- package/dist/viewer/assets/mermaid.core-BCRBv8Us.js +0 -312
- package/dist/viewer/assets/method-badge-DMYDKdDZ.js +0 -1
- package/dist/viewer/assets/nav-links-8vT2ixiI.js +0 -61
- package/dist/viewer/assets/not-found-page-CnfLlhS4.js +0 -1
- package/dist/viewer/assets/organization-dependency-graph-page-3xqIxjNx.js +0 -1
- package/dist/viewer/assets/pegDiagram-XKGWAZYB-CMXqeAum.js +0 -1
- package/dist/viewer/assets/popover-CN9ni1N3.js +0 -1
- package/dist/viewer/assets/railroadDiagram-O6MQD6OU-CfqToX3W.js +0 -1
- package/dist/viewer/assets/react-flow-wrapper-BPlRstyO.js +0 -276
- package/dist/viewer/assets/scroll-area-BodE-n7W.js +0 -6
- package/dist/viewer/assets/section-loader-BzpYM_Nx.js +0 -1
- package/dist/viewer/assets/select-DDtnmSET.js +0 -1
- package/dist/viewer/assets/service-database-page-CMi1wF4h.js +0 -2
- package/dist/viewer/assets/service-database-page-inner-CoqAj93L.js +0 -85
- package/dist/viewer/assets/service-layout-0NMm_bKG.js +0 -1
- package/dist/viewer/assets/service-pages-dfnc91SU.js +0 -1
- package/dist/viewer/assets/sizeCapture-INFHLROL-BRJi3gCg.js +0 -1
- package/dist/viewer/assets/stateDiagram-D77RDMKH-DdLsfxgy.js +0 -1
- package/dist/viewer/assets/stateDiagram-v2-MP3YSRHH-DFwg8Qki.js +0 -1
- package/dist/viewer/assets/super-circle-loader-D8MElOMh.js +0 -1
- package/dist/viewer/assets/super-logo-loader-DJYiRbCS.js +0 -1
- package/dist/viewer/assets/swimlanes-42K2YHIH-AuYjrPqh.js +0 -1
- package/dist/viewer/assets/swimlanesDiagram-VR7AAH4N-0nIGA8_4.js +0 -8
- package/dist/viewer/assets/table-Bi4Auk9Z.js +0 -1
- package/dist/viewer/assets/textarea-MgxZ5x6-.js +0 -1
- package/dist/viewer/assets/ui-graph-logo-D4jRhd4z.js +0 -1
- package/dist/viewer/assets/use-permissions-6JVET753.js +0 -1
- package/dist/viewer/assets/useMutation-CmVaR9zz.js +0 -1
- package/dist/viewer/assets/useQuery-B8yYhO0c.js +0 -1
- package/dist/viewer/assets/zod-Dc4H6VVJ.js +0 -1
- package/dist/virtual-modules-DnCx0Gdx.mjs +0 -340
|
@@ -1,2822 +0,0 @@
|
|
|
1
|
-
import { a as __toESM } from "./bin.mjs";
|
|
2
|
-
import { t as require_cross_spawn } from "./cross-spawn-B2jIQPlu.mjs";
|
|
3
|
-
import { d as getAgentDir, s as VERSION, t as APP_NAME } from "./config-D8DMGzIq.mjs";
|
|
4
|
-
import { d as resolveHeadersOrThrow, n as FileAuthStorageBackend, u as resolveConfigValueOrThrow, x as require_proper_lockfile, y as mcpNamespace } from "./auth-storage-BvNH97p9.mjs";
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
|
-
import { appendFileSync, mkdirSync, renameSync, statSync } from "node:fs";
|
|
7
|
-
import fE from "node:process";
|
|
8
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
9
|
-
import { homedir } from "node:os";
|
|
10
|
-
import { createServer } from "node:http";
|
|
11
|
-
import { spawn } from "node:child_process";
|
|
12
|
-
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
13
|
-
import { pathToFileURL } from "node:url";
|
|
14
|
-
//#region node_modules/.pnpm/@earendil-works+pi-mcp@1.2.0/node_modules/@earendil-works/pi-mcp/dist/protocol/jsonrpc.js
|
|
15
|
-
const JSON_RPC_ERROR_CODES = {
|
|
16
|
-
parseError: -32700,
|
|
17
|
-
invalidRequest: -32600,
|
|
18
|
-
methodNotFound: -32601,
|
|
19
|
-
invalidParams: -32602,
|
|
20
|
-
internalError: -32603
|
|
21
|
-
};
|
|
22
|
-
var McpError = class extends Error {
|
|
23
|
-
code;
|
|
24
|
-
data;
|
|
25
|
-
constructor(code, message, data) {
|
|
26
|
-
super(message);
|
|
27
|
-
this.name = "McpError";
|
|
28
|
-
this.code = code;
|
|
29
|
-
this.data = data;
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
var McpConnectionClosedError = class extends Error {
|
|
33
|
-
constructor(message = "MCP connection closed") {
|
|
34
|
-
super(message);
|
|
35
|
-
this.name = "McpConnectionClosedError";
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
var McpTimeoutError = class extends Error {
|
|
39
|
-
timeoutMs;
|
|
40
|
-
constructor(timeoutMs) {
|
|
41
|
-
super(`MCP request timed out after ${timeoutMs}ms`);
|
|
42
|
-
this.name = "McpTimeoutError";
|
|
43
|
-
this.timeoutMs = timeoutMs;
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
var McpAbortError = class extends Error {
|
|
47
|
-
constructor(message = "MCP request aborted") {
|
|
48
|
-
super(message);
|
|
49
|
-
this.name = "AbortError";
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
function isObject(value) {
|
|
53
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
54
|
-
}
|
|
55
|
-
function toError(value) {
|
|
56
|
-
return value instanceof Error ? value : new Error(String(value));
|
|
57
|
-
}
|
|
58
|
-
function isJsonRpcId(value) {
|
|
59
|
-
return typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
60
|
-
}
|
|
61
|
-
function isJsonRpcRequest(message) {
|
|
62
|
-
return isObject(message) && message.jsonrpc === "2.0" && isJsonRpcId(message.id) && typeof message.method === "string";
|
|
63
|
-
}
|
|
64
|
-
function isJsonRpcNotification(message) {
|
|
65
|
-
return isObject(message) && message.jsonrpc === "2.0" && !("id" in message) && typeof message.method === "string";
|
|
66
|
-
}
|
|
67
|
-
function isJsonRpcResponse(message) {
|
|
68
|
-
if (!isObject(message) || message.jsonrpc !== "2.0" || !isJsonRpcId(message.id)) return false;
|
|
69
|
-
if ("result" in message) return !("error" in message);
|
|
70
|
-
if (!("error" in message) || !isObject(message.error)) return false;
|
|
71
|
-
return typeof message.error.code === "number" && typeof message.error.message === "string";
|
|
72
|
-
}
|
|
73
|
-
function parseJsonRpcMessage(value) {
|
|
74
|
-
if (isJsonRpcRequest(value) || isJsonRpcNotification(value) || isJsonRpcResponse(value)) return value;
|
|
75
|
-
throw new McpError(JSON_RPC_ERROR_CODES.invalidRequest, "Invalid JSON-RPC message");
|
|
76
|
-
}
|
|
77
|
-
//#endregion
|
|
78
|
-
//#region node_modules/.pnpm/@earendil-works+pi-mcp@1.2.0/node_modules/@earendil-works/pi-mcp/dist/protocol/types.js
|
|
79
|
-
const LATEST_PROTOCOL_VERSION = "2025-11-25";
|
|
80
|
-
/**
|
|
81
|
-
* Versions the client accepts from a server. Servers that do not support the requested version answer
|
|
82
|
-
* with their own latest one, so older versions stay accepted for servers built on older SDKs.
|
|
83
|
-
*/
|
|
84
|
-
const SUPPORTED_PROTOCOL_VERSIONS = [
|
|
85
|
-
LATEST_PROTOCOL_VERSION,
|
|
86
|
-
"2025-06-18",
|
|
87
|
-
"2025-03-26",
|
|
88
|
-
"2024-11-05"
|
|
89
|
-
];
|
|
90
|
-
//#endregion
|
|
91
|
-
//#region node_modules/.pnpm/@earendil-works+pi-mcp@1.2.0/node_modules/@earendil-works/pi-mcp/dist/client.js
|
|
92
|
-
const DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
93
|
-
const MAX_LIST_PAGES = 1e3;
|
|
94
|
-
function validateInitializeResult(value) {
|
|
95
|
-
if (!isObject(value) || typeof value.protocolVersion !== "string" || !isObject(value.capabilities) || !isObject(value.serverInfo) || typeof value.serverInfo.name !== "string" || typeof value.serverInfo.version !== "string" || value.instructions !== void 0 && typeof value.instructions !== "string") throw new McpError(JSON_RPC_ERROR_CODES.invalidRequest, "Invalid MCP initialize result");
|
|
96
|
-
return value;
|
|
97
|
-
}
|
|
98
|
-
function invalid(message) {
|
|
99
|
-
return new McpError(JSON_RPC_ERROR_CODES.invalidRequest, message);
|
|
100
|
-
}
|
|
101
|
-
/** One page of a paginated list: the items under `key`, each checked by `isItem`. */
|
|
102
|
-
function validateListPage(method, key, value, isItem) {
|
|
103
|
-
const items = isObject(value) ? value[key] : void 0;
|
|
104
|
-
if (!isObject(value) || !Array.isArray(items)) throw invalid(`Invalid MCP ${method} result`);
|
|
105
|
-
for (const item of items) if (!isObject(item) || !isItem(item)) throw invalid(`Invalid entry in MCP ${method} result`);
|
|
106
|
-
const nextCursor = value.nextCursor === null || value.nextCursor === "" ? void 0 : value.nextCursor;
|
|
107
|
-
if (nextCursor !== void 0 && typeof nextCursor !== "string") throw invalid(`Invalid MCP ${method} cursor`);
|
|
108
|
-
return {
|
|
109
|
-
items,
|
|
110
|
-
...nextCursor === void 0 ? {} : { nextCursor }
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
const isTool = (tool) => typeof tool.name === "string" && isObject(tool.inputSchema);
|
|
114
|
-
const isResource = (resource) => typeof resource.uri === "string" && (resource.name === void 0 || typeof resource.name === "string");
|
|
115
|
-
const isResourceTemplate = (template) => typeof template.uriTemplate === "string" && (template.name === void 0 || typeof template.name === "string");
|
|
116
|
-
function toResource(item) {
|
|
117
|
-
return {
|
|
118
|
-
...item,
|
|
119
|
-
name: item.name ?? item.uri
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
function toResourceTemplate(item) {
|
|
123
|
-
return {
|
|
124
|
-
...item,
|
|
125
|
-
name: item.name ?? item.uriTemplate
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
function pageCursor(page) {
|
|
129
|
-
return page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor };
|
|
130
|
-
}
|
|
131
|
-
function validateReadResourceResult(value) {
|
|
132
|
-
if (!isObject(value) || !Array.isArray(value.contents)) throw invalid("Invalid MCP resources/read result");
|
|
133
|
-
for (const contents of value.contents) if (!isObject(contents) || typeof contents.uri !== "string" || typeof contents.text !== "string" && typeof contents.blob !== "string") throw invalid("Invalid contents in MCP resources/read result");
|
|
134
|
-
return value;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* `content` is required by the spec, but servers that only return `structuredContent` omit it (the SDK defaults it too).
|
|
138
|
-
* `structuredContent` may be any JSON value, as MCP 2026-07-28 allows.
|
|
139
|
-
*/
|
|
140
|
-
function validateCallToolResult(value) {
|
|
141
|
-
if (!isObject(value) || value.content !== void 0 && !Array.isArray(value.content)) throw new McpError(JSON_RPC_ERROR_CODES.invalidRequest, "Invalid MCP tools/call result");
|
|
142
|
-
return value.content === void 0 ? {
|
|
143
|
-
...value,
|
|
144
|
-
content: []
|
|
145
|
-
} : value;
|
|
146
|
-
}
|
|
147
|
-
var McpClient = class {
|
|
148
|
-
options;
|
|
149
|
-
state = "idle";
|
|
150
|
-
transport;
|
|
151
|
-
nextRequestId = 1;
|
|
152
|
-
serverInfoValue;
|
|
153
|
-
serverCapabilitiesValue;
|
|
154
|
-
instructionsValue;
|
|
155
|
-
protocolVersionValue;
|
|
156
|
-
pending = /* @__PURE__ */ new Map();
|
|
157
|
-
progressRequests = /* @__PURE__ */ new Map();
|
|
158
|
-
incoming = /* @__PURE__ */ new Map();
|
|
159
|
-
requestHandlers = /* @__PURE__ */ new Map();
|
|
160
|
-
notificationListeners = /* @__PURE__ */ new Map();
|
|
161
|
-
errorListeners = /* @__PURE__ */ new Set();
|
|
162
|
-
closeListeners = /* @__PURE__ */ new Set();
|
|
163
|
-
disposers = [];
|
|
164
|
-
constructor(options) {
|
|
165
|
-
this.options = Object.freeze({ ...options });
|
|
166
|
-
this.requestHandlers.set("ping", () => ({}));
|
|
167
|
-
const roots = options.roots;
|
|
168
|
-
if (roots) this.requestHandlers.set("roots/list", async () => ({ roots: [...typeof roots === "function" ? await roots() : roots] }));
|
|
169
|
-
}
|
|
170
|
-
get connectionState() {
|
|
171
|
-
return this.state;
|
|
172
|
-
}
|
|
173
|
-
get serverInfo() {
|
|
174
|
-
return this.serverInfoValue;
|
|
175
|
-
}
|
|
176
|
-
get serverCapabilities() {
|
|
177
|
-
return this.serverCapabilitiesValue;
|
|
178
|
-
}
|
|
179
|
-
get instructions() {
|
|
180
|
-
return this.instructionsValue;
|
|
181
|
-
}
|
|
182
|
-
get protocolVersion() {
|
|
183
|
-
return this.protocolVersionValue;
|
|
184
|
-
}
|
|
185
|
-
async connect(transport) {
|
|
186
|
-
if (this.state !== "idle") throw new Error(`Cannot connect MCP client in ${this.state} state`);
|
|
187
|
-
this.state = "connecting";
|
|
188
|
-
this.transport = transport;
|
|
189
|
-
this.disposers = [
|
|
190
|
-
transport.onMessage((message) => this.handleMessage(message)),
|
|
191
|
-
transport.onError((error) => this.emitError(error)),
|
|
192
|
-
transport.onClose(() => this.handleTransportClose())
|
|
193
|
-
];
|
|
194
|
-
try {
|
|
195
|
-
await transport.start();
|
|
196
|
-
const capabilities = { ...this.options.capabilities };
|
|
197
|
-
if (this.options.roots && capabilities.roots === void 0) capabilities.roots = {};
|
|
198
|
-
const result = validateInitializeResult(await this.requestInternal("initialize", {
|
|
199
|
-
protocolVersion: this.options.protocolVersion ?? "2025-11-25",
|
|
200
|
-
capabilities,
|
|
201
|
-
clientInfo: {
|
|
202
|
-
name: this.options.name,
|
|
203
|
-
version: this.options.version,
|
|
204
|
-
...this.options.title === void 0 ? {} : { title: this.options.title }
|
|
205
|
-
}
|
|
206
|
-
}, {}, true));
|
|
207
|
-
if (!SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) throw new Error(`MCP server selected unsupported protocol version ${result.protocolVersion}`);
|
|
208
|
-
this.protocolVersionValue = result.protocolVersion;
|
|
209
|
-
this.serverInfoValue = result.serverInfo;
|
|
210
|
-
this.serverCapabilitiesValue = result.capabilities;
|
|
211
|
-
this.instructionsValue = result.instructions;
|
|
212
|
-
transport.setProtocolVersion?.(result.protocolVersion);
|
|
213
|
-
await this.notifyInternal("notifications/initialized", void 0, true);
|
|
214
|
-
this.state = "connected";
|
|
215
|
-
return result;
|
|
216
|
-
} catch (error) {
|
|
217
|
-
await this.close().catch(() => {});
|
|
218
|
-
throw error;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
request(method, params, options = {}) {
|
|
222
|
-
return this.requestInternal(method, params, options, false);
|
|
223
|
-
}
|
|
224
|
-
notify(method, params) {
|
|
225
|
-
return this.notifyInternal(method, params, false);
|
|
226
|
-
}
|
|
227
|
-
setRequestHandler(method, handler) {
|
|
228
|
-
this.requestHandlers.set(method, handler);
|
|
229
|
-
return () => {
|
|
230
|
-
if (this.requestHandlers.get(method) === handler) this.requestHandlers.delete(method);
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
onNotification(method, listener) {
|
|
234
|
-
const listeners = this.notificationListeners.get(method) ?? /* @__PURE__ */ new Set();
|
|
235
|
-
this.notificationListeners.set(method, listeners);
|
|
236
|
-
listeners.add(listener);
|
|
237
|
-
return () => {
|
|
238
|
-
listeners.delete(listener);
|
|
239
|
-
if (listeners.size === 0) this.notificationListeners.delete(method);
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
onError(listener) {
|
|
243
|
-
this.errorListeners.add(listener);
|
|
244
|
-
return () => this.errorListeners.delete(listener);
|
|
245
|
-
}
|
|
246
|
-
/** Called once when the connection closes, whether the transport dropped or `close()` was called. */
|
|
247
|
-
onClose(listener) {
|
|
248
|
-
this.closeListeners.add(listener);
|
|
249
|
-
return () => this.closeListeners.delete(listener);
|
|
250
|
-
}
|
|
251
|
-
async ping(options = {}) {
|
|
252
|
-
await this.request("ping", void 0, options);
|
|
253
|
-
}
|
|
254
|
-
async listTools(options = {}) {
|
|
255
|
-
return await this.listAll("tools/list", "tools", isTool, options);
|
|
256
|
-
}
|
|
257
|
-
/** Every resource, following `nextCursor` through all pages. */
|
|
258
|
-
async listResources(options = {}) {
|
|
259
|
-
return (await this.listAll("resources/list", "resources", isResource, options)).map(toResource);
|
|
260
|
-
}
|
|
261
|
-
/** One page of resources, starting at `cursor`. */
|
|
262
|
-
async listResourcesPage(cursor, options = {}) {
|
|
263
|
-
const page = await this.listPage("resources/list", "resources", isResource, cursor, options);
|
|
264
|
-
return {
|
|
265
|
-
resources: page.items.map(toResource),
|
|
266
|
-
...pageCursor(page)
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
/** Every resource template, following `nextCursor` through all pages. */
|
|
270
|
-
async listResourceTemplates(options = {}) {
|
|
271
|
-
return (await this.listAll("resources/templates/list", "resourceTemplates", isResourceTemplate, options)).map(toResourceTemplate);
|
|
272
|
-
}
|
|
273
|
-
/** One page of resource templates, starting at `cursor`. */
|
|
274
|
-
async listResourceTemplatesPage(cursor, options = {}) {
|
|
275
|
-
const page = await this.listPage("resources/templates/list", "resourceTemplates", isResourceTemplate, cursor, options);
|
|
276
|
-
return {
|
|
277
|
-
resourceTemplates: page.items.map(toResourceTemplate),
|
|
278
|
-
...pageCursor(page)
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
async readResource(uri, options = {}) {
|
|
282
|
-
return validateReadResourceResult(await this.request("resources/read", { uri }, options));
|
|
283
|
-
}
|
|
284
|
-
async listPage(method, key, isItem, cursor, options) {
|
|
285
|
-
return validateListPage(method, key, await this.request(method, cursor === void 0 ? void 0 : { cursor }, options), isItem);
|
|
286
|
-
}
|
|
287
|
-
/** Every item of a paginated list method. */
|
|
288
|
-
async listAll(method, key, isItem, options) {
|
|
289
|
-
const items = [];
|
|
290
|
-
const cursors = /* @__PURE__ */ new Set();
|
|
291
|
-
let cursor;
|
|
292
|
-
for (let pageNumber = 0; pageNumber < MAX_LIST_PAGES; pageNumber++) {
|
|
293
|
-
const page = await this.listPage(method, key, isItem, cursor, options);
|
|
294
|
-
items.push(...page.items);
|
|
295
|
-
if (page.nextCursor === void 0) return items;
|
|
296
|
-
if (cursors.has(page.nextCursor)) throw new Error(`MCP ${method} returned duplicate cursor: ${page.nextCursor}`);
|
|
297
|
-
cursors.add(page.nextCursor);
|
|
298
|
-
cursor = page.nextCursor;
|
|
299
|
-
}
|
|
300
|
-
throw new Error(`MCP ${method} exceeded ${MAX_LIST_PAGES} pages`);
|
|
301
|
-
}
|
|
302
|
-
async callTool(name, args, options = {}) {
|
|
303
|
-
return validateCallToolResult(await this.request("tools/call", {
|
|
304
|
-
name,
|
|
305
|
-
...args === void 0 ? {} : { arguments: args }
|
|
306
|
-
}, options));
|
|
307
|
-
}
|
|
308
|
-
async close() {
|
|
309
|
-
const transport = this.transport;
|
|
310
|
-
this.transport = void 0;
|
|
311
|
-
this.disposeTransportListeners();
|
|
312
|
-
this.markClosed(new McpConnectionClosedError());
|
|
313
|
-
await transport?.close();
|
|
314
|
-
}
|
|
315
|
-
async requestInternal(method, params, options, allowConnecting) {
|
|
316
|
-
const transport = this.requireTransport(allowConnecting);
|
|
317
|
-
if (options.signal?.aborted) throw new McpAbortError();
|
|
318
|
-
const id = this.nextRequestId++;
|
|
319
|
-
const progressToken = options.onProgress ? id : void 0;
|
|
320
|
-
const requestParams = progressToken === void 0 ? params : {
|
|
321
|
-
...params,
|
|
322
|
-
_meta: {
|
|
323
|
-
...isObject(params?._meta) ? params._meta : {},
|
|
324
|
-
progressToken
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
const message = {
|
|
328
|
-
jsonrpc: "2.0",
|
|
329
|
-
id,
|
|
330
|
-
method,
|
|
331
|
-
...requestParams === void 0 ? {} : { params: requestParams }
|
|
332
|
-
};
|
|
333
|
-
return new Promise((resolve, reject) => {
|
|
334
|
-
const entry = {
|
|
335
|
-
resolve,
|
|
336
|
-
reject,
|
|
337
|
-
timeoutMs: options.timeoutMs ?? this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
|
|
338
|
-
timer: void 0,
|
|
339
|
-
signal: options.signal,
|
|
340
|
-
onAbort: () => this.cancelPending(id, new McpAbortError(), method !== "initialize", String(options.signal?.reason ?? "Aborted")),
|
|
341
|
-
sendController: new AbortController(),
|
|
342
|
-
cancellable: method !== "initialize",
|
|
343
|
-
onProgress: options.onProgress,
|
|
344
|
-
progressToken
|
|
345
|
-
};
|
|
346
|
-
this.pending.set(id, entry);
|
|
347
|
-
if (progressToken !== void 0) this.progressRequests.set(progressToken, id);
|
|
348
|
-
options.signal?.addEventListener("abort", entry.onAbort, { once: true });
|
|
349
|
-
this.armTimeout(id, entry);
|
|
350
|
-
transport.send(message, { signal: entry.sendController.signal }).catch((error) => this.cancelPending(id, error, false));
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
async notifyInternal(method, params, allowConnecting) {
|
|
354
|
-
await this.requireTransport(allowConnecting).send({
|
|
355
|
-
jsonrpc: "2.0",
|
|
356
|
-
method,
|
|
357
|
-
...params === void 0 ? {} : { params }
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
requireTransport(allowConnecting) {
|
|
361
|
-
if (this.transport && (this.state === "connected" || allowConnecting && this.state === "connecting")) return this.transport;
|
|
362
|
-
throw new McpConnectionClosedError(`MCP client is ${this.state}`);
|
|
363
|
-
}
|
|
364
|
-
handleMessage(message) {
|
|
365
|
-
if (isJsonRpcResponse(message)) {
|
|
366
|
-
this.handleResponse(message);
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
if (isJsonRpcRequest(message)) {
|
|
370
|
-
this.handleRequest(message);
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
if (isJsonRpcNotification(message)) {
|
|
374
|
-
this.handleNotification(message.method, message.params);
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
this.emitError(new McpError(JSON_RPC_ERROR_CODES.invalidRequest, "Received invalid JSON-RPC message"));
|
|
378
|
-
}
|
|
379
|
-
handleResponse(message) {
|
|
380
|
-
const entry = this.pending.get(message.id);
|
|
381
|
-
if (!entry) {
|
|
382
|
-
this.emitError(/* @__PURE__ */ new Error(`Received response for unknown MCP request ${String(message.id)}`));
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
this.removePending(message.id, entry);
|
|
386
|
-
if ("error" in message) entry.reject(new McpError(message.error.code, message.error.message, message.error.data));
|
|
387
|
-
else entry.resolve(message.result);
|
|
388
|
-
}
|
|
389
|
-
async handleRequest(message) {
|
|
390
|
-
const transport = this.transport;
|
|
391
|
-
if (!transport) return;
|
|
392
|
-
const handler = this.requestHandlers.get(message.method);
|
|
393
|
-
if (!handler) {
|
|
394
|
-
await transport.send({
|
|
395
|
-
jsonrpc: "2.0",
|
|
396
|
-
id: message.id,
|
|
397
|
-
error: {
|
|
398
|
-
code: JSON_RPC_ERROR_CODES.methodNotFound,
|
|
399
|
-
message: `Method not found: ${message.method}`
|
|
400
|
-
}
|
|
401
|
-
}).catch((error) => this.emitError(error));
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
const controller = new AbortController();
|
|
405
|
-
this.incoming.set(message.id, controller);
|
|
406
|
-
try {
|
|
407
|
-
const result = await handler(message.params, { signal: controller.signal });
|
|
408
|
-
await transport.send({
|
|
409
|
-
jsonrpc: "2.0",
|
|
410
|
-
id: message.id,
|
|
411
|
-
result: result ?? {}
|
|
412
|
-
});
|
|
413
|
-
} catch (error) {
|
|
414
|
-
const responseError = error instanceof McpError ? {
|
|
415
|
-
code: error.code,
|
|
416
|
-
message: error.message,
|
|
417
|
-
data: error.data
|
|
418
|
-
} : {
|
|
419
|
-
code: JSON_RPC_ERROR_CODES.internalError,
|
|
420
|
-
message: toError(error).message
|
|
421
|
-
};
|
|
422
|
-
await transport.send({
|
|
423
|
-
jsonrpc: "2.0",
|
|
424
|
-
id: message.id,
|
|
425
|
-
error: responseError
|
|
426
|
-
}).catch((sendError) => this.emitError(sendError));
|
|
427
|
-
} finally {
|
|
428
|
-
this.incoming.delete(message.id);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
handleNotification(method, params) {
|
|
432
|
-
if (method === "notifications/progress") this.handleProgress(params);
|
|
433
|
-
else if (method === "notifications/cancelled") this.handleCancelled(params);
|
|
434
|
-
for (const listener of this.notificationListeners.get(method) ?? []) try {
|
|
435
|
-
listener(params);
|
|
436
|
-
} catch (error) {
|
|
437
|
-
this.emitError(error);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
handleProgress(params) {
|
|
441
|
-
if (!isObject(params) || !isJsonRpcId(params.progressToken) || typeof params.progress !== "number") return;
|
|
442
|
-
const requestId = this.progressRequests.get(params.progressToken);
|
|
443
|
-
const entry = requestId === void 0 ? void 0 : this.pending.get(requestId);
|
|
444
|
-
if (requestId === void 0 || !entry) return;
|
|
445
|
-
this.armTimeout(requestId, entry);
|
|
446
|
-
try {
|
|
447
|
-
entry.onProgress?.(params);
|
|
448
|
-
} catch (error) {
|
|
449
|
-
this.emitError(error);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
handleCancelled(params) {
|
|
453
|
-
if (isObject(params) && isJsonRpcId(params.requestId)) this.incoming.get(params.requestId)?.abort(params.reason);
|
|
454
|
-
}
|
|
455
|
-
armTimeout(id, entry) {
|
|
456
|
-
if (entry.timer) clearTimeout(entry.timer);
|
|
457
|
-
if (!Number.isFinite(entry.timeoutMs) || entry.timeoutMs <= 0) return;
|
|
458
|
-
entry.timer = setTimeout(() => {
|
|
459
|
-
this.cancelPending(id, new McpTimeoutError(entry.timeoutMs), entry.cancellable, "Request timed out");
|
|
460
|
-
}, entry.timeoutMs);
|
|
461
|
-
}
|
|
462
|
-
cancelPending(id, error, notifyServer, reason) {
|
|
463
|
-
const entry = this.pending.get(id);
|
|
464
|
-
if (!entry) return;
|
|
465
|
-
this.removePending(id, entry);
|
|
466
|
-
entry.reject(error);
|
|
467
|
-
if (notifyServer && this.transport) this.transport.send({
|
|
468
|
-
jsonrpc: "2.0",
|
|
469
|
-
method: "notifications/cancelled",
|
|
470
|
-
params: {
|
|
471
|
-
requestId: id,
|
|
472
|
-
...reason ? { reason } : {}
|
|
473
|
-
}
|
|
474
|
-
}).catch((sendError) => this.emitError(sendError));
|
|
475
|
-
}
|
|
476
|
-
removePending(id, entry) {
|
|
477
|
-
this.pending.delete(id);
|
|
478
|
-
if (entry.timer) clearTimeout(entry.timer);
|
|
479
|
-
if (entry.progressToken !== void 0) this.progressRequests.delete(entry.progressToken);
|
|
480
|
-
entry.signal?.removeEventListener("abort", entry.onAbort);
|
|
481
|
-
entry.sendController.abort();
|
|
482
|
-
}
|
|
483
|
-
rejectPending(error) {
|
|
484
|
-
for (const [id, entry] of this.pending) {
|
|
485
|
-
this.removePending(id, entry);
|
|
486
|
-
entry.reject(error);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
handleTransportClose() {
|
|
490
|
-
this.markClosed(new McpConnectionClosedError());
|
|
491
|
-
}
|
|
492
|
-
/** Idempotent: rejects in-flight requests, aborts server requests we are serving, and flips the state. */
|
|
493
|
-
markClosed(error) {
|
|
494
|
-
const wasClosed = this.state === "closed";
|
|
495
|
-
this.state = "closed";
|
|
496
|
-
this.rejectPending(error);
|
|
497
|
-
for (const controller of this.incoming.values()) controller.abort(error);
|
|
498
|
-
this.incoming.clear();
|
|
499
|
-
if (wasClosed) return;
|
|
500
|
-
for (const listener of [...this.closeListeners]) try {
|
|
501
|
-
listener();
|
|
502
|
-
} catch (listenerError) {
|
|
503
|
-
this.emitError(listenerError);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
emitError(error) {
|
|
507
|
-
const normalized = toError(error);
|
|
508
|
-
for (const listener of this.errorListeners) listener(normalized);
|
|
509
|
-
}
|
|
510
|
-
disposeTransportListeners() {
|
|
511
|
-
for (const dispose of this.disposers.splice(0)) dispose();
|
|
512
|
-
}
|
|
513
|
-
};
|
|
514
|
-
/** Listener bookkeeping shared by transports. `emitClose` fires at most once per transport. */
|
|
515
|
-
var TransportEvents = class {
|
|
516
|
-
messageListeners = /* @__PURE__ */ new Set();
|
|
517
|
-
errorListeners = /* @__PURE__ */ new Set();
|
|
518
|
-
closeListeners = /* @__PURE__ */ new Set();
|
|
519
|
-
closeEmitted = false;
|
|
520
|
-
onMessage(listener) {
|
|
521
|
-
this.messageListeners.add(listener);
|
|
522
|
-
return () => this.messageListeners.delete(listener);
|
|
523
|
-
}
|
|
524
|
-
onError(listener) {
|
|
525
|
-
this.errorListeners.add(listener);
|
|
526
|
-
return () => this.errorListeners.delete(listener);
|
|
527
|
-
}
|
|
528
|
-
onClose(listener) {
|
|
529
|
-
this.closeListeners.add(listener);
|
|
530
|
-
return () => this.closeListeners.delete(listener);
|
|
531
|
-
}
|
|
532
|
-
emitMessage(message) {
|
|
533
|
-
for (const listener of this.messageListeners) listener(message);
|
|
534
|
-
}
|
|
535
|
-
emitError(error) {
|
|
536
|
-
const normalized = toError(error);
|
|
537
|
-
for (const listener of this.errorListeners) listener(normalized);
|
|
538
|
-
}
|
|
539
|
-
emitClose() {
|
|
540
|
-
if (this.closeEmitted) return;
|
|
541
|
-
this.closeEmitted = true;
|
|
542
|
-
for (const listener of this.closeListeners) listener();
|
|
543
|
-
}
|
|
544
|
-
};
|
|
545
|
-
//#endregion
|
|
546
|
-
//#region node_modules/.pnpm/@earendil-works+pi-mcp@1.2.0/node_modules/@earendil-works/pi-mcp/dist/transports/stdio.js
|
|
547
|
-
var import_cross_spawn = /* @__PURE__ */ __toESM(require_cross_spawn(), 1);
|
|
548
|
-
const DEFAULT_MAX_STDERR_BYTES = 65536;
|
|
549
|
-
const DEFAULT_CLOSE_TIMEOUT_MS = 2e3;
|
|
550
|
-
/** How long a server gets to exit on its own after stdin closes, before it is sent SIGTERM. */
|
|
551
|
-
const STDIN_CLOSE_GRACE_MS = 500;
|
|
552
|
-
const USE_PROCESS_GROUPS = fE.platform !== "win32";
|
|
553
|
-
/** Process groups of running servers, killed if the host exits without closing them. */
|
|
554
|
-
const liveProcessGroups = /* @__PURE__ */ new Set();
|
|
555
|
-
let exitHookInstalled = false;
|
|
556
|
-
function killProcessTree(child, signal) {
|
|
557
|
-
const pid = child.pid;
|
|
558
|
-
if (fE.platform === "win32" && pid !== void 0) {
|
|
559
|
-
if (child.exitCode !== null) return;
|
|
560
|
-
spawn("taskkill", [
|
|
561
|
-
"/pid",
|
|
562
|
-
String(pid),
|
|
563
|
-
"/T",
|
|
564
|
-
"/F"
|
|
565
|
-
], {
|
|
566
|
-
stdio: "ignore",
|
|
567
|
-
windowsHide: true
|
|
568
|
-
}).on("error", () => {});
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
if (USE_PROCESS_GROUPS && pid !== void 0) try {
|
|
572
|
-
fE.kill(-pid, signal);
|
|
573
|
-
return;
|
|
574
|
-
} catch {}
|
|
575
|
-
try {
|
|
576
|
-
child.kill(signal);
|
|
577
|
-
} catch {}
|
|
578
|
-
}
|
|
579
|
-
function installExitHook() {
|
|
580
|
-
if (exitHookInstalled) return;
|
|
581
|
-
exitHookInstalled = true;
|
|
582
|
-
fE.once("exit", () => {
|
|
583
|
-
for (const pid of liveProcessGroups) try {
|
|
584
|
-
fE.kill(-pid, "SIGTERM");
|
|
585
|
-
} catch {}
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
var StdioTransport = class extends TransportEvents {
|
|
589
|
-
options;
|
|
590
|
-
child;
|
|
591
|
-
stdoutBuffer = Buffer.alloc(0);
|
|
592
|
-
stderrBuffer = Buffer.alloc(0);
|
|
593
|
-
started = false;
|
|
594
|
-
closed = false;
|
|
595
|
-
constructor(options) {
|
|
596
|
-
super();
|
|
597
|
-
this.options = Object.freeze({
|
|
598
|
-
...options,
|
|
599
|
-
args: options.args ? [...options.args] : void 0
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
get pid() {
|
|
603
|
-
return this.child?.pid;
|
|
604
|
-
}
|
|
605
|
-
get stderr() {
|
|
606
|
-
return this.stderrBuffer.toString("utf8");
|
|
607
|
-
}
|
|
608
|
-
async start() {
|
|
609
|
-
if (this.started) throw new Error("MCP stdio transport already started");
|
|
610
|
-
if (this.closed) throw new McpConnectionClosedError();
|
|
611
|
-
this.started = true;
|
|
612
|
-
const env = this.options.inheritEnv === false ? { ...this.options.env } : {
|
|
613
|
-
...fE.env,
|
|
614
|
-
...this.options.env
|
|
615
|
-
};
|
|
616
|
-
const child = (0, import_cross_spawn.default)(this.options.command, this.options.args ?? [], {
|
|
617
|
-
cwd: this.options.cwd,
|
|
618
|
-
env,
|
|
619
|
-
stdio: [
|
|
620
|
-
"pipe",
|
|
621
|
-
"pipe",
|
|
622
|
-
this.options.stderr === "inherit" ? "inherit" : "pipe"
|
|
623
|
-
],
|
|
624
|
-
windowsHide: true,
|
|
625
|
-
detached: USE_PROCESS_GROUPS
|
|
626
|
-
});
|
|
627
|
-
this.child = child;
|
|
628
|
-
const pid = child.pid;
|
|
629
|
-
if (USE_PROCESS_GROUPS && pid !== void 0) {
|
|
630
|
-
installExitHook();
|
|
631
|
-
liveProcessGroups.add(pid);
|
|
632
|
-
child.once("exit", () => liveProcessGroups.delete(pid));
|
|
633
|
-
}
|
|
634
|
-
child.stdout?.on("data", (chunk) => this.handleStdout(chunk));
|
|
635
|
-
child.stdout?.on("error", (error) => this.emitError(error));
|
|
636
|
-
child.stdin?.on("error", (error) => {
|
|
637
|
-
if (!this.closed) this.emitError(error);
|
|
638
|
-
});
|
|
639
|
-
child.stderr?.on("data", (chunk) => this.handleStderr(chunk));
|
|
640
|
-
child.stderr?.on("error", (error) => this.emitError(error));
|
|
641
|
-
child.on("close", () => {
|
|
642
|
-
this.child = void 0;
|
|
643
|
-
if (this.stdoutBuffer.toString("utf8").trim()) this.emitError(/* @__PURE__ */ new Error("MCP stdio server closed with an incomplete JSON-RPC message"));
|
|
644
|
-
this.stdoutBuffer = Buffer.alloc(0);
|
|
645
|
-
this.emitClose();
|
|
646
|
-
});
|
|
647
|
-
await new Promise((resolve, reject) => {
|
|
648
|
-
const onSpawn = () => {
|
|
649
|
-
child.off("error", onError);
|
|
650
|
-
resolve();
|
|
651
|
-
};
|
|
652
|
-
const onError = (error) => {
|
|
653
|
-
child.off("spawn", onSpawn);
|
|
654
|
-
reject(error);
|
|
655
|
-
};
|
|
656
|
-
child.once("spawn", onSpawn);
|
|
657
|
-
child.once("error", onError);
|
|
658
|
-
});
|
|
659
|
-
child.on("error", (error) => {
|
|
660
|
-
if (!this.closed) this.emitError(error);
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
async send(message) {
|
|
664
|
-
const stdin = this.child?.stdin;
|
|
665
|
-
if (!this.started || this.closed || !stdin?.writable) throw new McpConnectionClosedError();
|
|
666
|
-
const payload = `${JSON.stringify(message)}\n`;
|
|
667
|
-
await new Promise((resolve, reject) => {
|
|
668
|
-
stdin.write(payload, (error) => error ? reject(error) : resolve());
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
async close() {
|
|
672
|
-
if (this.closed) return;
|
|
673
|
-
this.closed = true;
|
|
674
|
-
const child = this.child;
|
|
675
|
-
if (!child) {
|
|
676
|
-
this.emitClose();
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
if (child.exitCode !== null || child.signalCode !== null) {
|
|
680
|
-
child.stdin?.end();
|
|
681
|
-
return;
|
|
682
|
-
}
|
|
683
|
-
const closeTimeoutMs = this.options.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS;
|
|
684
|
-
await new Promise((resolve) => {
|
|
685
|
-
const timers = [];
|
|
686
|
-
child.once("close", () => {
|
|
687
|
-
for (const timer of timers) clearTimeout(timer);
|
|
688
|
-
killProcessTree(child, "SIGTERM");
|
|
689
|
-
resolve();
|
|
690
|
-
});
|
|
691
|
-
const grace = Math.min(STDIN_CLOSE_GRACE_MS, closeTimeoutMs);
|
|
692
|
-
timers.push(setTimeout(() => killProcessTree(child, "SIGTERM"), grace));
|
|
693
|
-
timers.push(setTimeout(() => killProcessTree(child, "SIGKILL"), grace + closeTimeoutMs));
|
|
694
|
-
child.stdin?.end();
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
handleStdout(chunk) {
|
|
698
|
-
this.stdoutBuffer = Buffer.concat([this.stdoutBuffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]);
|
|
699
|
-
const maxMessageBytes = this.options.maxMessageBytes ?? 16777216;
|
|
700
|
-
while (true) {
|
|
701
|
-
const newline = this.stdoutBuffer.indexOf(10);
|
|
702
|
-
if (newline < 0) {
|
|
703
|
-
if (this.stdoutBuffer.length > maxMessageBytes) {
|
|
704
|
-
this.stdoutBuffer = Buffer.alloc(0);
|
|
705
|
-
this.emitError(/* @__PURE__ */ new Error(`MCP stdio message exceeds ${maxMessageBytes} bytes`));
|
|
706
|
-
}
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
const line = this.stdoutBuffer.subarray(0, newline);
|
|
710
|
-
this.stdoutBuffer = this.stdoutBuffer.subarray(newline + 1);
|
|
711
|
-
if (line.length > maxMessageBytes) {
|
|
712
|
-
this.emitError(/* @__PURE__ */ new Error(`MCP stdio message exceeds ${maxMessageBytes} bytes`));
|
|
713
|
-
continue;
|
|
714
|
-
}
|
|
715
|
-
const text = line.toString("utf8").replace(/\r$/, "");
|
|
716
|
-
if (!text.trim()) continue;
|
|
717
|
-
try {
|
|
718
|
-
this.emitMessage(parseJsonRpcMessage(JSON.parse(text)));
|
|
719
|
-
} catch (error) {
|
|
720
|
-
this.emitError(error);
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
handleStderr(chunk) {
|
|
725
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
726
|
-
const maxStderrBytes = this.options.maxStderrBytes ?? DEFAULT_MAX_STDERR_BYTES;
|
|
727
|
-
this.stderrBuffer = Buffer.concat([this.stderrBuffer, buffer]);
|
|
728
|
-
if (this.stderrBuffer.length > maxStderrBytes) this.stderrBuffer = this.stderrBuffer.subarray(-maxStderrBytes);
|
|
729
|
-
this.options.onStderr?.(buffer.toString("utf8"));
|
|
730
|
-
}
|
|
731
|
-
};
|
|
732
|
-
//#endregion
|
|
733
|
-
//#region node_modules/.pnpm/@earendil-works+pi-mcp@1.2.0/node_modules/@earendil-works/pi-mcp/dist/transports/streamable-http.js
|
|
734
|
-
const MAX_ERROR_BODY_BYTES = 8192;
|
|
735
|
-
const ERROR_MESSAGE_BODY_CHARS = 500;
|
|
736
|
-
const DEFAULT_RECONNECT_INITIAL_DELAY_MS = 1e3;
|
|
737
|
-
const DEFAULT_RECONNECT_MAX_DELAY_MS = 3e4;
|
|
738
|
-
const DEFAULT_RECONNECT_MAX_RETRIES = 5;
|
|
739
|
-
async function consumeSseStream(stream, options) {
|
|
740
|
-
const reader = stream.getReader();
|
|
741
|
-
const decoder = new TextDecoder();
|
|
742
|
-
let buffered = "";
|
|
743
|
-
let eventName;
|
|
744
|
-
let eventId;
|
|
745
|
-
let dataLines = [];
|
|
746
|
-
let dataBytes = 0;
|
|
747
|
-
const maxEventBytes = options.maxEventBytes ?? 16777216;
|
|
748
|
-
const dispatch = () => {
|
|
749
|
-
if (dataLines.length === 0) {
|
|
750
|
-
eventName = void 0;
|
|
751
|
-
eventId = void 0;
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
|
-
const data = dataLines.join("\n");
|
|
755
|
-
options.onEvent({
|
|
756
|
-
...eventName ? { event: eventName } : {},
|
|
757
|
-
data,
|
|
758
|
-
...eventId ? { id: eventId } : {}
|
|
759
|
-
});
|
|
760
|
-
eventName = void 0;
|
|
761
|
-
eventId = void 0;
|
|
762
|
-
dataLines = [];
|
|
763
|
-
dataBytes = 0;
|
|
764
|
-
};
|
|
765
|
-
const processLine = (rawLine) => {
|
|
766
|
-
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
767
|
-
if (line === "") {
|
|
768
|
-
dispatch();
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
if (line.startsWith(":")) return;
|
|
772
|
-
const colon = line.indexOf(":");
|
|
773
|
-
const field = colon < 0 ? line : line.slice(0, colon);
|
|
774
|
-
let value = colon < 0 ? "" : line.slice(colon + 1);
|
|
775
|
-
if (value.startsWith(" ")) value = value.slice(1);
|
|
776
|
-
if (field === "data") {
|
|
777
|
-
dataBytes += Buffer.byteLength(value) + (dataLines.length > 0 ? 1 : 0);
|
|
778
|
-
if (dataBytes > maxEventBytes) throw new Error(`MCP SSE event exceeds ${maxEventBytes} bytes`);
|
|
779
|
-
dataLines.push(value);
|
|
780
|
-
} else if (field === "event") eventName = value;
|
|
781
|
-
else if (field === "id" && !value.includes("\0")) {
|
|
782
|
-
eventId = value;
|
|
783
|
-
options.onId?.(value);
|
|
784
|
-
} else if (field === "retry" && /^\d+$/.test(value)) options.onRetry?.(Number(value));
|
|
785
|
-
};
|
|
786
|
-
try {
|
|
787
|
-
while (true) {
|
|
788
|
-
const { done, value } = await reader.read();
|
|
789
|
-
if (done) break;
|
|
790
|
-
buffered += decoder.decode(value, { stream: true });
|
|
791
|
-
let newline = buffered.indexOf("\n");
|
|
792
|
-
while (newline >= 0) {
|
|
793
|
-
processLine(buffered.slice(0, newline));
|
|
794
|
-
buffered = buffered.slice(newline + 1);
|
|
795
|
-
newline = buffered.indexOf("\n");
|
|
796
|
-
}
|
|
797
|
-
if (Buffer.byteLength(buffered) > maxEventBytes) throw new Error(`MCP SSE event exceeds ${maxEventBytes} bytes`);
|
|
798
|
-
}
|
|
799
|
-
buffered += decoder.decode();
|
|
800
|
-
if (buffered) processLine(buffered);
|
|
801
|
-
dispatch();
|
|
802
|
-
} finally {
|
|
803
|
-
reader.releaseLock();
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
var McpHttpError = class extends Error {
|
|
807
|
-
status;
|
|
808
|
-
body;
|
|
809
|
-
constructor(status, message, body = "") {
|
|
810
|
-
super(message);
|
|
811
|
-
this.name = "McpHttpError";
|
|
812
|
-
this.status = status;
|
|
813
|
-
this.body = body;
|
|
814
|
-
}
|
|
815
|
-
};
|
|
816
|
-
var McpAuthRequiredError = class extends McpHttpError {
|
|
817
|
-
wwwAuthenticate;
|
|
818
|
-
constructor(response, body = "") {
|
|
819
|
-
super(401, "MCP server requires authentication", body);
|
|
820
|
-
this.name = "McpAuthRequiredError";
|
|
821
|
-
this.wwwAuthenticate = response.headers.get("www-authenticate");
|
|
822
|
-
}
|
|
823
|
-
};
|
|
824
|
-
var McpSessionExpiredError = class extends McpHttpError {
|
|
825
|
-
constructor(body = "") {
|
|
826
|
-
super(404, "MCP session expired", body);
|
|
827
|
-
this.name = "McpSessionExpiredError";
|
|
828
|
-
}
|
|
829
|
-
};
|
|
830
|
-
function contentType(response) {
|
|
831
|
-
return response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
832
|
-
}
|
|
833
|
-
/** 401, or 403 with an `insufficient_scope` bearer challenge (step-up authorization). */
|
|
834
|
-
function needsAuthorization(response) {
|
|
835
|
-
if (response.status === 401) return true;
|
|
836
|
-
if (response.status !== 403) return false;
|
|
837
|
-
return /(?:^|[\s,])error="?insufficient_scope"?/i.test(response.headers.get("www-authenticate") ?? "");
|
|
838
|
-
}
|
|
839
|
-
/** Statuses worth retrying when a stream fails to (re)open. */
|
|
840
|
-
function isTransientStatus(status) {
|
|
841
|
-
return status === 408 || status === 429 || status >= 500;
|
|
842
|
-
}
|
|
843
|
-
function discard$1(response) {
|
|
844
|
-
return response.body?.cancel().catch(() => {}) ?? Promise.resolve();
|
|
845
|
-
}
|
|
846
|
-
function describeHttpFailure(status, body) {
|
|
847
|
-
const text = body.trim();
|
|
848
|
-
const snippet = text.length > ERROR_MESSAGE_BODY_CHARS ? `${text.slice(0, 497)}...` : text;
|
|
849
|
-
return `MCP HTTP request failed with status ${status}${snippet ? `: ${snippet}` : ""}`;
|
|
850
|
-
}
|
|
851
|
-
var StreamableHttpTransport = class extends TransportEvents {
|
|
852
|
-
url;
|
|
853
|
-
options;
|
|
854
|
-
fetch;
|
|
855
|
-
controller = new AbortController();
|
|
856
|
-
started = false;
|
|
857
|
-
closed = false;
|
|
858
|
-
sessionIdValue;
|
|
859
|
-
protocolVersion;
|
|
860
|
-
getStreamStarted = false;
|
|
861
|
-
/** Access token of the latest request, which `close()` reuses instead of asking the auth provider. */
|
|
862
|
-
lastToken;
|
|
863
|
-
constructor(options) {
|
|
864
|
-
super();
|
|
865
|
-
this.options = Object.freeze({
|
|
866
|
-
...options,
|
|
867
|
-
headers: options.headers ? { ...options.headers } : void 0
|
|
868
|
-
});
|
|
869
|
-
this.url = new URL(options.url);
|
|
870
|
-
const fetch = options.fetch ?? globalThis.fetch;
|
|
871
|
-
this.fetch = (input, init) => fetch(input, init);
|
|
872
|
-
}
|
|
873
|
-
get sessionId() {
|
|
874
|
-
return this.sessionIdValue;
|
|
875
|
-
}
|
|
876
|
-
async start() {
|
|
877
|
-
if (this.started) throw new Error("MCP Streamable HTTP transport already started");
|
|
878
|
-
if (this.closed) throw new McpConnectionClosedError();
|
|
879
|
-
this.started = true;
|
|
880
|
-
}
|
|
881
|
-
setProtocolVersion(version) {
|
|
882
|
-
this.protocolVersion = version;
|
|
883
|
-
}
|
|
884
|
-
async send(message, { signal } = {}) {
|
|
885
|
-
if (!this.started || this.closed) throw new McpConnectionClosedError();
|
|
886
|
-
const response = await this.authorizedFetch("POST", {
|
|
887
|
-
headers: {
|
|
888
|
-
accept: "application/json, text/event-stream",
|
|
889
|
-
"content-type": "application/json"
|
|
890
|
-
},
|
|
891
|
-
body: JSON.stringify(message)
|
|
892
|
-
}, signal);
|
|
893
|
-
await this.checkResponse(response);
|
|
894
|
-
this.captureSession(response);
|
|
895
|
-
if (!isJsonRpcRequest(message)) {
|
|
896
|
-
await discard$1(response);
|
|
897
|
-
if ("method" in message && message.method === "notifications/initialized") this.startGetStream();
|
|
898
|
-
return;
|
|
899
|
-
}
|
|
900
|
-
if (response.status === 202 || response.status === 204) throw new McpHttpError(response.status, `MCP server accepted request ${message.method} without a response`);
|
|
901
|
-
const type = contentType(response);
|
|
902
|
-
if (type === "application/json") {
|
|
903
|
-
const body = await response.json();
|
|
904
|
-
for (const item of Array.isArray(body) ? body : [body]) this.emitMessage(parseJsonRpcMessage(item));
|
|
905
|
-
return;
|
|
906
|
-
}
|
|
907
|
-
if (type === "text/event-stream" && response.body) {
|
|
908
|
-
this.consumeResponseStream(response.body, message.id);
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
await discard$1(response);
|
|
912
|
-
throw new McpHttpError(response.status, `Unsupported MCP response content type: ${type ?? "missing"}`);
|
|
913
|
-
}
|
|
914
|
-
async close() {
|
|
915
|
-
if (this.closed) return;
|
|
916
|
-
this.closed = true;
|
|
917
|
-
this.controller.abort();
|
|
918
|
-
if (this.started && this.sessionIdValue) try {
|
|
919
|
-
await discard$1(await this.fetch(this.url, {
|
|
920
|
-
method: "DELETE",
|
|
921
|
-
headers: this.buildHeaders({}, this.lastToken),
|
|
922
|
-
signal: AbortSignal.timeout(1e3)
|
|
923
|
-
}));
|
|
924
|
-
} catch {}
|
|
925
|
-
this.emitClose();
|
|
926
|
-
}
|
|
927
|
-
/**
|
|
928
|
-
* Fetch with auth headers. A 401 (or a 403 asking for more scope) is handed to the auth provider
|
|
929
|
-
* once, and the request is retried with whatever credentials it left behind.
|
|
930
|
-
*
|
|
931
|
-
* `signal` only stops dispatch: getting a token or the authorization recovery can take a while, and a
|
|
932
|
-
* request its sender gave up on in the meantime must not reach the server. It does not abort the shared
|
|
933
|
-
* recovery, which other requests may wait for.
|
|
934
|
-
*/
|
|
935
|
-
async authorizedFetch(method, init, signal) {
|
|
936
|
-
const onUnauthorized = this.options.authProvider?.onUnauthorized?.bind(this.options.authProvider);
|
|
937
|
-
for (let attempt = 0;; attempt++) {
|
|
938
|
-
const { headers, token } = await this.headers(init.headers);
|
|
939
|
-
if (signal?.aborted) throw new McpAbortError();
|
|
940
|
-
const response = await this.fetch(this.url, {
|
|
941
|
-
method,
|
|
942
|
-
headers,
|
|
943
|
-
body: init.body,
|
|
944
|
-
signal: this.controller.signal
|
|
945
|
-
});
|
|
946
|
-
if (attempt > 0 || !onUnauthorized || !needsAuthorization(response)) return response;
|
|
947
|
-
try {
|
|
948
|
-
await onUnauthorized({
|
|
949
|
-
response,
|
|
950
|
-
serverUrl: this.url,
|
|
951
|
-
fetch: this.fetch,
|
|
952
|
-
token
|
|
953
|
-
});
|
|
954
|
-
} finally {
|
|
955
|
-
await discard$1(response);
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
async headers(extra = {}) {
|
|
960
|
-
const token = await this.options.authProvider?.token();
|
|
961
|
-
this.lastToken = token;
|
|
962
|
-
return {
|
|
963
|
-
headers: this.buildHeaders(extra, token),
|
|
964
|
-
...token ? { token } : {}
|
|
965
|
-
};
|
|
966
|
-
}
|
|
967
|
-
buildHeaders(extra, token) {
|
|
968
|
-
const headers = new Headers(this.options.headers);
|
|
969
|
-
for (const [name, value] of Object.entries(extra)) headers.set(name, value);
|
|
970
|
-
if (this.sessionIdValue) headers.set("Mcp-Session-Id", this.sessionIdValue);
|
|
971
|
-
if (this.protocolVersion) headers.set("MCP-Protocol-Version", this.protocolVersion);
|
|
972
|
-
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
973
|
-
return headers;
|
|
974
|
-
}
|
|
975
|
-
captureSession(response) {
|
|
976
|
-
const sessionId = response.headers.get("mcp-session-id");
|
|
977
|
-
if (sessionId) this.sessionIdValue = sessionId;
|
|
978
|
-
}
|
|
979
|
-
async checkResponse(response) {
|
|
980
|
-
if (response.ok) return;
|
|
981
|
-
const body = (await response.text().catch(() => "")).slice(0, MAX_ERROR_BODY_BYTES);
|
|
982
|
-
if (response.status === 401) throw new McpAuthRequiredError(response, body);
|
|
983
|
-
if (response.status === 404 && this.sessionIdValue) throw new McpSessionExpiredError(body);
|
|
984
|
-
throw new McpHttpError(response.status, describeHttpFailure(response.status, body), body);
|
|
985
|
-
}
|
|
986
|
-
async consumeSse(stream, cursor, onMessage) {
|
|
987
|
-
await consumeSseStream(stream, {
|
|
988
|
-
maxEventBytes: this.options.maxMessageBytes ?? 16777216,
|
|
989
|
-
onId: (id) => {
|
|
990
|
-
cursor.lastEventId = id;
|
|
991
|
-
},
|
|
992
|
-
onRetry: (delayMs) => {
|
|
993
|
-
cursor.retryMs = delayMs;
|
|
994
|
-
},
|
|
995
|
-
onEvent: (event) => {
|
|
996
|
-
cursor.received = true;
|
|
997
|
-
if (!event.data.trim() || event.event !== void 0 && event.event !== "message") return;
|
|
998
|
-
let message;
|
|
999
|
-
try {
|
|
1000
|
-
message = parseJsonRpcMessage(JSON.parse(event.data));
|
|
1001
|
-
} catch (error) {
|
|
1002
|
-
this.emitError(error);
|
|
1003
|
-
return;
|
|
1004
|
-
}
|
|
1005
|
-
onMessage?.(message);
|
|
1006
|
-
this.emitMessage(message);
|
|
1007
|
-
}
|
|
1008
|
-
});
|
|
1009
|
-
}
|
|
1010
|
-
/**
|
|
1011
|
-
* Read the SSE stream answering one request. When the stream ends or breaks before the response
|
|
1012
|
-
* arrives and the server assigned event IDs, resume it with GET and `Last-Event-ID`, as the
|
|
1013
|
-
* server may close response streams at will. Otherwise only this request fails.
|
|
1014
|
-
*/
|
|
1015
|
-
async consumeResponseStream(body, requestId) {
|
|
1016
|
-
const cursor = {
|
|
1017
|
-
lastEventId: void 0,
|
|
1018
|
-
retryMs: void 0,
|
|
1019
|
-
received: false
|
|
1020
|
-
};
|
|
1021
|
-
let answered = false;
|
|
1022
|
-
const onMessage = (message) => {
|
|
1023
|
-
if (isJsonRpcResponse(message) && message.id === requestId) answered = true;
|
|
1024
|
-
};
|
|
1025
|
-
let stream = body;
|
|
1026
|
-
let failure;
|
|
1027
|
-
for (let attempt = 0;;) {
|
|
1028
|
-
if (stream) try {
|
|
1029
|
-
await this.consumeSse(stream, cursor, onMessage);
|
|
1030
|
-
failure = void 0;
|
|
1031
|
-
} catch (error) {
|
|
1032
|
-
failure = error;
|
|
1033
|
-
}
|
|
1034
|
-
if (answered || this.closed) return;
|
|
1035
|
-
if (failure !== void 0 && !this.isRetryable(failure)) break;
|
|
1036
|
-
if (cursor.lastEventId === void 0 || attempt >= this.maxRetries()) break;
|
|
1037
|
-
if (cursor.received) attempt = 0;
|
|
1038
|
-
cursor.received = false;
|
|
1039
|
-
if (!await this.sleep(this.reconnectDelay(attempt++, cursor.retryMs))) return;
|
|
1040
|
-
try {
|
|
1041
|
-
stream = await this.openSseStream(cursor.lastEventId);
|
|
1042
|
-
} catch (error) {
|
|
1043
|
-
failure = error;
|
|
1044
|
-
if (!this.isRetryable(error)) break;
|
|
1045
|
-
stream = void 0;
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
if (this.closed) return;
|
|
1049
|
-
const reason = failure === void 0 ? "stream ended without a response" : toError(failure).message;
|
|
1050
|
-
this.emitMessage({
|
|
1051
|
-
jsonrpc: "2.0",
|
|
1052
|
-
id: requestId,
|
|
1053
|
-
error: {
|
|
1054
|
-
code: JSON_RPC_ERROR_CODES.internalError,
|
|
1055
|
-
message: `MCP response stream failed: ${reason}`
|
|
1056
|
-
}
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
1059
|
-
startGetStream() {
|
|
1060
|
-
if (this.options.openGetStream === false || this.getStreamStarted || this.closed) return;
|
|
1061
|
-
this.getStreamStarted = true;
|
|
1062
|
-
this.runGetStream();
|
|
1063
|
-
}
|
|
1064
|
-
/** Keep the server-to-client stream open, reconnecting with backoff when it drops. */
|
|
1065
|
-
async runGetStream() {
|
|
1066
|
-
const cursor = {
|
|
1067
|
-
lastEventId: void 0,
|
|
1068
|
-
retryMs: void 0,
|
|
1069
|
-
received: false
|
|
1070
|
-
};
|
|
1071
|
-
for (let attempt = 0; !this.closed;) {
|
|
1072
|
-
try {
|
|
1073
|
-
const stream = await this.openSseStream(cursor.lastEventId);
|
|
1074
|
-
if (!stream) return;
|
|
1075
|
-
const openedAt = Date.now();
|
|
1076
|
-
await this.consumeSse(stream, cursor);
|
|
1077
|
-
if (cursor.received || Date.now() - openedAt > this.maxDelay()) attempt = 0;
|
|
1078
|
-
} catch (error) {
|
|
1079
|
-
if (this.closed) return;
|
|
1080
|
-
if (!this.isRetryable(error)) {
|
|
1081
|
-
this.emitError(error);
|
|
1082
|
-
return;
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
cursor.received = false;
|
|
1086
|
-
if (attempt >= this.maxRetries()) {
|
|
1087
|
-
this.emitError(/* @__PURE__ */ new Error("MCP server-to-client stream dropped and could not be reopened"));
|
|
1088
|
-
return;
|
|
1089
|
-
}
|
|
1090
|
-
if (!await this.sleep(this.reconnectDelay(attempt++, cursor.retryMs))) return;
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
/** Open a GET SSE stream. Resolves to undefined when the server answers 405 (no GET stream). */
|
|
1094
|
-
async openSseStream(lastEventId) {
|
|
1095
|
-
const response = await this.authorizedFetch("GET", { headers: {
|
|
1096
|
-
accept: "text/event-stream",
|
|
1097
|
-
...lastEventId === void 0 ? {} : { "last-event-id": lastEventId }
|
|
1098
|
-
} });
|
|
1099
|
-
if (response.status === 405) {
|
|
1100
|
-
await discard$1(response);
|
|
1101
|
-
return;
|
|
1102
|
-
}
|
|
1103
|
-
await this.checkResponse(response);
|
|
1104
|
-
this.captureSession(response);
|
|
1105
|
-
const type = contentType(response);
|
|
1106
|
-
if (type !== "text/event-stream" || !response.body) {
|
|
1107
|
-
await discard$1(response);
|
|
1108
|
-
throw new McpHttpError(response.status, `Unsupported MCP GET response content type: ${type ?? "missing"}`);
|
|
1109
|
-
}
|
|
1110
|
-
return response.body;
|
|
1111
|
-
}
|
|
1112
|
-
/**
|
|
1113
|
-
* Network failures and transient statuses are retried; auth, session, and protocol errors are not.
|
|
1114
|
-
* `fetch` reports network failures, including a connection dropped mid-body, as `TypeError`.
|
|
1115
|
-
*/
|
|
1116
|
-
isRetryable(error) {
|
|
1117
|
-
if (error instanceof McpHttpError) return isTransientStatus(error.status);
|
|
1118
|
-
if (error instanceof TypeError) return true;
|
|
1119
|
-
const code = error?.code;
|
|
1120
|
-
return typeof code === "string" && (code.startsWith("E") || code.startsWith("UND_ERR"));
|
|
1121
|
-
}
|
|
1122
|
-
reconnectDelay(attempt, serverDelayMs) {
|
|
1123
|
-
if (serverDelayMs !== void 0) return serverDelayMs;
|
|
1124
|
-
const initial = this.options.reconnect?.initialDelayMs ?? DEFAULT_RECONNECT_INITIAL_DELAY_MS;
|
|
1125
|
-
return Math.min(initial * 2 ** attempt, this.maxDelay());
|
|
1126
|
-
}
|
|
1127
|
-
maxDelay() {
|
|
1128
|
-
return this.options.reconnect?.maxDelayMs ?? DEFAULT_RECONNECT_MAX_DELAY_MS;
|
|
1129
|
-
}
|
|
1130
|
-
maxRetries() {
|
|
1131
|
-
return this.options.reconnect?.maxRetries ?? DEFAULT_RECONNECT_MAX_RETRIES;
|
|
1132
|
-
}
|
|
1133
|
-
/** Resolves false when the transport closed while waiting. */
|
|
1134
|
-
sleep(ms) {
|
|
1135
|
-
const signal = this.controller.signal;
|
|
1136
|
-
if (signal.aborted) return Promise.resolve(false);
|
|
1137
|
-
return new Promise((resolve) => {
|
|
1138
|
-
const onAbort = () => {
|
|
1139
|
-
clearTimeout(timer);
|
|
1140
|
-
resolve(false);
|
|
1141
|
-
};
|
|
1142
|
-
const timer = setTimeout(() => {
|
|
1143
|
-
signal.removeEventListener("abort", onAbort);
|
|
1144
|
-
resolve(true);
|
|
1145
|
-
}, ms);
|
|
1146
|
-
timer.unref?.();
|
|
1147
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
1148
|
-
});
|
|
1149
|
-
}
|
|
1150
|
-
};
|
|
1151
|
-
//#endregion
|
|
1152
|
-
//#region node_modules/.pnpm/@earendil-works+pi-mcp@1.2.0/node_modules/@earendil-works/pi-mcp/dist/oauth/callback.js
|
|
1153
|
-
function plainText(page) {
|
|
1154
|
-
if (page.ok) return "Authorization complete. You may close this window.";
|
|
1155
|
-
return page.details ? `${page.message}\n\n${page.details}` : page.message;
|
|
1156
|
-
}
|
|
1157
|
-
var OAuthCallbackServer = class OAuthCallbackServer {
|
|
1158
|
-
redirectUrl;
|
|
1159
|
-
server;
|
|
1160
|
-
paths;
|
|
1161
|
-
timeoutMs;
|
|
1162
|
-
renderPage;
|
|
1163
|
-
pending = /* @__PURE__ */ new Map();
|
|
1164
|
-
constructor(server, redirectUrl, paths, timeoutMs, renderPage) {
|
|
1165
|
-
this.server = server;
|
|
1166
|
-
this.redirectUrl = redirectUrl;
|
|
1167
|
-
this.paths = paths;
|
|
1168
|
-
this.timeoutMs = timeoutMs;
|
|
1169
|
-
this.renderPage = renderPage;
|
|
1170
|
-
}
|
|
1171
|
-
static async listen(options = {}) {
|
|
1172
|
-
const host = options.host ?? "127.0.0.1";
|
|
1173
|
-
const redirectHost = options.redirectHost ?? host;
|
|
1174
|
-
const path = options.path ?? "/callback";
|
|
1175
|
-
let instance;
|
|
1176
|
-
const server = createServer((request, response) => instance?.handle(request.url ?? "/", response));
|
|
1177
|
-
await new Promise((resolve, reject) => {
|
|
1178
|
-
server.once("error", reject);
|
|
1179
|
-
server.listen(options.port ?? 0, host, () => {
|
|
1180
|
-
server.off("error", reject);
|
|
1181
|
-
resolve();
|
|
1182
|
-
});
|
|
1183
|
-
});
|
|
1184
|
-
const address = server.address();
|
|
1185
|
-
if (!address || typeof address === "string") throw new Error("OAuth callback server did not bind to TCP");
|
|
1186
|
-
instance = new OAuthCallbackServer(server, `http://${redirectHost.includes(":") ? `[${redirectHost}]` : redirectHost}:${address.port}${path}`, [path, ...options.extraPaths ?? []], options.timeoutMs ?? 3e5, options.renderPage);
|
|
1187
|
-
return instance;
|
|
1188
|
-
}
|
|
1189
|
-
/**
|
|
1190
|
-
* Wait for the authorization response with `state`. With `path`, a response on another path fails, so
|
|
1191
|
-
* a server-specific redirect URI can tell authorization servers apart (RFC 9700 section 4.4.2.2).
|
|
1192
|
-
*/
|
|
1193
|
-
waitForCallback(state, path) {
|
|
1194
|
-
if (this.pending.has(state)) throw new Error("OAuth state is already pending");
|
|
1195
|
-
return new Promise((resolve, reject) => {
|
|
1196
|
-
const timer = setTimeout(() => {
|
|
1197
|
-
this.pending.delete(state);
|
|
1198
|
-
reject(/* @__PURE__ */ new Error("OAuth callback timed out"));
|
|
1199
|
-
}, this.timeoutMs);
|
|
1200
|
-
this.pending.set(state, {
|
|
1201
|
-
resolve,
|
|
1202
|
-
reject,
|
|
1203
|
-
timer,
|
|
1204
|
-
path
|
|
1205
|
-
});
|
|
1206
|
-
});
|
|
1207
|
-
}
|
|
1208
|
-
async close() {
|
|
1209
|
-
for (const pending of this.pending.values()) {
|
|
1210
|
-
clearTimeout(pending.timer);
|
|
1211
|
-
pending.reject(/* @__PURE__ */ new Error("OAuth callback server closed"));
|
|
1212
|
-
}
|
|
1213
|
-
this.pending.clear();
|
|
1214
|
-
await new Promise((resolve, reject) => {
|
|
1215
|
-
this.server.close((error) => error ? reject(error) : resolve());
|
|
1216
|
-
});
|
|
1217
|
-
}
|
|
1218
|
-
reply(response, status, page) {
|
|
1219
|
-
if (this.renderPage) {
|
|
1220
|
-
response.writeHead(status, {
|
|
1221
|
-
"content-type": "text/html; charset=utf-8",
|
|
1222
|
-
"cache-control": "no-store"
|
|
1223
|
-
});
|
|
1224
|
-
response.end(this.renderPage(page));
|
|
1225
|
-
} else response.writeHead(status, { "content-type": "text/plain; charset=utf-8" }).end(plainText(page));
|
|
1226
|
-
}
|
|
1227
|
-
handle(rawUrl, response) {
|
|
1228
|
-
const url = new URL(rawUrl, this.redirectUrl);
|
|
1229
|
-
if (!this.paths.includes(url.pathname)) {
|
|
1230
|
-
this.reply(response, 404, {
|
|
1231
|
-
ok: false,
|
|
1232
|
-
message: "Not found"
|
|
1233
|
-
});
|
|
1234
|
-
return;
|
|
1235
|
-
}
|
|
1236
|
-
const state = url.searchParams.get("state");
|
|
1237
|
-
const pending = state ? this.pending.get(state) : void 0;
|
|
1238
|
-
if (!state || !pending) {
|
|
1239
|
-
this.reply(response, 400, {
|
|
1240
|
-
ok: false,
|
|
1241
|
-
message: "Invalid or expired OAuth state"
|
|
1242
|
-
});
|
|
1243
|
-
return;
|
|
1244
|
-
}
|
|
1245
|
-
clearTimeout(pending.timer);
|
|
1246
|
-
this.pending.delete(state);
|
|
1247
|
-
if (pending.path !== void 0 && url.pathname !== pending.path) {
|
|
1248
|
-
pending.reject(/* @__PURE__ */ new Error("The authorization response arrived on another redirect URI"));
|
|
1249
|
-
this.reply(response, 400, {
|
|
1250
|
-
ok: false,
|
|
1251
|
-
message: "Unexpected redirect URI"
|
|
1252
|
-
});
|
|
1253
|
-
return;
|
|
1254
|
-
}
|
|
1255
|
-
const error = url.searchParams.get("error");
|
|
1256
|
-
if (error) {
|
|
1257
|
-
const description = url.searchParams.get("error_description") ?? error;
|
|
1258
|
-
pending.reject(new Error(description));
|
|
1259
|
-
this.reply(response, 200, {
|
|
1260
|
-
ok: false,
|
|
1261
|
-
message: "Authorization failed. You may close this window.",
|
|
1262
|
-
details: description
|
|
1263
|
-
});
|
|
1264
|
-
return;
|
|
1265
|
-
}
|
|
1266
|
-
const code = url.searchParams.get("code");
|
|
1267
|
-
if (!code) {
|
|
1268
|
-
pending.reject(/* @__PURE__ */ new Error("OAuth callback did not include an authorization code"));
|
|
1269
|
-
this.reply(response, 400, {
|
|
1270
|
-
ok: false,
|
|
1271
|
-
message: "Missing authorization code"
|
|
1272
|
-
});
|
|
1273
|
-
return;
|
|
1274
|
-
}
|
|
1275
|
-
const iss = url.searchParams.get("iss");
|
|
1276
|
-
pending.resolve({
|
|
1277
|
-
code,
|
|
1278
|
-
state,
|
|
1279
|
-
...iss ? { iss } : {}
|
|
1280
|
-
});
|
|
1281
|
-
this.reply(response, 200, { ok: true });
|
|
1282
|
-
}
|
|
1283
|
-
};
|
|
1284
|
-
//#endregion
|
|
1285
|
-
//#region node_modules/.pnpm/@earendil-works+pi-mcp@1.2.0/node_modules/@earendil-works/pi-mcp/dist/oauth/errors.js
|
|
1286
|
-
var OAuthError = class extends Error {
|
|
1287
|
-
code;
|
|
1288
|
-
errorUri;
|
|
1289
|
-
constructor(code, message, errorUri) {
|
|
1290
|
-
super(message || code);
|
|
1291
|
-
this.name = "OAuthError";
|
|
1292
|
-
this.code = code;
|
|
1293
|
-
this.errorUri = errorUri;
|
|
1294
|
-
}
|
|
1295
|
-
};
|
|
1296
|
-
var OAuthIssuerMismatchError = class extends Error {
|
|
1297
|
-
/** `undefined` when no authorization server metadata was discovered. */
|
|
1298
|
-
expected;
|
|
1299
|
-
/** `undefined` when an authorization response lacks the `iss` parameter its server promised (RFC 9207). */
|
|
1300
|
-
received;
|
|
1301
|
-
constructor(expected, received) {
|
|
1302
|
-
const format = (issuer) => issuer === void 0 ? "none" : JSON.stringify(issuer);
|
|
1303
|
-
super(`OAuth issuer mismatch: expected ${format(expected)}, received ${format(received)}`);
|
|
1304
|
-
this.name = "OAuthIssuerMismatchError";
|
|
1305
|
-
this.expected = expected;
|
|
1306
|
-
this.received = received;
|
|
1307
|
-
}
|
|
1308
|
-
};
|
|
1309
|
-
var OAuthInsecureEndpointError = class extends Error {
|
|
1310
|
-
endpoint;
|
|
1311
|
-
constructor(endpoint) {
|
|
1312
|
-
super(`Refusing to send OAuth credentials to non-HTTPS endpoint ${endpoint}`);
|
|
1313
|
-
this.name = "OAuthInsecureEndpointError";
|
|
1314
|
-
this.endpoint = endpoint;
|
|
1315
|
-
}
|
|
1316
|
-
};
|
|
1317
|
-
var OAuthRegistrationError = class extends Error {
|
|
1318
|
-
status;
|
|
1319
|
-
body;
|
|
1320
|
-
constructor(status, body) {
|
|
1321
|
-
super(`OAuth dynamic client registration failed with status ${status}: ${body}`);
|
|
1322
|
-
this.name = "OAuthRegistrationError";
|
|
1323
|
-
this.status = status;
|
|
1324
|
-
this.body = body;
|
|
1325
|
-
}
|
|
1326
|
-
};
|
|
1327
|
-
var McpOAuthAuthorizationRequiredError = class extends Error {
|
|
1328
|
-
constructor() {
|
|
1329
|
-
super("MCP OAuth authorization requires user interaction");
|
|
1330
|
-
this.name = "McpOAuthAuthorizationRequiredError";
|
|
1331
|
-
}
|
|
1332
|
-
};
|
|
1333
|
-
//#endregion
|
|
1334
|
-
//#region node_modules/.pnpm/@earendil-works+pi-mcp@1.2.0/node_modules/@earendil-works/pi-mcp/dist/oauth/types.js
|
|
1335
|
-
function object(value, name) {
|
|
1336
|
-
if (!isObject(value)) throw new Error(`Invalid ${name}`);
|
|
1337
|
-
return value;
|
|
1338
|
-
}
|
|
1339
|
-
/** Drops `undefined` values so optional fields are absent rather than present-but-undefined. */
|
|
1340
|
-
function compact(value) {
|
|
1341
|
-
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== void 0));
|
|
1342
|
-
}
|
|
1343
|
-
function requiredString(value, name) {
|
|
1344
|
-
if (typeof value !== "string" || value.length === 0) throw new Error(`Invalid ${name}`);
|
|
1345
|
-
return value;
|
|
1346
|
-
}
|
|
1347
|
-
/** Treats `null` and `""` as absent: servers send them for fields they have no value for, like `scope: ""`. */
|
|
1348
|
-
function absent(value) {
|
|
1349
|
-
return value === void 0 || value === null || value === "";
|
|
1350
|
-
}
|
|
1351
|
-
function optionalString(value, name) {
|
|
1352
|
-
if (absent(value)) return void 0;
|
|
1353
|
-
return requiredString(value, name);
|
|
1354
|
-
}
|
|
1355
|
-
function optionalStrings(value, name) {
|
|
1356
|
-
if (value === void 0 || value === null) return void 0;
|
|
1357
|
-
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) throw new Error(`Invalid ${name}`);
|
|
1358
|
-
return [...value];
|
|
1359
|
-
}
|
|
1360
|
-
function safeUrl(value, name) {
|
|
1361
|
-
const text = requiredString(value, name);
|
|
1362
|
-
if (!URL.canParse(text)) throw new Error(`Invalid ${name}`);
|
|
1363
|
-
const url = new URL(text);
|
|
1364
|
-
if ([
|
|
1365
|
-
"javascript:",
|
|
1366
|
-
"data:",
|
|
1367
|
-
"vbscript:"
|
|
1368
|
-
].includes(url.protocol)) throw new Error(`Invalid ${name}`);
|
|
1369
|
-
return text;
|
|
1370
|
-
}
|
|
1371
|
-
function optionalUrl(value, name) {
|
|
1372
|
-
return absent(value) ? void 0 : safeUrl(value, name);
|
|
1373
|
-
}
|
|
1374
|
-
function parseProtectedResourceMetadata(value) {
|
|
1375
|
-
const input = object(value, "OAuth protected resource metadata");
|
|
1376
|
-
return compact({
|
|
1377
|
-
...input,
|
|
1378
|
-
resource: safeUrl(input.resource, "OAuth protected resource metadata resource"),
|
|
1379
|
-
authorization_servers: optionalStrings(input.authorization_servers, "authorization_servers")?.map((url) => safeUrl(url, "authorization server URL")),
|
|
1380
|
-
scopes_supported: optionalStrings(input.scopes_supported, "scopes_supported")
|
|
1381
|
-
});
|
|
1382
|
-
}
|
|
1383
|
-
function parseAuthorizationServerMetadata(value) {
|
|
1384
|
-
const input = object(value, "authorization server metadata");
|
|
1385
|
-
const responseTypes = optionalStrings(input.response_types_supported, "response_types_supported");
|
|
1386
|
-
if (!responseTypes) throw new Error("Invalid response_types_supported");
|
|
1387
|
-
return compact({
|
|
1388
|
-
...input,
|
|
1389
|
-
issuer: safeUrl(input.issuer, "authorization server issuer"),
|
|
1390
|
-
authorization_endpoint: safeUrl(input.authorization_endpoint, "authorization endpoint"),
|
|
1391
|
-
token_endpoint: safeUrl(input.token_endpoint, "token endpoint"),
|
|
1392
|
-
registration_endpoint: optionalUrl(input.registration_endpoint, "registration endpoint"),
|
|
1393
|
-
scopes_supported: optionalStrings(input.scopes_supported, "scopes_supported"),
|
|
1394
|
-
response_types_supported: responseTypes,
|
|
1395
|
-
grant_types_supported: optionalStrings(input.grant_types_supported, "grant_types_supported"),
|
|
1396
|
-
token_endpoint_auth_methods_supported: optionalStrings(input.token_endpoint_auth_methods_supported, "token_endpoint_auth_methods_supported"),
|
|
1397
|
-
code_challenge_methods_supported: optionalStrings(input.code_challenge_methods_supported, "code_challenge_methods_supported"),
|
|
1398
|
-
client_id_metadata_document_supported: typeof input.client_id_metadata_document_supported === "boolean" ? input.client_id_metadata_document_supported : void 0,
|
|
1399
|
-
authorization_response_iss_parameter_supported: typeof input.authorization_response_iss_parameter_supported === "boolean" ? input.authorization_response_iss_parameter_supported : void 0
|
|
1400
|
-
});
|
|
1401
|
-
}
|
|
1402
|
-
function parseOAuthTokens(value) {
|
|
1403
|
-
const input = object(value, "OAuth token response");
|
|
1404
|
-
const expires = absent(input.expires_in) ? void 0 : Number(input.expires_in);
|
|
1405
|
-
if (expires !== void 0 && !Number.isFinite(expires)) throw new Error("Invalid expires_in");
|
|
1406
|
-
return compact({
|
|
1407
|
-
access_token: requiredString(input.access_token, "access_token"),
|
|
1408
|
-
token_type: requiredString(input.token_type, "token_type"),
|
|
1409
|
-
expires_in: expires,
|
|
1410
|
-
scope: optionalString(input.scope, "scope"),
|
|
1411
|
-
refresh_token: optionalString(input.refresh_token, "refresh_token"),
|
|
1412
|
-
id_token: optionalString(input.id_token, "id_token")
|
|
1413
|
-
});
|
|
1414
|
-
}
|
|
1415
|
-
function parseClientInformation(value) {
|
|
1416
|
-
const input = object(value, "OAuth client registration response");
|
|
1417
|
-
return compact({
|
|
1418
|
-
...input,
|
|
1419
|
-
client_id: requiredString(input.client_id, "client_id"),
|
|
1420
|
-
client_secret: optionalString(input.client_secret, "client_secret"),
|
|
1421
|
-
client_id_issued_at: typeof input.client_id_issued_at === "number" ? input.client_id_issued_at : void 0,
|
|
1422
|
-
client_secret_expires_at: typeof input.client_secret_expires_at === "number" ? input.client_secret_expires_at : void 0,
|
|
1423
|
-
redirect_uris: optionalStrings(input.redirect_uris, "redirect_uris") ?? []
|
|
1424
|
-
});
|
|
1425
|
-
}
|
|
1426
|
-
//#endregion
|
|
1427
|
-
//#region node_modules/.pnpm/@earendil-works+pi-mcp@1.2.0/node_modules/@earendil-works/pi-mcp/dist/oauth/discovery.js
|
|
1428
|
-
function discard(response) {
|
|
1429
|
-
response?.body?.cancel().catch(() => {});
|
|
1430
|
-
}
|
|
1431
|
-
/** 4xx and 502 mean "not here", so discovery tries the next candidate URL. */
|
|
1432
|
-
function isDiscoveryMiss(status) {
|
|
1433
|
-
return status >= 400 && status < 500 || status === 502;
|
|
1434
|
-
}
|
|
1435
|
-
/** Path suffix for `/.well-known/<kind><path>`; empty for the root path. */
|
|
1436
|
-
function pathSuffix(pathname) {
|
|
1437
|
-
return pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
|
|
1438
|
-
}
|
|
1439
|
-
function field(header, name) {
|
|
1440
|
-
const match = header.match(new RegExp(String.raw`(?:^|[,\s])${name}=(?:"([^"]*)"|([^\s,]+))`, "i"));
|
|
1441
|
-
return match?.[1] || match?.[2] || void 0;
|
|
1442
|
-
}
|
|
1443
|
-
function parseWwwAuthenticate(header) {
|
|
1444
|
-
if (!header) return {};
|
|
1445
|
-
const scheme = header.trimStart().split(/\s+/, 1)[0]?.toLowerCase();
|
|
1446
|
-
if (scheme !== "bearer" && scheme !== "dpop") return {};
|
|
1447
|
-
const resourceMetadata = field(header, "resource_metadata");
|
|
1448
|
-
let resourceMetadataUrl;
|
|
1449
|
-
if (resourceMetadata) try {
|
|
1450
|
-
resourceMetadataUrl = new URL(resourceMetadata);
|
|
1451
|
-
} catch {}
|
|
1452
|
-
return {
|
|
1453
|
-
resourceMetadataUrl,
|
|
1454
|
-
scope: field(header, "scope"),
|
|
1455
|
-
error: field(header, "error"),
|
|
1456
|
-
errorDescription: field(header, "error_description")
|
|
1457
|
-
};
|
|
1458
|
-
}
|
|
1459
|
-
async function fetchMetadata(url, fetch, protocolVersion, signal) {
|
|
1460
|
-
return fetch(url, {
|
|
1461
|
-
headers: {
|
|
1462
|
-
Accept: "application/json",
|
|
1463
|
-
"MCP-Protocol-Version": protocolVersion
|
|
1464
|
-
},
|
|
1465
|
-
signal
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
async function discoverProtectedResourceMetadata(serverUrl, options = {}) {
|
|
1469
|
-
const server = new URL(serverUrl);
|
|
1470
|
-
const fetch = options.fetch ?? globalThis.fetch;
|
|
1471
|
-
const version = options.protocolVersion ?? "2025-11-25";
|
|
1472
|
-
let response = await fetchMetadata(options.resourceMetadataUrl ? new URL(options.resourceMetadataUrl) : new URL(`/.well-known/oauth-protected-resource${pathSuffix(server.pathname)}`, server.origin), fetch, version, options.signal);
|
|
1473
|
-
if (!options.resourceMetadataUrl && server.pathname !== "/" && isDiscoveryMiss(response.status)) {
|
|
1474
|
-
discard(response);
|
|
1475
|
-
response = await fetchMetadata(new URL("/.well-known/oauth-protected-resource", server.origin), fetch, version, options.signal);
|
|
1476
|
-
}
|
|
1477
|
-
if (!response.ok) {
|
|
1478
|
-
discard(response);
|
|
1479
|
-
throw new Error(`HTTP ${response.status} loading OAuth protected resource metadata`);
|
|
1480
|
-
}
|
|
1481
|
-
return parseProtectedResourceMetadata(await response.json());
|
|
1482
|
-
}
|
|
1483
|
-
function buildAuthorizationServerDiscoveryUrls(authorizationServerUrl) {
|
|
1484
|
-
const issuer = new URL(authorizationServerUrl);
|
|
1485
|
-
const path = pathSuffix(issuer.pathname);
|
|
1486
|
-
const urls = [{
|
|
1487
|
-
url: new URL(`/.well-known/oauth-authorization-server${path}`, issuer.origin),
|
|
1488
|
-
type: "oauth"
|
|
1489
|
-
}, {
|
|
1490
|
-
url: new URL(`/.well-known/openid-configuration${path}`, issuer.origin),
|
|
1491
|
-
type: "oidc"
|
|
1492
|
-
}];
|
|
1493
|
-
if (path) urls.push({
|
|
1494
|
-
url: new URL(`${path}/.well-known/openid-configuration`, issuer.origin),
|
|
1495
|
-
type: "oidc"
|
|
1496
|
-
});
|
|
1497
|
-
return urls;
|
|
1498
|
-
}
|
|
1499
|
-
async function discoverAuthorizationServerMetadata(authorizationServerUrl, options = {}) {
|
|
1500
|
-
const fetch = options.fetch ?? globalThis.fetch;
|
|
1501
|
-
for (const { url } of buildAuthorizationServerDiscoveryUrls(authorizationServerUrl)) {
|
|
1502
|
-
const response = await fetchMetadata(url, fetch, options.protocolVersion ?? "2025-11-25", options.signal);
|
|
1503
|
-
if (!response.ok) {
|
|
1504
|
-
discard(response);
|
|
1505
|
-
if (isDiscoveryMiss(response.status)) continue;
|
|
1506
|
-
throw new Error(`HTTP ${response.status} loading authorization server metadata from ${url}`);
|
|
1507
|
-
}
|
|
1508
|
-
const metadata = parseAuthorizationServerMetadata(await response.json());
|
|
1509
|
-
if (!options.skipIssuerValidation) {
|
|
1510
|
-
const expected = String(authorizationServerUrl);
|
|
1511
|
-
const trim = (value) => value.endsWith("/") ? value.slice(0, -1) : value;
|
|
1512
|
-
if (trim(metadata.issuer) !== trim(expected)) throw new OAuthIssuerMismatchError(expected, metadata.issuer);
|
|
1513
|
-
}
|
|
1514
|
-
return metadata;
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
async function discoverOAuthServerInfo(serverUrl, options = {}) {
|
|
1518
|
-
let resourceMetadata;
|
|
1519
|
-
try {
|
|
1520
|
-
resourceMetadata = await discoverProtectedResourceMetadata(serverUrl, {
|
|
1521
|
-
resourceMetadataUrl: options.resourceMetadataUrl,
|
|
1522
|
-
fetch: options.fetch,
|
|
1523
|
-
signal: options.signal
|
|
1524
|
-
});
|
|
1525
|
-
} catch (error) {
|
|
1526
|
-
if (error instanceof TypeError) throw error;
|
|
1527
|
-
}
|
|
1528
|
-
if (options.authorizationServerMetadataUrl) {
|
|
1529
|
-
const url = options.authorizationServerMetadataUrl;
|
|
1530
|
-
const response = await fetchMetadata(url, options.fetch ?? globalThis.fetch, LATEST_PROTOCOL_VERSION, options.signal);
|
|
1531
|
-
if (!response.ok) {
|
|
1532
|
-
discard(response);
|
|
1533
|
-
throw new Error(`HTTP ${response.status} loading authorization server metadata from ${url}`);
|
|
1534
|
-
}
|
|
1535
|
-
const metadata = parseAuthorizationServerMetadata(await response.json());
|
|
1536
|
-
return {
|
|
1537
|
-
authorizationServerUrl: metadata.issuer,
|
|
1538
|
-
authorizationServerMetadata: metadata,
|
|
1539
|
-
resourceMetadata
|
|
1540
|
-
};
|
|
1541
|
-
}
|
|
1542
|
-
const authorizationServerUrl = resourceMetadata?.authorization_servers?.[0] ?? String(new URL("/", serverUrl));
|
|
1543
|
-
return {
|
|
1544
|
-
authorizationServerUrl,
|
|
1545
|
-
authorizationServerMetadata: await discoverAuthorizationServerMetadata(authorizationServerUrl, {
|
|
1546
|
-
fetch: options.fetch,
|
|
1547
|
-
skipIssuerValidation: options.skipIssuerValidation,
|
|
1548
|
-
signal: options.signal
|
|
1549
|
-
}),
|
|
1550
|
-
resourceMetadata
|
|
1551
|
-
};
|
|
1552
|
-
}
|
|
1553
|
-
function resourceUrlFromServerUrl(value) {
|
|
1554
|
-
const url = new URL(value);
|
|
1555
|
-
url.hash = "";
|
|
1556
|
-
return url;
|
|
1557
|
-
}
|
|
1558
|
-
function selectResource(serverUrl, metadata) {
|
|
1559
|
-
if (!metadata) return void 0;
|
|
1560
|
-
const requested = resourceUrlFromServerUrl(serverUrl);
|
|
1561
|
-
const configured = new URL(metadata.resource);
|
|
1562
|
-
if (requested.origin !== configured.origin) throw new Error(`Protected resource ${metadata.resource} does not match MCP server ${requested}`);
|
|
1563
|
-
const requestedPath = requested.pathname.endsWith("/") ? requested.pathname : `${requested.pathname}/`;
|
|
1564
|
-
const configuredPath = configured.pathname.endsWith("/") ? configured.pathname : `${configured.pathname}/`;
|
|
1565
|
-
if (!requestedPath.startsWith(configuredPath)) throw new Error(`Protected resource ${metadata.resource} does not match MCP server ${requested}`);
|
|
1566
|
-
return metadata.resource;
|
|
1567
|
-
}
|
|
1568
|
-
//#endregion
|
|
1569
|
-
//#region node_modules/.pnpm/@earendil-works+pi-mcp@1.2.0/node_modules/@earendil-works/pi-mcp/dist/oauth/flow.js
|
|
1570
|
-
function loopback(hostname) {
|
|
1571
|
-
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]" || hostname === "::1";
|
|
1572
|
-
}
|
|
1573
|
-
/**
|
|
1574
|
-
* The OpenID Connect `application_type` for `redirect_uris` (MCP SEP-837). Without one, OpenID Connect servers
|
|
1575
|
-
* assume `web`, which rejects http loopback redirect URIs. Loopback hosts and custom schemes are native (RFC 8252).
|
|
1576
|
-
*/
|
|
1577
|
-
function applicationType(redirectUris) {
|
|
1578
|
-
return redirectUris.some((uri) => {
|
|
1579
|
-
if (!URL.canParse(uri)) return false;
|
|
1580
|
-
const url = new URL(uri);
|
|
1581
|
-
return url.protocol !== "http:" && url.protocol !== "https:" || loopback(url.hostname);
|
|
1582
|
-
}) ? "native" : "web";
|
|
1583
|
-
}
|
|
1584
|
-
function secureEndpoint(value) {
|
|
1585
|
-
const url = new URL(value);
|
|
1586
|
-
if (url.protocol !== "https:" && !loopback(url.hostname)) throw new OAuthInsecureEndpointError(url.href);
|
|
1587
|
-
return url;
|
|
1588
|
-
}
|
|
1589
|
-
function selectClientAuthMethod(information, supported) {
|
|
1590
|
-
const hinted = "token_endpoint_auth_method" in information ? information.token_endpoint_auth_method : void 0;
|
|
1591
|
-
if (hinted && [
|
|
1592
|
-
"client_secret_basic",
|
|
1593
|
-
"client_secret_post",
|
|
1594
|
-
"none"
|
|
1595
|
-
].includes(hinted) && (supported.length === 0 || supported.includes(hinted))) return hinted;
|
|
1596
|
-
if (supported.length === 0) return information.client_secret ? "client_secret_basic" : "none";
|
|
1597
|
-
if (information.client_secret && supported.includes("client_secret_basic")) return "client_secret_basic";
|
|
1598
|
-
if (information.client_secret && supported.includes("client_secret_post")) return "client_secret_post";
|
|
1599
|
-
if (supported.includes("none")) return "none";
|
|
1600
|
-
return information.client_secret ? "client_secret_post" : "none";
|
|
1601
|
-
}
|
|
1602
|
-
function applyClientAuthentication(method, information, headers, params) {
|
|
1603
|
-
if (method === "client_secret_basic") {
|
|
1604
|
-
if (!information.client_secret) throw new Error("client_secret_basic requires a client secret");
|
|
1605
|
-
headers.set("Authorization", `Basic ${Buffer.from(`${information.client_id}:${information.client_secret}`).toString("base64")}`);
|
|
1606
|
-
} else {
|
|
1607
|
-
params.set("client_id", information.client_id);
|
|
1608
|
-
if (method === "client_secret_post" && information.client_secret) params.set("client_secret", information.client_secret);
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
async function pkce() {
|
|
1612
|
-
const bytes = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(32));
|
|
1613
|
-
const verifier = Buffer.from(bytes).toString("base64url");
|
|
1614
|
-
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
1615
|
-
return {
|
|
1616
|
-
verifier,
|
|
1617
|
-
challenge: Buffer.from(digest).toString("base64url")
|
|
1618
|
-
};
|
|
1619
|
-
}
|
|
1620
|
-
async function startAuthorization(authorizationServerUrl, options) {
|
|
1621
|
-
const metadata = options.metadata;
|
|
1622
|
-
if (metadata && !metadata.response_types_supported.includes("code")) throw new Error("Authorization server does not support authorization codes");
|
|
1623
|
-
if (metadata?.code_challenge_methods_supported && !metadata.code_challenge_methods_supported.includes("S256")) throw new Error("Authorization server does not support PKCE S256");
|
|
1624
|
-
const url = new URL(metadata?.authorization_endpoint ?? new URL("/authorize", authorizationServerUrl));
|
|
1625
|
-
const { verifier, challenge } = await pkce();
|
|
1626
|
-
url.searchParams.set("response_type", "code");
|
|
1627
|
-
url.searchParams.set("client_id", options.clientInformation.client_id);
|
|
1628
|
-
url.searchParams.set("code_challenge", challenge);
|
|
1629
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
1630
|
-
url.searchParams.set("redirect_uri", String(options.redirectUrl));
|
|
1631
|
-
if (options.state) url.searchParams.set("state", options.state);
|
|
1632
|
-
if (options.scope) url.searchParams.set("scope", options.scope);
|
|
1633
|
-
if (options.scope?.split(/\s+/).includes("offline_access")) url.searchParams.set("prompt", "consent");
|
|
1634
|
-
if (options.resource) url.searchParams.set("resource", options.resource);
|
|
1635
|
-
return {
|
|
1636
|
-
authorizationUrl: url,
|
|
1637
|
-
codeVerifier: verifier
|
|
1638
|
-
};
|
|
1639
|
-
}
|
|
1640
|
-
async function tokenRequest(authorizationServerUrl, options, params) {
|
|
1641
|
-
const url = secureEndpoint(options.metadata?.token_endpoint ?? new URL("/token", authorizationServerUrl));
|
|
1642
|
-
const headers = new Headers({
|
|
1643
|
-
Accept: "application/json",
|
|
1644
|
-
"content-type": "application/x-www-form-urlencoded"
|
|
1645
|
-
});
|
|
1646
|
-
if (options.resource) params.set("resource", options.resource);
|
|
1647
|
-
if (options.addClientAuthentication) await options.addClientAuthentication(headers, params, url, options.metadata);
|
|
1648
|
-
else applyClientAuthentication(selectClientAuthMethod(options.clientInformation, options.metadata?.token_endpoint_auth_methods_supported ?? []), options.clientInformation, headers, params);
|
|
1649
|
-
const response = await (options.fetch ?? globalThis.fetch)(url, {
|
|
1650
|
-
method: "POST",
|
|
1651
|
-
headers,
|
|
1652
|
-
body: params,
|
|
1653
|
-
signal: options.signal
|
|
1654
|
-
});
|
|
1655
|
-
const text = await response.text();
|
|
1656
|
-
let value;
|
|
1657
|
-
try {
|
|
1658
|
-
value = JSON.parse(text);
|
|
1659
|
-
} catch {}
|
|
1660
|
-
if (isObject(value) && typeof value.error === "string") throw new OAuthError(value.error, typeof value.error_description === "string" ? value.error_description : value.error, typeof value.error_uri === "string" ? value.error_uri : void 0);
|
|
1661
|
-
if (!response.ok) throw new OAuthError("server_error", `HTTP ${response.status}: ${text}`);
|
|
1662
|
-
return parseOAuthTokens(value);
|
|
1663
|
-
}
|
|
1664
|
-
async function registerClient(authorizationServerUrl, options) {
|
|
1665
|
-
const endpoint = options.metadata?.registration_endpoint;
|
|
1666
|
-
if (options.metadata && !endpoint) throw new Error("Authorization server does not support dynamic client registration");
|
|
1667
|
-
const response = await (options.fetch ?? globalThis.fetch)(new URL(endpoint ?? new URL("/register", authorizationServerUrl)), {
|
|
1668
|
-
method: "POST",
|
|
1669
|
-
headers: {
|
|
1670
|
-
Accept: "application/json",
|
|
1671
|
-
"content-type": "application/json"
|
|
1672
|
-
},
|
|
1673
|
-
body: JSON.stringify({
|
|
1674
|
-
...options.clientMetadata,
|
|
1675
|
-
application_type: options.clientMetadata.application_type ?? applicationType(options.clientMetadata.redirect_uris),
|
|
1676
|
-
...options.scope ? { scope: options.scope } : {}
|
|
1677
|
-
}),
|
|
1678
|
-
signal: options.signal
|
|
1679
|
-
});
|
|
1680
|
-
if (!response.ok) throw new OAuthRegistrationError(response.status, await response.text());
|
|
1681
|
-
return parseClientInformation(await response.json());
|
|
1682
|
-
}
|
|
1683
|
-
async function exchangeAuthorizationCode(authorizationServerUrl, options) {
|
|
1684
|
-
return tokenRequest(authorizationServerUrl, options, new URLSearchParams({
|
|
1685
|
-
grant_type: "authorization_code",
|
|
1686
|
-
code: options.code,
|
|
1687
|
-
code_verifier: options.codeVerifier,
|
|
1688
|
-
redirect_uri: String(options.redirectUrl)
|
|
1689
|
-
}));
|
|
1690
|
-
}
|
|
1691
|
-
async function refreshAuthorization(authorizationServerUrl, options) {
|
|
1692
|
-
const tokens = await tokenRequest(authorizationServerUrl, options, new URLSearchParams({
|
|
1693
|
-
grant_type: "refresh_token",
|
|
1694
|
-
refresh_token: options.refreshToken
|
|
1695
|
-
}));
|
|
1696
|
-
return {
|
|
1697
|
-
refresh_token: options.refreshToken,
|
|
1698
|
-
...tokens
|
|
1699
|
-
};
|
|
1700
|
-
}
|
|
1701
|
-
function withScope(tokens, scope) {
|
|
1702
|
-
return tokens.scope === void 0 && scope ? {
|
|
1703
|
-
...tokens,
|
|
1704
|
-
scope
|
|
1705
|
-
} : tokens;
|
|
1706
|
-
}
|
|
1707
|
-
/**
|
|
1708
|
-
* Scopes for a step-up authorization: the challenged scopes plus the ones granted so far, since a
|
|
1709
|
-
* challenge may list only the missing scopes and a token with just those would lose access the old
|
|
1710
|
-
* one had (SEP-2350). Without challenged scopes, `undefined` lets the flow pick its default.
|
|
1711
|
-
*/
|
|
1712
|
-
function stepUpScope(granted, challenged) {
|
|
1713
|
-
if (!challenged) return void 0;
|
|
1714
|
-
const scopes = [granted, challenged].flatMap((scope) => scope?.split(/\s+/).filter(Boolean) ?? []);
|
|
1715
|
-
return [...new Set(scopes)].join(" ");
|
|
1716
|
-
}
|
|
1717
|
-
async function runFlow(provider, options) {
|
|
1718
|
-
const metadataUrl = options.authorizationServerMetadataUrl && secureEndpoint(options.authorizationServerMetadataUrl);
|
|
1719
|
-
const cached = metadataUrl ? void 0 : await provider.discoveryState?.();
|
|
1720
|
-
const discovered = cached?.authorizationServerUrl ? {
|
|
1721
|
-
authorizationServerUrl: cached.authorizationServerUrl,
|
|
1722
|
-
authorizationServerMetadata: cached.authorizationServerMetadata ?? await discoverAuthorizationServerMetadata(cached.authorizationServerUrl, {
|
|
1723
|
-
fetch: options.fetch,
|
|
1724
|
-
skipIssuerValidation: options.skipIssuerValidation,
|
|
1725
|
-
signal: options.signal
|
|
1726
|
-
}),
|
|
1727
|
-
resourceMetadata: cached.resourceMetadata
|
|
1728
|
-
} : await discoverOAuthServerInfo(options.serverUrl, {
|
|
1729
|
-
resourceMetadataUrl: options.resourceMetadataUrl,
|
|
1730
|
-
authorizationServerMetadataUrl: metadataUrl,
|
|
1731
|
-
fetch: options.fetch,
|
|
1732
|
-
skipIssuerValidation: options.skipIssuerValidation,
|
|
1733
|
-
signal: options.signal
|
|
1734
|
-
});
|
|
1735
|
-
if (!metadataUrl) await provider.saveDiscoveryState?.({
|
|
1736
|
-
...discovered,
|
|
1737
|
-
...options.resourceMetadataUrl ? { resourceMetadataUrl: options.resourceMetadataUrl.href } : {}
|
|
1738
|
-
});
|
|
1739
|
-
const metadata = discovered.authorizationServerMetadata;
|
|
1740
|
-
const resource = selectResource(options.serverUrl, discovered.resourceMetadata);
|
|
1741
|
-
const scope = options.scope || discovered.resourceMetadata?.scopes_supported?.join(" ") || provider.clientMetadata.scope;
|
|
1742
|
-
const stored = await provider.clientInformation();
|
|
1743
|
-
const clientDocument = stored ? void 0 : provider.clientMetadataDocument?.(metadata);
|
|
1744
|
-
if (clientDocument) {
|
|
1745
|
-
const url = new URL(clientDocument.url);
|
|
1746
|
-
if (url.protocol !== "https:" || url.pathname === "/") throw new Error("Invalid OAuth client metadata URL");
|
|
1747
|
-
}
|
|
1748
|
-
let client = stored ?? (clientDocument && { client_id: clientDocument.url });
|
|
1749
|
-
if (!client) {
|
|
1750
|
-
if (options.authorizationCode) throw new Error("OAuth client information is missing during code exchange");
|
|
1751
|
-
if (!provider.saveClientInformation) throw new Error("OAuth client information cannot be persisted");
|
|
1752
|
-
client = await registerClient(discovered.authorizationServerUrl, {
|
|
1753
|
-
metadata,
|
|
1754
|
-
clientMetadata: provider.clientMetadata,
|
|
1755
|
-
scope,
|
|
1756
|
-
fetch: options.fetch,
|
|
1757
|
-
signal: options.signal
|
|
1758
|
-
});
|
|
1759
|
-
await provider.saveClientInformation(client);
|
|
1760
|
-
}
|
|
1761
|
-
const redirectUrl = clientDocument?.redirectUrl ?? provider.redirectUrl;
|
|
1762
|
-
const tokenOptions = {
|
|
1763
|
-
metadata,
|
|
1764
|
-
clientInformation: client,
|
|
1765
|
-
resource,
|
|
1766
|
-
addClientAuthentication: provider.addClientAuthentication,
|
|
1767
|
-
fetch: options.fetch,
|
|
1768
|
-
signal: options.signal
|
|
1769
|
-
};
|
|
1770
|
-
if (options.authorizationCode) {
|
|
1771
|
-
const iss = options.iss;
|
|
1772
|
-
if (iss !== void 0 || metadata?.authorization_response_iss_parameter_supported) {
|
|
1773
|
-
if (iss !== metadata?.issuer) throw new OAuthIssuerMismatchError(metadata?.issuer, iss);
|
|
1774
|
-
}
|
|
1775
|
-
const tokens = await exchangeAuthorizationCode(discovered.authorizationServerUrl, {
|
|
1776
|
-
...tokenOptions,
|
|
1777
|
-
code: options.authorizationCode,
|
|
1778
|
-
codeVerifier: await provider.codeVerifier(),
|
|
1779
|
-
redirectUrl
|
|
1780
|
-
});
|
|
1781
|
-
await provider.saveTokens(withScope(tokens, scope));
|
|
1782
|
-
return "AUTHORIZED";
|
|
1783
|
-
}
|
|
1784
|
-
const existing = options.skipRefresh ? void 0 : await provider.tokens();
|
|
1785
|
-
if (existing?.refresh_token) try {
|
|
1786
|
-
const tokens = await refreshAuthorization(discovered.authorizationServerUrl, {
|
|
1787
|
-
...tokenOptions,
|
|
1788
|
-
refreshToken: existing.refresh_token
|
|
1789
|
-
});
|
|
1790
|
-
await provider.saveTokens(withScope(tokens, existing.scope));
|
|
1791
|
-
return "AUTHORIZED";
|
|
1792
|
-
} catch (error) {
|
|
1793
|
-
if (options.signal?.aborted || error instanceof OAuthInsecureEndpointError) throw error;
|
|
1794
|
-
if (error instanceof OAuthError && error.code !== "server_error") throw error;
|
|
1795
|
-
}
|
|
1796
|
-
const state = await provider.state?.();
|
|
1797
|
-
const authorization = await startAuthorization(discovered.authorizationServerUrl, {
|
|
1798
|
-
metadata,
|
|
1799
|
-
clientInformation: client,
|
|
1800
|
-
redirectUrl,
|
|
1801
|
-
scope,
|
|
1802
|
-
state,
|
|
1803
|
-
resource
|
|
1804
|
-
});
|
|
1805
|
-
await provider.saveCodeVerifier(authorization.codeVerifier);
|
|
1806
|
-
await provider.redirectToAuthorization(authorization.authorizationUrl);
|
|
1807
|
-
return "REDIRECT";
|
|
1808
|
-
}
|
|
1809
|
-
async function authorizeMcp(provider, options) {
|
|
1810
|
-
try {
|
|
1811
|
-
return await runFlow(provider, options);
|
|
1812
|
-
} catch (error) {
|
|
1813
|
-
if (error instanceof OAuthError && ["invalid_client", "unauthorized_client"].includes(error.code)) {
|
|
1814
|
-
await provider.invalidateCredentials?.("all");
|
|
1815
|
-
return runFlow(provider, options);
|
|
1816
|
-
}
|
|
1817
|
-
if (error instanceof OAuthError && error.code === "invalid_grant") {
|
|
1818
|
-
await provider.invalidateCredentials?.("tokens");
|
|
1819
|
-
return runFlow(provider, options);
|
|
1820
|
-
}
|
|
1821
|
-
throw error;
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
//#endregion
|
|
1825
|
-
//#region node_modules/.pnpm/@earendil-works+pi-mcp@1.2.0/node_modules/@earendil-works/pi-mcp/dist/oauth/provider.js
|
|
1826
|
-
var MemoryOAuthStateStore = class {
|
|
1827
|
-
value;
|
|
1828
|
-
load() {
|
|
1829
|
-
return this.value === void 0 ? void 0 : structuredClone(this.value);
|
|
1830
|
-
}
|
|
1831
|
-
save(state) {
|
|
1832
|
-
this.value = structuredClone(state);
|
|
1833
|
-
}
|
|
1834
|
-
};
|
|
1835
|
-
/** Default stateful provider for one exact MCP server URL. Applications inject durable storage if needed. */
|
|
1836
|
-
var McpOAuthProvider = class {
|
|
1837
|
-
redirectUrl;
|
|
1838
|
-
clientMetadata;
|
|
1839
|
-
clientMetadataDocument;
|
|
1840
|
-
serverUrl;
|
|
1841
|
-
configuredClient;
|
|
1842
|
-
store;
|
|
1843
|
-
onRedirect;
|
|
1844
|
-
writes = Promise.resolve();
|
|
1845
|
-
constructor(options) {
|
|
1846
|
-
this.serverUrl = String(new URL(options.serverUrl));
|
|
1847
|
-
this.redirectUrl = String(options.redirectUrl);
|
|
1848
|
-
this.clientMetadata = {
|
|
1849
|
-
...options.clientMetadata,
|
|
1850
|
-
redirect_uris: options.clientMetadata.redirect_uris ?? [this.redirectUrl],
|
|
1851
|
-
grant_types: options.clientMetadata.grant_types ?? ["authorization_code", "refresh_token"],
|
|
1852
|
-
response_types: options.clientMetadata.response_types ?? ["code"],
|
|
1853
|
-
token_endpoint_auth_method: options.clientMetadata.token_endpoint_auth_method ?? (options.clientSecret ? "client_secret_post" : "none")
|
|
1854
|
-
};
|
|
1855
|
-
this.clientMetadataDocument = options.clientMetadataDocument;
|
|
1856
|
-
this.configuredClient = options.clientId ? {
|
|
1857
|
-
client_id: options.clientId,
|
|
1858
|
-
...options.clientSecret ? { client_secret: options.clientSecret } : {}
|
|
1859
|
-
} : void 0;
|
|
1860
|
-
this.store = options.store ?? new MemoryOAuthStateStore();
|
|
1861
|
-
this.onRedirect = options.onRedirect;
|
|
1862
|
-
}
|
|
1863
|
-
async state() {
|
|
1864
|
-
const existing = (await this.load()).oauthState;
|
|
1865
|
-
if (existing) return existing;
|
|
1866
|
-
const state = Buffer.from(crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(32))).toString("hex");
|
|
1867
|
-
await this.update((value) => ({
|
|
1868
|
-
...value,
|
|
1869
|
-
oauthState: state
|
|
1870
|
-
}));
|
|
1871
|
-
return state;
|
|
1872
|
-
}
|
|
1873
|
-
async clientInformation() {
|
|
1874
|
-
return this.configuredClient ?? (await this.load()).clientInformation;
|
|
1875
|
-
}
|
|
1876
|
-
async saveClientInformation(information) {
|
|
1877
|
-
if (this.configuredClient) return;
|
|
1878
|
-
await this.update((value) => ({
|
|
1879
|
-
...value,
|
|
1880
|
-
clientInformation: information
|
|
1881
|
-
}));
|
|
1882
|
-
}
|
|
1883
|
-
async tokens() {
|
|
1884
|
-
return (await this.load()).tokens;
|
|
1885
|
-
}
|
|
1886
|
-
async saveTokens(tokens) {
|
|
1887
|
-
const expiresAt = tokens.expires_in === void 0 ? void 0 : Date.now() + tokens.expires_in * 1e3;
|
|
1888
|
-
await this.update((value) => {
|
|
1889
|
-
const next = {
|
|
1890
|
-
...value,
|
|
1891
|
-
tokens
|
|
1892
|
-
};
|
|
1893
|
-
if (expiresAt === void 0) delete next.tokensExpireAt;
|
|
1894
|
-
else next.tokensExpireAt = expiresAt;
|
|
1895
|
-
return next;
|
|
1896
|
-
});
|
|
1897
|
-
}
|
|
1898
|
-
async redirectToAuthorization(url) {
|
|
1899
|
-
await this.onRedirect(url);
|
|
1900
|
-
}
|
|
1901
|
-
async saveCodeVerifier(verifier) {
|
|
1902
|
-
await this.update((value) => ({
|
|
1903
|
-
...value,
|
|
1904
|
-
codeVerifier: verifier
|
|
1905
|
-
}));
|
|
1906
|
-
}
|
|
1907
|
-
async codeVerifier() {
|
|
1908
|
-
const verifier = (await this.load()).codeVerifier;
|
|
1909
|
-
if (!verifier) throw new Error("No OAuth PKCE code verifier is stored");
|
|
1910
|
-
return verifier;
|
|
1911
|
-
}
|
|
1912
|
-
async invalidateCredentials(kind) {
|
|
1913
|
-
await this.update((value) => {
|
|
1914
|
-
const next = { ...value };
|
|
1915
|
-
if (kind === "all" || kind === "client") delete next.clientInformation;
|
|
1916
|
-
if (kind === "all" || kind === "tokens") {
|
|
1917
|
-
delete next.tokens;
|
|
1918
|
-
delete next.tokensExpireAt;
|
|
1919
|
-
}
|
|
1920
|
-
if (kind === "all" || kind === "verifier") delete next.codeVerifier;
|
|
1921
|
-
if (kind === "all" || kind === "discovery") delete next.discovery;
|
|
1922
|
-
if (kind === "all") delete next.oauthState;
|
|
1923
|
-
return next;
|
|
1924
|
-
});
|
|
1925
|
-
}
|
|
1926
|
-
async saveDiscoveryState(discovery) {
|
|
1927
|
-
await this.update((value) => ({
|
|
1928
|
-
...value,
|
|
1929
|
-
discovery
|
|
1930
|
-
}));
|
|
1931
|
-
}
|
|
1932
|
-
async discoveryState() {
|
|
1933
|
-
return (await this.load()).discovery;
|
|
1934
|
-
}
|
|
1935
|
-
async load() {
|
|
1936
|
-
await this.writes;
|
|
1937
|
-
return this.own(await this.store.load());
|
|
1938
|
-
}
|
|
1939
|
-
async update(update) {
|
|
1940
|
-
this.writes = this.writes.then(async () => {
|
|
1941
|
-
await this.store.save(update(this.own(await this.store.load())));
|
|
1942
|
-
});
|
|
1943
|
-
await this.writes;
|
|
1944
|
-
}
|
|
1945
|
-
/** Stored state for another server URL is ignored so credentials never leak across servers. */
|
|
1946
|
-
own(state) {
|
|
1947
|
-
return state?.serverUrl === this.serverUrl ? state : { serverUrl: this.serverUrl };
|
|
1948
|
-
}
|
|
1949
|
-
};
|
|
1950
|
-
//#endregion
|
|
1951
|
-
//#region node_modules/.pnpm/@earendil-works+pi-ai@1.2.0_@aws-sdk+credential-provider-node@3.972.86_@modelcontextpro_dcf109e705e96235bfb9a180457030de/node_modules/@earendil-works/pi-ai/dist/utils/oauth-page.js
|
|
1952
|
-
const LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#F09082" d="M165.29 165.29H517.36V400H400V282.65H165.29Z"/><path fill="#4D9ABF" d="M165.29 282.65H282.65V400H400V517.36H282.65V634.72H165.29Z"/><path fill="#F1BE58" d="M517.36 400H634.72V634.72H517.36Z"/></svg>`;
|
|
1953
|
-
function escapeHtml(value) {
|
|
1954
|
-
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
1955
|
-
}
|
|
1956
|
-
function renderPage(options) {
|
|
1957
|
-
const title = escapeHtml(options.title);
|
|
1958
|
-
const heading = escapeHtml(options.heading);
|
|
1959
|
-
const message = escapeHtml(options.message);
|
|
1960
|
-
const details = options.details ? escapeHtml(options.details) : void 0;
|
|
1961
|
-
return `<!doctype html>
|
|
1962
|
-
<html lang="en">
|
|
1963
|
-
<head>
|
|
1964
|
-
<meta charset="utf-8" />
|
|
1965
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1966
|
-
<title>${title}</title>
|
|
1967
|
-
<style>
|
|
1968
|
-
:root {
|
|
1969
|
-
--text: #fafafa;
|
|
1970
|
-
--text-dim: #a1a1aa;
|
|
1971
|
-
--page-bg: #09090b;
|
|
1972
|
-
--font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
1973
|
-
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
1974
|
-
}
|
|
1975
|
-
* { box-sizing: border-box; }
|
|
1976
|
-
html { color-scheme: dark; }
|
|
1977
|
-
body {
|
|
1978
|
-
margin: 0;
|
|
1979
|
-
min-height: 100vh;
|
|
1980
|
-
display: flex;
|
|
1981
|
-
align-items: center;
|
|
1982
|
-
justify-content: center;
|
|
1983
|
-
padding: 24px;
|
|
1984
|
-
background: var(--page-bg);
|
|
1985
|
-
color: var(--text);
|
|
1986
|
-
font-family: var(--font-sans);
|
|
1987
|
-
text-align: center;
|
|
1988
|
-
}
|
|
1989
|
-
main {
|
|
1990
|
-
width: 100%;
|
|
1991
|
-
max-width: 560px;
|
|
1992
|
-
display: flex;
|
|
1993
|
-
flex-direction: column;
|
|
1994
|
-
align-items: center;
|
|
1995
|
-
justify-content: center;
|
|
1996
|
-
}
|
|
1997
|
-
.logo {
|
|
1998
|
-
width: 72px;
|
|
1999
|
-
height: 72px;
|
|
2000
|
-
display: block;
|
|
2001
|
-
margin-bottom: 24px;
|
|
2002
|
-
}
|
|
2003
|
-
h1 {
|
|
2004
|
-
margin: 0 0 10px;
|
|
2005
|
-
font-size: 28px;
|
|
2006
|
-
line-height: 1.15;
|
|
2007
|
-
font-weight: 650;
|
|
2008
|
-
color: var(--text);
|
|
2009
|
-
}
|
|
2010
|
-
p {
|
|
2011
|
-
margin: 0;
|
|
2012
|
-
line-height: 1.7;
|
|
2013
|
-
color: var(--text-dim);
|
|
2014
|
-
font-size: 15px;
|
|
2015
|
-
}
|
|
2016
|
-
.details {
|
|
2017
|
-
margin-top: 16px;
|
|
2018
|
-
font-family: var(--font-mono);
|
|
2019
|
-
font-size: 13px;
|
|
2020
|
-
color: var(--text-dim);
|
|
2021
|
-
white-space: pre-wrap;
|
|
2022
|
-
word-break: break-word;
|
|
2023
|
-
}
|
|
2024
|
-
</style>
|
|
2025
|
-
</head>
|
|
2026
|
-
<body>
|
|
2027
|
-
<main>
|
|
2028
|
-
<div class="logo">${LOGO_SVG}</div>
|
|
2029
|
-
<h1>${heading}</h1>
|
|
2030
|
-
<p>${message}</p>
|
|
2031
|
-
${details ? `<div class="details">${details}</div>` : ""}
|
|
2032
|
-
</main>
|
|
2033
|
-
</body>
|
|
2034
|
-
</html>`;
|
|
2035
|
-
}
|
|
2036
|
-
function oauthSuccessHtml(message) {
|
|
2037
|
-
return renderPage({
|
|
2038
|
-
title: "Authentication successful",
|
|
2039
|
-
heading: "Authentication successful",
|
|
2040
|
-
message
|
|
2041
|
-
});
|
|
2042
|
-
}
|
|
2043
|
-
function oauthErrorHtml(message, details) {
|
|
2044
|
-
return renderPage({
|
|
2045
|
-
title: "Authentication failed",
|
|
2046
|
-
heading: "Authentication failed",
|
|
2047
|
-
message,
|
|
2048
|
-
details
|
|
2049
|
-
});
|
|
2050
|
-
}
|
|
2051
|
-
//#endregion
|
|
2052
|
-
//#region node_modules/.pnpm/@earendil-works+pi-coding-agent@1.2.0_@aws-sdk+credential-provider-node@3.972.86_@model_cb83c1bd7a7ff5cede8c43e329c5cf76/node_modules/@earendil-works/pi-coding-agent/dist/extensions/mcp/oauth.js
|
|
2053
|
-
/**
|
|
2054
|
-
* OAuth sign-in for remote MCP servers.
|
|
2055
|
-
*
|
|
2056
|
-
* Connections never start a browser flow on their own. They send the stored access token and, after
|
|
2057
|
-
* a 401, try the stored refresh token. When that is not possible they fail with
|
|
2058
|
-
* `McpOAuthAuthorizationRequiredError`, and the user signs in through `/mcp`, which runs
|
|
2059
|
-
* the authorization code flow (PKCE, a Client ID Metadata Document or dynamic client registration)
|
|
2060
|
-
* against a loopback callback.
|
|
2061
|
-
*
|
|
2062
|
-
* Credentials live in `<agent-dir>/mcp-auth.json`, keyed by server name and URL.
|
|
2063
|
-
*/
|
|
2064
|
-
var import_proper_lockfile = /* @__PURE__ */ __toESM(require_proper_lockfile(), 1);
|
|
2065
|
-
const CALLBACK_HOST = "127.0.0.1";
|
|
2066
|
-
const CALLBACK_PATH = "/callback";
|
|
2067
|
-
/** Where pi.dev serves pi's Client ID Metadata Documents: `client.json` and `<callback ID>/client.json`. */
|
|
2068
|
-
const CLIENT_METADATA_BASE_URL = "https://pi.dev/oauth";
|
|
2069
|
-
/** Redirect URI for refreshes when none is stored. Refreshing never redirects the user. */
|
|
2070
|
-
const FALLBACK_REDIRECT_URL = `http://${CALLBACK_HOST}${CALLBACK_PATH}`;
|
|
2071
|
-
/** Access tokens this close to expiry are refreshed before they are sent. */
|
|
2072
|
-
const REFRESH_SKEW_MS = 3e4;
|
|
2073
|
-
/**
|
|
2074
|
-
* Bounds each request to the authorization server, so an unresponsive one cannot hold the refresh lock,
|
|
2075
|
-
* delay shutdown, or keep a sign-in waiting for long.
|
|
2076
|
-
*/
|
|
2077
|
-
const OAUTH_REQUEST_TIMEOUT_MS = 15e3;
|
|
2078
|
-
/** A refresh lock that its holder stopped renewing (the process was killed) is taken over after this. */
|
|
2079
|
-
const REFRESH_LOCK_STALE_MS = 2e4;
|
|
2080
|
-
/** How long to wait for another process's refresh: longer than a stale lock lives. */
|
|
2081
|
-
const REFRESH_LOCK_WAIT_MS = 25e3;
|
|
2082
|
-
const REFRESH_LOCK_RETRY_MS = 100;
|
|
2083
|
-
function callbackSettings(settings) {
|
|
2084
|
-
const url = new URL(settings.callbackUrl ?? `http://${CALLBACK_HOST}${CALLBACK_PATH}`);
|
|
2085
|
-
const address = url.hostname.replace(/^\[|\]$/g, "");
|
|
2086
|
-
const port = url.port ? Number(url.port) : settings.callbackPort;
|
|
2087
|
-
let fixedRedirectUrl;
|
|
2088
|
-
if (url.port) fixedRedirectUrl = settings.callbackUrl;
|
|
2089
|
-
else if (port !== void 0) {
|
|
2090
|
-
url.port = String(port);
|
|
2091
|
-
fixedRedirectUrl = url.href;
|
|
2092
|
-
}
|
|
2093
|
-
return {
|
|
2094
|
-
host: address === "localhost" ? CALLBACK_HOST : address,
|
|
2095
|
-
redirectHost: address,
|
|
2096
|
-
port,
|
|
2097
|
-
path: url.pathname,
|
|
2098
|
-
fixedRedirectUrl
|
|
2099
|
-
};
|
|
2100
|
-
}
|
|
2101
|
-
/** Scopes of both lists, each once. */
|
|
2102
|
-
function mergeScopes(...scopes) {
|
|
2103
|
-
const merged = [...new Set(scopes.flatMap((scope) => scope?.split(/\s+/).filter(Boolean) ?? []))];
|
|
2104
|
-
return merged.length > 0 ? merged.join(" ") : void 0;
|
|
2105
|
-
}
|
|
2106
|
-
function parseStates(content) {
|
|
2107
|
-
if (!content?.trim()) return {};
|
|
2108
|
-
const parsed = JSON.parse(content);
|
|
2109
|
-
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
2110
|
-
}
|
|
2111
|
-
function serializeStates(states) {
|
|
2112
|
-
return `${JSON.stringify(states, null, 2)}\n`;
|
|
2113
|
-
}
|
|
2114
|
-
/**
|
|
2115
|
-
* Keys of a server's state: by name and URL, so servers sharing a URL keep separate accounts, and the
|
|
2116
|
-
* legacy key by URL alone, written by older versions.
|
|
2117
|
-
*/
|
|
2118
|
-
function storeKeys(name, serverUrl) {
|
|
2119
|
-
const legacyKey = String(new URL(serverUrl));
|
|
2120
|
-
return {
|
|
2121
|
-
key: `${mcpNamespace(name)}|${legacyKey}`,
|
|
2122
|
-
legacyKey
|
|
2123
|
-
};
|
|
2124
|
-
}
|
|
2125
|
-
/** Per-server OAuth state (client registration, tokens, pending PKCE verifier) in `mcp-auth.json`. */
|
|
2126
|
-
var McpOAuthCredentialStore = class {
|
|
2127
|
-
backend;
|
|
2128
|
-
/** Directory for the refresh lock files. Without one, refreshes are only serialized in this process. */
|
|
2129
|
-
lockDir;
|
|
2130
|
-
constructor(backend, lockDir) {
|
|
2131
|
-
this.backend = backend ?? new FileAuthStorageBackend(join(getAgentDir(), "mcp-auth.json"));
|
|
2132
|
-
this.lockDir = backend ? lockDir : getAgentDir();
|
|
2133
|
-
}
|
|
2134
|
-
forServer(name, serverUrl) {
|
|
2135
|
-
const { key, legacyKey } = storeKeys(name, serverUrl);
|
|
2136
|
-
return {
|
|
2137
|
-
load: () => this.backend.withLock((current) => {
|
|
2138
|
-
const states = parseStates(current);
|
|
2139
|
-
if (states[key] || !states[legacyKey]) return { result: states[key] };
|
|
2140
|
-
states[key] = states[legacyKey];
|
|
2141
|
-
delete states[legacyKey];
|
|
2142
|
-
return {
|
|
2143
|
-
result: states[key],
|
|
2144
|
-
next: serializeStates(states)
|
|
2145
|
-
};
|
|
2146
|
-
}),
|
|
2147
|
-
save: (state) => this.write((states) => {
|
|
2148
|
-
states[key] = state;
|
|
2149
|
-
}),
|
|
2150
|
-
withRefreshLock: (fn) => this.withRefreshLock(key, fn)
|
|
2151
|
-
};
|
|
2152
|
-
}
|
|
2153
|
-
/**
|
|
2154
|
-
* A lock file per server. When the process exits, proper-lockfile removes the locks it holds; when
|
|
2155
|
-
* it is killed, the lock goes stale because it is no longer renewed, and the next process takes it over.
|
|
2156
|
-
*/
|
|
2157
|
-
async withRefreshLock(key, fn) {
|
|
2158
|
-
if (!this.lockDir) return fn();
|
|
2159
|
-
mkdirSync(this.lockDir, {
|
|
2160
|
-
recursive: true,
|
|
2161
|
-
mode: 448
|
|
2162
|
-
});
|
|
2163
|
-
const hash = createHash("sha256").update(key).digest("hex").slice(0, 16);
|
|
2164
|
-
const release = await import_proper_lockfile.default.lock(join(this.lockDir, `mcp-auth-refresh-${hash}`), {
|
|
2165
|
-
realpath: false,
|
|
2166
|
-
stale: REFRESH_LOCK_STALE_MS,
|
|
2167
|
-
retries: {
|
|
2168
|
-
retries: REFRESH_LOCK_WAIT_MS / REFRESH_LOCK_RETRY_MS,
|
|
2169
|
-
factor: 1,
|
|
2170
|
-
minTimeout: REFRESH_LOCK_RETRY_MS,
|
|
2171
|
-
maxTimeout: REFRESH_LOCK_RETRY_MS
|
|
2172
|
-
},
|
|
2173
|
-
onCompromised: () => {}
|
|
2174
|
-
});
|
|
2175
|
-
try {
|
|
2176
|
-
return await fn();
|
|
2177
|
-
} finally {
|
|
2178
|
-
await release().catch(() => void 0);
|
|
2179
|
-
}
|
|
2180
|
-
}
|
|
2181
|
-
/** The stored tokens of a server, for noticing sign-ins done by another process. Does not take over legacy state. */
|
|
2182
|
-
tokens(name, serverUrl) {
|
|
2183
|
-
const { key, legacyKey } = storeKeys(name, serverUrl);
|
|
2184
|
-
const states = this.backend.withLock((current) => ({ result: parseStates(current) }));
|
|
2185
|
-
return (states[key] ?? states[legacyKey])?.tokens;
|
|
2186
|
-
}
|
|
2187
|
-
/** Returns whether credentials were stored for the server. Removes legacy state the server would take over. */
|
|
2188
|
-
remove(name, serverUrl) {
|
|
2189
|
-
const { key, legacyKey } = storeKeys(name, serverUrl);
|
|
2190
|
-
return this.backend.withLock((current) => {
|
|
2191
|
-
const states = parseStates(current);
|
|
2192
|
-
const stored = key in states ? key : legacyKey in states ? legacyKey : void 0;
|
|
2193
|
-
if (!stored) return { result: false };
|
|
2194
|
-
delete states[stored];
|
|
2195
|
-
return {
|
|
2196
|
-
result: true,
|
|
2197
|
-
next: serializeStates(states)
|
|
2198
|
-
};
|
|
2199
|
-
});
|
|
2200
|
-
}
|
|
2201
|
-
write(update) {
|
|
2202
|
-
this.backend.withLock((current) => {
|
|
2203
|
-
const states = parseStates(current);
|
|
2204
|
-
update(states);
|
|
2205
|
-
return {
|
|
2206
|
-
result: void 0,
|
|
2207
|
-
next: serializeStates(states)
|
|
2208
|
-
};
|
|
2209
|
-
});
|
|
2210
|
-
}
|
|
2211
|
-
};
|
|
2212
|
-
/** `fetch` with `OAUTH_REQUEST_TIMEOUT_MS` per request, on top of the request's own signal. */
|
|
2213
|
-
function timedFetch(fetch = globalThis.fetch) {
|
|
2214
|
-
return (input, init) => {
|
|
2215
|
-
const timeout = AbortSignal.timeout(OAUTH_REQUEST_TIMEOUT_MS);
|
|
2216
|
-
return fetch(input, {
|
|
2217
|
-
...init,
|
|
2218
|
-
signal: init?.signal ? AbortSignal.any([init.signal, timeout]) : timeout
|
|
2219
|
-
});
|
|
2220
|
-
};
|
|
2221
|
-
}
|
|
2222
|
-
function registeredRedirectUrls(client) {
|
|
2223
|
-
return client && "redirect_uris" in client ? client.redirect_uris : [];
|
|
2224
|
-
}
|
|
2225
|
-
/** 12 characters identifying an MCP server URL in callback paths, computed like Codex does. */
|
|
2226
|
-
function callbackId(serverUrl) {
|
|
2227
|
-
const url = new URL(serverUrl);
|
|
2228
|
-
url.hash = "";
|
|
2229
|
-
return createHash("sha256").update(url.href).digest().subarray(0, 9).toString("base64url");
|
|
2230
|
-
}
|
|
2231
|
-
/**
|
|
2232
|
-
* pi's Client ID Metadata Document, for `clientRegistration: "cimd"`, chosen like Codex chooses its own.
|
|
2233
|
-
* The configuration ensures the default callback path. Without the `iss` parameter in authorization
|
|
2234
|
-
* responses (RFC 9207), the redirect URI and the document are specific to the MCP server, so a response
|
|
2235
|
-
* cannot be mixed up with one from another authorization server (RFC 9700 section 4.4.2.2).
|
|
2236
|
-
*/
|
|
2237
|
-
function clientMetadataDocument(serverUrl, redirectUrl, metadata) {
|
|
2238
|
-
if (!metadata?.client_id_metadata_document_supported || !metadata.token_endpoint_auth_methods_supported?.includes("none")) throw new Error("The authorization server does not support Client ID Metadata Documents for public clients; remove oauth.clientRegistration \"cimd\"");
|
|
2239
|
-
if (metadata.authorization_response_iss_parameter_supported) return {
|
|
2240
|
-
url: `${CLIENT_METADATA_BASE_URL}/client.json`,
|
|
2241
|
-
redirectUrl
|
|
2242
|
-
};
|
|
2243
|
-
const id = callbackId(serverUrl);
|
|
2244
|
-
const redirect = new URL(redirectUrl);
|
|
2245
|
-
redirect.pathname = `${CALLBACK_PATH}/${id}`;
|
|
2246
|
-
return {
|
|
2247
|
-
url: `${CLIENT_METADATA_BASE_URL}/${id}/client.json`,
|
|
2248
|
-
redirectUrl: redirect.href
|
|
2249
|
-
};
|
|
2250
|
-
}
|
|
2251
|
-
function createProvider(serverUrl, store, settings, redirectUrl, onRedirect) {
|
|
2252
|
-
return new McpOAuthProvider({
|
|
2253
|
-
serverUrl,
|
|
2254
|
-
redirectUrl,
|
|
2255
|
-
clientMetadata: { client_name: settings.clientName ?? APP_NAME },
|
|
2256
|
-
clientMetadataDocument: settings.clientRegistration === "cimd" ? (metadata) => clientMetadataDocument(serverUrl, redirectUrl, metadata) : void 0,
|
|
2257
|
-
clientId: settings.clientId,
|
|
2258
|
-
clientSecret: settings.clientSecret,
|
|
2259
|
-
store,
|
|
2260
|
-
onRedirect
|
|
2261
|
-
});
|
|
2262
|
-
}
|
|
2263
|
-
/**
|
|
2264
|
-
* Auth provider for MCP connections: sends the stored access token and refreshes it when it is about
|
|
2265
|
-
* to expire or after a 401. Throws `McpOAuthAuthorizationRequiredError` when the user has to sign in,
|
|
2266
|
-
* including when the server asks for more scope (`insufficient_scope`). `onChallenge` receives the
|
|
2267
|
-
* server's `WWW-Authenticate` challenge so sign-in can use its resource metadata URL and scope.
|
|
2268
|
-
* `settings` is only called when a refresh is needed, so a secret that fails to resolve fails the
|
|
2269
|
-
* refresh instead of the whole connection setup.
|
|
2270
|
-
*
|
|
2271
|
-
* Many servers rotate refresh tokens, so two refreshes with the same refresh token lose the grant.
|
|
2272
|
-
* Requests in this process share one refresh, and other processes are kept out by the store's
|
|
2273
|
-
* refresh lock, held from reading the tokens to saving new ones. Tokens that changed meanwhile
|
|
2274
|
-
* (another process refreshed them, or the user signed in) are used without refreshing.
|
|
2275
|
-
*/
|
|
2276
|
-
function createMcpAuthProvider(options) {
|
|
2277
|
-
const { serverUrl, store } = options;
|
|
2278
|
-
let refreshing;
|
|
2279
|
-
/** Replace `staleToken`, the access token that expired or was rejected. */
|
|
2280
|
-
const refresh = (staleToken, fetch = globalThis.fetch, challenge) => {
|
|
2281
|
-
refreshing ??= store.withRefreshLock(async () => {
|
|
2282
|
-
const state = await store.load();
|
|
2283
|
-
if (state?.tokens?.access_token !== staleToken) return;
|
|
2284
|
-
if (!state?.tokens?.refresh_token) throw new McpOAuthAuthorizationRequiredError();
|
|
2285
|
-
const settings = options.settings();
|
|
2286
|
-
const redirectUrl = callbackSettings(settings).fixedRedirectUrl ?? registeredRedirectUrls(state.clientInformation)[0] ?? FALLBACK_REDIRECT_URL;
|
|
2287
|
-
if (await authorizeMcp(createProvider(serverUrl, store, settings, redirectUrl, () => {}), {
|
|
2288
|
-
serverUrl,
|
|
2289
|
-
resourceMetadataUrl: challenge?.resourceMetadataUrl,
|
|
2290
|
-
authorizationServerMetadataUrl: settings.authServerMetadataUrl,
|
|
2291
|
-
scope: challenge?.scope,
|
|
2292
|
-
fetch: timedFetch(fetch)
|
|
2293
|
-
}) === "REDIRECT") throw new McpOAuthAuthorizationRequiredError();
|
|
2294
|
-
}).finally(() => {
|
|
2295
|
-
refreshing = void 0;
|
|
2296
|
-
});
|
|
2297
|
-
return refreshing;
|
|
2298
|
-
};
|
|
2299
|
-
return {
|
|
2300
|
-
token: async () => {
|
|
2301
|
-
await refreshing?.catch(() => void 0);
|
|
2302
|
-
const state = await store.load();
|
|
2303
|
-
const token = state?.tokens?.access_token;
|
|
2304
|
-
if (!(state?.tokensExpireAt !== void 0 && state.tokensExpireAt - REFRESH_SKEW_MS <= Date.now()) || !state?.tokens?.refresh_token) return token;
|
|
2305
|
-
await refresh(token).catch(() => void 0);
|
|
2306
|
-
return (await store.load())?.tokens?.access_token;
|
|
2307
|
-
},
|
|
2308
|
-
onUnauthorized: async (context) => {
|
|
2309
|
-
const challenge = parseWwwAuthenticate(context.response.headers.get("www-authenticate"));
|
|
2310
|
-
options.onChallenge(challenge);
|
|
2311
|
-
if (challenge.error === "insufficient_scope") throw new McpOAuthAuthorizationRequiredError();
|
|
2312
|
-
await refresh(context.token, context.fetch, challenge);
|
|
2313
|
-
},
|
|
2314
|
-
settled: async () => {
|
|
2315
|
-
await refreshing?.catch(() => void 0);
|
|
2316
|
-
}
|
|
2317
|
-
};
|
|
2318
|
-
}
|
|
2319
|
-
var McpSignInCancelledError = class extends Error {
|
|
2320
|
-
constructor() {
|
|
2321
|
-
super("Sign-in cancelled");
|
|
2322
|
-
this.name = "McpSignInCancelledError";
|
|
2323
|
-
}
|
|
2324
|
-
};
|
|
2325
|
-
function responseFromRedirectUrl(input, state, redirectUrl) {
|
|
2326
|
-
let url;
|
|
2327
|
-
try {
|
|
2328
|
-
url = new URL(input.trim());
|
|
2329
|
-
} catch {
|
|
2330
|
-
throw new Error("Expected the full redirect URL from the browser address bar");
|
|
2331
|
-
}
|
|
2332
|
-
if (url.origin !== redirectUrl.origin || url.pathname !== redirectUrl.pathname) throw new Error("The redirect URL does not match this sign-in's redirect URI");
|
|
2333
|
-
const error = url.searchParams.get("error");
|
|
2334
|
-
if (error) throw new Error(url.searchParams.get("error_description") ?? error);
|
|
2335
|
-
if (url.searchParams.get("state") !== state) throw new Error("The redirect URL belongs to a different sign-in");
|
|
2336
|
-
const code = url.searchParams.get("code");
|
|
2337
|
-
if (!code) throw new Error("The redirect URL does not contain an authorization code");
|
|
2338
|
-
return {
|
|
2339
|
-
code,
|
|
2340
|
-
iss: url.searchParams.get("iss") ?? void 0
|
|
2341
|
-
};
|
|
2342
|
-
}
|
|
2343
|
-
/**
|
|
2344
|
-
* Wait for the browser callback or a pasted redirect URL, whichever comes first. Aborting `signal`
|
|
2345
|
-
* aborts the prompt, which then cancels the sign-in.
|
|
2346
|
-
*/
|
|
2347
|
-
async function waitForAuthorizationResponse(callback, state, redirectUrl, prompt, signal) {
|
|
2348
|
-
const controller = new AbortController();
|
|
2349
|
-
const promptSignal = signal ? AbortSignal.any([controller.signal, signal]) : controller.signal;
|
|
2350
|
-
const fromBrowser = callback.waitForCallback(state, redirectUrl.pathname);
|
|
2351
|
-
const fromUser = prompt.promptForRedirectUrl(promptSignal).then((input) => {
|
|
2352
|
-
if (!input?.trim()) throw new McpSignInCancelledError();
|
|
2353
|
-
return responseFromRedirectUrl(input, state, redirectUrl);
|
|
2354
|
-
});
|
|
2355
|
-
try {
|
|
2356
|
-
return await Promise.race([fromBrowser, fromUser]);
|
|
2357
|
-
} finally {
|
|
2358
|
-
controller.abort();
|
|
2359
|
-
fromBrowser.catch(() => void 0);
|
|
2360
|
-
fromUser.catch(() => void 0);
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2363
|
-
/** Listen on `port`, or on a free port when it is taken and not `required`. */
|
|
2364
|
-
async function listenForCallback(settings, extraPaths, port, required) {
|
|
2365
|
-
const options = {
|
|
2366
|
-
host: settings.host,
|
|
2367
|
-
redirectHost: settings.redirectHost,
|
|
2368
|
-
path: settings.path,
|
|
2369
|
-
extraPaths,
|
|
2370
|
-
renderPage: (page) => page.ok ? oauthSuccessHtml("Signed in to the MCP server. You may now close this page.") : oauthErrorHtml(page.message, page.details)
|
|
2371
|
-
};
|
|
2372
|
-
try {
|
|
2373
|
-
return await OAuthCallbackServer.listen({
|
|
2374
|
-
...options,
|
|
2375
|
-
port: port ?? 0
|
|
2376
|
-
});
|
|
2377
|
-
} catch (error) {
|
|
2378
|
-
if (required || port === void 0) throw error;
|
|
2379
|
-
return OAuthCallbackServer.listen(options);
|
|
2380
|
-
}
|
|
2381
|
-
}
|
|
2382
|
-
/**
|
|
2383
|
-
* Sign in to an MCP server. Uses the stored refresh token when possible; otherwise runs the browser
|
|
2384
|
-
* authorization code flow. Tokens are saved to `store`. Aborting `signal` stops the sign-in at any
|
|
2385
|
-
* step with `McpSignInCancelledError`; requests to the authorization server are also time-limited.
|
|
2386
|
-
*/
|
|
2387
|
-
async function signInMcpServer(options) {
|
|
2388
|
-
const { serverUrl, store, settings, signal } = options;
|
|
2389
|
-
if (signal?.aborted) throw new McpSignInCancelledError();
|
|
2390
|
-
const stored = await store.load();
|
|
2391
|
-
const stepUp = options.challenge?.error === "insufficient_scope";
|
|
2392
|
-
const callbackOptions = callbackSettings(settings);
|
|
2393
|
-
const registered = registeredRedirectUrls(stored?.clientInformation)[0];
|
|
2394
|
-
const preferredPort = callbackOptions.port ?? (registered ? Number(new URL(registered).port) || void 0 : void 0);
|
|
2395
|
-
const cimd = settings.clientRegistration === "cimd";
|
|
2396
|
-
const callback = await listenForCallback(callbackOptions, cimd ? [`${CALLBACK_PATH}/${callbackId(serverUrl)}`] : [], preferredPort, callbackOptions.port !== void 0);
|
|
2397
|
-
const redirectUrl = callbackOptions.fixedRedirectUrl ?? callback.redirectUrl;
|
|
2398
|
-
try {
|
|
2399
|
-
if (stored) {
|
|
2400
|
-
const next = { ...stored };
|
|
2401
|
-
delete next.oauthState;
|
|
2402
|
-
if (!(settings.clientId || (cimd ? !stored.clientInformation : registeredRedirectUrls(stored.clientInformation).includes(redirectUrl)))) {
|
|
2403
|
-
delete next.clientInformation;
|
|
2404
|
-
delete next.tokens;
|
|
2405
|
-
delete next.tokensExpireAt;
|
|
2406
|
-
}
|
|
2407
|
-
await store.save(next);
|
|
2408
|
-
}
|
|
2409
|
-
let authorizationUrl;
|
|
2410
|
-
const provider = createProvider(serverUrl, store, settings, redirectUrl, (url) => {
|
|
2411
|
-
authorizationUrl = url;
|
|
2412
|
-
});
|
|
2413
|
-
const flow = {
|
|
2414
|
-
serverUrl,
|
|
2415
|
-
fetch: timedFetch(),
|
|
2416
|
-
signal,
|
|
2417
|
-
resourceMetadataUrl: options.challenge?.resourceMetadataUrl,
|
|
2418
|
-
authorizationServerMetadataUrl: settings.authServerMetadataUrl,
|
|
2419
|
-
scope: mergeScopes(settings.scope, stepUp ? stepUpScope(stored?.tokens?.scope, options.challenge?.scope) : options.challenge?.scope)
|
|
2420
|
-
};
|
|
2421
|
-
const skipRefresh = stepUp;
|
|
2422
|
-
if (await authorizeMcp(provider, {
|
|
2423
|
-
...flow,
|
|
2424
|
-
skipRefresh
|
|
2425
|
-
}) === "AUTHORIZED") return;
|
|
2426
|
-
if (!authorizationUrl) throw new Error("OAuth flow did not produce an authorization URL");
|
|
2427
|
-
const state = await provider.state();
|
|
2428
|
-
const authorizationRedirectUrl = new URL(authorizationUrl.searchParams.get("redirect_uri") ?? redirectUrl);
|
|
2429
|
-
options.prompt.showAuthorizationUrl(authorizationUrl);
|
|
2430
|
-
const { code, iss } = await waitForAuthorizationResponse(callback, state, authorizationRedirectUrl, options.prompt, signal);
|
|
2431
|
-
await authorizeMcp(provider, {
|
|
2432
|
-
...flow,
|
|
2433
|
-
authorizationCode: code,
|
|
2434
|
-
iss
|
|
2435
|
-
});
|
|
2436
|
-
} catch (error) {
|
|
2437
|
-
if (signal?.aborted) throw new McpSignInCancelledError();
|
|
2438
|
-
throw error;
|
|
2439
|
-
} finally {
|
|
2440
|
-
await callback.close();
|
|
2441
|
-
}
|
|
2442
|
-
}
|
|
2443
|
-
//#endregion
|
|
2444
|
-
//#region node_modules/.pnpm/@earendil-works+pi-coding-agent@1.2.0_@aws-sdk+credential-provider-node@3.972.86_@model_cb83c1bd7a7ff5cede8c43e329c5cf76/node_modules/@earendil-works/pi-coding-agent/dist/extensions/mcp/log.js
|
|
2445
|
-
/**
|
|
2446
|
-
* Log messages MCP servers send with `notifications/message`, appended to `mcp.log` in the agent
|
|
2447
|
-
* directory. Several pi processes may write to the same file, so every message is one synchronous
|
|
2448
|
-
* append. The file is rotated to `mcp.log.1` once it grows past `MAX_LOG_BYTES`.
|
|
2449
|
-
*/
|
|
2450
|
-
const MAX_LOG_BYTES = 5242880;
|
|
2451
|
-
function isRecord(value) {
|
|
2452
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2453
|
-
}
|
|
2454
|
-
function formatData(data) {
|
|
2455
|
-
if (typeof data === "string") return data;
|
|
2456
|
-
try {
|
|
2457
|
-
return JSON.stringify(data) ?? String(data);
|
|
2458
|
-
} catch {
|
|
2459
|
-
return String(data);
|
|
2460
|
-
}
|
|
2461
|
-
}
|
|
2462
|
-
/** Format one `notifications/message` from `server` as a log line; continuation lines are indented. */
|
|
2463
|
-
function formatMcpLogMessage(server, params, now = /* @__PURE__ */ new Date()) {
|
|
2464
|
-
const message = isRecord(params) ? params : { data: params };
|
|
2465
|
-
const level = typeof message.level === "string" ? message.level : "info";
|
|
2466
|
-
const logger = typeof message.logger === "string" && message.logger ? ` ${message.logger}:` : "";
|
|
2467
|
-
const text = formatData(message.data).replace(/\r?\n/g, "\n ");
|
|
2468
|
-
return `${now.toISOString()} [${server}] ${level}${logger} ${text}\n`;
|
|
2469
|
-
}
|
|
2470
|
-
/** Appends server log messages to one file. Write errors are ignored: logging must not break tools. */
|
|
2471
|
-
var McpServerLog = class {
|
|
2472
|
-
path;
|
|
2473
|
-
size;
|
|
2474
|
-
constructor(path) {
|
|
2475
|
-
this.path = path;
|
|
2476
|
-
}
|
|
2477
|
-
write(server, params) {
|
|
2478
|
-
const line = formatMcpLogMessage(server, params);
|
|
2479
|
-
try {
|
|
2480
|
-
if (this.size === void 0) {
|
|
2481
|
-
mkdirSync(dirname(this.path), { recursive: true });
|
|
2482
|
-
this.size = this.currentSize();
|
|
2483
|
-
}
|
|
2484
|
-
if (this.size > MAX_LOG_BYTES) {
|
|
2485
|
-
if (this.currentSize() > MAX_LOG_BYTES) renameSync(this.path, `${this.path}.1`);
|
|
2486
|
-
this.size = this.currentSize();
|
|
2487
|
-
}
|
|
2488
|
-
appendFileSync(this.path, line);
|
|
2489
|
-
this.size += Buffer.byteLength(line);
|
|
2490
|
-
} catch {}
|
|
2491
|
-
}
|
|
2492
|
-
currentSize() {
|
|
2493
|
-
try {
|
|
2494
|
-
return statSync(this.path).size;
|
|
2495
|
-
} catch {
|
|
2496
|
-
return 0;
|
|
2497
|
-
}
|
|
2498
|
-
}
|
|
2499
|
-
};
|
|
2500
|
-
//#endregion
|
|
2501
|
-
//#region node_modules/.pnpm/@earendil-works+pi-coding-agent@1.2.0_@aws-sdk+credential-provider-node@3.972.86_@model_cb83c1bd7a7ff5cede8c43e329c5cf76/node_modules/@earendil-works/pi-coding-agent/dist/extensions/mcp/runtime.js
|
|
2502
|
-
/**
|
|
2503
|
-
* The part of the MCP integration that talks to servers: connections, transports, and OAuth
|
|
2504
|
-
* sign-in. It pulls in the MCP client, so index.ts loads it through runtime.lazy.ts only when a
|
|
2505
|
-
* server is configured.
|
|
2506
|
-
*/
|
|
2507
|
-
const DEFAULT_TIMEOUT_SECONDS = 60;
|
|
2508
|
-
/** Delays between attempts to connect to an HTTP server that failed with a transient error. */
|
|
2509
|
-
const CONNECT_RETRY_DELAYS_MS = [250, 1e3];
|
|
2510
|
-
function errorMessage(error) {
|
|
2511
|
-
return error instanceof Error ? error.message : String(error);
|
|
2512
|
-
}
|
|
2513
|
-
/** Network failures and overloaded or restarting servers, which are worth another attempt. */
|
|
2514
|
-
function isTransientError(error) {
|
|
2515
|
-
if (error instanceof McpHttpError) return error.status === 408 || error.status === 429 || error.status >= 500 && error.status !== 501;
|
|
2516
|
-
return error instanceof TypeError;
|
|
2517
|
-
}
|
|
2518
|
-
function signInRequiredMessage(entry) {
|
|
2519
|
-
const provider = "url" in entry.config ? entry.config.auth?.provider : void 0;
|
|
2520
|
-
return `MCP server "${entry.name}" requires sign-in. Run ${provider ? `/login ${provider}` : "/mcp"} to sign in.`;
|
|
2521
|
-
}
|
|
2522
|
-
/** HTTP servers authenticate with OAuth unless the config supplies an `Authorization` header or `auth`. */
|
|
2523
|
-
function usesOAuth(entry) {
|
|
2524
|
-
const { config } = entry;
|
|
2525
|
-
if (!("url" in config) || config.auth) return false;
|
|
2526
|
-
return !Object.keys(config.headers ?? {}).some((header) => header.toLowerCase() === "authorization");
|
|
2527
|
-
}
|
|
2528
|
-
/** `~` and `~/…` (also `~\…` on Windows) name the home directory, like in a shell. */
|
|
2529
|
-
function expandHome(value) {
|
|
2530
|
-
if (value === "~") return homedir();
|
|
2531
|
-
if (value.startsWith("~/") || process.platform === "win32" && value.startsWith("~\\")) return join(homedir(), value.slice(2));
|
|
2532
|
-
return value;
|
|
2533
|
-
}
|
|
2534
|
-
function createDefaultTransport(entry, cwd, authProvider) {
|
|
2535
|
-
const { config, name } = entry;
|
|
2536
|
-
if ("url" in config) return new StreamableHttpTransport({
|
|
2537
|
-
url: config.url,
|
|
2538
|
-
headers: resolveHeadersOrThrow(config.headers, `MCP server "${name}"`),
|
|
2539
|
-
authProvider
|
|
2540
|
-
});
|
|
2541
|
-
const env = {};
|
|
2542
|
-
for (const [key, value] of Object.entries(config.env ?? {})) env[key] = resolveConfigValueOrThrow(value, `MCP server "${name}" env "${key}"`);
|
|
2543
|
-
return new StdioTransport({
|
|
2544
|
-
command: expandHome(config.command),
|
|
2545
|
-
args: config.args?.map(expandHome),
|
|
2546
|
-
cwd: resolve(cwd, expandHome(config.cwd ?? ".")),
|
|
2547
|
-
env,
|
|
2548
|
-
stderr: "pipe"
|
|
2549
|
-
});
|
|
2550
|
-
}
|
|
2551
|
-
/** Servers that do not implement `resources/templates/list` have no templates. */
|
|
2552
|
-
async function withoutTemplates(list, empty) {
|
|
2553
|
-
try {
|
|
2554
|
-
return await list();
|
|
2555
|
-
} catch (error) {
|
|
2556
|
-
if (error instanceof McpError && error.code === JSON_RPC_ERROR_CODES.methodNotFound) return empty;
|
|
2557
|
-
throw error;
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
function listTemplates(client, options = {}) {
|
|
2561
|
-
return withoutTemplates(() => client.listResourceTemplates(options), []);
|
|
2562
|
-
}
|
|
2563
|
-
/** One configured server. Reconnects lazily when a call finds the connection gone. */
|
|
2564
|
-
var McpServerConnection = class {
|
|
2565
|
-
entry;
|
|
2566
|
-
state = "connecting";
|
|
2567
|
-
error;
|
|
2568
|
-
tools = [];
|
|
2569
|
-
/**
|
|
2570
|
-
* Whether the server offers resources, which the resource tools list and read on demand. They are
|
|
2571
|
-
* not listed at connect: listing every resource can take long (a large drive, say).
|
|
2572
|
-
*/
|
|
2573
|
-
hasResources = false;
|
|
2574
|
-
/** Server instructions from `initialize`, describing its tools as a group. */
|
|
2575
|
-
instructions;
|
|
2576
|
-
/** Last OAuth challenge from the server; sign-in uses its resource metadata URL and scope. */
|
|
2577
|
-
challenge;
|
|
2578
|
-
client;
|
|
2579
|
-
opening;
|
|
2580
|
-
/** Aborted by `close()`; cancels a connect in progress, including the wait between retries. */
|
|
2581
|
-
shutdown = new AbortController();
|
|
2582
|
-
/** Stderr of the last stdio server that failed to connect. */
|
|
2583
|
-
stderrTail;
|
|
2584
|
-
cwd;
|
|
2585
|
-
createTransport;
|
|
2586
|
-
authProvider;
|
|
2587
|
-
onTools;
|
|
2588
|
-
onChange;
|
|
2589
|
-
log;
|
|
2590
|
-
constructor(options) {
|
|
2591
|
-
this.entry = options.entry;
|
|
2592
|
-
this.cwd = options.cwd;
|
|
2593
|
-
this.createTransport = options.createTransport;
|
|
2594
|
-
this.onTools = options.onTools;
|
|
2595
|
-
this.onChange = options.onChange;
|
|
2596
|
-
this.log = options.log;
|
|
2597
|
-
const url = this.oauthUrl;
|
|
2598
|
-
const provider = "url" in this.entry.config ? this.entry.config.auth?.provider : void 0;
|
|
2599
|
-
this.authProvider = url ? createMcpAuthProvider({
|
|
2600
|
-
serverUrl: url,
|
|
2601
|
-
store: options.credentials.forServer(this.entry.name, url),
|
|
2602
|
-
settings: () => this.oauthSettings(),
|
|
2603
|
-
onChallenge: (challenge) => {
|
|
2604
|
-
this.challenge = challenge;
|
|
2605
|
-
}
|
|
2606
|
-
}) : provider ? {
|
|
2607
|
-
token: async () => options.providerToken?.(provider),
|
|
2608
|
-
settled: async () => {}
|
|
2609
|
-
} : void 0;
|
|
2610
|
-
}
|
|
2611
|
-
get name() {
|
|
2612
|
-
return this.entry.name;
|
|
2613
|
-
}
|
|
2614
|
-
get closed() {
|
|
2615
|
-
return this.shutdown.signal.aborted;
|
|
2616
|
-
}
|
|
2617
|
-
get timeoutMs() {
|
|
2618
|
-
return (this.entry.config.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1e3;
|
|
2619
|
-
}
|
|
2620
|
-
/** Server URL when the server authenticates with OAuth. */
|
|
2621
|
-
get oauthUrl() {
|
|
2622
|
-
return usesOAuth(this.entry) && "url" in this.entry.config ? this.entry.config.url : void 0;
|
|
2623
|
-
}
|
|
2624
|
-
oauthSettings() {
|
|
2625
|
-
const oauth = "url" in this.entry.config ? this.entry.config.oauth : void 0;
|
|
2626
|
-
if (!oauth) return {};
|
|
2627
|
-
return {
|
|
2628
|
-
clientId: oauth.clientId,
|
|
2629
|
-
clientSecret: oauth.clientSecret === void 0 ? void 0 : resolveConfigValueOrThrow(oauth.clientSecret, `MCP server "${this.entry.name}" oauth.clientSecret`),
|
|
2630
|
-
callbackPort: oauth.callbackPort,
|
|
2631
|
-
callbackUrl: oauth.callbackUrl,
|
|
2632
|
-
scope: oauth.scope,
|
|
2633
|
-
clientName: oauth.clientName,
|
|
2634
|
-
clientRegistration: oauth.clientRegistration,
|
|
2635
|
-
authServerMetadataUrl: oauth.authServerMetadataUrl ? new URL(oauth.authServerMetadataUrl) : void 0
|
|
2636
|
-
};
|
|
2637
|
-
}
|
|
2638
|
-
getClient() {
|
|
2639
|
-
if (this.closed) return Promise.reject(/* @__PURE__ */ new Error(`MCP server "${this.entry.name}" is shut down`));
|
|
2640
|
-
if (this.client?.connectionState === "connected") return Promise.resolve(this.client);
|
|
2641
|
-
this.opening ??= this.open().finally(() => {
|
|
2642
|
-
this.opening = void 0;
|
|
2643
|
-
});
|
|
2644
|
-
return this.opening;
|
|
2645
|
-
}
|
|
2646
|
-
callTool(name, args, options) {
|
|
2647
|
-
return this.withClient((client) => client.callTool(name, args, options));
|
|
2648
|
-
}
|
|
2649
|
-
readResource(uri, options) {
|
|
2650
|
-
return this.withClient((client) => client.readResource(uri, options), true);
|
|
2651
|
-
}
|
|
2652
|
-
resourcesPage(cursor, options) {
|
|
2653
|
-
return this.withClient((client) => client.listResourcesPage(cursor, options), true);
|
|
2654
|
-
}
|
|
2655
|
-
resourceTemplatesPage(cursor, options) {
|
|
2656
|
-
return this.withClient((client) => withoutTemplates(() => client.listResourceTemplatesPage(cursor, options), { resourceTemplates: [] }), true);
|
|
2657
|
-
}
|
|
2658
|
-
allResources(options) {
|
|
2659
|
-
return this.withClient((client) => client.listResources(options), true);
|
|
2660
|
-
}
|
|
2661
|
-
allResourceTemplates(options) {
|
|
2662
|
-
return this.withClient((client) => listTemplates(client, options), true);
|
|
2663
|
-
}
|
|
2664
|
-
/**
|
|
2665
|
-
* Run a request, reconnecting when needed. `readOnly` requests are retried once after a transient
|
|
2666
|
-
* HTTP error; tool calls are not, since they may have run.
|
|
2667
|
-
*/
|
|
2668
|
-
async withClient(run, readOnly = false) {
|
|
2669
|
-
for (let attempt = 1;; attempt++) {
|
|
2670
|
-
const client = await this.getClient();
|
|
2671
|
-
try {
|
|
2672
|
-
return await run(client);
|
|
2673
|
-
} catch (error) {
|
|
2674
|
-
if (readOnly && attempt === 1 && error instanceof McpHttpError && isTransientError(error)) {
|
|
2675
|
-
await new Promise((resolve) => setTimeout(resolve, CONNECT_RETRY_DELAYS_MS[0]));
|
|
2676
|
-
continue;
|
|
2677
|
-
}
|
|
2678
|
-
if (error instanceof McpSessionExpiredError && attempt === 1) {
|
|
2679
|
-
if (this.client === client) this.client = void 0;
|
|
2680
|
-
continue;
|
|
2681
|
-
}
|
|
2682
|
-
if (!this.needsSignIn(error)) throw error;
|
|
2683
|
-
await this.dropClient(client);
|
|
2684
|
-
this.markNeedsAuth();
|
|
2685
|
-
throw new Error(signInRequiredMessage(this.entry));
|
|
2686
|
-
}
|
|
2687
|
-
}
|
|
2688
|
-
}
|
|
2689
|
-
/** Connect again with fresh credentials, for example after signing in. */
|
|
2690
|
-
async reconnect() {
|
|
2691
|
-
await this.opening?.catch(() => void 0);
|
|
2692
|
-
if (this.client) await this.dropClient(this.client);
|
|
2693
|
-
await this.getClient();
|
|
2694
|
-
}
|
|
2695
|
-
/** Disconnect after the stored credentials were removed. */
|
|
2696
|
-
async signOut() {
|
|
2697
|
-
await this.opening?.catch(() => void 0);
|
|
2698
|
-
if (this.client) await this.dropClient(this.client);
|
|
2699
|
-
if (!this.closed) this.markNeedsAuth();
|
|
2700
|
-
}
|
|
2701
|
-
/** OAuth servers that still reject the request after a refresh need the user to sign in again. */
|
|
2702
|
-
needsSignIn(error) {
|
|
2703
|
-
return error instanceof McpOAuthAuthorizationRequiredError || this.authProvider !== void 0 && error instanceof McpAuthRequiredError;
|
|
2704
|
-
}
|
|
2705
|
-
markNeedsAuth() {
|
|
2706
|
-
this.state = "needs-auth";
|
|
2707
|
-
this.error = void 0;
|
|
2708
|
-
this.changed();
|
|
2709
|
-
}
|
|
2710
|
-
changed() {
|
|
2711
|
-
this.onChange?.(this);
|
|
2712
|
-
}
|
|
2713
|
-
async dropClient(client) {
|
|
2714
|
-
if (this.client === client) this.client = void 0;
|
|
2715
|
-
await client.close().catch(() => void 0);
|
|
2716
|
-
}
|
|
2717
|
-
async open() {
|
|
2718
|
-
this.state = "connecting";
|
|
2719
|
-
this.changed();
|
|
2720
|
-
const retries = "url" in this.entry.config ? CONNECT_RETRY_DELAYS_MS : [];
|
|
2721
|
-
for (let attempt = 0;; attempt++) {
|
|
2722
|
-
this.stderrTail = void 0;
|
|
2723
|
-
try {
|
|
2724
|
-
return await this.connectOnce();
|
|
2725
|
-
} catch (error) {
|
|
2726
|
-
const delay = retries[attempt];
|
|
2727
|
-
if (this.closed || delay === void 0 || !isTransientError(error)) throw this.connectFailed(error);
|
|
2728
|
-
await setTimeout$1(delay, void 0, { signal: this.shutdown.signal }).catch(() => void 0);
|
|
2729
|
-
if (this.closed) throw this.connectFailed(error);
|
|
2730
|
-
}
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
|
-
async connectOnce() {
|
|
2734
|
-
const client = new McpClient({
|
|
2735
|
-
name: "pi",
|
|
2736
|
-
version: VERSION,
|
|
2737
|
-
requestTimeoutMs: this.timeoutMs,
|
|
2738
|
-
roots: [{
|
|
2739
|
-
uri: pathToFileURL(this.cwd).href,
|
|
2740
|
-
name: basename(this.cwd)
|
|
2741
|
-
}]
|
|
2742
|
-
});
|
|
2743
|
-
const log = this.log;
|
|
2744
|
-
if (log) client.onNotification("notifications/message", (params) => log.write(this.entry.name, params));
|
|
2745
|
-
let closing;
|
|
2746
|
-
const closeClient = () => {
|
|
2747
|
-
closing ??= client.close().catch(() => void 0);
|
|
2748
|
-
return closing;
|
|
2749
|
-
};
|
|
2750
|
-
this.shutdown.signal.addEventListener("abort", closeClient, { once: true });
|
|
2751
|
-
let transport;
|
|
2752
|
-
try {
|
|
2753
|
-
transport = this.createTransport(this.entry, this.cwd, this.authProvider);
|
|
2754
|
-
await client.connect(transport);
|
|
2755
|
-
client.onNotification("notifications/tools/list_changed", () => {
|
|
2756
|
-
this.refreshTools(client);
|
|
2757
|
-
});
|
|
2758
|
-
const stdio = transport instanceof StdioTransport ? transport : void 0;
|
|
2759
|
-
client.onClose(() => this.handleClientClose(client, stdio));
|
|
2760
|
-
const tools = client.serverCapabilities?.tools ? await client.listTools() : [];
|
|
2761
|
-
if (this.closed) throw new Error("shut down while connecting");
|
|
2762
|
-
if (client.connectionState !== "connected") throw new Error("connection closed during setup");
|
|
2763
|
-
this.client = client;
|
|
2764
|
-
this.tools = tools;
|
|
2765
|
-
this.hasResources = client.serverCapabilities?.resources !== void 0;
|
|
2766
|
-
this.instructions = client.instructions?.trim() || void 0;
|
|
2767
|
-
this.state = "connected";
|
|
2768
|
-
this.error = void 0;
|
|
2769
|
-
this.onTools(this);
|
|
2770
|
-
this.changed();
|
|
2771
|
-
return client;
|
|
2772
|
-
} catch (error) {
|
|
2773
|
-
await closeClient();
|
|
2774
|
-
if (transport instanceof StdioTransport) this.stderrTail = transport.stderr.trim().slice(-2e3) || void 0;
|
|
2775
|
-
throw error;
|
|
2776
|
-
} finally {
|
|
2777
|
-
this.shutdown.signal.removeEventListener("abort", closeClient);
|
|
2778
|
-
}
|
|
2779
|
-
}
|
|
2780
|
-
connectFailed(error) {
|
|
2781
|
-
if (this.needsSignIn(error) && !this.closed) {
|
|
2782
|
-
this.markNeedsAuth();
|
|
2783
|
-
return new Error(signInRequiredMessage(this.entry));
|
|
2784
|
-
}
|
|
2785
|
-
this.state = this.closed ? "closed" : "failed";
|
|
2786
|
-
this.error = this.stderrTail ? `${errorMessage(error)}\n${this.stderrTail}` : errorMessage(error);
|
|
2787
|
-
this.changed();
|
|
2788
|
-
return /* @__PURE__ */ new Error(`MCP server "${this.entry.name}" failed to connect: ${this.error}`);
|
|
2789
|
-
}
|
|
2790
|
-
/** The transport dropped. The next call reconnects; until then the status shows why. */
|
|
2791
|
-
handleClientClose(client, stdio) {
|
|
2792
|
-
if (this.client !== client || this.closed) return;
|
|
2793
|
-
this.client = void 0;
|
|
2794
|
-
this.state = "disconnected";
|
|
2795
|
-
const stderr = stdio?.stderr.trim().slice(-2e3);
|
|
2796
|
-
this.error = stderr ? `Connection closed\n${stderr}` : "Connection closed";
|
|
2797
|
-
this.changed();
|
|
2798
|
-
}
|
|
2799
|
-
async refreshTools(client) {
|
|
2800
|
-
try {
|
|
2801
|
-
const tools = await client.listTools();
|
|
2802
|
-
if (this.client !== client || this.closed) return;
|
|
2803
|
-
this.tools = tools;
|
|
2804
|
-
this.onTools(this);
|
|
2805
|
-
} catch (error) {
|
|
2806
|
-
this.error = `Failed to refresh tools: ${errorMessage(error)}`;
|
|
2807
|
-
}
|
|
2808
|
-
this.changed();
|
|
2809
|
-
}
|
|
2810
|
-
/** Resolves once no transport of this server is open, including one that was still connecting. */
|
|
2811
|
-
async close() {
|
|
2812
|
-
this.shutdown.abort();
|
|
2813
|
-
this.state = "closed";
|
|
2814
|
-
this.changed();
|
|
2815
|
-
const client = this.client;
|
|
2816
|
-
this.client = void 0;
|
|
2817
|
-
await Promise.all([client?.close().catch(() => void 0), this.opening?.catch(() => void 0)]);
|
|
2818
|
-
await this.authProvider?.settled();
|
|
2819
|
-
}
|
|
2820
|
-
};
|
|
2821
|
-
//#endregion
|
|
2822
|
-
export { McpOAuthCredentialStore, McpServerConnection, McpServerLog, McpSignInCancelledError, createDefaultTransport, signInMcpServer };
|