@iblai/web-containers 1.57.0 → 1.58.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/_virtual/index2.js +2 -2
- package/dist/cjs/_virtual/index3.js +2 -2
- package/dist/cjs/components/chat/chat-download-dialog.js.map +1 -1
- package/dist/cjs/components/chat/ui/button.js +4 -4
- package/dist/cjs/components/chat/ui/button.js.map +1 -1
- package/dist/cjs/components/chat-input-form/composer-plus-menu.js +94 -17
- package/dist/cjs/components/chat-input-form/composer-plus-menu.js.map +1 -1
- package/dist/cjs/components/chat-input-form/mcp-connect-explainer-dialog.js +29 -0
- package/dist/cjs/components/chat-input-form/mcp-connect-explainer-dialog.js.map +1 -0
- package/dist/cjs/components/chat-input-form/mcp-servers-menu.js +199 -0
- package/dist/cjs/components/chat-input-form/mcp-servers-menu.js.map +1 -0
- package/dist/cjs/components/chat-input-form/use-mcp-active-count.js +56 -0
- package/dist/cjs/components/chat-input-form/use-mcp-active-count.js.map +1 -0
- package/dist/cjs/components/chat-input-form.js +17 -4
- package/dist/cjs/components/chat-input-form.js.map +1 -1
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/api-tab/create-api-modal.js +29 -13
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/api-tab/create-api-modal.js.map +1 -1
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/api-tab/labels.js +8 -0
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/api-tab/labels.js.map +1 -1
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/api-tab.js +23 -13
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/api-tab.js.map +1 -1
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-dialogs.js +755 -90
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-dialogs.js.map +1 -1
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-management-content.js +386 -984
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-management-content.js.map +1 -1
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/field-info.js +16 -0
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/field-info.js.map +1 -0
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/index.js +6 -3
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/index.js.map +1 -1
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/mcp-connector-helpers.js +463 -0
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/mcp-connector-helpers.js.map +1 -0
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/oauth-app-fields.js +329 -0
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/oauth-app-fields.js.map +1 -0
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/remove-connector-dialog.js +20 -0
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/remove-connector-dialog.js.map +1 -0
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/use-mcp-server-actions.js +744 -0
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/use-mcp-server-actions.js.map +1 -0
- package/dist/cjs/components/modals/user-profile-modal.js +2 -0
- package/dist/cjs/components/modals/user-profile-modal.js.map +1 -1
- package/dist/cjs/components/profile/integration/api-tab/create-api-modal.js +17 -10
- package/dist/cjs/components/profile/integration/api-tab/create-api-modal.js.map +1 -1
- package/dist/cjs/components/ui/accordion.js +40 -0
- package/dist/cjs/components/ui/accordion.js.map +1 -0
- package/dist/cjs/i18n/messages/en.json.js +156 -19
- package/dist/cjs/i18n/messages/en.json.js.map +1 -1
- package/dist/cjs/i18n/messages/es.json.js +156 -19
- package/dist/cjs/i18n/messages/es.json.js.map +1 -1
- package/dist/cjs/i18n/messages/fr.json.js +156 -19
- package/dist/cjs/i18n/messages/fr.json.js.map +1 -1
- package/dist/cjs/i18n/messages/zh.json.js +156 -19
- package/dist/cjs/i18n/messages/zh.json.js.map +1 -1
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/node_modules/.pnpm/@radix-ui_react-accordion@1.2.8_@types_react-dom@19.2.1_@types_react@19.1.17__@types_re_b91476821b9fe8f172a947f727875267/node_modules/@radix-ui/react-accordion/dist/index.js +317 -0
- package/dist/cjs/node_modules/.pnpm/@radix-ui_react-accordion@1.2.8_@types_react-dom@19.2.1_@types_react@19.1.17__@types_re_b91476821b9fe8f172a947f727875267/node_modules/@radix-ui/react-accordion/dist/index.js.map +1 -0
- package/dist/cjs/node_modules/.pnpm/@radix-ui_react-collapsible@1.1.8_@types_react-dom@19.2.1_@types_react@19.1.17__@types__d84e28844e8116e61b459b615fab358a/node_modules/@radix-ui/react-collapsible/dist/index.js +173 -0
- package/dist/cjs/node_modules/.pnpm/@radix-ui_react-collapsible@1.1.8_@types_react-dom@19.2.1_@types_react@19.1.17__@types__d84e28844e8116e61b459b615fab358a/node_modules/@radix-ui/react-collapsible/dist/index.js.map +1 -0
- package/dist/cjs/node_modules/.pnpm/@radix-ui_react-collection@1.1.4_@types_react-dom@19.2.1_@types_react@19.1.17__@types_r_7047fbfe61c39c77a2126486502acdb6/node_modules/@radix-ui/react-collection/dist/index.js +73 -0
- package/dist/cjs/node_modules/.pnpm/@radix-ui_react-collection@1.1.4_@types_react-dom@19.2.1_@types_react@19.1.17__@types_r_7047fbfe61c39c77a2126486502acdb6/node_modules/@radix-ui/react-collection/dist/index.js.map +1 -0
- package/dist/cjs/node_modules/.pnpm/css-mediaquery@0.1.2/node_modules/css-mediaquery/index.js +1 -1
- package/dist/cjs/node_modules/.pnpm/matchmediaquery@0.4.2/node_modules/matchmediaquery/index.js +1 -1
- package/dist/cjs/node_modules/.pnpm/prop-types@15.8.1/node_modules/prop-types/index.js +1 -1
- package/dist/cjs/web-containers/tsconfig.build.tsbuildinfo +1 -0
- package/dist/esm/_virtual/index2.js +2 -2
- package/dist/esm/_virtual/index3.js +2 -2
- package/dist/esm/components/chat/chat-download-dialog.js.map +1 -1
- package/dist/esm/components/chat-input-form/composer-plus-menu.js +94 -17
- package/dist/esm/components/chat-input-form/composer-plus-menu.js.map +1 -1
- package/dist/esm/components/chat-input-form/mcp-connect-explainer-dialog.js +27 -0
- package/dist/esm/components/chat-input-form/mcp-connect-explainer-dialog.js.map +1 -0
- package/dist/esm/components/chat-input-form/mcp-servers-menu.js +197 -0
- package/dist/esm/components/chat-input-form/mcp-servers-menu.js.map +1 -0
- package/dist/esm/components/chat-input-form/use-mcp-active-count.js +54 -0
- package/dist/esm/components/chat-input-form/use-mcp-active-count.js.map +1 -0
- package/dist/esm/components/chat-input-form.js +17 -4
- package/dist/esm/components/chat-input-form.js.map +1 -1
- package/dist/esm/components/modals/edit-mentor-modal/tabs/api-tab/create-api-modal.js +30 -14
- package/dist/esm/components/modals/edit-mentor-modal/tabs/api-tab/create-api-modal.js.map +1 -1
- package/dist/esm/components/modals/edit-mentor-modal/tabs/api-tab/labels.js +8 -0
- package/dist/esm/components/modals/edit-mentor-modal/tabs/api-tab/labels.js.map +1 -1
- package/dist/esm/components/modals/edit-mentor-modal/tabs/api-tab.js +25 -15
- package/dist/esm/components/modals/edit-mentor-modal/tabs/api-tab.js.map +1 -1
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-dialogs.js +755 -92
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-dialogs.js.map +1 -1
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-management-content.js +361 -968
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-management-content.js.map +1 -1
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/field-info.js +14 -0
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/field-info.js.map +1 -0
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/index.js +6 -3
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/index.js.map +1 -1
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/mcp-connector-helpers.js +431 -0
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/mcp-connector-helpers.js.map +1 -0
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/oauth-app-fields.js +313 -0
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/oauth-app-fields.js.map +1 -0
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/remove-connector-dialog.js +18 -0
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/remove-connector-dialog.js.map +1 -0
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/use-mcp-server-actions.js +742 -0
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/use-mcp-server-actions.js.map +1 -0
- package/dist/esm/components/modals/user-profile-modal.js +2 -0
- package/dist/esm/components/modals/user-profile-modal.js.map +1 -1
- package/dist/esm/components/profile/integration/api-tab/create-api-modal.js +17 -10
- package/dist/esm/components/profile/integration/api-tab/create-api-modal.js.map +1 -1
- package/dist/esm/components/ui/accordion.js +16 -0
- package/dist/esm/components/ui/accordion.js.map +1 -0
- package/dist/esm/i18n/messages/en.json.js +154 -20
- package/dist/esm/i18n/messages/en.json.js.map +1 -1
- package/dist/esm/i18n/messages/es.json.js +154 -20
- package/dist/esm/i18n/messages/es.json.js.map +1 -1
- package/dist/esm/i18n/messages/fr.json.js +154 -20
- package/dist/esm/i18n/messages/fr.json.js.map +1 -1
- package/dist/esm/i18n/messages/zh.json.js +154 -20
- package/dist/esm/i18n/messages/zh.json.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/node_modules/.pnpm/@radix-ui_react-accordion@1.2.8_@types_react-dom@19.2.1_@types_react@19.1.17__@types_re_b91476821b9fe8f172a947f727875267/node_modules/@radix-ui/react-accordion/dist/index.js +306 -0
- package/dist/esm/node_modules/.pnpm/@radix-ui_react-accordion@1.2.8_@types_react-dom@19.2.1_@types_react@19.1.17__@types_re_b91476821b9fe8f172a947f727875267/node_modules/@radix-ui/react-accordion/dist/index.js.map +1 -0
- package/dist/esm/node_modules/.pnpm/@radix-ui_react-collapsible@1.1.8_@types_react-dom@19.2.1_@types_react@19.1.17__@types__d84e28844e8116e61b459b615fab358a/node_modules/@radix-ui/react-collapsible/dist/index.js +146 -0
- package/dist/esm/node_modules/.pnpm/@radix-ui_react-collapsible@1.1.8_@types_react-dom@19.2.1_@types_react@19.1.17__@types__d84e28844e8116e61b459b615fab358a/node_modules/@radix-ui/react-collapsible/dist/index.js.map +1 -0
- package/dist/esm/node_modules/.pnpm/@radix-ui_react-collection@1.1.4_@types_react-dom@19.2.1_@types_react@19.1.17__@types_r_7047fbfe61c39c77a2126486502acdb6/node_modules/@radix-ui/react-collection/dist/index.js +71 -0
- package/dist/esm/node_modules/.pnpm/@radix-ui_react-collection@1.1.4_@types_react-dom@19.2.1_@types_react@19.1.17__@types_r_7047fbfe61c39c77a2126486502acdb6/node_modules/@radix-ui/react-collection/dist/index.js.map +1 -0
- package/dist/esm/node_modules/.pnpm/css-mediaquery@0.1.2/node_modules/css-mediaquery/index.js +1 -1
- package/dist/esm/node_modules/.pnpm/matchmediaquery@0.4.2/node_modules/matchmediaquery/index.js +1 -1
- package/dist/esm/node_modules/.pnpm/prop-types@15.8.1/node_modules/prop-types/index.js +1 -1
- package/dist/esm/web-containers/tsconfig.build.tsbuildinfo +1 -0
- package/dist/index.d.ts +31 -2
- package/dist/next/index.d.ts +6 -0
- package/dist/package.json +1 -1
- package/package.json +3 -3
- package/dist/cjs/web-containers/tsconfig.tsbuildinfo +0 -1
- package/dist/esm/web-containers/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var dataLayer = require('@iblai/data-layer');
|
|
5
|
+
var sonner = require('sonner');
|
|
6
|
+
var useLocalStorage = require('../../../../../hooks/use-local-storage.js');
|
|
7
|
+
var context = require('../../../../../i18n/context.js');
|
|
8
|
+
require('../../../../../packages/i18n/dist/index.esm.js');
|
|
9
|
+
var mcpConnectorHelpers = require('./mcp-connector-helpers.js');
|
|
10
|
+
|
|
11
|
+
const skipRefetch = () => Promise.resolve();
|
|
12
|
+
function useMcpServerActions({ tenantKey, username, mentorId, refetchServers, softUserScopeConnectFailure = false, onConnectNotConfigured, }) {
|
|
13
|
+
var _a;
|
|
14
|
+
const t = context.useT();
|
|
15
|
+
const [togglingServerIds, setTogglingServerIds] = React.useState(() => new Set());
|
|
16
|
+
const [connectingServiceIds, setConnectingServiceIds] = React.useState(() => new Set());
|
|
17
|
+
const [disconnectingServiceIds, setDisconnectingServiceIds] = React.useState(() => new Set());
|
|
18
|
+
const [deletingServerId, setDeletingServerId] = React.useState(null);
|
|
19
|
+
const [, setPendingOAuthServer, removePendingOAuthServer] = useLocalStorage.useLocalStorage('oauth_pending_server', null);
|
|
20
|
+
// Tracks in-flight OAuth cleanup callbacks so they fire on unmount, not just
|
|
21
|
+
// on successful completion / timeout / popup-closed paths inside
|
|
22
|
+
// `handleConnectOAuth`. Without this, navigating away mid-flow would leak
|
|
23
|
+
// the `storage` / `message` / `focus` window listeners until the 5-minute
|
|
24
|
+
// safety timer eventually fires.
|
|
25
|
+
const oauthCleanupsRef = React.useRef(new Set());
|
|
26
|
+
// The callback page's answer reaches every flow for the service, whichever
|
|
27
|
+
// tab it came from, so a second flow for one connector would create its
|
|
28
|
+
// connection twice.
|
|
29
|
+
const signInsInFlightRef = React.useRef(new Set());
|
|
30
|
+
React.useEffect(() => () => {
|
|
31
|
+
oauthCleanupsRef.current.forEach((fn) => fn());
|
|
32
|
+
oauthCleanupsRef.current.clear();
|
|
33
|
+
}, []);
|
|
34
|
+
const { data: mentorSettings, isLoading: isSettingsLoading, refetch: refetchMentorSettings, } = dataLayer.useGetMentorSettingsQuery({
|
|
35
|
+
mentor: mentorId,
|
|
36
|
+
org: tenantKey,
|
|
37
|
+
// @ts-expect-error userId is not part of useGetMentorSettingsQuery Query definition
|
|
38
|
+
userId: username,
|
|
39
|
+
}, { skip: !username });
|
|
40
|
+
const { data: connectedServices, refetch: refetchConnected } = dataLayer.useGetConnectedServicesQuery({ org: tenantKey, userId: username }, { skip: !tenantKey || !username });
|
|
41
|
+
const { data: mcpServerConnections, refetch: refetchMCPServerConnections } = dataLayer.useGetMCPServerConnectionsQuery({ org: tenantKey, userId: username }, { skip: !tenantKey || !username });
|
|
42
|
+
const [createMCPServer] = dataLayer.useCreateMCPServerMutation();
|
|
43
|
+
const [updateMCPServer] = dataLayer.useUpdateMCPServerMutation();
|
|
44
|
+
const [deleteMCPServer] = dataLayer.useDeleteMCPServerMutation();
|
|
45
|
+
const [editMentorJson] = dataLayer.useEditMentorJsonMutation();
|
|
46
|
+
const [startOAuthFlow] = dataLayer.useLazyStartOAuthFlowQuery();
|
|
47
|
+
const [disconnectService] = dataLayer.useDisconnectServiceMutation();
|
|
48
|
+
const [createMCPServerConnection] = dataLayer.useCreateMCPServerConnectionMutation();
|
|
49
|
+
// Field-level RBAC blanks mcp_servers for users who may not read it — to []
|
|
50
|
+
// on the settings endpoint, flagged by permissions.field.mcp_servers.read ===
|
|
51
|
+
// false — so an empty list alone does not mean "no servers attached".
|
|
52
|
+
const attachmentsKnown = React.useMemo(() => {
|
|
53
|
+
var _a, _b, _c;
|
|
54
|
+
if (!mentorSettings || typeof mentorSettings !== 'object')
|
|
55
|
+
return false;
|
|
56
|
+
const settings = mentorSettings;
|
|
57
|
+
return (Array.isArray(settings.mcp_servers) &&
|
|
58
|
+
((_c = (_b = (_a = settings.permissions) === null || _a === void 0 ? void 0 : _a.field) === null || _b === void 0 ? void 0 : _b.mcp_servers) === null || _c === void 0 ? void 0 : _c.read) !== false);
|
|
59
|
+
}, [mentorSettings]);
|
|
60
|
+
const activeMcpServerIds = React.useMemo(() => {
|
|
61
|
+
if (!mentorSettings || typeof mentorSettings !== 'object')
|
|
62
|
+
return [];
|
|
63
|
+
const maybeServers = mentorSettings.mcp_servers;
|
|
64
|
+
if (!Array.isArray(maybeServers))
|
|
65
|
+
return [];
|
|
66
|
+
return maybeServers
|
|
67
|
+
.map((entry) => {
|
|
68
|
+
if (typeof entry === 'number' && Number.isFinite(entry))
|
|
69
|
+
return entry;
|
|
70
|
+
if (typeof entry === 'object' && entry !== null && 'id' in entry) {
|
|
71
|
+
const id = entry.id;
|
|
72
|
+
if (typeof id === 'number')
|
|
73
|
+
return id;
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
})
|
|
77
|
+
.filter((id) => typeof id === 'number' && Number.isFinite(id));
|
|
78
|
+
}, [mentorSettings]);
|
|
79
|
+
const toolsKnown = React.useMemo(() => !!mentorSettings &&
|
|
80
|
+
typeof mentorSettings === 'object' &&
|
|
81
|
+
Array.isArray(mentorSettings.mentor_tools), [mentorSettings]);
|
|
82
|
+
// mentor_tools is an array of objects like: [{ name: "MCP", slug: "mcp", ... }]
|
|
83
|
+
const currentToolSlugs = React.useMemo(() => {
|
|
84
|
+
if (!mentorSettings || typeof mentorSettings !== 'object')
|
|
85
|
+
return [];
|
|
86
|
+
const maybeTools = mentorSettings.mentor_tools;
|
|
87
|
+
if (!Array.isArray(maybeTools))
|
|
88
|
+
return [];
|
|
89
|
+
return maybeTools
|
|
90
|
+
.map((tool) => {
|
|
91
|
+
if (typeof tool === 'object' && tool !== null && 'slug' in tool) {
|
|
92
|
+
const slug = tool.slug;
|
|
93
|
+
if (typeof slug === 'string')
|
|
94
|
+
return slug;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
})
|
|
98
|
+
.filter((slug) => typeof slug === 'string');
|
|
99
|
+
}, [mentorSettings]);
|
|
100
|
+
const fieldPermissions = (_a = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _a === void 0 ? void 0 : _a.field;
|
|
101
|
+
const isOAuthServerConnected = React.useCallback((server) => {
|
|
102
|
+
const connection = mcpConnectorHelpers.findMCPServerConnection(mcpServerConnections === null || mcpServerConnections === void 0 ? void 0 : mcpServerConnections.results, server.id, username, mentorId);
|
|
103
|
+
return !!connection && mcpConnectorHelpers.connectionHasSignIn(connection);
|
|
104
|
+
}, [mcpServerConnections, username, mentorId]);
|
|
105
|
+
const hasConnectionForScope = React.useCallback((serverId, scope) => mcpConnectorHelpers.connectionForScopeExists(mcpServerConnections === null || mcpServerConnections === void 0 ? void 0 : mcpServerConnections.results, serverId, scope, username, mentorId), [mcpServerConnections, username, mentorId]);
|
|
106
|
+
const getConnectedServiceId = React.useCallback((server) => {
|
|
107
|
+
var _a;
|
|
108
|
+
const connection = mcpConnectorHelpers.findMCPServerConnection(mcpServerConnections === null || mcpServerConnections === void 0 ? void 0 : mcpServerConnections.results, server.id, username, mentorId);
|
|
109
|
+
return (_a = connection === null || connection === void 0 ? void 0 : connection.connected_service) !== null && _a !== void 0 ? _a : null;
|
|
110
|
+
}, [mcpServerConnections, username, mentorId]);
|
|
111
|
+
/** The current user's own sign-in for the server's OAuth service, if any. */
|
|
112
|
+
const getSignedInServiceId = React.useCallback((server) => {
|
|
113
|
+
var _a;
|
|
114
|
+
const oauthData = server.oauth_service_data;
|
|
115
|
+
if (!oauthData || !Array.isArray(connectedServices))
|
|
116
|
+
return null;
|
|
117
|
+
const service = connectedServices.find((cs) => cs.provider === oauthData.oauth_provider && cs.service === oauthData.name);
|
|
118
|
+
return (_a = service === null || service === void 0 ? void 0 : service.id) !== null && _a !== void 0 ? _a : null;
|
|
119
|
+
}, [connectedServices]);
|
|
120
|
+
/** Whether a connected service belongs to the current user (only theirs can be deleted). */
|
|
121
|
+
const ownsConnectedService = React.useCallback((connectedServiceId) => connectedServiceId !== null &&
|
|
122
|
+
Array.isArray(connectedServices) &&
|
|
123
|
+
connectedServices.some((cs) => cs.id === connectedServiceId), [connectedServices]);
|
|
124
|
+
const getConnectionId = React.useCallback((server) => {
|
|
125
|
+
var _a;
|
|
126
|
+
const connection = mcpConnectorHelpers.findMCPServerConnection(mcpServerConnections === null || mcpServerConnections === void 0 ? void 0 : mcpServerConnections.results, server.id, username, mentorId);
|
|
127
|
+
return (_a = connection === null || connection === void 0 ? void 0 : connection.id) !== null && _a !== void 0 ? _a : null;
|
|
128
|
+
}, [mcpServerConnections, username, mentorId]);
|
|
129
|
+
const updateMCPServers = React.useCallback(async (serverIds, options) => {
|
|
130
|
+
if (!mentorId)
|
|
131
|
+
throw new Error('Invalid mentor ID');
|
|
132
|
+
const uniqueIds = [...new Set(serverIds.filter((id) => Number.isFinite(id) && id > 0))];
|
|
133
|
+
const requestBody = { mcp_servers: uniqueIds };
|
|
134
|
+
if ((options === null || options === void 0 ? void 0 : options.toolSlugs) !== undefined) {
|
|
135
|
+
requestBody.tool_slugs = options.toolSlugs;
|
|
136
|
+
}
|
|
137
|
+
if ((options === null || options === void 0 ? void 0 : options.canUseTools) !== undefined) {
|
|
138
|
+
requestBody.can_use_tools = options.canUseTools;
|
|
139
|
+
}
|
|
140
|
+
await editMentorJson({
|
|
141
|
+
mentorId,
|
|
142
|
+
org: tenantKey,
|
|
143
|
+
requestBody,
|
|
144
|
+
userId: username,
|
|
145
|
+
}).unwrap();
|
|
146
|
+
}, [mentorId, tenantKey, username, editMentorJson]);
|
|
147
|
+
// Taking the last server off the agent also drops the 'mcp' tool slug, so the switch,
|
|
148
|
+
// Disconnect and Delete all leave the agent's tools matching its servers.
|
|
149
|
+
const detachServerFromAgent = React.useCallback(async (serverId) => {
|
|
150
|
+
const updatedIds = activeMcpServerIds.filter((id) => id !== serverId);
|
|
151
|
+
if (updatedIds.length === 0 && currentToolSlugs.includes('mcp')) {
|
|
152
|
+
const updatedToolSlugs = currentToolSlugs.filter((slug) => slug !== 'mcp');
|
|
153
|
+
await updateMCPServers(updatedIds, {
|
|
154
|
+
toolSlugs: updatedToolSlugs,
|
|
155
|
+
canUseTools: updatedToolSlugs.length > 0,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
await updateMCPServers(updatedIds);
|
|
160
|
+
}
|
|
161
|
+
}, [activeMcpServerIds, currentToolSlugs, updateMCPServers]);
|
|
162
|
+
const handleToggleConnector = React.useCallback(async (serverId, serverName, isActive, options) => {
|
|
163
|
+
var _a, _b;
|
|
164
|
+
if (!Number.isFinite(serverId) || serverId <= 0) {
|
|
165
|
+
sonner.toast.error(t('mcpTabConnectorManagementContent.invalidServerId'));
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
setTogglingServerIds((prev) => new Set(prev).add(serverId));
|
|
169
|
+
try {
|
|
170
|
+
const currentIds = Array.isArray(activeMcpServerIds) ? activeMcpServerIds : [];
|
|
171
|
+
const updatedIds = isActive
|
|
172
|
+
? currentIds.includes(serverId)
|
|
173
|
+
? currentIds
|
|
174
|
+
: [...currentIds, serverId]
|
|
175
|
+
: currentIds.filter((id) => id !== serverId);
|
|
176
|
+
/* istanbul ignore next -- @preserve optimization path, tested via toggle tests */
|
|
177
|
+
if (updatedIds.length === currentIds.length && isActive && currentIds.includes(serverId)) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
const hasMcpInToolSlugs = currentToolSlugs.includes('mcp');
|
|
181
|
+
if (isActive) {
|
|
182
|
+
// Enabling any connector needs the 'mcp' tool slug and can_use_tools on.
|
|
183
|
+
if (!hasMcpInToolSlugs) {
|
|
184
|
+
const updatedToolSlugs = [...currentToolSlugs, 'mcp'];
|
|
185
|
+
await updateMCPServers(updatedIds, {
|
|
186
|
+
toolSlugs: updatedToolSlugs,
|
|
187
|
+
canUseTools: true,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
await updateMCPServers(updatedIds, {
|
|
192
|
+
canUseTools: true,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
await detachServerFromAgent(serverId);
|
|
198
|
+
}
|
|
199
|
+
await refetchMentorSettings();
|
|
200
|
+
if (options === null || options === void 0 ? void 0 : options.quiet)
|
|
201
|
+
return true;
|
|
202
|
+
sonner.toast.success(isActive
|
|
203
|
+
? t('mcpTabConnectorManagementContent.serverActivated', { serverName })
|
|
204
|
+
: t('mcpTabConnectorManagementContent.serverDeactivated', { serverName }));
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
const err = error;
|
|
209
|
+
const msg = ((_a = err === null || err === void 0 ? void 0 : err.data) === null || _a === void 0 ? void 0 : _a.detail) || ((_b = err === null || err === void 0 ? void 0 : err.data) === null || _b === void 0 ? void 0 : _b.error) || (err === null || err === void 0 ? void 0 : err.message) || '';
|
|
210
|
+
if (msg.includes('does not exist') || msg.includes('not accessible')) {
|
|
211
|
+
sonner.toast.error(t('mcpTabConnectorManagementContent.serverNotAccessible', { serverName }));
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
sonner.toast.error(isActive
|
|
215
|
+
? t('mcpTabConnectorManagementContent.failedToActivate', { serverName, msg })
|
|
216
|
+
: t('mcpTabConnectorManagementContent.failedToDeactivate', { serverName, msg }));
|
|
217
|
+
}
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
setTogglingServerIds((prev) => {
|
|
222
|
+
const next = new Set(prev);
|
|
223
|
+
next.delete(serverId);
|
|
224
|
+
return next;
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}, [
|
|
228
|
+
activeMcpServerIds,
|
|
229
|
+
currentToolSlugs,
|
|
230
|
+
updateMCPServers,
|
|
231
|
+
detachServerFromAgent,
|
|
232
|
+
refetchMentorSettings,
|
|
233
|
+
t,
|
|
234
|
+
]);
|
|
235
|
+
/** Creates or updates a server; returns whether it was saved. New servers are auto-attached. */
|
|
236
|
+
const saveConnector = React.useCallback(async (connector, options = {}) => {
|
|
237
|
+
var _a, _b;
|
|
238
|
+
const { editingServer, onSaved } = options;
|
|
239
|
+
if (!tenantKey || !username) {
|
|
240
|
+
sonner.toast.error(t('mcpTabConnectorManagementContent.missingRequiredParams'));
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const hasFileImage = connector.image instanceof File;
|
|
245
|
+
const trimmedCredentials = (_a = connector.credentials) === null || _a === void 0 ? void 0 : _a.trim();
|
|
246
|
+
const authType = (_b = connector.authType) !== null && _b !== void 0 ? _b : (trimmedCredentials ? 'token' : 'none');
|
|
247
|
+
const resolvedTransport = mcpConnectorHelpers.normalizeTransportValue(connector.transport);
|
|
248
|
+
const basePayload = {
|
|
249
|
+
name: connector.name,
|
|
250
|
+
url: connector.url || `https://api.${connector.name.toLowerCase()}.com/mcp`,
|
|
251
|
+
transport: resolvedTransport,
|
|
252
|
+
auth_type: authType,
|
|
253
|
+
description: connector.description || '',
|
|
254
|
+
// For edits, undefined credentials means "keep the existing ones".
|
|
255
|
+
...(trimmedCredentials !== undefined && {
|
|
256
|
+
credentials: trimmedCredentials,
|
|
257
|
+
}),
|
|
258
|
+
...(connector.authScope && { auth_scope: connector.authScope }),
|
|
259
|
+
...(connector.mentor !== undefined && { mentor: connector.mentor }),
|
|
260
|
+
...(connector.userCanDisable !== undefined && {
|
|
261
|
+
user_can_disable: connector.userCanDisable,
|
|
262
|
+
}),
|
|
263
|
+
};
|
|
264
|
+
/* istanbul ignore next -- @preserve update path tested via ConnectorDialogs integration */
|
|
265
|
+
if (editingServer) {
|
|
266
|
+
const args = {
|
|
267
|
+
id: editingServer.id,
|
|
268
|
+
org: tenantKey,
|
|
269
|
+
userId: username,
|
|
270
|
+
...(hasFileImage
|
|
271
|
+
? {
|
|
272
|
+
formData: mcpConnectorHelpers.createMCPServerFormData({
|
|
273
|
+
...basePayload,
|
|
274
|
+
image: connector.image,
|
|
275
|
+
}),
|
|
276
|
+
}
|
|
277
|
+
: { body: basePayload }),
|
|
278
|
+
};
|
|
279
|
+
await updateMCPServer(args).unwrap();
|
|
280
|
+
sonner.toast.success(t('mcpTabConnectorManagementContent.connectorUpdated', { name: connector.name }));
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
const args = {
|
|
284
|
+
org: tenantKey,
|
|
285
|
+
userId: username,
|
|
286
|
+
...(hasFileImage
|
|
287
|
+
? {
|
|
288
|
+
formData: mcpConnectorHelpers.createMCPServerFormData({
|
|
289
|
+
...basePayload,
|
|
290
|
+
image: connector.image,
|
|
291
|
+
}),
|
|
292
|
+
}
|
|
293
|
+
: { body: basePayload }),
|
|
294
|
+
};
|
|
295
|
+
const newServer = await createMCPServer(args).unwrap();
|
|
296
|
+
sonner.toast.success(t('mcpTabConnectorManagementContent.connectorAdded', { name: connector.name }));
|
|
297
|
+
try {
|
|
298
|
+
const newId = Number(newServer.id);
|
|
299
|
+
if (Number.isFinite(newId)) {
|
|
300
|
+
// The PUT replaces the whole list: built from unread (redacted or unloaded)
|
|
301
|
+
// settings it would drop every other server and tool from the agent.
|
|
302
|
+
if (!attachmentsKnown || !toolsKnown) {
|
|
303
|
+
throw new Error('Agent attachments are unknown');
|
|
304
|
+
}
|
|
305
|
+
const updatedIds = [...activeMcpServerIds, newId];
|
|
306
|
+
const hasMcpInToolSlugs = currentToolSlugs.includes('mcp');
|
|
307
|
+
if (!hasMcpInToolSlugs) {
|
|
308
|
+
const updatedToolSlugs = [...currentToolSlugs, 'mcp'];
|
|
309
|
+
await updateMCPServers(updatedIds, {
|
|
310
|
+
toolSlugs: updatedToolSlugs,
|
|
311
|
+
canUseTools: true,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
await updateMCPServers(updatedIds, {
|
|
316
|
+
canUseTools: true,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
await refetchMentorSettings();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
catch (_c) {
|
|
323
|
+
sonner.toast.warning(t('mcpTabConnectorManagementContent.connectorNotActivated', { name: connector.name }));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
onSaved === null || onSaved === void 0 ? void 0 : onSaved();
|
|
327
|
+
await refetchServers();
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
catch (_d) {
|
|
331
|
+
sonner.toast.error(editingServer
|
|
332
|
+
? t('mcpTabConnectorManagementContent.failedToUpdateConnector', {
|
|
333
|
+
name: connector.name,
|
|
334
|
+
})
|
|
335
|
+
: t('mcpTabConnectorManagementContent.failedToAddConnector', { name: connector.name }));
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
}, [
|
|
339
|
+
tenantKey,
|
|
340
|
+
username,
|
|
341
|
+
attachmentsKnown,
|
|
342
|
+
toolsKnown,
|
|
343
|
+
activeMcpServerIds,
|
|
344
|
+
currentToolSlugs,
|
|
345
|
+
createMCPServer,
|
|
346
|
+
updateMCPServer,
|
|
347
|
+
updateMCPServers,
|
|
348
|
+
refetchServers,
|
|
349
|
+
refetchMentorSettings,
|
|
350
|
+
]);
|
|
351
|
+
const handleConnectOAuth = React.useCallback(async (server) => {
|
|
352
|
+
var _a;
|
|
353
|
+
if (!tenantKey || !username || !server.oauth_service_data) {
|
|
354
|
+
sonner.toast.error(t('mcpTabConnectorManagementContent.missingOAuthParams'));
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const serviceKey = `server-${server.id}`;
|
|
358
|
+
if (signInsInFlightRef.current.has(serviceKey))
|
|
359
|
+
return;
|
|
360
|
+
signInsInFlightRef.current.add(serviceKey);
|
|
361
|
+
// Opened before the first await, while the click still counts: the tab
|
|
362
|
+
// shows at once and pop-up blockers let it through. It keeps its opener,
|
|
363
|
+
// which the callback page posts the result to.
|
|
364
|
+
const tab = window.open('', '_blank');
|
|
365
|
+
const closeTab = () => tab === null || tab === void 0 ? void 0 : tab.close();
|
|
366
|
+
const oauthData = server.oauth_service_data;
|
|
367
|
+
const { oauth_provider: provider, name } = oauthData;
|
|
368
|
+
const serverId = server.id;
|
|
369
|
+
const oauthDisplayName = oauthData.display_name || name;
|
|
370
|
+
// The agent's own override, else the connector's: the connection's scope.
|
|
371
|
+
const authScope = (_a = server.effective_auth_scope) !== null && _a !== void 0 ? _a : server.auth_scope;
|
|
372
|
+
const softCreateFailureMessage = softUserScopeConnectFailure && (authScope || 'user') === 'user'
|
|
373
|
+
? t('mcpServersMenu.signedInReadyNextMessage', { name: oauthDisplayName })
|
|
374
|
+
: undefined;
|
|
375
|
+
setPendingOAuthServer({
|
|
376
|
+
serverId,
|
|
377
|
+
provider,
|
|
378
|
+
service: name,
|
|
379
|
+
timestamp: Date.now(),
|
|
380
|
+
});
|
|
381
|
+
setConnectingServiceIds((prev) => new Set(prev).add(serviceKey));
|
|
382
|
+
// State the cleanup closure references. Declared before the first
|
|
383
|
+
// `await` so we can register cleanup synchronously — guaranteeing an
|
|
384
|
+
// unmount mid-flow tears down listeners, intervals, and timers.
|
|
385
|
+
let pollCount = 0;
|
|
386
|
+
const maxPolls = 60;
|
|
387
|
+
let pollInterval = null;
|
|
388
|
+
let popupCheckInterval = null;
|
|
389
|
+
let isCreatingConnection = false;
|
|
390
|
+
// Listener handlers — reassigned with real implementations once the
|
|
391
|
+
// OAuth URL resolves. Default noops let cleanup safely call
|
|
392
|
+
// removeEventListener even if we never reached addEventListener (e.g.
|
|
393
|
+
// unmount before startOAuthFlow resolves).
|
|
394
|
+
let handleFocus = () => { };
|
|
395
|
+
let handleStorageChange = () => { };
|
|
396
|
+
let handleMessage = () => { };
|
|
397
|
+
// Idempotent — tracked in `oauthCleanupsRef` so unmount can also drive it.
|
|
398
|
+
let cleanedUp = false;
|
|
399
|
+
// Set while the connection is being created from a completed sign-in.
|
|
400
|
+
let creating = false;
|
|
401
|
+
const stopListening = () => {
|
|
402
|
+
if (pollInterval)
|
|
403
|
+
clearInterval(pollInterval);
|
|
404
|
+
if (popupCheckInterval)
|
|
405
|
+
clearInterval(popupCheckInterval);
|
|
406
|
+
pollInterval = null;
|
|
407
|
+
popupCheckInterval = null;
|
|
408
|
+
window.removeEventListener('focus', handleFocus);
|
|
409
|
+
window.removeEventListener('storage', handleStorageChange);
|
|
410
|
+
window.removeEventListener('message', handleMessage);
|
|
411
|
+
};
|
|
412
|
+
// The connecting key stays until the connection exists, so "Connecting..."
|
|
413
|
+
// gives way to Connected, never back to Connect while the connection and
|
|
414
|
+
// its refetch settle.
|
|
415
|
+
const cleanup = () => {
|
|
416
|
+
if (cleanedUp)
|
|
417
|
+
return;
|
|
418
|
+
cleanedUp = true;
|
|
419
|
+
oauthCleanupsRef.current.delete(cleanup);
|
|
420
|
+
signInsInFlightRef.current.delete(serviceKey);
|
|
421
|
+
stopListening();
|
|
422
|
+
setConnectingServiceIds((prev) => {
|
|
423
|
+
const next = new Set(prev);
|
|
424
|
+
next.delete(serviceKey);
|
|
425
|
+
return next;
|
|
426
|
+
});
|
|
427
|
+
removePendingOAuthServer();
|
|
428
|
+
isCreatingConnection = false;
|
|
429
|
+
};
|
|
430
|
+
oauthCleanupsRef.current.add(cleanup);
|
|
431
|
+
try {
|
|
432
|
+
const result = await startOAuthFlow({
|
|
433
|
+
org: tenantKey,
|
|
434
|
+
userId: username,
|
|
435
|
+
provider,
|
|
436
|
+
service: name,
|
|
437
|
+
}).unwrap();
|
|
438
|
+
if (!result.auth_url)
|
|
439
|
+
throw new Error('No authorization URL returned');
|
|
440
|
+
// Bail if the flow was already torn down while we awaited the auth URL
|
|
441
|
+
// (e.g. the component unmounted). `cleanup` has run and been dropped
|
|
442
|
+
// from `oauthCleanupsRef`, so anything registered past this point —
|
|
443
|
+
// listeners, intervals, timers — would leak with nothing left to
|
|
444
|
+
// remove it.
|
|
445
|
+
if (cleanedUp) {
|
|
446
|
+
closeTab();
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const authUrl = result.auth_url;
|
|
450
|
+
const popup = tab !== null && tab !== void 0 ? tab : window.open(authUrl, '_blank');
|
|
451
|
+
// A tab closed while the address was on its way means the sign-in was
|
|
452
|
+
// given up; the closed-tab watcher below ends the flow.
|
|
453
|
+
if (tab && !tab.closed)
|
|
454
|
+
tab.location.href = authUrl;
|
|
455
|
+
const doCreateConnection = async (connectedServiceId) => {
|
|
456
|
+
creating = true;
|
|
457
|
+
try {
|
|
458
|
+
const result = await mcpConnectorHelpers.createOAuthConnection({
|
|
459
|
+
connectedServiceId,
|
|
460
|
+
isCreatingConnection,
|
|
461
|
+
createMCPServerConnection,
|
|
462
|
+
tenantKey,
|
|
463
|
+
username,
|
|
464
|
+
serverId,
|
|
465
|
+
authScope,
|
|
466
|
+
mentorId,
|
|
467
|
+
oauthDisplayName,
|
|
468
|
+
refetchFeatured: refetchServers,
|
|
469
|
+
refetchMy: skipRefetch,
|
|
470
|
+
refetchConnected,
|
|
471
|
+
refetchMCPServerConnections,
|
|
472
|
+
refetchMentorSettings,
|
|
473
|
+
softCreateFailureMessage,
|
|
474
|
+
successMessage: t('mcpTabConnectorManagementContent.connectedSuccessfully', {
|
|
475
|
+
name: oauthDisplayName,
|
|
476
|
+
}),
|
|
477
|
+
formatErrorMessage: (detail) => t('mcpTabConnectorManagementContent.failedToCreateConnection', {
|
|
478
|
+
detail: detail || t('mcpTabConnectorManagementContent.unknownError'),
|
|
479
|
+
}),
|
|
480
|
+
},
|
|
481
|
+
/* istanbul ignore next -- @preserve success callback, tested via createOAuthConnection */
|
|
482
|
+
() => {
|
|
483
|
+
isCreatingConnection = false;
|
|
484
|
+
},
|
|
485
|
+
/* istanbul ignore next -- @preserve error callback, tested via createOAuthConnection */
|
|
486
|
+
() => {
|
|
487
|
+
isCreatingConnection = false;
|
|
488
|
+
});
|
|
489
|
+
if (result) {
|
|
490
|
+
isCreatingConnection = true;
|
|
491
|
+
}
|
|
492
|
+
return result;
|
|
493
|
+
}
|
|
494
|
+
finally {
|
|
495
|
+
creating = false;
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
const checkConnection = async () => {
|
|
499
|
+
const earlyCheck = mcpConnectorHelpers.determineCheckConnectionAction(isCreatingConnection, null, false, pollCount, maxPolls);
|
|
500
|
+
if (earlyCheck.action === 'skip')
|
|
501
|
+
return;
|
|
502
|
+
try {
|
|
503
|
+
const connectedServiceId = await mcpConnectorHelpers.checkOAuthConnectionComplete(refetchConnected, provider, name);
|
|
504
|
+
/* istanbul ignore next -- @preserve callback wrapper, logic tested via helper */
|
|
505
|
+
if (connectedServiceId) {
|
|
506
|
+
const result = await mcpConnectorHelpers.executeConnectionFoundCleanup({
|
|
507
|
+
connectedServiceId,
|
|
508
|
+
doCreateConnection,
|
|
509
|
+
pollCount,
|
|
510
|
+
maxPolls,
|
|
511
|
+
cleanup,
|
|
512
|
+
});
|
|
513
|
+
if (result.cleaned) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
await refetchServers();
|
|
518
|
+
pollCount++;
|
|
519
|
+
mcpConnectorHelpers.executeMaxPollsCleanup({ pollCount, maxPolls, cleanup });
|
|
520
|
+
}
|
|
521
|
+
catch (_a) {
|
|
522
|
+
// The interval retries on its next tick.
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
handleFocus = () => checkConnection();
|
|
526
|
+
/* istanbul ignore next -- @preserve callback wrapper, logic tested via helper */
|
|
527
|
+
handleMessage = async (event) => {
|
|
528
|
+
const { executed } = await mcpConnectorHelpers.executeHandleMessageCallback({
|
|
529
|
+
event,
|
|
530
|
+
expectedOrigin: window.location.origin,
|
|
531
|
+
provider,
|
|
532
|
+
serviceName: name,
|
|
533
|
+
// Stop listening at once, but keep the connecting key until the create settles.
|
|
534
|
+
cleanup: stopListening,
|
|
535
|
+
doCreateConnection,
|
|
536
|
+
});
|
|
537
|
+
if (executed)
|
|
538
|
+
cleanup();
|
|
539
|
+
};
|
|
540
|
+
handleStorageChange = async (e) => {
|
|
541
|
+
const result = mcpConnectorHelpers.processOAuthStorageEvent(e, provider, name);
|
|
542
|
+
if (result) {
|
|
543
|
+
if (result.isMatch) {
|
|
544
|
+
stopListening();
|
|
545
|
+
await doCreateConnection(result.connectedServiceId);
|
|
546
|
+
cleanup();
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
await checkConnection();
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
else if (e.key === 'oauth_connection_complete') {
|
|
553
|
+
/* istanbul ignore next */
|
|
554
|
+
await checkConnection();
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
window.addEventListener('storage', handleStorageChange);
|
|
558
|
+
window.addEventListener('message', handleMessage);
|
|
559
|
+
window.addEventListener('focus', handleFocus);
|
|
560
|
+
pollInterval = setInterval(checkConnection, 5000);
|
|
561
|
+
setTimeout(cleanup, 5 * 60 * 1000);
|
|
562
|
+
if (popup) {
|
|
563
|
+
popupCheckInterval = setInterval(() => {
|
|
564
|
+
if (popup.closed) {
|
|
565
|
+
if (popupCheckInterval)
|
|
566
|
+
clearInterval(popupCheckInterval);
|
|
567
|
+
popupCheckInterval = null;
|
|
568
|
+
// Grace period for OAuth callback/redirect to complete
|
|
569
|
+
setTimeout(() => {
|
|
570
|
+
if (!isCreatingConnection && !creating) {
|
|
571
|
+
cleanup();
|
|
572
|
+
}
|
|
573
|
+
}, 10000);
|
|
574
|
+
}
|
|
575
|
+
}, 2000);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
closeTab();
|
|
580
|
+
// 400 from the start endpoint: no sign-in app for this organization.
|
|
581
|
+
const status = error === null || error === void 0 ? void 0 : error.status;
|
|
582
|
+
if (status === 400 && (onConnectNotConfigured === null || onConnectNotConfigured === void 0 ? void 0 : onConnectNotConfigured(server))) {
|
|
583
|
+
cleanup();
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
sonner.toast.error(t('mcpTabConnectorManagementContent.failedToConnect', {
|
|
587
|
+
name: oauthData.display_name || name,
|
|
588
|
+
}));
|
|
589
|
+
cleanup();
|
|
590
|
+
}
|
|
591
|
+
}, [
|
|
592
|
+
tenantKey,
|
|
593
|
+
username,
|
|
594
|
+
onConnectNotConfigured,
|
|
595
|
+
startOAuthFlow,
|
|
596
|
+
createMCPServerConnection,
|
|
597
|
+
refetchServers,
|
|
598
|
+
refetchConnected,
|
|
599
|
+
refetchMCPServerConnections,
|
|
600
|
+
refetchMentorSettings,
|
|
601
|
+
mentorId,
|
|
602
|
+
softUserScopeConnectFailure,
|
|
603
|
+
setPendingOAuthServer,
|
|
604
|
+
removePendingOAuthServer,
|
|
605
|
+
]);
|
|
606
|
+
const handleDisconnectOAuth = React.useCallback(async (server, { detachFromAgent = true, connectedServiceId: signedInServiceId = null, } = {}) => {
|
|
607
|
+
var _a, _b, _c;
|
|
608
|
+
const validation = mcpConnectorHelpers.validateDisconnectOAuthParams(tenantKey, username, (_a = getConnectedServiceId(server)) !== null && _a !== void 0 ? _a : signedInServiceId);
|
|
609
|
+
/* istanbul ignore next -- @preserve validation error branch */
|
|
610
|
+
if (!validation.isValid) {
|
|
611
|
+
sonner.toast.error(validation.error || 'Validation failed');
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
const connectedServiceId = validation.connectedServiceId;
|
|
615
|
+
setDisconnectingServiceIds((prev) => new Set(prev).add(server.id));
|
|
616
|
+
try {
|
|
617
|
+
// Delete the sign-in first: detaching changes the agent for everyone, so it must
|
|
618
|
+
// not happen when the sign-in could not be removed.
|
|
619
|
+
await disconnectService({
|
|
620
|
+
org: tenantKey,
|
|
621
|
+
userId: username,
|
|
622
|
+
id: connectedServiceId,
|
|
623
|
+
}).unwrap();
|
|
624
|
+
if (detachFromAgent) {
|
|
625
|
+
try {
|
|
626
|
+
await detachServerFromAgent(server.id);
|
|
627
|
+
}
|
|
628
|
+
catch (error) {
|
|
629
|
+
const err = error;
|
|
630
|
+
sonner.toast.error(t('mcpTabConnectorManagementContent.failedToDeactivate', {
|
|
631
|
+
serverName: server.name,
|
|
632
|
+
msg: ((_b = err === null || err === void 0 ? void 0 : err.data) === null || _b === void 0 ? void 0 : _b.detail) || ((_c = err === null || err === void 0 ? void 0 : err.data) === null || _c === void 0 ? void 0 : _c.error) || (err === null || err === void 0 ? void 0 : err.message) || '',
|
|
633
|
+
}));
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
await Promise.all([
|
|
637
|
+
refetchServers(),
|
|
638
|
+
refetchConnected(),
|
|
639
|
+
refetchMCPServerConnections(),
|
|
640
|
+
refetchMentorSettings(),
|
|
641
|
+
]);
|
|
642
|
+
sonner.toast.success(t('mcpTabConnectorManagementContent.serviceDisconnected'));
|
|
643
|
+
}
|
|
644
|
+
catch (_d) {
|
|
645
|
+
sonner.toast.error(t('mcpTabConnectorManagementContent.failedToDisconnect'));
|
|
646
|
+
}
|
|
647
|
+
finally {
|
|
648
|
+
setDisconnectingServiceIds((prev) => {
|
|
649
|
+
const next = new Set(prev);
|
|
650
|
+
next.delete(server.id);
|
|
651
|
+
return next;
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
}, [
|
|
655
|
+
tenantKey,
|
|
656
|
+
username,
|
|
657
|
+
getConnectedServiceId,
|
|
658
|
+
detachServerFromAgent,
|
|
659
|
+
disconnectService,
|
|
660
|
+
refetchServers,
|
|
661
|
+
refetchConnected,
|
|
662
|
+
refetchMCPServerConnections,
|
|
663
|
+
refetchMentorSettings,
|
|
664
|
+
]);
|
|
665
|
+
/** Deletes the server, then removes it from the agent. Resolves true once the server is deleted. */
|
|
666
|
+
const deleteServer = React.useCallback(async (serverId, serverName) => {
|
|
667
|
+
if (!tenantKey || !username) {
|
|
668
|
+
sonner.toast.error(t('mcpTabConnectorManagementContent.missingRequiredParams'));
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
setDeletingServerId(serverId);
|
|
672
|
+
try {
|
|
673
|
+
await deleteMCPServer({
|
|
674
|
+
id: serverId,
|
|
675
|
+
org: tenantKey,
|
|
676
|
+
userId: username,
|
|
677
|
+
}).unwrap();
|
|
678
|
+
sonner.toast.success(t('mcpTabConnectorManagementContent.connectorRemoved', { serverName }));
|
|
679
|
+
await refetchServers();
|
|
680
|
+
// Without readable attachments activeMcpServerIds is empty, and writing it
|
|
681
|
+
// back would detach every server from the agent.
|
|
682
|
+
if (attachmentsKnown) {
|
|
683
|
+
try {
|
|
684
|
+
await detachServerFromAgent(serverId);
|
|
685
|
+
await refetchMentorSettings();
|
|
686
|
+
}
|
|
687
|
+
catch (_a) {
|
|
688
|
+
sonner.toast.warning(t('mcpTabConnectorManagementContent.connectorDeletedStillActive', { serverName }));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return true;
|
|
692
|
+
}
|
|
693
|
+
catch (_b) {
|
|
694
|
+
sonner.toast.error(t('mcpTabConnectorManagementContent.failedToRemoveConnector', { serverName }));
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
finally {
|
|
698
|
+
setDeletingServerId(null);
|
|
699
|
+
}
|
|
700
|
+
}, [
|
|
701
|
+
tenantKey,
|
|
702
|
+
username,
|
|
703
|
+
attachmentsKnown,
|
|
704
|
+
deleteMCPServer,
|
|
705
|
+
detachServerFromAgent,
|
|
706
|
+
refetchServers,
|
|
707
|
+
refetchMentorSettings,
|
|
708
|
+
t,
|
|
709
|
+
]);
|
|
710
|
+
const refetchAfterOAuth = React.useCallback(async () => {
|
|
711
|
+
await Promise.all([refetchServers(), refetchMCPServerConnections(), refetchMentorSettings()]);
|
|
712
|
+
}, [refetchServers, refetchMCPServerConnections, refetchMentorSettings]);
|
|
713
|
+
return {
|
|
714
|
+
mentorSettings,
|
|
715
|
+
isSettingsLoading,
|
|
716
|
+
attachmentsKnown,
|
|
717
|
+
activeMcpServerIds,
|
|
718
|
+
currentToolSlugs,
|
|
719
|
+
fieldPermissions,
|
|
720
|
+
isOAuthServerConnected,
|
|
721
|
+
hasConnectionForScope,
|
|
722
|
+
getConnectedServiceId,
|
|
723
|
+
getSignedInServiceId,
|
|
724
|
+
ownsConnectedService,
|
|
725
|
+
getConnectionId,
|
|
726
|
+
updateMCPServers,
|
|
727
|
+
handleToggleConnector,
|
|
728
|
+
saveConnector,
|
|
729
|
+
deleteServer,
|
|
730
|
+
handleConnectOAuth,
|
|
731
|
+
handleDisconnectOAuth,
|
|
732
|
+
refetchAfterOAuth,
|
|
733
|
+
refetchMentorSettings,
|
|
734
|
+
refetchConnected,
|
|
735
|
+
refetchMCPServerConnections,
|
|
736
|
+
togglingServerIds,
|
|
737
|
+
connectingServiceIds,
|
|
738
|
+
disconnectingServiceIds,
|
|
739
|
+
deletingServerId,
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
exports.useMcpServerActions = useMcpServerActions;
|
|
744
|
+
//# sourceMappingURL=use-mcp-server-actions.js.map
|