chainlesschain 0.163.2 → 0.163.4
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/package.json +3 -2
- package/scripts/command-lifecycle-report.mjs +330 -38
- package/src/assets/web-panel/assets/{AIOps-DnGsmsqJ.js → AIOps-DB5X7qqV.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CrO6fcQ7.js → ActionButton-DxHRtKLk.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DsBdOaP2.js → Analytics-DIH5QrxT.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DmQslKUB.js → AppLayout-uu0oxVz7.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BvyrT_QQ.js → Artifacts-BNSMiW9Y.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-prndoLiC.js → Audit-DcAzB4_x.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DOTFyA55.js → BackgroundAgents-ClSsvjIc.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-9zOnPp_8.js → Backup-BMciY0js.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C4PC6sAG.js → BaseInput-Hn3b57Sh.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BBAF-W9v.js → Chat-CoSlC53V.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CWhvvloO.js → ChatBubbleRenderer-CFevJ3So.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BI9iculd.js → Checkbox-B4L4Eu4w.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-3DP9BCKb.js → Codegen-C77FgRxF.js} +1 -1
- package/src/assets/web-panel/assets/{Col-df-2TnJX.js → Col-BrLtJLH3.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CHs8gG_A.js → Community-Be7pXXhA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CDfF6q7V.js → Compact-DuGPecjo.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CthrDnO7.js → Compliance-CzS66mjP.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-HjIqsWJw.js → Cowork-CEhjCoyK.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BKc196fb.js → Cron-D7ir8Vja.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CJnjdc8W.js → Crosschain-BcXmuLxf.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BHUy-8Q2.js → DID-Cto1dtBy.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-QDB7sKeY.js → Dashboard-B7KjgwOX.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DStcVK11.js → Dropdown-COMqxijg.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BwSUskMJ.js → EmailListRenderer-BZrya-nf.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DgF_kurT.js → FamilyGuardDashboard-AmVeMq0J.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-k8wvv-wN.js → Federation-kZU8c-27.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C11qoq2r.js → FormItemContext-sOV22v2-.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BIIpr9kg.js → GenericCardRenderer-BUuLtGCz.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BlA1exTe.js → Git-K-ZxsLsH.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CYa1ZYM4.js → Governance-D3OEnawI.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-6_kKVIt6.js → Inference-Bll0gRmP.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CB4AwxYy.js → KnowledgeGraph-DBXwjr53.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-xf7nHFAe.js → Logs-CEzPwEfP.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-Bf3DEqP6.js → MarkdownRenderer-CLmCbHx6.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-aPg8e4Jt.js → Marketplace-BYyRyOPz.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C-TNlVUf.js → McpTools-DVu8w_rM.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-DMTPIpGa.js → Memory-tenuZ_hO.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BxyvveRq.js → MobileBridge-Bxgy4ozS.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DyPCrAyQ.js → MobileProjects-Dgx1D6sc.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BdhqNXp6.js → Mtc-CRfJ8r8j.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Cz-HFxzW.js → MtcAudit-BBJq4Aiz.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-CIUaFNsx.js → Multisig-B3ii9lai.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CWe6-J17.js → NLProgramming-FIi-Cubv.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-vYAKeviU.js → Notes-DIq8MPf1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DQL9-2d7.js → NotificationSettings-BFOD9eGd.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DnXj1jBn.js → OrderTableRenderer--41u7vre.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-B3spU8AN.js → Organization-B_0AWztW.js} +2 -2
- package/src/assets/web-panel/assets/{Overflow-DgEwo7vm.js → Overflow-B6I7TuXv.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-hujMRH8X.js → P2P-Cdf6qens.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-C_qhdg9h.js → PdhVaultBrowser-B_0FcQJY.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-vp7LMcSf.js → Permissions-6nFBjsQp.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dxr5Hgnq.js → PersonalDataHub-nQYV7xpn.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-DEPZqqH4.js → Pipeline-CBVod5jm.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DTpDiKPe.js → Privacy-D52jW1Nt.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-jDJsRWmF.js → ProjectInit-Bdx3BYhf.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Dhor5RWH.js → ProjectSettings-b7xr4DUa.js} +2 -2
- package/src/assets/web-panel/assets/Projects-DhfifCKx.js +1 -0
- package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-CjIw-wIb.js → QrScannerModal-FQKSmc3e.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DX6jcqoi.js → QuickAsk-CSTrhzcF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DopXkhar.js → Recommend-CxMABOUh.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BLvqJE9c.js → RemoteSession-Cv2T8yRa.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DhRj2Me7.js → Reputation-CE1k4Vvu.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DQ53vuPq.js → Row-BNgNcAV_.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BslvwiBD.js → RssFeed-5ZiBZ3ph.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DJKBDNKZ.js → Search-mDOIF1UV.js} +1 -1
- package/src/assets/web-panel/assets/{Security-COhpxHER.js → Security-CtuCTC-e.js} +3 -3
- package/src/assets/web-panel/assets/{Services-dbulD6cI.js → Services-DxFO2Zg7.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-ciIVNfhC.js → Skeleton-CKmKkH9y.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-AiRTkTCk.js → Skills-Bmzl9qkk.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DDMcWYBc.js → Sla-9RqaWHOC.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BPxWbpXO.js → SpeechSettings-CGdzFIYw.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BoEOXCyg.js → SyncSettings-CvNuCeBT.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-B_sLdcnw.js +1 -0
- package/src/assets/web-panel/assets/{Templates-UowY8Ixe.js → Templates-D9_YijdK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DauSLQXN.js → Tenant-Cuc7BUSP.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DBj7Jneq.js → Terminal-6VYRnJgd.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-UMrPwoZC.js → TimelineRenderer-GtK8xd8Y.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-4ea96-bn.js → Tokens-BZ94Lt5W.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger--ISCqt4b.js → Trigger-DEcZgDaX.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-D4GVimRJ.js → Trust-CC3z9THe.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Ci-nXRQc.js → UkeySign-Cvtw8cV3.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Cjk7mKPw.js → VideoEditing-CL4e6Bn3.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BgOLOO3Z.js → Wallet-ycEemP27.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CCX0K29i.js → WebAuthn-DgZ4X_aw.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-Ch3zgIsE.js → WorkflowEditor-D0qMhSv7.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DKOU_2Vb.js → chat-DsAg01Zn.js} +1 -1
- package/src/assets/web-panel/assets/{colors-6OVo4u3C.js → colors-B0oqAhrQ.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BH78Hlsq.js → compact-item-CmX0Rabz.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CFRpSbbC.js → createContext-Be4cn1Ub.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DNQhgcVi.js → hasIn-D5v12kVq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkN3iTTp.js → index-0eWLkQKT.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkNEMANR.js → index-301ogenv.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3RtTT4K.js → index-5LGY0JbG.js} +1 -1
- package/src/assets/web-panel/assets/{index-dDkoL4Pe.js → index-7VKDn-t1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHa4WyTZ.js → index-8WvYGnON.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaYqY35Z.js → index-B1YGjOWC.js} +1 -1
- package/src/assets/web-panel/assets/{index-bKm3TUDp.js → index-B3mUHuVc.js} +1 -1
- package/src/assets/web-panel/assets/{index--rq90rL9.js → index-BA4oulaP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaDgdJYj.js → index-BBREnn1V.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_Z-YoxX.js → index-BBvyHguy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkqHgG-b.js → index-BKmsS2jj.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrcLHN2F.js → index-BRIqLy1m.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaVE6E8y.js → index-BXGSbelg.js} +3 -3
- package/src/assets/web-panel/assets/{index-BYrnm5Ob.js → index-BaE0Ui07.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDSR7ecX.js → index-BoAIAZ5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNuCHHQT.js → index-BqltWojQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Eag8e_mA.js → index-CGiIyTZw.js} +1 -1
- package/src/assets/web-panel/assets/index-CKB0vbJU.js +1 -0
- package/src/assets/web-panel/assets/{index-BepBTpa8.js → index-CZ2wUEgr.js} +1 -1
- package/src/assets/web-panel/assets/{index-HNGGVF6o.js → index-CZpfzcuy.js} +1 -1
- package/src/assets/web-panel/assets/{index-AuBhLqOm.js → index-Ckrk3pIV.js} +1 -1
- package/src/assets/web-panel/assets/{index-7TC3S8SN.js → index-CqoCZ5vR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFPJ8yzF.js → index-CyWBir7N.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bb6kDrUx.js → index-CyyGTMKd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKN2noIF.js → index-D0BSkxYQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Du0rnksC.js → index-D5YVopyr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxRy5LPR.js → index-DKUBj36_.js} +1 -1
- package/src/assets/web-panel/assets/{index-sm7W8AXy.js → index-DM724NgU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BujsOZfc.js → index-DQRsQen0.js} +1 -1
- package/src/assets/web-panel/assets/index-DgoXmwqB.js +1 -0
- package/src/assets/web-panel/assets/{index-Y9MJv28e.js → index-DikJ0Qqe.js} +1 -1
- package/src/assets/web-panel/assets/{index-caye7AOE.js → index-Dn2dSdXx.js} +1 -1
- package/src/assets/web-panel/assets/{index-cdf7fd7d.js → index-DyUaFWEk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnnXQot_.js → index-Hfe_dEGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvHGtNnF.js → index-_JjRLSD0.js} +1 -1
- package/src/assets/web-panel/assets/{index-nbfHl1oV.js → index-frSojpHj.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cu3L4r5l.js → index-jK3ax6Z6.js} +1 -1
- package/src/assets/web-panel/assets/{index-D84id2Y4.js → index-qMuZ-NRR.js} +1 -1
- package/src/assets/web-panel/assets/{index-hPY0fFrd.js → index-q_fQ94SE.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BZ9w6jPL.js → initDefaultProps-D4VdhJ9_.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BnwUiOho.js → motion-CZJA0MQW.js} +1 -1
- package/src/assets/web-panel/assets/{move-ByUKJnNm.js → move-BqEs7Sb1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-V7yK1KEl.js → mtc-parser-BpHEtsu1.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BEXBBt4j.js → omit-BW94tY9W.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-qDm_46Gi.js → pickAttrs-BnBjPWP9.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BIbhEo_0.js → placementArrow-uUkjQ_hu.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-NbPbDgA_.js → responsiveObserve-B-klUqXK.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BnUWglZ0.js → slide-CvY1cNAs.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BX8_wZI5.js → statusUtils-Ix5sEDT2.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BV4OycQX.js → styleChecker-C8qcXOne.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Od_H5llY.js → useFlexGapSupport-KNF6Kj8f.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BWI-PyiX.js → useFs-DGJr345n.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BqMuhG3g.js → usePersonalDataHub-BboN99rI.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DNd23z6f.js → vnode-Bgt8Vgte.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Cjaxnm7l.js → zoom-BhJtErgt.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +2 -2
- package/src/commands/agenda.js +6 -5
- package/src/commands/agent.js +237 -75
- package/src/commands/agents.js +6 -0
- package/src/commands/artifacts.js +98 -0
- package/src/commands/background-session.js +11 -1
- package/src/commands/mcp.js +218 -31
- package/src/commands/review.js +221 -22
- package/src/commands/session-mcp-recovery.js +5 -5
- package/src/data/changelog.json +27 -1
- package/src/gateways/ws/ws-agent-handler.js +492 -14
- package/src/harness/jsonl-session-store.js +39 -1
- package/src/harness/mcp-client.js +414 -71
- package/src/harness/prompt-compressor.js +24 -0
- package/src/harness/provider-backed-compaction.js +205 -0
- package/src/harness/worktree-isolator.js +26 -3
- package/src/lib/agent-worktree.js +41 -2
- package/src/lib/app-preview.js +3 -0
- package/src/lib/background-agent-supervisor.js +790 -186
- package/src/lib/background-command-argv.js +318 -0
- package/src/lib/background-launch-profile.js +1 -0
- package/src/lib/background-phase-reporter.js +22 -2
- package/src/lib/background-worktree-policy.js +109 -0
- package/src/lib/command-lifecycle-report.js +1340 -145
- package/src/lib/cowork-mcp-tools.js +51 -6
- package/src/lib/cowork-task-runner.js +1 -0
- package/src/lib/delivery-coordinator.js +30 -5
- package/src/lib/delivery-production-adapter.js +1777 -0
- package/src/lib/delivery-production-runner.js +240 -0
- package/src/lib/durable-security-store.js +29 -17
- package/src/lib/execution-trace/trace-context.cjs +179 -0
- package/src/lib/execution-trace/trace-context.js +8 -175
- package/src/lib/mcp-call-ledger-store.js +58 -6
- package/src/lib/mcp-headers-helper.js +76 -8
- package/src/lib/mcp-host-recovery-runtime.js +2 -0
- package/src/lib/mcp-ledger-recovery-admission.js +39 -3
- package/src/lib/mcp-recovery-adjudication.js +256 -19
- package/src/lib/mcp-sandbox-policy.js +184 -0
- package/src/lib/mcp-stdio-capsule-builder-worker.cjs +495 -0
- package/src/lib/mcp-stdio-executable-identity.js +33 -2
- package/src/lib/mcp-stdio-execution-authority.js +163 -0
- package/src/lib/mcp-stdio-immutable-vfs-resolver.cjs +830 -0
- package/src/lib/mcp-stdio-package-materialization.js +721 -333
- package/src/lib/mcp-stdio-workspace-authority.js +252 -0
- package/src/lib/observability/index.js +2 -0
- package/src/lib/observability/otlp-exporter.js +2 -2
- package/src/lib/packer/pack-update-applier.js +8 -1
- package/src/lib/packer/pkg-config-generator.js +34 -0
- package/src/lib/plugin-runtime/sandbox-policy.js +42 -2
- package/src/lib/process-execution-broker/index.js +213 -109
- package/src/lib/process-execution-broker/platform-sandbox.js +1024 -169
- package/src/lib/process-tree-termination.js +17 -1
- package/src/lib/scheduler-kernel/contract.js +315 -0
- package/src/lib/scheduler-kernel/store.js +1345 -0
- package/src/lib/session-host-lease.js +403 -30
- package/src/lib/session-persistence-failure.js +4 -1
- package/src/lib/skill-mcp.js +65 -4
- package/src/lib/sub-agent-context.js +8 -0
- package/src/lib/web-fetch.js +4 -4
- package/src/repl/agent-repl.js +285 -38
- package/src/runtime/agent-core.js +1044 -90
- package/src/runtime/agent-runtime.js +61 -8
- package/src/runtime/headless-runner.js +260 -166
- package/src/runtime/headless-stream.js +254 -25
- package/src/runtime/mcp-config.js +235 -28
- package/src/workers/background-agent-worker.js +492 -71
- package/src/assets/web-panel/assets/Projects-08xUDZGt.js +0 -1
- package/src/assets/web-panel/assets/Providers-BGs47s1a.js +0 -1
- package/src/assets/web-panel/assets/Tasks-CEYFJwmr.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DEMblAIm.js +0 -1
- package/src/assets/web-panel/assets/index-BTvq-36D.js +0 -1
- package/src/assets/web-panel/assets/index-hUpvk6Jl.js +0 -1
|
@@ -15,9 +15,14 @@ import { pathToFileURL } from "url";
|
|
|
15
15
|
import { isProxy } from "node:util/types";
|
|
16
16
|
import WebSocket from "ws";
|
|
17
17
|
import { safeJsonParse } from "../lib/safe-json.js";
|
|
18
|
+
import {
|
|
19
|
+
enforceMcpStdioCapsuleHostSandboxPolicy,
|
|
20
|
+
MCP_SANDBOX_POLICY_INVALID_CODE,
|
|
21
|
+
normalizeMcpSandboxPolicy,
|
|
22
|
+
readMcpStdioCwd,
|
|
23
|
+
} from "../lib/mcp-sandbox-policy.js";
|
|
18
24
|
import { EventRuntimeProducer } from "../lib/event-runtime-producer.js";
|
|
19
25
|
import { currentHostHooksV2WorkspaceBinding } from "../lib/hooks-v2-workspace-context.js";
|
|
20
|
-
import { resolvePluginWorkspaceAuthority } from "../lib/plugin-runtime/sandbox-policy.js";
|
|
21
26
|
import {
|
|
22
27
|
admitMcpToolList,
|
|
23
28
|
isMcpToolMetadataError,
|
|
@@ -33,11 +38,13 @@ import {
|
|
|
33
38
|
consumeMcpStdioExecutionAuthority,
|
|
34
39
|
materializeApprovedMcpStdioInvocation,
|
|
35
40
|
renewMcpStdioExecutionAuthority,
|
|
41
|
+
verifyMcpStdioApprovedWorkingDirectory,
|
|
36
42
|
} from "../lib/mcp-stdio-execution-authority.js";
|
|
37
43
|
import {
|
|
38
|
-
|
|
44
|
+
MCP_STDIO_CAPSULE_REQUIRED_BOUNDARIES,
|
|
39
45
|
prepareMcpStdioExecutableIdentity,
|
|
40
46
|
} from "../lib/mcp-stdio-executable-identity.js";
|
|
47
|
+
import { resolveMcpStdioSandboxContext } from "../lib/mcp-stdio-workspace-authority.js";
|
|
41
48
|
|
|
42
49
|
/**
|
|
43
50
|
* Injectable dependencies — overridable from tests.
|
|
@@ -53,7 +60,9 @@ export const _deps = {
|
|
|
53
60
|
consumeMcpStdioExecutionAuthority,
|
|
54
61
|
materializeApprovedMcpStdioInvocation,
|
|
55
62
|
renewMcpStdioExecutionAuthority,
|
|
63
|
+
verifyMcpStdioApprovedWorkingDirectory,
|
|
56
64
|
prepareMcpStdioExecutableIdentity,
|
|
65
|
+
resolveMcpStdioSandboxContext,
|
|
57
66
|
// Backoff sleep seam (tests override with a no-op so retries don't wait).
|
|
58
67
|
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
59
68
|
};
|
|
@@ -116,6 +125,60 @@ export function normalizeMcpConfigScope(scope = "user") {
|
|
|
116
125
|
return normalized;
|
|
117
126
|
}
|
|
118
127
|
|
|
128
|
+
const INVALID_MCP_CONFIG_ROW = Symbol("invalidMcpConfigRow");
|
|
129
|
+
const INVALID_MCP_CONFIG_ERROR = Symbol("invalidMcpConfigError");
|
|
130
|
+
const INVALID_MCP_CONFIG_RAW_ROW = Symbol("invalidMcpConfigRawRow");
|
|
131
|
+
|
|
132
|
+
function rawMcpRowValue(row, key) {
|
|
133
|
+
if (!row || typeof row !== "object" || isProxy(row)) return undefined;
|
|
134
|
+
const descriptor = Object.getOwnPropertyDescriptor(row, key);
|
|
135
|
+
return descriptor && "value" in descriptor ? descriptor.value : undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function mcpRowVisibility(row, { invalid = false, error = null } = {}) {
|
|
139
|
+
const displayName = rawMcpRowValue(row, "display_name");
|
|
140
|
+
const storageName = rawMcpRowValue(row, "name");
|
|
141
|
+
const name =
|
|
142
|
+
typeof displayName === "string" && displayName.length > 0
|
|
143
|
+
? displayName
|
|
144
|
+
: typeof storageName === "string" && storageName.length > 0
|
|
145
|
+
? storageName
|
|
146
|
+
: null;
|
|
147
|
+
if (!name) return null;
|
|
148
|
+
|
|
149
|
+
let configScope;
|
|
150
|
+
try {
|
|
151
|
+
configScope = normalizeMcpConfigScope(
|
|
152
|
+
rawMcpRowValue(row, "config_scope") || "user",
|
|
153
|
+
);
|
|
154
|
+
} catch {
|
|
155
|
+
// An unparseable scope cannot safely be treated as the lowest tier.
|
|
156
|
+
configScope = "managed";
|
|
157
|
+
}
|
|
158
|
+
const rawProjectPath = rawMcpRowValue(row, "project_path");
|
|
159
|
+
const visibility = {
|
|
160
|
+
name,
|
|
161
|
+
configScope,
|
|
162
|
+
projectPath:
|
|
163
|
+
typeof rawProjectPath === "string" && rawProjectPath.length > 0
|
|
164
|
+
? rawProjectPath
|
|
165
|
+
: null,
|
|
166
|
+
autoConnect: rawMcpRowValue(row, "auto_connect") === 1,
|
|
167
|
+
[INVALID_MCP_CONFIG_ROW]: invalid,
|
|
168
|
+
};
|
|
169
|
+
if (invalid) {
|
|
170
|
+
Object.defineProperties(visibility, {
|
|
171
|
+
[INVALID_MCP_CONFIG_ERROR]: { value: error },
|
|
172
|
+
[INVALID_MCP_CONFIG_RAW_ROW]: { value: row },
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return visibility;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function isInvalidMcpConfigRow(config) {
|
|
179
|
+
return config?.[INVALID_MCP_CONFIG_ROW] === true;
|
|
180
|
+
}
|
|
181
|
+
|
|
119
182
|
/**
|
|
120
183
|
* Default per-call timeout for HTTP MCP requests, mirroring the 30s stdio
|
|
121
184
|
* timeout so a hung/dead HTTP server can't block a request forever. Servers
|
|
@@ -549,6 +612,102 @@ function mcpTransportError(code, message, details = {}) {
|
|
|
549
612
|
return error;
|
|
550
613
|
}
|
|
551
614
|
|
|
615
|
+
function dispatchMcpToolRequest(options, metadata, dispatch) {
|
|
616
|
+
const admission = options?.dispatchAdmission ?? null;
|
|
617
|
+
if (metadata.method !== "tools/call" || admission === null) {
|
|
618
|
+
return dispatch();
|
|
619
|
+
}
|
|
620
|
+
if (typeof admission !== "function") {
|
|
621
|
+
throw mcpTransportError(
|
|
622
|
+
"CC_MCP_DISPATCH_ADMISSION_INVALID",
|
|
623
|
+
"MCP tool dispatch authority is invalid",
|
|
624
|
+
{
|
|
625
|
+
transport: metadata.transport,
|
|
626
|
+
dispatched: false,
|
|
627
|
+
outcomeUnknown: false,
|
|
628
|
+
},
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
let admissionOpen = true;
|
|
633
|
+
let dispatched = false;
|
|
634
|
+
let dispatchResult;
|
|
635
|
+
const admittedDispatch = () => {
|
|
636
|
+
if (!admissionOpen || dispatched) {
|
|
637
|
+
throw mcpTransportError(
|
|
638
|
+
"CC_MCP_DISPATCH_ADMISSION_INVALID",
|
|
639
|
+
"MCP tool dispatch authority invoked its one-shot send outside the admission window",
|
|
640
|
+
{ transport: metadata.transport },
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
dispatched = true;
|
|
644
|
+
dispatchResult = dispatch();
|
|
645
|
+
return dispatchResult;
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
const admissionResult = admission(
|
|
650
|
+
Object.freeze({ ...metadata }),
|
|
651
|
+
admittedDispatch,
|
|
652
|
+
);
|
|
653
|
+
if (!dispatched) {
|
|
654
|
+
throw mcpTransportError(
|
|
655
|
+
"CC_MCP_DISPATCH_ADMISSION_REQUIRED",
|
|
656
|
+
"MCP tool dispatch authority did not synchronously admit the request",
|
|
657
|
+
{ transport: metadata.transport },
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
if (admissionResult !== dispatchResult) {
|
|
661
|
+
throw mcpTransportError(
|
|
662
|
+
"CC_MCP_DISPATCH_AUTHORITY_OUTCOME_UNKNOWN",
|
|
663
|
+
"MCP tool dispatch started, but the host did not synchronously return the admitted send",
|
|
664
|
+
{
|
|
665
|
+
transport: metadata.transport,
|
|
666
|
+
dispatched: true,
|
|
667
|
+
outcomeUnknown: true,
|
|
668
|
+
},
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
return dispatchResult;
|
|
672
|
+
} catch (cause) {
|
|
673
|
+
if (!dispatched) {
|
|
674
|
+
if (safeRpcProperty(cause, "dispatched") === false) throw cause;
|
|
675
|
+
const causeCode = safeRpcProperty(cause, "code");
|
|
676
|
+
const error = mcpTransportError(
|
|
677
|
+
typeof causeCode === "string" && causeCode.startsWith("CC_")
|
|
678
|
+
? causeCode
|
|
679
|
+
: "CC_MCP_DISPATCH_NOT_ADMITTED",
|
|
680
|
+
"MCP tool request was not dispatched because host authority denied admission",
|
|
681
|
+
{
|
|
682
|
+
transport: metadata.transport,
|
|
683
|
+
dispatched: false,
|
|
684
|
+
outcomeUnknown: false,
|
|
685
|
+
},
|
|
686
|
+
);
|
|
687
|
+
error.cause = cause;
|
|
688
|
+
throw error;
|
|
689
|
+
}
|
|
690
|
+
if (safeRpcProperty(cause, "outcomeUnknown") === true) {
|
|
691
|
+
throw cause;
|
|
692
|
+
}
|
|
693
|
+
const error = mcpTransportError(
|
|
694
|
+
"CC_MCP_DISPATCH_AUTHORITY_OUTCOME_UNKNOWN",
|
|
695
|
+
"MCP tool dispatch started, but the host could not durably confirm its dispatch authority",
|
|
696
|
+
{
|
|
697
|
+
transport: metadata.transport,
|
|
698
|
+
dispatched: true,
|
|
699
|
+
outcomeUnknown: true,
|
|
700
|
+
},
|
|
701
|
+
);
|
|
702
|
+
error.cause = cause;
|
|
703
|
+
throw error;
|
|
704
|
+
} finally {
|
|
705
|
+
// An admission callback must invoke the send before returning. This also
|
|
706
|
+
// prevents an async callback from retaining a usable dispatch capability.
|
|
707
|
+
admissionOpen = false;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
552
711
|
function mcpWebSocketPayloadTooLargeError(
|
|
553
712
|
serverName,
|
|
554
713
|
{ transport, url, limitBytes, closeCode = 1009 },
|
|
@@ -1775,6 +1934,11 @@ export class MCPClient extends EventEmitter {
|
|
|
1775
1934
|
else this._reconnectors.delete(name);
|
|
1776
1935
|
}
|
|
1777
1936
|
|
|
1937
|
+
/** Return the opaque host authority used to resolve policy-bearing MCP cwd. */
|
|
1938
|
+
executionWorkspaceBinding() {
|
|
1939
|
+
return this._workspaceBinding;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1778
1942
|
async _connectionHeaders(name, config, transportKind) {
|
|
1779
1943
|
const staticHeaders = { ...(config?.headers || {}) };
|
|
1780
1944
|
if (!config?.headersHelper) return staticHeaders;
|
|
@@ -1853,7 +2017,8 @@ export class MCPClient extends EventEmitter {
|
|
|
1853
2017
|
mcpStdioExecutionAuthority: _stdioExecutionAuthority,
|
|
1854
2018
|
...untrustedSourceConfig
|
|
1855
2019
|
} = config;
|
|
1856
|
-
|
|
2020
|
+
void _stdioExecutionAuthority;
|
|
2021
|
+
let sourceConfig = approvedStdioInvocation
|
|
1857
2022
|
? { ...untrustedSourceConfig, ...approvedStdioInvocation }
|
|
1858
2023
|
: untrustedSourceConfig;
|
|
1859
2024
|
let pluginWorkspaceRoot = null;
|
|
@@ -1863,34 +2028,34 @@ export class MCPClient extends EventEmitter {
|
|
|
1863
2028
|
let stdioExecutableWorkingDirectory = null;
|
|
1864
2029
|
let stdioExecutableIdentityDigest = null;
|
|
1865
2030
|
let stdioCapsuleSandboxExecutionContract = null;
|
|
2031
|
+
let stdioTrustedWorkingDirectory = null;
|
|
1866
2032
|
let runtimeConfig = sourceConfig;
|
|
1867
2033
|
if (transportKind === "stdio") {
|
|
1868
|
-
const
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
: null;
|
|
1880
|
-
if (
|
|
1881
|
-
isPlugin &&
|
|
1882
|
-
sourceConfig.sandboxPolicy?.requiredBoundaries?.length > 0 &&
|
|
1883
|
-
!pluginWorkspaceRoot
|
|
1884
|
-
) {
|
|
1885
|
-
throw new Error(
|
|
1886
|
-
`plugin MCP server "${name}" is missing its trusted workspace authority`,
|
|
2034
|
+
const sandboxContext = _deps.resolveMcpStdioSandboxContext({
|
|
2035
|
+
serverName: name,
|
|
2036
|
+
config: sourceConfig,
|
|
2037
|
+
workspaceBinding: this._workspaceBinding,
|
|
2038
|
+
});
|
|
2039
|
+
pluginWorkspaceRoot = sandboxContext.pluginWorkspaceRoot;
|
|
2040
|
+
stdioTrustedWorkingDirectory = sandboxContext.workingDirectory;
|
|
2041
|
+
if (sandboxContext.sandboxPolicy && sourceConfig.cwd) {
|
|
2042
|
+
_deps.verifyMcpStdioApprovedWorkingDirectory(
|
|
2043
|
+
stdioExecutionApproval,
|
|
2044
|
+
stdioTrustedWorkingDirectory,
|
|
1887
2045
|
);
|
|
1888
2046
|
}
|
|
2047
|
+
if (sandboxContext.sandboxPolicy) {
|
|
2048
|
+
sourceConfig = {
|
|
2049
|
+
...sourceConfig,
|
|
2050
|
+
sandboxPolicy: sandboxContext.sandboxPolicy,
|
|
2051
|
+
};
|
|
2052
|
+
}
|
|
1889
2053
|
const prepared = _deps.prepareMcpStdioExecutableIdentity({
|
|
1890
2054
|
serverName: name,
|
|
1891
2055
|
config: sourceConfig,
|
|
1892
2056
|
approval: stdioExecutionApproval,
|
|
1893
|
-
cwd:
|
|
2057
|
+
cwd:
|
|
2058
|
+
stdioTrustedWorkingDirectory || pluginWorkspaceRoot || process.cwd(),
|
|
1894
2059
|
env: { ...process.env, ...this._agentIdentityEnv() },
|
|
1895
2060
|
});
|
|
1896
2061
|
runtimeConfig = {
|
|
@@ -1905,6 +2070,17 @@ export class MCPClient extends EventEmitter {
|
|
|
1905
2070
|
stdioExecutableIdentityDigest = prepared.identityDigest;
|
|
1906
2071
|
stdioCapsuleSandboxExecutionContract =
|
|
1907
2072
|
prepared.sandboxExecutionContract || null;
|
|
2073
|
+
if (stdioCapsuleSandboxExecutionContract) {
|
|
2074
|
+
runtimeConfig = {
|
|
2075
|
+
...runtimeConfig,
|
|
2076
|
+
sandboxPolicy: enforceMcpStdioCapsuleHostSandboxPolicy(
|
|
2077
|
+
sourceConfig.sandboxPolicy,
|
|
2078
|
+
{
|
|
2079
|
+
label: `MCP stdio server "${name}" capsule sandboxPolicy`,
|
|
2080
|
+
},
|
|
2081
|
+
),
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
1908
2084
|
}
|
|
1909
2085
|
const connectionHeaders = await this._connectionHeaders(
|
|
1910
2086
|
name,
|
|
@@ -2306,6 +2482,7 @@ export class MCPClient extends EventEmitter {
|
|
|
2306
2482
|
const spawnOptions = {
|
|
2307
2483
|
cwd:
|
|
2308
2484
|
stdioExecutableWorkingDirectory ||
|
|
2485
|
+
stdioTrustedWorkingDirectory ||
|
|
2309
2486
|
pluginWorkspaceRoot ||
|
|
2310
2487
|
process.cwd(),
|
|
2311
2488
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -2325,17 +2502,13 @@ export class MCPClient extends EventEmitter {
|
|
|
2325
2502
|
// Broker's Job Object boundary so an unexpectedly exiting MCP root
|
|
2326
2503
|
// cannot orphan grandchildren before taskkill can enumerate them.
|
|
2327
2504
|
detached: process.platform !== "win32",
|
|
2328
|
-
...(
|
|
2329
|
-
stdioCapsuleSandboxExecutionContract
|
|
2505
|
+
...(stdioCapsuleSandboxExecutionContract
|
|
2330
2506
|
? {
|
|
2331
|
-
requiredBoundaries:
|
|
2332
|
-
...(process.platform === "win32" ? ["process-tree"] : []),
|
|
2333
|
-
...(stdioCapsuleSandboxExecutionContract
|
|
2334
|
-
? [MCP_STDIO_CAPSULE_CODE_SNAPSHOT_BOUNDARY]
|
|
2335
|
-
: []),
|
|
2336
|
-
],
|
|
2507
|
+
requiredBoundaries: MCP_STDIO_CAPSULE_REQUIRED_BOUNDARIES,
|
|
2337
2508
|
}
|
|
2338
|
-
:
|
|
2509
|
+
: process.platform === "win32"
|
|
2510
|
+
? { requiredBoundaries: ["process-tree"] }
|
|
2511
|
+
: {}),
|
|
2339
2512
|
pluginId: config.pluginId,
|
|
2340
2513
|
pluginVersion: config.pluginVersion,
|
|
2341
2514
|
pluginSource: config.pluginSource,
|
|
@@ -2353,15 +2526,20 @@ export class MCPClient extends EventEmitter {
|
|
|
2353
2526
|
config.args || [],
|
|
2354
2527
|
{ ...spawnOptions, detached: false },
|
|
2355
2528
|
stdioExecutableWorkingDirectory ||
|
|
2529
|
+
stdioTrustedWorkingDirectory ||
|
|
2356
2530
|
pluginWorkspaceRoot ||
|
|
2357
2531
|
process.cwd(),
|
|
2358
2532
|
)
|
|
2359
2533
|
: null);
|
|
2534
|
+
const effectiveRuntimeSandboxBoundaries = new Set([
|
|
2535
|
+
...(spawnOptions.requiredBoundaries || []),
|
|
2536
|
+
...(config.sandboxPolicy?.requiredBoundaries || []),
|
|
2537
|
+
]);
|
|
2360
2538
|
const capsuleUsesLinuxProcessTreeSandbox =
|
|
2361
2539
|
Boolean(stdioCapsuleSandboxExecutionContract) &&
|
|
2362
2540
|
process.platform === "linux" &&
|
|
2363
|
-
|
|
2364
|
-
|
|
2541
|
+
MCP_STDIO_CAPSULE_REQUIRED_BOUNDARIES.every((boundary) =>
|
|
2542
|
+
effectiveRuntimeSandboxBoundaries.has(boundary),
|
|
2365
2543
|
);
|
|
2366
2544
|
const capsuleUsesPosixProcessGroup =
|
|
2367
2545
|
Boolean(stdioCapsuleSandboxExecutionContract) &&
|
|
@@ -2825,7 +3003,7 @@ export class MCPClient extends EventEmitter {
|
|
|
2825
3003
|
* @param {string} serverName - Server name
|
|
2826
3004
|
* @param {string} toolName - Tool name
|
|
2827
3005
|
* @param {object} args - Tool arguments
|
|
2828
|
-
* @param {{signal?: AbortSignal}} options - Host-owned cancellation
|
|
3006
|
+
* @param {{signal?: AbortSignal, dispatchAdmission?: Function}} options - Host-owned cancellation and dispatch authority
|
|
2829
3007
|
*/
|
|
2830
3008
|
async callTool(serverName, toolName, args = {}, options = {}) {
|
|
2831
3009
|
if (options?.signal?.aborted) {
|
|
@@ -3311,8 +3489,19 @@ export class MCPClient extends EventEmitter {
|
|
|
3311
3489
|
}
|
|
3312
3490
|
|
|
3313
3491
|
try {
|
|
3314
|
-
|
|
3315
|
-
|
|
3492
|
+
dispatchMcpToolRequest(
|
|
3493
|
+
options,
|
|
3494
|
+
{
|
|
3495
|
+
serverName,
|
|
3496
|
+
method,
|
|
3497
|
+
transport: entry.transportKind || "stdio",
|
|
3498
|
+
requestId: id,
|
|
3499
|
+
},
|
|
3500
|
+
() => {
|
|
3501
|
+
dispatched = true;
|
|
3502
|
+
return entry.process.stdin.write(message + "\n");
|
|
3503
|
+
},
|
|
3504
|
+
);
|
|
3316
3505
|
} catch (err) {
|
|
3317
3506
|
settle(reject, err);
|
|
3318
3507
|
}
|
|
@@ -3429,13 +3618,26 @@ export class MCPClient extends EventEmitter {
|
|
|
3429
3618
|
);
|
|
3430
3619
|
};
|
|
3431
3620
|
try {
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3621
|
+
dispatchMcpToolRequest(
|
|
3622
|
+
options,
|
|
3623
|
+
{
|
|
3624
|
+
serverName,
|
|
3625
|
+
method,
|
|
3626
|
+
transport: entry.transportKind || "ws",
|
|
3627
|
+
requestId: id,
|
|
3628
|
+
},
|
|
3629
|
+
() => {
|
|
3630
|
+
dispatched = true;
|
|
3631
|
+
return entry.socket.send(message, (cause) => {
|
|
3632
|
+
if (!cause) return;
|
|
3633
|
+
failDispatch();
|
|
3634
|
+
});
|
|
3635
|
+
},
|
|
3636
|
+
);
|
|
3637
|
+
} catch (error) {
|
|
3638
|
+
if (error?.outcomeUnknown === true) settle(reject, error);
|
|
3639
|
+
else if (!dispatched) settle(reject, error);
|
|
3640
|
+
else failDispatch();
|
|
3439
3641
|
}
|
|
3440
3642
|
});
|
|
3441
3643
|
}
|
|
@@ -3773,13 +3975,24 @@ export class MCPClient extends EventEmitter {
|
|
|
3773
3975
|
|
|
3774
3976
|
try {
|
|
3775
3977
|
throwIfAborted();
|
|
3776
|
-
const responsePromise =
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3978
|
+
const responsePromise = dispatchMcpToolRequest(
|
|
3979
|
+
options,
|
|
3980
|
+
{
|
|
3981
|
+
serverName,
|
|
3982
|
+
method,
|
|
3983
|
+
transport: entry.transportKind || "http",
|
|
3984
|
+
requestId: id,
|
|
3985
|
+
},
|
|
3986
|
+
() => {
|
|
3987
|
+
if (request) request.dispatched = true;
|
|
3988
|
+
return _deps.fetch(entry.httpUrl, {
|
|
3989
|
+
method: "POST",
|
|
3990
|
+
headers,
|
|
3991
|
+
body,
|
|
3992
|
+
...(controller ? { signal: controller.signal } : {}),
|
|
3993
|
+
});
|
|
3994
|
+
},
|
|
3995
|
+
);
|
|
3783
3996
|
const response = await responsePromise;
|
|
3784
3997
|
if (request) request.response = response;
|
|
3785
3998
|
throwIfAborted();
|
|
@@ -4933,6 +5146,8 @@ export class MCPServerConfig {
|
|
|
4933
5146
|
args TEXT DEFAULT '[]',
|
|
4934
5147
|
env TEXT DEFAULT '{}',
|
|
4935
5148
|
runtime_kind TEXT,
|
|
5149
|
+
cwd TEXT,
|
|
5150
|
+
sandbox_policy TEXT,
|
|
4936
5151
|
auto_connect INTEGER DEFAULT 0,
|
|
4937
5152
|
url TEXT,
|
|
4938
5153
|
transport TEXT DEFAULT 'stdio',
|
|
@@ -4949,9 +5164,9 @@ export class MCPServerConfig {
|
|
|
4949
5164
|
}
|
|
4950
5165
|
|
|
4951
5166
|
/**
|
|
4952
|
-
* Idempotent schema migration for databases that predate
|
|
4953
|
-
* columns. Silently skipped on mocks or anything
|
|
4954
|
-
* `pragma()`.
|
|
5167
|
+
* Idempotent schema migration for databases that predate runtime, transport,
|
|
5168
|
+
* sandbox-policy, and scope columns. Silently skipped on mocks or anything
|
|
5169
|
+
* that doesn't expose `pragma()`.
|
|
4955
5170
|
*/
|
|
4956
5171
|
_migrateSchema() {
|
|
4957
5172
|
try {
|
|
@@ -4967,6 +5182,12 @@ export class MCPServerConfig {
|
|
|
4967
5182
|
if (!cols.has("runtime_kind")) {
|
|
4968
5183
|
this.db.exec("ALTER TABLE mcp_servers ADD COLUMN runtime_kind TEXT");
|
|
4969
5184
|
}
|
|
5185
|
+
if (!cols.has("cwd")) {
|
|
5186
|
+
this.db.exec("ALTER TABLE mcp_servers ADD COLUMN cwd TEXT");
|
|
5187
|
+
}
|
|
5188
|
+
if (!cols.has("sandbox_policy")) {
|
|
5189
|
+
this.db.exec("ALTER TABLE mcp_servers ADD COLUMN sandbox_policy TEXT");
|
|
5190
|
+
}
|
|
4970
5191
|
if (!cols.has("transport")) {
|
|
4971
5192
|
this.db.exec(
|
|
4972
5193
|
"ALTER TABLE mcp_servers ADD COLUMN transport TEXT DEFAULT 'stdio'",
|
|
@@ -5000,6 +5221,13 @@ export class MCPServerConfig {
|
|
|
5000
5221
|
}
|
|
5001
5222
|
|
|
5002
5223
|
add(name, config) {
|
|
5224
|
+
if (isProxy(config)) {
|
|
5225
|
+
const error = new TypeError(
|
|
5226
|
+
`MCP server "${name}" config must not be a Proxy`,
|
|
5227
|
+
);
|
|
5228
|
+
error.code = MCP_SANDBOX_POLICY_INVALID_CODE;
|
|
5229
|
+
throw error;
|
|
5230
|
+
}
|
|
5003
5231
|
const url = config.url || null;
|
|
5004
5232
|
const transport =
|
|
5005
5233
|
config.transport || (url ? inferTransport({ url }) : "stdio");
|
|
@@ -5027,6 +5255,17 @@ export class MCPServerConfig {
|
|
|
5027
5255
|
typeof config.headersHelper === "string" && config.headersHelper.trim()
|
|
5028
5256
|
? config.headersHelper
|
|
5029
5257
|
: null;
|
|
5258
|
+
const sandboxPolicyDescriptor = Object.getOwnPropertyDescriptor(
|
|
5259
|
+
config,
|
|
5260
|
+
"sandboxPolicy",
|
|
5261
|
+
);
|
|
5262
|
+
if (sandboxPolicyDescriptor && !("value" in sandboxPolicyDescriptor)) {
|
|
5263
|
+
const error = new TypeError(
|
|
5264
|
+
`MCP server "${name}" sandboxPolicy must be an own data property`,
|
|
5265
|
+
);
|
|
5266
|
+
error.code = MCP_SANDBOX_POLICY_INVALID_CODE;
|
|
5267
|
+
throw error;
|
|
5268
|
+
}
|
|
5030
5269
|
// The legacy table uses `name` as its primary key. Encode workspace-bound
|
|
5031
5270
|
// identities in that key so `foo` can coexist at local/project/user scope
|
|
5032
5271
|
// while retaining the old key for user rows and backwards compatibility.
|
|
@@ -5034,9 +5273,32 @@ export class MCPServerConfig {
|
|
|
5034
5273
|
configScope === "user"
|
|
5035
5274
|
? name
|
|
5036
5275
|
: `${configScope}:${Buffer.from(projectPath || "managed", "utf8").toString("base64url")}:${name}`;
|
|
5276
|
+
const existingRow = this.db
|
|
5277
|
+
.prepare("SELECT * FROM mcp_servers WHERE name = ?")
|
|
5278
|
+
.get(storageName);
|
|
5279
|
+
const cwdUpdate = readMcpStdioCwd(config, {
|
|
5280
|
+
label: `MCP server "${name}"`,
|
|
5281
|
+
});
|
|
5282
|
+
const storedCwd = existingRow
|
|
5283
|
+
? readMcpStdioCwd(existingRow, {
|
|
5284
|
+
label: `Stored MCP server "${name}"`,
|
|
5285
|
+
}).cwd
|
|
5286
|
+
: null;
|
|
5287
|
+
let workingDirectory = storedCwd;
|
|
5288
|
+
if (cwdUpdate.present) workingDirectory = cwdUpdate.cwd;
|
|
5289
|
+
let sandboxPolicy;
|
|
5290
|
+
if (sandboxPolicyDescriptor) {
|
|
5291
|
+
sandboxPolicy = normalizeMcpSandboxPolicy(sandboxPolicyDescriptor.value, {
|
|
5292
|
+
label: `MCP server "${name}" sandboxPolicy`,
|
|
5293
|
+
});
|
|
5294
|
+
} else {
|
|
5295
|
+
sandboxPolicy = existingRow
|
|
5296
|
+
? this._rowToConfig(existingRow).sandboxPolicy || null
|
|
5297
|
+
: null;
|
|
5298
|
+
}
|
|
5037
5299
|
this.db
|
|
5038
5300
|
.prepare(
|
|
5039
|
-
"INSERT OR REPLACE INTO mcp_servers (name, command, args, env, runtime_kind, auto_connect, url, transport, headers, headers_helper, config_scope, config_source, project_path, display_name, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))",
|
|
5301
|
+
"INSERT OR REPLACE INTO mcp_servers (name, command, args, env, runtime_kind, cwd, sandbox_policy, auto_connect, url, transport, headers, headers_helper, config_scope, config_source, project_path, display_name, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))",
|
|
5040
5302
|
)
|
|
5041
5303
|
.run(
|
|
5042
5304
|
storageName,
|
|
@@ -5044,6 +5306,8 @@ export class MCPServerConfig {
|
|
|
5044
5306
|
JSON.stringify(config.args || []),
|
|
5045
5307
|
JSON.stringify(config.env || {}),
|
|
5046
5308
|
config.runtimeKind || null,
|
|
5309
|
+
workingDirectory,
|
|
5310
|
+
sandboxPolicy ? JSON.stringify(sandboxPolicy) : null,
|
|
5047
5311
|
config.autoConnect ? 1 : 0,
|
|
5048
5312
|
url,
|
|
5049
5313
|
transport,
|
|
@@ -5080,6 +5344,26 @@ export class MCPServerConfig {
|
|
|
5080
5344
|
}
|
|
5081
5345
|
|
|
5082
5346
|
_rowToConfig(row) {
|
|
5347
|
+
const { cwd } = readMcpStdioCwd(row, {
|
|
5348
|
+
label: `Stored MCP server "${row?.display_name || row?.name || "unknown"}"`,
|
|
5349
|
+
});
|
|
5350
|
+
let sandboxPolicy = null;
|
|
5351
|
+
if (row.sandbox_policy != null && row.sandbox_policy !== "") {
|
|
5352
|
+
let parsedPolicy;
|
|
5353
|
+
try {
|
|
5354
|
+
parsedPolicy = JSON.parse(row.sandbox_policy);
|
|
5355
|
+
} catch (cause) {
|
|
5356
|
+
const error = new TypeError(
|
|
5357
|
+
`Stored MCP sandbox policy for "${row.display_name || row.name}" is not valid JSON`,
|
|
5358
|
+
{ cause },
|
|
5359
|
+
);
|
|
5360
|
+
error.code = MCP_SANDBOX_POLICY_INVALID_CODE;
|
|
5361
|
+
throw error;
|
|
5362
|
+
}
|
|
5363
|
+
sandboxPolicy = normalizeMcpSandboxPolicy(parsedPolicy, {
|
|
5364
|
+
label: `Stored MCP sandbox policy for "${row.display_name || row.name}"`,
|
|
5365
|
+
});
|
|
5366
|
+
}
|
|
5083
5367
|
const config = {
|
|
5084
5368
|
name: row.display_name || row.name,
|
|
5085
5369
|
command: row.command || null,
|
|
@@ -5089,6 +5373,8 @@ export class MCPServerConfig {
|
|
|
5089
5373
|
args: safeJsonParse(row.args, []),
|
|
5090
5374
|
env: safeJsonParse(row.env, {}),
|
|
5091
5375
|
runtimeKind: row.runtime_kind || null,
|
|
5376
|
+
...(cwd ? { cwd } : {}),
|
|
5377
|
+
...(sandboxPolicy ? { sandboxPolicy } : {}),
|
|
5092
5378
|
autoConnect: row.auto_connect === 1,
|
|
5093
5379
|
url: row.url || null,
|
|
5094
5380
|
transport:
|
|
@@ -5116,39 +5402,96 @@ export class MCPServerConfig {
|
|
|
5116
5402
|
return this.list(options).find((config) => config.name === name) || null;
|
|
5117
5403
|
}
|
|
5118
5404
|
|
|
5405
|
+
_rowsToConfigs(rows, options = {}) {
|
|
5406
|
+
const configs = [];
|
|
5407
|
+
for (const row of rows) {
|
|
5408
|
+
const visibility = mcpRowVisibility(row);
|
|
5409
|
+
if (!visibility || !this._isVisible(visibility, options)) continue;
|
|
5410
|
+
let config;
|
|
5411
|
+
try {
|
|
5412
|
+
config = this._rowToConfig(row);
|
|
5413
|
+
} catch (error) {
|
|
5414
|
+
const invalid = mcpRowVisibility(row, { invalid: true, error });
|
|
5415
|
+
if (invalid) configs.push(invalid);
|
|
5416
|
+
continue;
|
|
5417
|
+
}
|
|
5418
|
+
configs.push(config);
|
|
5419
|
+
}
|
|
5420
|
+
return configs;
|
|
5421
|
+
}
|
|
5422
|
+
|
|
5423
|
+
_publicConfigs(configs) {
|
|
5424
|
+
return configs.filter((config) => !isInvalidMcpConfigRow(config));
|
|
5425
|
+
}
|
|
5426
|
+
|
|
5427
|
+
_reportInvalidConfigs(configs, options = {}) {
|
|
5428
|
+
if (typeof options.onInvalidRow !== "function") return;
|
|
5429
|
+
for (const config of configs) {
|
|
5430
|
+
if (!isInvalidMcpConfigRow(config)) continue;
|
|
5431
|
+
try {
|
|
5432
|
+
options.onInvalidRow(
|
|
5433
|
+
config[INVALID_MCP_CONFIG_ERROR],
|
|
5434
|
+
config[INVALID_MCP_CONFIG_RAW_ROW],
|
|
5435
|
+
);
|
|
5436
|
+
} catch {
|
|
5437
|
+
// Diagnostics must not turn one corrupt row into a global outage.
|
|
5438
|
+
}
|
|
5439
|
+
}
|
|
5440
|
+
}
|
|
5441
|
+
|
|
5119
5442
|
list(options = {}) {
|
|
5120
5443
|
const rows = this.db
|
|
5121
5444
|
.prepare("SELECT * FROM mcp_servers ORDER BY name")
|
|
5122
5445
|
.all();
|
|
5123
|
-
const visible = rows
|
|
5124
|
-
|
|
5125
|
-
.
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
: this._effectiveByName(visible);
|
|
5446
|
+
const visible = this._rowsToConfigs(rows, options);
|
|
5447
|
+
const selected =
|
|
5448
|
+
options.allScopes === true ? visible : this._effectiveByName(visible);
|
|
5449
|
+
this._reportInvalidConfigs(selected, options);
|
|
5450
|
+
return this._publicConfigs(selected);
|
|
5129
5451
|
}
|
|
5130
5452
|
|
|
5131
5453
|
getAutoConnect(options = {}) {
|
|
5132
5454
|
const rows = this.db
|
|
5133
|
-
.prepare("SELECT * FROM mcp_servers
|
|
5134
|
-
.all(
|
|
5135
|
-
const visible = rows
|
|
5136
|
-
|
|
5137
|
-
.
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5455
|
+
.prepare("SELECT * FROM mcp_servers ORDER BY name")
|
|
5456
|
+
.all();
|
|
5457
|
+
const visible = this._rowsToConfigs(rows, options);
|
|
5458
|
+
const selected =
|
|
5459
|
+
options.allScopes === true ? visible : this._effectiveByName(visible);
|
|
5460
|
+
this._reportInvalidConfigs(selected, options);
|
|
5461
|
+
return this._publicConfigs(selected).filter(
|
|
5462
|
+
(config) => config.autoConnect === true,
|
|
5463
|
+
);
|
|
5141
5464
|
}
|
|
5142
5465
|
|
|
5143
5466
|
_effectiveByName(configs) {
|
|
5144
5467
|
const priority = { managed: 4, local: 3, project: 2, user: 1 };
|
|
5468
|
+
const specificity = (config) => {
|
|
5469
|
+
if (config.configScope !== "local" && config.configScope !== "project") {
|
|
5470
|
+
return 0;
|
|
5471
|
+
}
|
|
5472
|
+
if (typeof config.projectPath !== "string") return 0;
|
|
5473
|
+
try {
|
|
5474
|
+
return pathToFileURL(config.projectPath).href.length;
|
|
5475
|
+
} catch {
|
|
5476
|
+
return 0;
|
|
5477
|
+
}
|
|
5478
|
+
};
|
|
5145
5479
|
const selected = new Map();
|
|
5146
5480
|
for (const config of configs) {
|
|
5147
5481
|
const current = selected.get(config.name);
|
|
5482
|
+
const candidatePriority = priority[config.configScope] || 0;
|
|
5483
|
+
const currentPriority = current ? priority[current.configScope] || 0 : -1;
|
|
5484
|
+
const candidateSpecificity = specificity(config);
|
|
5485
|
+
const currentSpecificity = current ? specificity(current) : -1;
|
|
5148
5486
|
if (
|
|
5149
5487
|
!current ||
|
|
5150
|
-
|
|
5151
|
-
|
|
5488
|
+
candidatePriority > currentPriority ||
|
|
5489
|
+
(candidatePriority === currentPriority &&
|
|
5490
|
+
candidateSpecificity > currentSpecificity) ||
|
|
5491
|
+
(candidatePriority === currentPriority &&
|
|
5492
|
+
candidateSpecificity === currentSpecificity &&
|
|
5493
|
+
isInvalidMcpConfigRow(config) &&
|
|
5494
|
+
!isInvalidMcpConfigRow(current))
|
|
5152
5495
|
) {
|
|
5153
5496
|
selected.set(config.name, config);
|
|
5154
5497
|
}
|