@xopcai/xopc 0.0.224 → 0.0.227
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/browser-ext/manifest.json +1 -1
- package/dist/extensions/demo-memory/src/index.js +10 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store--GBciH10.js → activity-store-BevwE16n.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-K7V4XXt7.js → agent-avatar-dicebear-BggVQrEZ.js} +1 -1
- package/dist/gateway/static/root/assets/agents-TJpjRHnr.js +68 -0
- package/dist/gateway/static/root/assets/{api-HoIz_ija.js → api-Bx_BpIt8.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-xQCmkAHh.js → app-management-settings-panel-BtKP9n2r.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BRoeA4kU.js → appearance-settings-BoH2NAUX.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-CZ4Da51l.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-CteCVsBP.js → archive-plugin-DnceUyxm.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-D7xNA2jB.js → attachment-load-Bh8_9gkr.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-BD1vK7r5.js → automations-page-ZKeAAVxD.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-2Y00PvJU.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-C3DaJa0W.js → binary-plugins-Cy9er6w-.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-lTkcHTve.js → block-editor-Cc0wWvNX.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-CbJD27_j.js +8 -0
- package/dist/gateway/static/root/assets/{browser-workflow-inputs-1UZ0vnIy.js → browser-workflow-inputs-BS7rpLLl.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-workflows-page-BHuJOF4M.js → browser-workflows-page-BdBVUZXm.js} +2 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-Dv8y6OuP.js +7 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-yNqTIuyd.js → capability-presets-api-D1Lqz0Tt.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-TZmjzWzt.js +3 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-krezGsKc.js → channel-recipient-api-CGuEl-l2.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-B4AtvQHk.js → channels-settings-g-68dutA.js} +1 -1
- package/dist/gateway/static/root/assets/channels-status-swr-D7KsAeWh.js +1 -0
- package/dist/gateway/static/root/assets/{chat-terminal-dock-BRrL2iHA.js → chat-terminal-dock-CdX_7aQh.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-DXA0rAvQ.js +2 -0
- package/dist/gateway/static/root/assets/{cron-expression-Bl-2DzBQ.js → cron-expression-BnSQc4Ek.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-CxTl6Wlz.js → date-picker-BbuG7zQ_.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-DSl0mOPa.js → dependency-picker-snZeqOmG.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-DTeCmCdb.js → desktop-pet-DJQ8JoR1.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-BNebxg0V.js → desktop-pet-settings-B9txsRtv.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-Dn9XpmXY.js → directory-picker-path-field-D7WKWkNB.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-D-J-eOTb.js → extension-debug-page-D_7cOCj2.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CpST24nU.js → extension-page-Bwu47iUb.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-4nQXFnFq.js → extension-settings-page-Dwg4dSZM.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-BHWHduDi.js → gateway-config-api-jURUSte_.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-yUxMMVjK.js → gateway-config-swr-6SQXF1yR.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-BimMKmkM.js → gateway-settings-UklJOMLX.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-9mrsL84j.js → gateway-startup-retry-DqWaxyvZ.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-Djs_nduD.js +1 -0
- package/dist/gateway/static/root/assets/{home-page-DSM6aPHF.js → home-page-i6bXCHdo.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-CgFAGDi8.js → image-generation-api-Bxmt6LEr.js} +1 -1
- package/dist/gateway/static/root/assets/index-BBBE89vm.css +2 -0
- package/dist/gateway/static/root/assets/index-BsD5wygl.js +74 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-6Ar5rfX4.js → keyboard-shortcuts-settings-ByVsCH3d.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-tzM0wTL8.js → local-app-workbench-page-BCKEDMlP.js} +2 -2
- package/dist/gateway/static/root/assets/{local-apps-page-B5n-x544.js → local-apps-page-D19R-xsz.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-CPylQjH9.js → locale-store-Ci1Qi0Q-.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-CgoQlera.js → logs-page-Cy9YQK7j.js} +2 -2
- package/dist/gateway/static/root/assets/{management-settings-CXBLyU-0.js → management-settings-G0y7zmfY.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-split-CtmQemJi.js → markdown-split-B69d2HWt.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-CUsob4Pz.js → markdown-view-CBM-v2ph.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-Dsd-IEOW.js → media-plugins-6TWEQgN_.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CtOJwGW3.js +3 -0
- package/dist/gateway/static/root/assets/{notes-api-CfKZYrBC.js → notes-api-DLsnnZCS.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-BKbOTE2n.js → notes-page-CkRJJKDm.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-B10T3hGX.js +4 -0
- package/dist/gateway/static/root/assets/{product-open-page-DqB9bsDB.js → product-open-page-HxArIvoN.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-DsqRlgsy.js +1 -0
- package/dist/gateway/static/root/assets/{projects-page-PeXUdGdO.js → projects-page-Bu2PN8OK.js} +1 -1
- package/dist/gateway/static/root/assets/{registry-api-B_5D8epF.js → registry-api-lEar5FOV.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CXACdJ2f.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DBTH_t0O.js → runtime-tools-settings-panel-Bf03UNGD.js} +2 -2
- package/dist/gateway/static/root/assets/{schema-form-CqMKAwV0.js → schema-form-CIXMBgx5.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-CS2knWDT.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-ByFGk0q_.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-dPriuuYq.js → settings-form-section-DgdB26mE.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-B-Mbm8Va.js → settings-loading-skeleton-efk5W6lG.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-Dyj3w-Ml.js → settings-page-BHVf4gEP.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-C4FxY5J3.js → setup-status-panel-WK0dGNMv.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-emEdx7YL.js → share-preview-page-Cod-w2qm.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-CUqQ31gt.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-DweAPjI8.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-BNtYnrRT.js → skills-page-DwCcipqK.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-DjnbNFs5.js → skills-page.utils-3WSbtl3S.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-DDStf-jZ.js → system-settings-panel-C5VxqC_s.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-DEj-BFMP.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-BN-32Fz5.js → theme-store-Dd89g_ki.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-C2mKZ-ua.js → use-autosave-CeV8VJh2.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-f7yh7aD5.js +1 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-D7OHeQby.js → voice-api-key-field-EmJ-8aZu.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-CIiXie7F.js → work-discovery-overlay-zsk6E3Mi.js} +1 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-CBAYvm3G.js +3 -0
- package/dist/gateway/static/root/assets/workflows-page-BN0JmHrG.js +5 -0
- package/dist/gateway/static/root/index.html +11 -11
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +3 -1
- package/dist/src/agent/agent-manager.js +22 -5
- package/dist/src/agent/embedded/run-turn.js +2 -0
- package/dist/src/agent/mcp/bundle-mcp-config.d.ts +9 -1
- package/dist/src/agent/mcp/bundle-mcp-config.js +11 -15
- package/dist/src/agent/mcp/embedded-mcp.d.ts +1 -1
- package/dist/src/agent/mcp/index.d.ts +1 -0
- package/dist/src/agent/mcp/index.js +7 -1
- package/dist/src/agent/mcp/mcp-http.d.ts +4 -0
- package/dist/src/agent/mcp/mcp-http.js +25 -1
- package/dist/src/agent/mcp/mcp-transport-config.d.ts +4 -0
- package/dist/src/agent/mcp/mcp-transport-config.js +1 -0
- package/dist/src/agent/mcp/mcp-transport.d.ts +7 -1
- package/dist/src/agent/mcp/mcp-transport.js +38 -11
- package/dist/src/agent/mcp/oauth/index.d.ts +6 -0
- package/dist/src/agent/mcp/oauth/index.js +6 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-errors.d.ts +5 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-errors.js +14 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-manager.d.ts +25 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-manager.js +219 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-provider.d.ts +33 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-provider.js +103 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-session.d.ts +29 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-session.js +169 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-store.d.ts +9 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +79 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-types.d.ts +22 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-types.js +1 -0
- package/dist/src/agent/memory/builtin-provider.js +23 -4
- package/dist/src/agent/memory/compaction-promotion.d.ts +18 -0
- package/dist/src/agent/memory/compaction-promotion.js +211 -0
- package/dist/src/agent/memory/context/types.d.ts +5 -0
- package/dist/src/agent/memory/manager.d.ts +3 -0
- package/dist/src/agent/memory/manager.js +12 -2
- package/dist/src/agent/memory/trusted-recall.d.ts +7 -0
- package/dist/src/agent/memory/trusted-recall.js +53 -0
- package/dist/src/agent/memory/turn-provenance.d.ts +14 -0
- package/dist/src/agent/memory/turn-provenance.js +60 -0
- package/dist/src/agent/memory/types.d.ts +18 -3
- package/dist/src/agent/memory/understanding/service.js +1 -0
- package/dist/src/agent/memory/understanding/types.d.ts +1 -0
- package/dist/src/agent/memory/user-context-coordinator.js +57 -6
- package/dist/src/agent/service.d.ts +2 -0
- package/dist/src/agent/service.js +6 -0
- package/dist/src/agent/skills/index.d.ts +22 -2
- package/dist/src/agent/skills/index.js +74 -32
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.d.ts +2 -0
- package/dist/src/agent/skills/skill-sources.d.ts +0 -1
- package/dist/src/agent/skills/skill-sources.js +1 -11
- package/dist/src/agent/tools/memory-tool.js +5 -1
- package/dist/src/capabilities/store-connector.js +99 -10
- package/dist/src/cli/commands/channels.js +1 -1
- package/dist/src/cli/commands/connectors.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/config/config-mutation.d.ts +14 -0
- package/dist/src/config/config-mutation.js +33 -0
- package/dist/src/config/index.js +3 -3
- package/dist/src/config/paths.d.ts +2 -0
- package/dist/src/config/paths.js +5 -1
- package/dist/src/config/schema.d.ts +17 -0
- package/dist/src/config/schema.js +26 -2
- package/dist/src/connectors/auth-provider-registry.d.ts +1 -1
- package/dist/src/connectors/auth-provider-registry.js +23 -2
- package/dist/src/connectors/connected-source-understanding.js +1 -1
- package/dist/src/connectors/install.js +3 -3
- package/dist/src/connectors/instances.d.ts +2 -1
- package/dist/src/connectors/instances.js +15 -5
- package/dist/src/connectors/materialize.d.ts +1 -0
- package/dist/src/connectors/materialize.js +9 -1
- package/dist/src/connectors/runtime-adapter-registry.js +1 -0
- package/dist/src/connectors/types.d.ts +4 -1
- package/dist/src/extensions/connector-dependencies.d.ts +10 -0
- package/dist/src/extensions/connector-dependencies.js +65 -0
- package/dist/src/extensions/install.js +27 -0
- package/dist/src/extensions/normalize-manifest.js +1 -0
- package/dist/src/extensions/sdk/index.js +1 -1
- package/dist/src/extensions/types/manifest.d.ts +2 -0
- package/dist/src/gateway/hono/routes/capabilities.js +8 -8
- package/dist/src/gateway/hono/routes/connectors.js +104 -70
- package/dist/src/gateway/hono/routes/mcp.js +77 -5
- package/dist/src/gateway/hono/routes/project-skills.js +30 -2
- package/dist/src/gateway/hono/routes/you.js +52 -0
- package/dist/src/gateway/service.js +8 -8
- package/dist/src/knowledge/connected-knowledge-pipeline.js +26 -4
- package/dist/src/knowledge/connected-understanding-pipeline.js +12 -3
- package/dist/src/knowledge/index.d.ts +0 -1
- package/dist/src/knowledge/index.js +1 -2
- package/dist/src/projects/project-skill-service.d.ts +28 -6
- package/dist/src/projects/project-skill-service.js +76 -29
- package/dist/src/session/store.js +27 -0
- package/dist/src/storage/sqlite/index.d.ts +1 -0
- package/dist/src/storage/sqlite/index.js +2 -1
- package/dist/src/storage/sqlite/memory-records-repository.d.ts +9 -1
- package/dist/src/storage/sqlite/memory-records-repository.js +40 -6
- package/dist/src/storage/sqlite/migrations/134_memory_provenance.sql +18 -0
- package/dist/src/storage/sqlite/migrations/135_user_people.sql +59 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/storage/sqlite/user-people-repository.d.ts +32 -0
- package/dist/src/storage/sqlite/user-people-repository.js +246 -0
- package/dist/src/tui/backends/embedded-backend.js +1 -1
- package/dist/src/user-context/planner.js +8 -3
- package/dist/src/user-context/relationships/indexer.d.ts +13 -0
- package/dist/src/user-context/relationships/indexer.js +185 -0
- package/dist/src/user-context/relationships/service.d.ts +22 -0
- package/dist/src/user-context/relationships/service.js +50 -0
- package/dist/src/user-context/relationships/types.d.ts +55 -0
- package/dist/src/user-context/relationships/types.js +10 -0
- package/dist/src/user-context/sources/repository.d.ts +1 -1
- package/dist/src/user-context/sources/repository.js +8 -2
- package/dist/src/work-discovery/service.js +1 -1
- package/package.json +2 -2
- package/dist/gateway/static/root/assets/agents-BsxTpXyq.js +0 -68
- package/dist/gateway/static/root/assets/apps-page-BXgpBA8w.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-wrgf-z7D.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-zhzNkeVG.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DuCmg2Ej.js +0 -7
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-DjiP3mQQ.js +0 -3
- package/dist/gateway/static/root/assets/channels-status-swr-DMxB7slL.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-fmda2NEP.js +0 -2
- package/dist/gateway/static/root/assets/heartbeat-settings-MUJ3mEC9.js +0 -1
- package/dist/gateway/static/root/assets/index-B3zYJBhK.js +0 -74
- package/dist/gateway/static/root/assets/index-D3_xSYKd.css +0 -2
- package/dist/gateway/static/root/assets/messages-B82nsi9k.js +0 -3
- package/dist/gateway/static/root/assets/notes-workbench-BScELo01.js +0 -4
- package/dist/gateway/static/root/assets/project-detail-page-DUoyty8X.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-sHho-uXP.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-DyvNgaP9.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CnYf1R7z.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-BQBVq-sN.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-BFoEpQ4I.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-ZVwr8KbS.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-CHjIinCW.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-9_dAGW3_.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-DXpYJxao.js +0 -5
- package/dist/src/extensions/bundle-mcp.d.ts +0 -18
- package/dist/src/extensions/bundle-mcp.js +0 -69
- package/dist/src/knowledge/people-graph.d.ts +0 -25
- package/dist/src/knowledge/people-graph.js +0 -92
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
import { normalizeConfiguredMcpServers } from "../../config/mcp-config-normalize.js";
|
|
2
|
-
import {
|
|
2
|
+
import { inspectExtensionConnectorDependencies } from "../../extensions/connector-dependencies.js";
|
|
3
3
|
//#region src/agent/mcp/bundle-mcp-config.ts
|
|
4
4
|
function listConfiguredMcpServers(params) {
|
|
5
5
|
const configuredMcp = normalizeConfiguredMcpServers(params.cfg?.mcp?.servers);
|
|
6
|
-
return Object.fromEntries(Object.entries(configuredMcp).
|
|
6
|
+
return Object.fromEntries(Object.entries(configuredMcp).filter(([, server]) => {
|
|
7
|
+
const marker = server.xopcConnector;
|
|
8
|
+
return !marker || typeof marker !== "object" || Array.isArray(marker) || marker.enabled !== false;
|
|
9
|
+
}).map(([name, server]) => [name, params.mapConfiguredServer(server, name)]));
|
|
7
10
|
}
|
|
8
11
|
function loadMergedBundleMcpConfig(params) {
|
|
9
12
|
const mapConfiguredServer = params.mapConfiguredServer ?? ((server) => server);
|
|
10
|
-
|
|
11
|
-
workspaceDir: params.workspaceDir,
|
|
12
|
-
cfg: params.cfg
|
|
13
|
-
});
|
|
14
|
-
const configured = listConfiguredMcpServers({
|
|
15
|
-
cfg: params.cfg,
|
|
16
|
-
mapConfiguredServer
|
|
17
|
-
});
|
|
13
|
+
params.workspaceDir;
|
|
18
14
|
return {
|
|
19
|
-
config: { mcpServers: {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} },
|
|
23
|
-
diagnostics:
|
|
15
|
+
config: { mcpServers: { ...listConfiguredMcpServers({
|
|
16
|
+
cfg: params.cfg,
|
|
17
|
+
mapConfiguredServer
|
|
18
|
+
}) } },
|
|
19
|
+
diagnostics: inspectExtensionConnectorDependencies({ cfg: params.cfg })
|
|
24
20
|
};
|
|
25
21
|
}
|
|
26
22
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Config } from "../../config/schema.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type BundleMcpDiagnostic, type BundleMcpServerConfig } from "./bundle-mcp-config.js";
|
|
3
3
|
export type EmbeddedMcpConfig = {
|
|
4
4
|
mcpServers: Record<string, BundleMcpServerConfig>;
|
|
5
5
|
diagnostics: BundleMcpDiagnostic[];
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { isMcpCatalogToolDenied, mcpToolPolicyId, resolveMcpToolPolicy } from "./bundle-mcp-policy.js";
|
|
2
2
|
import { loadMergedBundleMcpConfig } from "./bundle-mcp-config.js";
|
|
3
3
|
import { loadEmbeddedMcpConfig } from "./embedded-mcp.js";
|
|
4
|
+
import { McpAuthorizationRequiredError, isMcpAuthorizationError } from "./oauth/mcp-oauth-errors.js";
|
|
5
|
+
import { McpOAuthStore, canonicalMcpServerUrl, mcpOAuthServerKey } from "./oauth/mcp-oauth-store.js";
|
|
6
|
+
import { XopcMcpOAuthClientProvider } from "./oauth/mcp-oauth-provider.js";
|
|
4
7
|
import { __testing, createSessionMcpRuntime, disposeAllSessionMcpRuntimes, disposeSessionMcpRuntime, getOrCreateSessionMcpRuntime, getSessionMcpRuntimeManager, retireSessionMcpRuntime, retireSessionMcpRuntimeForSessionKey } from "./bundle-mcp-runtime.js";
|
|
5
8
|
import "./bundle-mcp-tools.js";
|
|
6
|
-
|
|
9
|
+
import { McpOAuthSession } from "./oauth/mcp-oauth-session.js";
|
|
10
|
+
import { McpOAuthManager, getMcpOAuthManager } from "./oauth/mcp-oauth-manager.js";
|
|
11
|
+
import "./oauth/index.js";
|
|
12
|
+
export { McpAuthorizationRequiredError, McpOAuthManager, McpOAuthSession, McpOAuthStore, XopcMcpOAuthClientProvider, __testing, canonicalMcpServerUrl, createSessionMcpRuntime, disposeAllSessionMcpRuntimes, disposeSessionMcpRuntime, getMcpOAuthManager, getOrCreateSessionMcpRuntime, getSessionMcpRuntimeManager, isMcpAuthorizationError, isMcpCatalogToolDenied, loadEmbeddedMcpConfig, loadMergedBundleMcpConfig, mcpOAuthServerKey, mcpToolPolicyId, resolveMcpToolPolicy, retireSessionMcpRuntime, retireSessionMcpRuntimeForSessionKey };
|
|
@@ -27,12 +27,36 @@ function resolveHttpMcpServerLaunchConfig(raw, options) {
|
|
|
27
27
|
let headers;
|
|
28
28
|
if (raw.headers !== void 0 && raw.headers !== null) if (!isMcpConfigRecord(raw.headers)) options?.onMalformedHeaders?.(raw.headers);
|
|
29
29
|
else headers = toMcpStringRecord(raw.headers, { onDroppedEntry: options?.onDroppedHeader });
|
|
30
|
+
let auth;
|
|
31
|
+
if (raw.auth !== void 0) {
|
|
32
|
+
if (!isMcpConfigRecord(raw.auth) || raw.auth.type !== "oauth") return {
|
|
33
|
+
ok: false,
|
|
34
|
+
reason: "its auth must be { \"type\": \"oauth\" }"
|
|
35
|
+
};
|
|
36
|
+
if (raw.auth.clientId !== void 0 && (typeof raw.auth.clientId !== "string" || !raw.auth.clientId.trim())) return {
|
|
37
|
+
ok: false,
|
|
38
|
+
reason: "its OAuth clientId must be a non-empty string"
|
|
39
|
+
};
|
|
40
|
+
if ((options?.transportType ?? "streamable-http") !== "streamable-http") return {
|
|
41
|
+
ok: false,
|
|
42
|
+
reason: "OAuth supports streamable HTTP only"
|
|
43
|
+
};
|
|
44
|
+
if (headers && Object.keys(headers).some((key) => key.toLowerCase() === "authorization")) return {
|
|
45
|
+
ok: false,
|
|
46
|
+
reason: "OAuth cannot be combined with a static Authorization header"
|
|
47
|
+
};
|
|
48
|
+
auth = {
|
|
49
|
+
type: "oauth",
|
|
50
|
+
...typeof raw.auth.clientId === "string" ? { clientId: raw.auth.clientId.trim() } : {}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
30
53
|
return {
|
|
31
54
|
ok: true,
|
|
32
55
|
config: {
|
|
33
56
|
transportType: options?.transportType ?? "streamable-http",
|
|
34
57
|
url,
|
|
35
|
-
headers
|
|
58
|
+
headers,
|
|
59
|
+
auth
|
|
36
60
|
}
|
|
37
61
|
};
|
|
38
62
|
}
|
|
@@ -17,6 +17,10 @@ export type ResolvedHttpMcpTransportConfig = ResolvedBaseMcpTransportConfig & {
|
|
|
17
17
|
transportType: HttpMcpTransportType;
|
|
18
18
|
url: string;
|
|
19
19
|
headers?: Record<string, string>;
|
|
20
|
+
auth?: {
|
|
21
|
+
type: "oauth";
|
|
22
|
+
clientId?: string;
|
|
23
|
+
};
|
|
20
24
|
};
|
|
21
25
|
export type ResolvedMcpTransportConfig = ResolvedStdioMcpTransportConfig | ResolvedHttpMcpTransportConfig;
|
|
22
26
|
export declare const DEFAULT_MCP_REQUEST_TIMEOUT_MS = 60000;
|
|
@@ -50,6 +50,7 @@ function resolveHttpTransportConfig(serverName, rawServer, transportType) {
|
|
|
50
50
|
transportType: launch.config.transportType,
|
|
51
51
|
url: launch.config.url,
|
|
52
52
|
headers: launch.config.headers,
|
|
53
|
+
auth: launch.config.auth,
|
|
53
54
|
description: describeHttpMcpServerLaunchConfig(launch.config),
|
|
54
55
|
connectionTimeoutMs: getConnectionTimeoutMs(rawServer),
|
|
55
56
|
requestTimeoutMs: getRequestTimeoutMs(rawServer)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
1
2
|
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
2
3
|
import type { Config } from "../../config/schema.js";
|
|
4
|
+
import { McpOAuthStore } from "./oauth/mcp-oauth-store.js";
|
|
3
5
|
export type ResolvedMcpTransport = {
|
|
4
6
|
transport: Transport;
|
|
5
7
|
description: string;
|
|
@@ -8,4 +10,8 @@ export type ResolvedMcpTransport = {
|
|
|
8
10
|
requestTimeoutMs: number;
|
|
9
11
|
detachStderr?: () => void;
|
|
10
12
|
};
|
|
11
|
-
export
|
|
13
|
+
export type ResolveMcpTransportOptions = {
|
|
14
|
+
oauthProvider?: OAuthClientProvider;
|
|
15
|
+
oauthStore?: McpOAuthStore;
|
|
16
|
+
};
|
|
17
|
+
export declare function resolveMcpTransport(serverName: string, rawServer: unknown, config?: Config, options?: ResolveMcpTransportOptions): Promise<ResolvedMcpTransport | null>;
|
|
@@ -5,6 +5,8 @@ import { init_string_coerce, normalizeOptionalString } from "../../utils/string-
|
|
|
5
5
|
import { buildRuntimeEnvironment, resolveRuntimeCommand } from "../../runtime-tools/environment.js";
|
|
6
6
|
import { XopcStdioClientTransport } from "./mcp-stdio-transport.js";
|
|
7
7
|
import { resolveMcpTransportConfig } from "./mcp-transport-config.js";
|
|
8
|
+
import { McpOAuthStore } from "./oauth/mcp-oauth-store.js";
|
|
9
|
+
import { XopcMcpOAuthClientProvider } from "./oauth/mcp-oauth-provider.js";
|
|
8
10
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
9
11
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
10
12
|
import { getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
@@ -48,7 +50,18 @@ function buildSseEventSourceFetch(headers) {
|
|
|
48
50
|
});
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
|
-
|
|
53
|
+
function buildScopedHttpFetch(serverUrl, headers) {
|
|
54
|
+
return async (url, init) => {
|
|
55
|
+
const mergedHeaders = new Headers();
|
|
56
|
+
if (new URL(url).origin === serverUrl.origin) for (const [key, value] of Object.entries(headers)) mergedHeaders.set(key, value);
|
|
57
|
+
if (init?.headers) new Headers(init.headers).forEach((value, key) => mergedHeaders.set(key, value));
|
|
58
|
+
return fetchWithUndici(url, {
|
|
59
|
+
...init,
|
|
60
|
+
headers: mergedHeaders
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function resolveMcpTransport(serverName, rawServer, config, options = {}) {
|
|
52
65
|
const resolved = resolveMcpTransportConfig(serverName, rawServer);
|
|
53
66
|
if (!resolved) return null;
|
|
54
67
|
if (resolved.kind === "stdio") {
|
|
@@ -82,16 +95,30 @@ async function resolveMcpTransport(serverName, rawServer, config) {
|
|
|
82
95
|
detachStderr: attachStderrLogging(serverName, transport)
|
|
83
96
|
};
|
|
84
97
|
}
|
|
85
|
-
if (resolved.transportType === "streamable-http")
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
if (resolved.transportType === "streamable-http") {
|
|
99
|
+
const serverUrl = new URL(resolved.url);
|
|
100
|
+
const oauthEnabled = Boolean(resolved.auth);
|
|
101
|
+
let oauthProvider = options.oauthProvider;
|
|
102
|
+
if (resolved.auth && !oauthProvider) {
|
|
103
|
+
const store = options.oauthStore ?? new McpOAuthStore();
|
|
104
|
+
if ((await store.load(serverUrl))?.tokens) oauthProvider = new XopcMcpOAuthClientProvider({
|
|
105
|
+
serverUrl,
|
|
106
|
+
clientId: resolved.auth.clientId,
|
|
107
|
+
store
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
transport: new StreamableHTTPClientTransport(serverUrl, {
|
|
112
|
+
authProvider: oauthProvider,
|
|
113
|
+
requestInit: !oauthEnabled && resolved.headers ? { headers: resolved.headers } : void 0,
|
|
114
|
+
fetch: oauthEnabled ? buildScopedHttpFetch(serverUrl, resolved.headers ?? {}) : fetchWithUndici
|
|
115
|
+
}),
|
|
116
|
+
description: resolved.description,
|
|
117
|
+
transportType: "streamable-http",
|
|
118
|
+
connectionTimeoutMs: resolved.connectionTimeoutMs,
|
|
119
|
+
requestTimeoutMs: resolved.requestTimeoutMs
|
|
120
|
+
};
|
|
121
|
+
}
|
|
95
122
|
const headers = { ...resolved.headers };
|
|
96
123
|
const hasHeaders = Object.keys(headers).length > 0;
|
|
97
124
|
return {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { McpAuthorizationRequiredError, isMcpAuthorizationError } from "./mcp-oauth-errors.js";
|
|
2
|
+
import { McpOAuthStore, canonicalMcpServerUrl, mcpOAuthServerKey } from "./mcp-oauth-store.js";
|
|
3
|
+
import { XopcMcpOAuthClientProvider } from "./mcp-oauth-provider.js";
|
|
4
|
+
import { McpOAuthSession } from "./mcp-oauth-session.js";
|
|
5
|
+
import { McpOAuthManager, getMcpOAuthManager } from "./mcp-oauth-manager.js";
|
|
6
|
+
export { McpAuthorizationRequiredError, McpOAuthManager, McpOAuthSession, McpOAuthStore, XopcMcpOAuthClientProvider, canonicalMcpServerUrl, getMcpOAuthManager, isMcpAuthorizationError, mcpOAuthServerKey };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
2
|
+
//#region src/agent/mcp/oauth/mcp-oauth-errors.ts
|
|
3
|
+
var McpAuthorizationRequiredError = class extends Error {
|
|
4
|
+
code = "MCP_AUTHORIZATION_REQUIRED";
|
|
5
|
+
constructor(message = "MCP server authorization is required") {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "McpAuthorizationRequiredError";
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
function isMcpAuthorizationError(error) {
|
|
11
|
+
return error instanceof UnauthorizedError || error instanceof McpAuthorizationRequiredError;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { McpAuthorizationRequiredError, isMcpAuthorizationError };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Config } from '../../../config/schema.js';
|
|
2
|
+
import { McpOAuthStore } from './mcp-oauth-store.js';
|
|
3
|
+
import type { McpOAuthSessionSnapshot } from './mcp-oauth-types.js';
|
|
4
|
+
export type McpOAuthConnectionStatus = 'not_configured' | 'disconnected' | 'authorizing' | 'connected' | 'error';
|
|
5
|
+
export type McpOAuthStatus = {
|
|
6
|
+
configured: boolean;
|
|
7
|
+
status: McpOAuthConnectionStatus;
|
|
8
|
+
session?: McpOAuthSessionSnapshot;
|
|
9
|
+
};
|
|
10
|
+
type StartMcpOAuthParams = {
|
|
11
|
+
serverId: string;
|
|
12
|
+
rawServer: unknown;
|
|
13
|
+
cfg?: Config;
|
|
14
|
+
};
|
|
15
|
+
export declare class McpOAuthManager {
|
|
16
|
+
private readonly store;
|
|
17
|
+
private readonly sessionsByServerUrl;
|
|
18
|
+
constructor(store?: McpOAuthStore);
|
|
19
|
+
status(serverId: string, rawServer: unknown): Promise<McpOAuthStatus>;
|
|
20
|
+
start(params: StartMcpOAuthParams): Promise<McpOAuthStatus>;
|
|
21
|
+
disconnect(serverId: string, rawServer: unknown): Promise<McpOAuthStatus>;
|
|
22
|
+
private finishAuthorization;
|
|
23
|
+
}
|
|
24
|
+
export declare function getMcpOAuthManager(): McpOAuthManager;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { createLogger } from "../../../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../../../utils/logger.js";
|
|
3
|
+
import { resolveGlobalSingleton } from "../../../utils/global-singleton.js";
|
|
4
|
+
import { redactSensitiveUrlLikeString } from "../../../utils/redact-sensitive-url.js";
|
|
5
|
+
import { resolveConnectorSecretReferences } from "../../../connectors/secret-store.js";
|
|
6
|
+
import { resolveMcpTransportConfig } from "../mcp-transport-config.js";
|
|
7
|
+
import { isMcpAuthorizationError } from "./mcp-oauth-errors.js";
|
|
8
|
+
import { McpOAuthStore, canonicalMcpServerUrl } from "./mcp-oauth-store.js";
|
|
9
|
+
import { XopcMcpOAuthClientProvider } from "./mcp-oauth-provider.js";
|
|
10
|
+
import { resolveMcpTransport } from "../mcp-transport.js";
|
|
11
|
+
import { disposeAllSessionMcpRuntimes } from "../bundle-mcp-runtime.js";
|
|
12
|
+
import { McpOAuthSession } from "./mcp-oauth-session.js";
|
|
13
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
14
|
+
//#region src/agent/mcp/oauth/mcp-oauth-manager.ts
|
|
15
|
+
init_logger();
|
|
16
|
+
const log = createLogger("Mcp:OAuth");
|
|
17
|
+
const MCP_OAUTH_MANAGER_KEY = Symbol.for("xopc.mcpOAuthManager");
|
|
18
|
+
function createClient() {
|
|
19
|
+
return new Client({
|
|
20
|
+
name: "xopc-mcp-oauth",
|
|
21
|
+
version: "0.0.0"
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async function connectClient(client, resolved) {
|
|
25
|
+
await new Promise((resolve, reject) => {
|
|
26
|
+
const timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`MCP server connection timed out after ${resolved.connectionTimeoutMs}ms`)), resolved.connectionTimeoutMs);
|
|
27
|
+
client.connect(resolved.transport).then(() => {
|
|
28
|
+
clearTimeout(timer);
|
|
29
|
+
resolve();
|
|
30
|
+
}, (error) => {
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
reject(error);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async function closeConnection(client, resolved) {
|
|
37
|
+
await client.close().catch(() => {});
|
|
38
|
+
await resolved.transport.close().catch(() => {});
|
|
39
|
+
resolved.detachStderr?.();
|
|
40
|
+
}
|
|
41
|
+
function isAuthorizing(snapshot) {
|
|
42
|
+
return [
|
|
43
|
+
"starting",
|
|
44
|
+
"waiting_browser",
|
|
45
|
+
"exchanging_code"
|
|
46
|
+
].includes(snapshot.status);
|
|
47
|
+
}
|
|
48
|
+
var McpOAuthManager = class {
|
|
49
|
+
store;
|
|
50
|
+
sessionsByServerUrl = /* @__PURE__ */ new Map();
|
|
51
|
+
constructor(store = new McpOAuthStore()) {
|
|
52
|
+
this.store = store;
|
|
53
|
+
}
|
|
54
|
+
async status(serverId, rawServer) {
|
|
55
|
+
const resolved = resolveMcpTransportConfig(serverId, rawServer);
|
|
56
|
+
if (!resolved || resolved.kind !== "http" || !resolved.auth) return {
|
|
57
|
+
configured: false,
|
|
58
|
+
status: "not_configured"
|
|
59
|
+
};
|
|
60
|
+
const session = this.sessionsByServerUrl.get(canonicalMcpServerUrl(resolved.url))?.snapshot();
|
|
61
|
+
if (session) {
|
|
62
|
+
if (isAuthorizing(session)) return {
|
|
63
|
+
configured: true,
|
|
64
|
+
status: "authorizing",
|
|
65
|
+
session
|
|
66
|
+
};
|
|
67
|
+
if (session.status === "failed" || session.status === "expired") return {
|
|
68
|
+
configured: true,
|
|
69
|
+
status: "error",
|
|
70
|
+
session
|
|
71
|
+
};
|
|
72
|
+
if (session.status === "connected") return {
|
|
73
|
+
configured: true,
|
|
74
|
+
status: "connected",
|
|
75
|
+
session
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
configured: true,
|
|
80
|
+
status: (await this.store.load(resolved.url))?.tokens ? "connected" : "disconnected",
|
|
81
|
+
session
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async start(params) {
|
|
85
|
+
const rawServer = await resolveConnectorSecretReferences(params.rawServer);
|
|
86
|
+
const resolvedConfig = resolveMcpTransportConfig(params.serverId, rawServer);
|
|
87
|
+
if (!resolvedConfig || resolvedConfig.kind !== "http" || !resolvedConfig.auth) throw new Error(`MCP server "${params.serverId}" is not configured for OAuth`);
|
|
88
|
+
const serverUrl = new URL(resolvedConfig.url);
|
|
89
|
+
const sessionKey = canonicalMcpServerUrl(serverUrl);
|
|
90
|
+
const previous = this.sessionsByServerUrl.get(sessionKey);
|
|
91
|
+
if (previous && isAuthorizing(previous.snapshot())) return this.status(params.serverId, params.rawServer);
|
|
92
|
+
if (previous) await previous.close();
|
|
93
|
+
const session = new McpOAuthSession(params.serverId, serverUrl);
|
|
94
|
+
this.sessionsByServerUrl.set(sessionKey, session);
|
|
95
|
+
try {
|
|
96
|
+
await session.start();
|
|
97
|
+
} catch (error) {
|
|
98
|
+
this.sessionsByServerUrl.delete(sessionKey);
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
await this.store.update(serverUrl, (current) => current ? {
|
|
103
|
+
...current,
|
|
104
|
+
clientInformation: void 0,
|
|
105
|
+
tokens: void 0,
|
|
106
|
+
tokensSavedAt: void 0
|
|
107
|
+
} : void 0);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
session.fail(error);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
let provider;
|
|
113
|
+
let resolved;
|
|
114
|
+
try {
|
|
115
|
+
provider = new XopcMcpOAuthClientProvider({
|
|
116
|
+
serverUrl,
|
|
117
|
+
clientId: resolvedConfig.auth.clientId,
|
|
118
|
+
store: this.store,
|
|
119
|
+
interaction: {
|
|
120
|
+
redirectUrl: session.redirectUrl,
|
|
121
|
+
state: session.state,
|
|
122
|
+
onRedirect: (authorizationUrl) => session.setAuthorizationUrl(authorizationUrl)
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
resolved = await resolveMcpTransport(params.serverId, rawServer, params.cfg, {
|
|
126
|
+
oauthProvider: provider,
|
|
127
|
+
oauthStore: this.store
|
|
128
|
+
});
|
|
129
|
+
} catch (error) {
|
|
130
|
+
session.fail(error);
|
|
131
|
+
return this.status(params.serverId, params.rawServer);
|
|
132
|
+
}
|
|
133
|
+
if (!resolved || resolved.transportType !== "streamable-http") {
|
|
134
|
+
session.fail(/* @__PURE__ */ new Error("MCP OAuth requires a streamable HTTP transport"));
|
|
135
|
+
await resolved?.transport.close().catch(() => {});
|
|
136
|
+
return this.status(params.serverId, params.rawServer);
|
|
137
|
+
}
|
|
138
|
+
const client = createClient();
|
|
139
|
+
try {
|
|
140
|
+
await connectClient(client, resolved);
|
|
141
|
+
session.complete();
|
|
142
|
+
await closeConnection(client, resolved);
|
|
143
|
+
await disposeAllSessionMcpRuntimes();
|
|
144
|
+
} catch (error) {
|
|
145
|
+
if (!isMcpAuthorizationError(error) || !session.snapshot().authorizationUrl) {
|
|
146
|
+
session.fail(error);
|
|
147
|
+
await closeConnection(client, resolved);
|
|
148
|
+
} else this.finishAuthorization({
|
|
149
|
+
session,
|
|
150
|
+
provider,
|
|
151
|
+
client,
|
|
152
|
+
resolved,
|
|
153
|
+
params: {
|
|
154
|
+
...params,
|
|
155
|
+
rawServer
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return this.status(params.serverId, params.rawServer);
|
|
160
|
+
}
|
|
161
|
+
async disconnect(serverId, rawServer) {
|
|
162
|
+
const resolved = resolveMcpTransportConfig(serverId, rawServer);
|
|
163
|
+
if (!resolved || resolved.kind !== "http" || !resolved.auth) throw new Error(`MCP server "${serverId}" is not configured for OAuth`);
|
|
164
|
+
const sessionKey = canonicalMcpServerUrl(resolved.url);
|
|
165
|
+
const session = this.sessionsByServerUrl.get(sessionKey);
|
|
166
|
+
this.sessionsByServerUrl.delete(sessionKey);
|
|
167
|
+
if (session) await session.close();
|
|
168
|
+
await this.store.delete(resolved.url);
|
|
169
|
+
await disposeAllSessionMcpRuntimes();
|
|
170
|
+
return {
|
|
171
|
+
configured: true,
|
|
172
|
+
status: "disconnected"
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async finishAuthorization(context) {
|
|
176
|
+
const { session, provider, client, resolved, params } = context;
|
|
177
|
+
try {
|
|
178
|
+
const code = await session.waitForCode();
|
|
179
|
+
await resolved.transport.finishAuth(code);
|
|
180
|
+
if (session.snapshot().status === "cancelled") {
|
|
181
|
+
await this.store.delete(session.serverUrl);
|
|
182
|
+
await closeConnection(client, resolved);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
await closeConnection(client, resolved);
|
|
186
|
+
const authenticated = await resolveMcpTransport(params.serverId, params.rawServer, params.cfg, {
|
|
187
|
+
oauthProvider: provider,
|
|
188
|
+
oauthStore: this.store
|
|
189
|
+
});
|
|
190
|
+
if (!authenticated) throw new Error("MCP transport could not be created after authorization");
|
|
191
|
+
const authenticatedClient = createClient();
|
|
192
|
+
try {
|
|
193
|
+
await connectClient(authenticatedClient, authenticated);
|
|
194
|
+
} finally {
|
|
195
|
+
await closeConnection(authenticatedClient, authenticated);
|
|
196
|
+
}
|
|
197
|
+
session.complete();
|
|
198
|
+
await disposeAllSessionMcpRuntimes();
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (session.snapshot().status === "cancelled") {
|
|
201
|
+
await this.store.delete(session.serverUrl);
|
|
202
|
+
await closeConnection(client, resolved);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
session.fail(error);
|
|
206
|
+
await closeConnection(client, resolved);
|
|
207
|
+
log.warn({
|
|
208
|
+
err: error,
|
|
209
|
+
phase: "mcp.oauth_finish",
|
|
210
|
+
serverId: params.serverId
|
|
211
|
+
}, `MCP OAuth failed for server "${params.serverId}": ${redactSensitiveUrlLikeString(error instanceof Error ? error.message : String(error))}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
function getMcpOAuthManager() {
|
|
216
|
+
return resolveGlobalSingleton(MCP_OAUTH_MANAGER_KEY, () => new McpOAuthManager());
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
219
|
+
export { McpOAuthManager, getMcpOAuthManager };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { OAuthClientProvider, OAuthDiscoveryState } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
2
|
+
import type { OAuthClientInformationMixed, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
3
|
+
import { McpOAuthStore } from './mcp-oauth-store.js';
|
|
4
|
+
export type McpOAuthInteraction = {
|
|
5
|
+
redirectUrl: URL;
|
|
6
|
+
state: string;
|
|
7
|
+
onRedirect: (authorizationUrl: URL) => void | Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
export type McpOAuthClientProviderOptions = {
|
|
10
|
+
serverUrl: URL;
|
|
11
|
+
clientId?: string;
|
|
12
|
+
interaction?: McpOAuthInteraction;
|
|
13
|
+
store?: McpOAuthStore;
|
|
14
|
+
};
|
|
15
|
+
export declare class XopcMcpOAuthClientProvider implements OAuthClientProvider {
|
|
16
|
+
private readonly options;
|
|
17
|
+
private readonly store;
|
|
18
|
+
private codeVerifierValue?;
|
|
19
|
+
constructor(options: McpOAuthClientProviderOptions);
|
|
20
|
+
get redirectUrl(): URL;
|
|
21
|
+
get clientMetadata(): OAuthClientMetadata;
|
|
22
|
+
state(): string | undefined;
|
|
23
|
+
clientInformation(): Promise<OAuthClientInformationMixed | undefined>;
|
|
24
|
+
saveClientInformation(clientInformation: OAuthClientInformationMixed): Promise<void>;
|
|
25
|
+
tokens(): Promise<OAuthTokens | undefined>;
|
|
26
|
+
saveTokens(tokens: OAuthTokens): Promise<void>;
|
|
27
|
+
redirectToAuthorization(authorizationUrl: URL): Promise<void>;
|
|
28
|
+
saveCodeVerifier(codeVerifier: string): void;
|
|
29
|
+
codeVerifier(): string;
|
|
30
|
+
saveDiscoveryState(discoveryState: OAuthDiscoveryState): Promise<void>;
|
|
31
|
+
discoveryState(): Promise<OAuthDiscoveryState | undefined>;
|
|
32
|
+
invalidateCredentials(scope: 'all' | 'client' | 'tokens' | 'verifier' | 'discovery'): Promise<void>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { McpAuthorizationRequiredError } from "./mcp-oauth-errors.js";
|
|
2
|
+
import { McpOAuthStore } from "./mcp-oauth-store.js";
|
|
3
|
+
//#region src/agent/mcp/oauth/mcp-oauth-provider.ts
|
|
4
|
+
var XopcMcpOAuthClientProvider = class {
|
|
5
|
+
store;
|
|
6
|
+
codeVerifierValue;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
this.store = options.store ?? new McpOAuthStore();
|
|
10
|
+
}
|
|
11
|
+
get redirectUrl() {
|
|
12
|
+
return this.options.interaction?.redirectUrl ?? new URL("http://127.0.0.1/oauth/callback");
|
|
13
|
+
}
|
|
14
|
+
get clientMetadata() {
|
|
15
|
+
return {
|
|
16
|
+
client_name: "XOPC",
|
|
17
|
+
client_uri: "https://xopc.ai",
|
|
18
|
+
redirect_uris: [this.redirectUrl.toString()],
|
|
19
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
20
|
+
response_types: ["code"],
|
|
21
|
+
token_endpoint_auth_method: "none"
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
state() {
|
|
25
|
+
return this.options.interaction?.state;
|
|
26
|
+
}
|
|
27
|
+
async clientInformation() {
|
|
28
|
+
if (this.options.clientId) return { client_id: this.options.clientId };
|
|
29
|
+
return (await this.store.load(this.options.serverUrl))?.clientInformation;
|
|
30
|
+
}
|
|
31
|
+
async saveClientInformation(clientInformation) {
|
|
32
|
+
await this.store.update(this.options.serverUrl, (current) => ({
|
|
33
|
+
...current,
|
|
34
|
+
version: 1,
|
|
35
|
+
serverUrl: this.options.serverUrl.toString(),
|
|
36
|
+
clientInformation,
|
|
37
|
+
updatedAt: current?.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
async tokens() {
|
|
41
|
+
return (await this.store.load(this.options.serverUrl))?.tokens;
|
|
42
|
+
}
|
|
43
|
+
async saveTokens(tokens) {
|
|
44
|
+
await this.store.update(this.options.serverUrl, (current) => ({
|
|
45
|
+
...current,
|
|
46
|
+
version: 1,
|
|
47
|
+
serverUrl: this.options.serverUrl.toString(),
|
|
48
|
+
tokens,
|
|
49
|
+
tokensSavedAt: Date.now(),
|
|
50
|
+
updatedAt: current?.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
async redirectToAuthorization(authorizationUrl) {
|
|
54
|
+
if (!this.options.interaction) throw new McpAuthorizationRequiredError();
|
|
55
|
+
await this.options.interaction.onRedirect(authorizationUrl);
|
|
56
|
+
}
|
|
57
|
+
saveCodeVerifier(codeVerifier) {
|
|
58
|
+
this.codeVerifierValue = codeVerifier;
|
|
59
|
+
}
|
|
60
|
+
codeVerifier() {
|
|
61
|
+
if (!this.codeVerifierValue) throw new Error("MCP OAuth code verifier is unavailable");
|
|
62
|
+
return this.codeVerifierValue;
|
|
63
|
+
}
|
|
64
|
+
async saveDiscoveryState(discoveryState) {
|
|
65
|
+
await this.store.update(this.options.serverUrl, (current) => {
|
|
66
|
+
const authorizationServerChanged = Boolean(current?.discoveryState?.authorizationServerUrl && current.discoveryState.authorizationServerUrl !== discoveryState.authorizationServerUrl);
|
|
67
|
+
return {
|
|
68
|
+
...current,
|
|
69
|
+
version: 1,
|
|
70
|
+
serverUrl: this.options.serverUrl.toString(),
|
|
71
|
+
discoveryState,
|
|
72
|
+
clientInformation: authorizationServerChanged ? void 0 : current?.clientInformation,
|
|
73
|
+
tokens: authorizationServerChanged ? void 0 : current?.tokens,
|
|
74
|
+
tokensSavedAt: authorizationServerChanged ? void 0 : current?.tokensSavedAt,
|
|
75
|
+
updatedAt: current?.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async discoveryState() {
|
|
80
|
+
return (await this.store.load(this.options.serverUrl))?.discoveryState;
|
|
81
|
+
}
|
|
82
|
+
async invalidateCredentials(scope) {
|
|
83
|
+
if (scope === "verifier") {
|
|
84
|
+
this.codeVerifierValue = void 0;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
await this.store.update(this.options.serverUrl, (current) => {
|
|
88
|
+
if (!current) return void 0;
|
|
89
|
+
if (scope === "all") return void 0;
|
|
90
|
+
return {
|
|
91
|
+
...current,
|
|
92
|
+
...scope === "client" ? { clientInformation: void 0 } : {},
|
|
93
|
+
...scope === "tokens" ? {
|
|
94
|
+
tokens: void 0,
|
|
95
|
+
tokensSavedAt: void 0
|
|
96
|
+
} : {},
|
|
97
|
+
...scope === "discovery" ? { discoveryState: void 0 } : {}
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
//#endregion
|
|
103
|
+
export { XopcMcpOAuthClientProvider };
|