@stigmer/react 0.0.77 → 0.0.78
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/index.d.ts +2 -2
- package/index.d.ts.map +1 -1
- package/index.js +2 -2
- package/index.js.map +1 -1
- package/mcp-server/McpServerConfigPanel.d.ts +28 -1
- package/mcp-server/McpServerConfigPanel.d.ts.map +1 -1
- package/mcp-server/McpServerConfigPanel.js +23 -2
- package/mcp-server/McpServerConfigPanel.js.map +1 -1
- package/mcp-server/McpServerDetailView.d.ts.map +1 -1
- package/mcp-server/McpServerDetailView.js +92 -13
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/mcp-server/McpServerPicker.d.ts.map +1 -1
- package/mcp-server/McpServerPicker.js +34 -2
- package/mcp-server/McpServerPicker.js.map +1 -1
- package/mcp-server/OAuthCallbackHandler.d.ts +52 -0
- package/mcp-server/OAuthCallbackHandler.d.ts.map +1 -0
- package/mcp-server/OAuthCallbackHandler.js +98 -0
- package/mcp-server/OAuthCallbackHandler.js.map +1 -0
- package/mcp-server/index.d.ts +6 -2
- package/mcp-server/index.d.ts.map +1 -1
- package/mcp-server/index.js +2 -0
- package/mcp-server/index.js.map +1 -1
- package/mcp-server/useMcpServerCredentials.d.ts +59 -7
- package/mcp-server/useMcpServerCredentials.d.ts.map +1 -1
- package/mcp-server/useMcpServerCredentials.js +37 -10
- package/mcp-server/useMcpServerCredentials.js.map +1 -1
- package/mcp-server/useMcpServerOAuthConnect.d.ts +81 -0
- package/mcp-server/useMcpServerOAuthConnect.d.ts.map +1 -0
- package/mcp-server/useMcpServerOAuthConnect.js +187 -0
- package/mcp-server/useMcpServerOAuthConnect.js.map +1 -0
- package/package.json +4 -4
- package/src/index.ts +9 -1
- package/src/mcp-server/McpServerConfigPanel.tsx +153 -3
- package/src/mcp-server/McpServerDetailView.tsx +231 -172
- package/src/mcp-server/McpServerPicker.tsx +40 -2
- package/src/mcp-server/OAuthCallbackHandler.tsx +237 -0
- package/src/mcp-server/index.ts +17 -1
- package/src/mcp-server/useMcpServerCredentials.ts +86 -13
- package/src/mcp-server/useMcpServerOAuthConnect.ts +298 -0
- package/styles.css +1 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { OAUTH_CALLBACK_MESSAGE_TYPE } from "./useMcpServerOAuthConnect";
|
|
6
|
+
import type { OAuthCallbackMessage } from "./useMcpServerOAuthConnect";
|
|
7
|
+
|
|
8
|
+
/** Parameters extracted from the OAuth callback URL. */
|
|
9
|
+
export interface OAuthCallbackParams {
|
|
10
|
+
readonly code: string;
|
|
11
|
+
readonly state: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Props for {@link OAuthCallbackHandler}. */
|
|
15
|
+
export interface OAuthCallbackHandlerProps {
|
|
16
|
+
/**
|
|
17
|
+
* Fallback callback invoked when the page was opened as a regular
|
|
18
|
+
* navigation (no `window.opener`), meaning the popup `postMessage`
|
|
19
|
+
* path cannot be used.
|
|
20
|
+
*
|
|
21
|
+
* Receives the extracted `code` and `state` so the host application
|
|
22
|
+
* can complete the OAuth flow via its own routing logic.
|
|
23
|
+
*
|
|
24
|
+
* When omitted and `window.opener` is unavailable, the component
|
|
25
|
+
* renders an instructional message asking the user to close the tab.
|
|
26
|
+
*/
|
|
27
|
+
readonly onFallback?: (params: OAuthCallbackParams) => void;
|
|
28
|
+
/** Additional CSS classes for the root container. */
|
|
29
|
+
readonly className?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Lightweight component for OAuth callback pages.
|
|
34
|
+
*
|
|
35
|
+
* Render this component at the URL configured as your OAuth redirect
|
|
36
|
+
* URI (`STIGMER_OAUTH_REDIRECT_URI`). It extracts the `code` and
|
|
37
|
+
* `state` query parameters from the current URL, posts them back to
|
|
38
|
+
* the opener window via `window.postMessage`, and closes the popup.
|
|
39
|
+
*
|
|
40
|
+
* The component handles three scenarios:
|
|
41
|
+
* 1. **Popup with opener** (primary): posts the message and closes.
|
|
42
|
+
* 2. **No opener, `onFallback` provided**: calls the fallback with
|
|
43
|
+
* the extracted parameters so the host app can handle them.
|
|
44
|
+
* 3. **No opener, no fallback**: shows a message asking the user to
|
|
45
|
+
* return to the main window.
|
|
46
|
+
*
|
|
47
|
+
* Platform builders create a route in their application that renders
|
|
48
|
+
* this component:
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```tsx
|
|
52
|
+
* // app/auth/oauth/callback/page.tsx (Next.js)
|
|
53
|
+
* import { OAuthCallbackHandler } from "@stigmer/react";
|
|
54
|
+
*
|
|
55
|
+
* export default function OAuthCallbackPage() {
|
|
56
|
+
* return <OAuthCallbackHandler />;
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function OAuthCallbackHandler({
|
|
61
|
+
onFallback,
|
|
62
|
+
className,
|
|
63
|
+
}: OAuthCallbackHandlerProps) {
|
|
64
|
+
const [status, setStatus] = useState<"processing" | "done" | "no-opener" | "error">("processing");
|
|
65
|
+
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
66
|
+
const didRun = useRef(false);
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (didRun.current) return;
|
|
70
|
+
didRun.current = true;
|
|
71
|
+
|
|
72
|
+
const params = new URLSearchParams(window.location.search);
|
|
73
|
+
const code = params.get("code");
|
|
74
|
+
const state = params.get("state");
|
|
75
|
+
|
|
76
|
+
const oauthError = params.get("error");
|
|
77
|
+
if (oauthError) {
|
|
78
|
+
const description = params.get("error_description") || oauthError;
|
|
79
|
+
setErrorMessage(`Authentication failed: ${description}`);
|
|
80
|
+
setStatus("error");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!code || !state) {
|
|
85
|
+
setErrorMessage(
|
|
86
|
+
"Missing authorization code or state parameter. " +
|
|
87
|
+
"This page should only be reached via an OAuth redirect.",
|
|
88
|
+
);
|
|
89
|
+
setStatus("error");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const opener = window.opener as Window | null;
|
|
94
|
+
if (opener && !opener.closed) {
|
|
95
|
+
const message: OAuthCallbackMessage = {
|
|
96
|
+
type: OAUTH_CALLBACK_MESSAGE_TYPE,
|
|
97
|
+
code,
|
|
98
|
+
state,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
opener.postMessage(message, window.location.origin);
|
|
103
|
+
setStatus("done");
|
|
104
|
+
window.close();
|
|
105
|
+
} catch {
|
|
106
|
+
setErrorMessage(
|
|
107
|
+
"Could not communicate with the parent window. " +
|
|
108
|
+
"Please close this tab and try again.",
|
|
109
|
+
);
|
|
110
|
+
setStatus("error");
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (onFallback) {
|
|
116
|
+
onFallback({ code, state });
|
|
117
|
+
setStatus("done");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
setStatus("no-opener");
|
|
122
|
+
}, [onFallback]);
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div
|
|
126
|
+
className={cn(
|
|
127
|
+
"flex min-h-[200px] items-center justify-center p-8",
|
|
128
|
+
className,
|
|
129
|
+
)}
|
|
130
|
+
>
|
|
131
|
+
<div className="max-w-sm text-center">
|
|
132
|
+
{status === "processing" && (
|
|
133
|
+
<>
|
|
134
|
+
<Spinner />
|
|
135
|
+
<p className="mt-3 text-sm text-muted-foreground">
|
|
136
|
+
Completing authentication...
|
|
137
|
+
</p>
|
|
138
|
+
</>
|
|
139
|
+
)}
|
|
140
|
+
|
|
141
|
+
{status === "done" && (
|
|
142
|
+
<p className="text-sm text-muted-foreground">
|
|
143
|
+
Authentication complete. You can close this window.
|
|
144
|
+
</p>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
{status === "no-opener" && (
|
|
148
|
+
<>
|
|
149
|
+
<CheckIcon />
|
|
150
|
+
<p className="mt-3 text-sm font-medium text-foreground">
|
|
151
|
+
Authentication successful
|
|
152
|
+
</p>
|
|
153
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
154
|
+
Please close this tab and return to the application to continue.
|
|
155
|
+
</p>
|
|
156
|
+
</>
|
|
157
|
+
)}
|
|
158
|
+
|
|
159
|
+
{status === "error" && (
|
|
160
|
+
<>
|
|
161
|
+
<WarningIcon />
|
|
162
|
+
<p className="mt-3 text-sm font-medium text-destructive">
|
|
163
|
+
Authentication failed
|
|
164
|
+
</p>
|
|
165
|
+
{errorMessage && (
|
|
166
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
167
|
+
{errorMessage}
|
|
168
|
+
</p>
|
|
169
|
+
)}
|
|
170
|
+
</>
|
|
171
|
+
)}
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
// Icons -- inline SVGs (no external icon dependency, matches SDK pattern)
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
function Spinner() {
|
|
182
|
+
return (
|
|
183
|
+
<svg
|
|
184
|
+
width="24"
|
|
185
|
+
height="24"
|
|
186
|
+
viewBox="0 0 16 16"
|
|
187
|
+
fill="none"
|
|
188
|
+
stroke="currentColor"
|
|
189
|
+
strokeWidth="2"
|
|
190
|
+
strokeLinecap="round"
|
|
191
|
+
className="mx-auto animate-spin text-muted-foreground"
|
|
192
|
+
aria-hidden="true"
|
|
193
|
+
>
|
|
194
|
+
<path d="M8 2a6 6 0 1 0 6 6" />
|
|
195
|
+
</svg>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function CheckIcon() {
|
|
200
|
+
return (
|
|
201
|
+
<svg
|
|
202
|
+
width="24"
|
|
203
|
+
height="24"
|
|
204
|
+
viewBox="0 0 16 16"
|
|
205
|
+
fill="none"
|
|
206
|
+
stroke="currentColor"
|
|
207
|
+
strokeWidth="2"
|
|
208
|
+
strokeLinecap="round"
|
|
209
|
+
strokeLinejoin="round"
|
|
210
|
+
className="mx-auto text-success"
|
|
211
|
+
aria-hidden="true"
|
|
212
|
+
>
|
|
213
|
+
<path d="m3 8.5 3.5 3.5 6.5-8" />
|
|
214
|
+
</svg>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function WarningIcon() {
|
|
219
|
+
return (
|
|
220
|
+
<svg
|
|
221
|
+
width="24"
|
|
222
|
+
height="24"
|
|
223
|
+
viewBox="0 0 16 16"
|
|
224
|
+
fill="none"
|
|
225
|
+
stroke="currentColor"
|
|
226
|
+
strokeWidth="1.5"
|
|
227
|
+
strokeLinecap="round"
|
|
228
|
+
strokeLinejoin="round"
|
|
229
|
+
className="mx-auto text-destructive"
|
|
230
|
+
aria-hidden="true"
|
|
231
|
+
>
|
|
232
|
+
<path d="M8 1.5 1 14h14L8 1.5Z" />
|
|
233
|
+
<path d="M8 6v3.5" />
|
|
234
|
+
<circle cx="8" cy="11.5" r="0.5" fill="currentColor" stroke="none" />
|
|
235
|
+
</svg>
|
|
236
|
+
);
|
|
237
|
+
}
|
package/src/mcp-server/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ export { McpServerConfigPanel } from "./McpServerConfigPanel";
|
|
|
32
32
|
export type {
|
|
33
33
|
McpServerConfigPanelProps,
|
|
34
34
|
McpServerCredentialsProps,
|
|
35
|
+
McpServerOAuthSignInProps,
|
|
35
36
|
} from "./McpServerConfigPanel";
|
|
36
37
|
|
|
37
38
|
export { useMcpServerSetup, toServerKey } from "./useMcpServerSetup";
|
|
@@ -52,5 +53,20 @@ export type {
|
|
|
52
53
|
export { useMcpServerConnect } from "./useMcpServerConnect";
|
|
53
54
|
export type { UseMcpServerConnectReturn } from "./useMcpServerConnect";
|
|
54
55
|
|
|
56
|
+
export { useMcpServerOAuthConnect } from "./useMcpServerOAuthConnect";
|
|
57
|
+
export type {
|
|
58
|
+
UseMcpServerOAuthConnectReturn,
|
|
59
|
+
OAuthConnectPhase,
|
|
60
|
+
} from "./useMcpServerOAuthConnect";
|
|
61
|
+
|
|
62
|
+
export { OAuthCallbackHandler } from "./OAuthCallbackHandler";
|
|
63
|
+
export type {
|
|
64
|
+
OAuthCallbackHandlerProps,
|
|
65
|
+
OAuthCallbackParams,
|
|
66
|
+
} from "./OAuthCallbackHandler";
|
|
67
|
+
|
|
55
68
|
export { useMcpServerCredentials } from "./useMcpServerCredentials";
|
|
56
|
-
export type {
|
|
69
|
+
export type {
|
|
70
|
+
UseMcpServerCredentialsReturn,
|
|
71
|
+
McpServerAuthMode,
|
|
72
|
+
} from "./useMcpServerCredentials";
|
|
@@ -8,17 +8,59 @@ import { diffEnvSpec } from "../environment/diffEnvSpec";
|
|
|
8
8
|
import { SYSTEM_ENV_VAR_KEYS } from "../environment/systemEnvVars";
|
|
9
9
|
import type { EnvVarFormVariable } from "../environment/EnvVarForm";
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Credential acquisition mode for an MCP server.
|
|
13
|
+
*
|
|
14
|
+
* - `"manual"` — all env vars are entered by the user via a form.
|
|
15
|
+
* - `"oauth"` — at least one env var (`target_env_var`) is acquired
|
|
16
|
+
* via OAuth. Additional manual vars may still be required (mixed mode).
|
|
17
|
+
*/
|
|
18
|
+
export type McpServerAuthMode = "manual" | "oauth";
|
|
19
|
+
|
|
11
20
|
/** Return value of {@link useMcpServerCredentials}. */
|
|
12
21
|
export interface UseMcpServerCredentialsReturn {
|
|
22
|
+
/**
|
|
23
|
+
* Credential acquisition mode derived from `spec.auth`.
|
|
24
|
+
*
|
|
25
|
+
* - `"manual"` when `spec.auth` is absent: all env vars are user-entered.
|
|
26
|
+
* - `"oauth"` when `spec.auth` is present: the `target_env_var` is
|
|
27
|
+
* acquired via OAuth. Check {@link missingVariables} for any
|
|
28
|
+
* additional manual vars that are also needed (mixed mode).
|
|
29
|
+
*/
|
|
30
|
+
readonly authMode: McpServerAuthMode;
|
|
31
|
+
/**
|
|
32
|
+
* The env var name managed by OAuth, or `null` when `authMode` is `"manual"`.
|
|
33
|
+
* Corresponds to `spec.auth.target_env_var`.
|
|
34
|
+
*/
|
|
35
|
+
readonly oauthTargetEnvVar: string | null;
|
|
36
|
+
/**
|
|
37
|
+
* `true` when the OAuth-managed env var exists in the personal
|
|
38
|
+
* environment. Always `false` when `authMode` is `"manual"`.
|
|
39
|
+
*/
|
|
40
|
+
readonly isOAuthConnected: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Informational hint about expected token lifetime, or `null`.
|
|
43
|
+
* Sourced from `spec.auth.token_lifetime_hint`.
|
|
44
|
+
*/
|
|
45
|
+
readonly tokenLifetimeHint: string | null;
|
|
13
46
|
/**
|
|
14
47
|
* Variables required by the MCP server that are missing from the
|
|
15
48
|
* user's personal environment. Empty when all variables are present
|
|
16
49
|
* or the server has no `env_spec`.
|
|
17
50
|
*
|
|
51
|
+
* When `authMode` is `"oauth"`, the OAuth-managed `target_env_var`
|
|
52
|
+
* is excluded from this list — it is acquired via the OAuth flow,
|
|
53
|
+
* not via a manual form. Only additional non-OAuth vars appear here.
|
|
54
|
+
*
|
|
18
55
|
* Suitable as direct input to {@link EnvVarForm}.
|
|
19
56
|
*/
|
|
20
57
|
readonly missingVariables: EnvVarFormVariable[];
|
|
21
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* `true` when all required credentials are available — both
|
|
60
|
+
* OAuth-managed and manual variables. For OAuth servers this means
|
|
61
|
+
* the OAuth token is in the personal env AND any additional manual
|
|
62
|
+
* vars are also present.
|
|
63
|
+
*/
|
|
22
64
|
readonly isReady: boolean;
|
|
23
65
|
/** `true` while the personal environment is being fetched. */
|
|
24
66
|
readonly isLoading: boolean;
|
|
@@ -47,6 +89,12 @@ export interface UseMcpServerCredentialsReturn {
|
|
|
47
89
|
* computes the missing set and exposes `saveCredentials` to persist
|
|
48
90
|
* them.
|
|
49
91
|
*
|
|
92
|
+
* **Auth-mode-aware**: when `spec.auth` is configured, the hook
|
|
93
|
+
* identifies the OAuth-managed variable (`target_env_var`) and
|
|
94
|
+
* excludes it from `missingVariables` — that variable is acquired
|
|
95
|
+
* via {@link useMcpServerOAuthConnect}, not a manual form. Additional
|
|
96
|
+
* non-OAuth vars still appear in `missingVariables` (mixed mode).
|
|
97
|
+
*
|
|
50
98
|
* Unlike {@link useMcpServerSetup} which manages multi-server setup
|
|
51
99
|
* for session creation, this hook is scoped to a single server and
|
|
52
100
|
* always persists to the personal environment (no one-time option).
|
|
@@ -55,15 +103,20 @@ export interface UseMcpServerCredentialsReturn {
|
|
|
55
103
|
*
|
|
56
104
|
* @example
|
|
57
105
|
* ```tsx
|
|
58
|
-
* const
|
|
59
|
-
* useMcpServerCredentials("acme", mcpServer);
|
|
106
|
+
* const creds = useMcpServerCredentials("acme", mcpServer);
|
|
60
107
|
*
|
|
61
|
-
*
|
|
108
|
+
* // OAuth server — sign-in button + optional manual form
|
|
109
|
+
* if (creds.authMode === "oauth" && !creds.isOAuthConnected) {
|
|
110
|
+
* return <button onClick={startOAuth}>Sign in</button>;
|
|
111
|
+
* }
|
|
112
|
+
*
|
|
113
|
+
* // Manual vars still needed (mixed mode or manual-only)
|
|
114
|
+
* if (creds.missingVariables.length > 0) {
|
|
62
115
|
* return (
|
|
63
116
|
* <EnvVarForm
|
|
64
|
-
* variables={missingVariables}
|
|
65
|
-
* onSubmit={(values) => saveCredentials(values)}
|
|
66
|
-
* isSubmitting={isSaving}
|
|
117
|
+
* variables={creds.missingVariables}
|
|
118
|
+
* onSubmit={(values) => creds.saveCredentials(values)}
|
|
119
|
+
* isSubmitting={creds.isSaving}
|
|
67
120
|
* hideSaveToggle
|
|
68
121
|
* />
|
|
69
122
|
* );
|
|
@@ -76,21 +129,37 @@ export function useMcpServerCredentials(
|
|
|
76
129
|
): UseMcpServerCredentialsReturn {
|
|
77
130
|
const personalEnv = usePersonalEnvironment(org);
|
|
78
131
|
|
|
79
|
-
const
|
|
132
|
+
const auth = mcpServer?.spec?.auth;
|
|
133
|
+
const authMode: McpServerAuthMode = auth ? "oauth" : "manual";
|
|
134
|
+
const oauthTargetEnvVar = auth?.targetEnvVar || null;
|
|
135
|
+
const tokenLifetimeHint = auth?.tokenLifetimeHint || null;
|
|
136
|
+
|
|
137
|
+
const existingKeys = useMemo(
|
|
138
|
+
() => new Set(Object.keys(personalEnv.environment?.spec?.data ?? {})),
|
|
139
|
+
[personalEnv.environment],
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const isOAuthConnected = authMode === "oauth"
|
|
143
|
+
&& oauthTargetEnvVar !== null
|
|
144
|
+
&& existingKeys.has(oauthTargetEnvVar);
|
|
145
|
+
|
|
146
|
+
const allMissingVariables = useMemo(() => {
|
|
80
147
|
if (!mcpServer) return [];
|
|
81
148
|
const envSpecData = mcpServer.spec?.envSpec?.data;
|
|
82
149
|
if (!envSpecData || Object.keys(envSpecData).length === 0) return [];
|
|
83
150
|
|
|
84
|
-
const existingKeys = new Set(
|
|
85
|
-
Object.keys(personalEnv.environment?.spec?.data ?? {}),
|
|
86
|
-
);
|
|
87
151
|
return diffEnvSpec(envSpecData, existingKeys).filter(
|
|
88
152
|
(v) => !SYSTEM_ENV_VAR_KEYS.has(v.key),
|
|
89
153
|
);
|
|
90
|
-
}, [mcpServer,
|
|
154
|
+
}, [mcpServer, existingKeys]);
|
|
155
|
+
|
|
156
|
+
const missingVariables = useMemo(() => {
|
|
157
|
+
if (!oauthTargetEnvVar) return allMissingVariables;
|
|
158
|
+
return allMissingVariables.filter((v) => v.key !== oauthTargetEnvVar);
|
|
159
|
+
}, [allMissingVariables, oauthTargetEnvVar]);
|
|
91
160
|
|
|
92
161
|
const isReady =
|
|
93
|
-
!personalEnv.isLoading &&
|
|
162
|
+
!personalEnv.isLoading && allMissingVariables.length === 0;
|
|
94
163
|
|
|
95
164
|
const saveCredentials = useCallback(
|
|
96
165
|
async (values: Record<string, EnvVarInput>): Promise<void> => {
|
|
@@ -101,6 +170,10 @@ export function useMcpServerCredentials(
|
|
|
101
170
|
);
|
|
102
171
|
|
|
103
172
|
return {
|
|
173
|
+
authMode,
|
|
174
|
+
oauthTargetEnvVar,
|
|
175
|
+
isOAuthConnected,
|
|
176
|
+
tokenLifetimeHint,
|
|
104
177
|
missingVariables,
|
|
105
178
|
isReady,
|
|
106
179
|
isLoading: personalEnv.isLoading,
|