@stigmer/react 3.1.6 → 3.1.7

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.
Files changed (30) hide show
  1. package/mcp-server/McpServerConnectDialog.d.ts.map +1 -1
  2. package/mcp-server/McpServerConnectDialog.js +7 -3
  3. package/mcp-server/McpServerConnectDialog.js.map +1 -1
  4. package/mcp-server/McpServerDetailView.d.ts.map +1 -1
  5. package/mcp-server/McpServerDetailView.js +6 -3
  6. package/mcp-server/McpServerDetailView.js.map +1 -1
  7. package/mcp-server/McpServerPicker.d.ts.map +1 -1
  8. package/mcp-server/McpServerPicker.js +3 -1
  9. package/mcp-server/McpServerPicker.js.map +1 -1
  10. package/mcp-server/OAuthRequiredNotice.d.ts +29 -0
  11. package/mcp-server/OAuthRequiredNotice.d.ts.map +1 -0
  12. package/mcp-server/OAuthRequiredNotice.js +29 -0
  13. package/mcp-server/OAuthRequiredNotice.js.map +1 -0
  14. package/mcp-server/StdioSandboxNotice.d.ts +43 -0
  15. package/mcp-server/StdioSandboxNotice.d.ts.map +1 -0
  16. package/mcp-server/StdioSandboxNotice.js +45 -0
  17. package/mcp-server/StdioSandboxNotice.js.map +1 -0
  18. package/mcp-server/useMcpServerCredentials.d.ts +12 -0
  19. package/mcp-server/useMcpServerCredentials.d.ts.map +1 -1
  20. package/mcp-server/useMcpServerCredentials.js +12 -4
  21. package/mcp-server/useMcpServerCredentials.js.map +1 -1
  22. package/package.json +4 -4
  23. package/src/mcp-server/McpServerConnectDialog.tsx +23 -11
  24. package/src/mcp-server/McpServerDetailView.tsx +22 -9
  25. package/src/mcp-server/McpServerPicker.tsx +12 -9
  26. package/src/mcp-server/OAuthRequiredNotice.tsx +77 -0
  27. package/src/mcp-server/StdioSandboxNotice.tsx +98 -0
  28. package/src/mcp-server/__tests__/OAuthRequiredNotice.test.tsx +25 -0
  29. package/src/mcp-server/__tests__/StdioSandboxNotice.test.tsx +71 -0
  30. package/src/mcp-server/useMcpServerCredentials.ts +25 -4
@@ -0,0 +1,71 @@
1
+ import { describe, it, expect, afterEach } from "vitest";
2
+ import { render, screen, cleanup } from "@testing-library/react";
3
+ import type { ReactNode } from "react";
4
+ import type { McpServerSpec } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/spec_pb";
5
+ import { DeploymentModeContext } from "../../deployment-mode";
6
+ import { StdioSandboxNotice, isStdioInCloud } from "../StdioSandboxNotice";
7
+
8
+ type McpServerType = McpServerSpec["serverType"];
9
+
10
+ const STDIO = { case: "stdio" } as McpServerType;
11
+ const HTTP = { case: "http" } as McpServerType;
12
+ const UNSET = { case: undefined } as McpServerType;
13
+
14
+ afterEach(cleanup);
15
+
16
+ describe("isStdioInCloud", () => {
17
+ it("is true only for stdio transport in cloud mode", () => {
18
+ expect(isStdioInCloud("cloud", STDIO)).toBe(true);
19
+ });
20
+
21
+ it("is false for stdio transport in local mode", () => {
22
+ expect(isStdioInCloud("local", STDIO)).toBe(false);
23
+ });
24
+
25
+ it("is false for http transport in cloud mode", () => {
26
+ expect(isStdioInCloud("cloud", HTTP)).toBe(false);
27
+ });
28
+
29
+ it("is false when the transport oneof is unset", () => {
30
+ expect(isStdioInCloud("cloud", UNSET)).toBe(false);
31
+ });
32
+
33
+ it("is false when serverType is undefined", () => {
34
+ expect(isStdioInCloud("cloud", undefined)).toBe(false);
35
+ });
36
+ });
37
+
38
+ function withMode(mode: "cloud" | "local", children: ReactNode) {
39
+ return (
40
+ <DeploymentModeContext.Provider value={mode}>
41
+ {children}
42
+ </DeploymentModeContext.Provider>
43
+ );
44
+ }
45
+
46
+ describe("StdioSandboxNotice", () => {
47
+ it("renders the sandbox-isolation caveat for stdio in cloud", () => {
48
+ render(withMode("cloud", <StdioSandboxNotice serverType={STDIO} />));
49
+ expect(screen.getByRole("status")).toBeTruthy();
50
+ expect(screen.getByText(/isolated cloud sandbox/i)).toBeTruthy();
51
+ });
52
+
53
+ it("does not suggest the CLI (avoids inaccurate guidance)", () => {
54
+ render(withMode("cloud", <StdioSandboxNotice serverType={STDIO} />));
55
+ expect(screen.queryByText(/stigmer connect/i)).toBeNull();
56
+ });
57
+
58
+ it("renders nothing for http servers in cloud", () => {
59
+ const { container } = render(
60
+ withMode("cloud", <StdioSandboxNotice serverType={HTTP} />),
61
+ );
62
+ expect(container.firstChild).toBeNull();
63
+ });
64
+
65
+ it("renders nothing for stdio servers in local mode", () => {
66
+ const { container } = render(
67
+ withMode("local", <StdioSandboxNotice serverType={STDIO} />),
68
+ );
69
+ expect(container.firstChild).toBeNull();
70
+ });
71
+ });
@@ -153,6 +153,18 @@ export interface UseMcpServerCredentialsReturn {
153
153
  * link (when vendor approval is granted).
154
154
  */
155
155
  readonly canBringOwnApp: boolean;
156
+ /**
157
+ * `true` when a manually-entered static token is a valid way to
158
+ * authenticate this server. `false` for `oauth_only` servers whose
159
+ * hosted endpoint rejects static tokens (`spec.auth.oauth_only`), where
160
+ * OAuth is the sole credential path.
161
+ *
162
+ * Connect surfaces use this to decide whether to offer the "enter token
163
+ * manually" affordance — offering it on an `oauth_only` server would send
164
+ * the user down a path that cannot succeed. Always `true` for manual-only
165
+ * and PAT-capable servers.
166
+ */
167
+ readonly manualEntrySupported: boolean;
156
168
  /**
157
169
  * When `true`, the user has opted to bypass OAuth and enter the
158
170
  * `target_env_var` token manually. In this state:
@@ -251,6 +263,14 @@ export function useMcpServerCredentials(
251
263
  const oauthTargetEnvVar = auth?.targetEnvVar || null;
252
264
  const tokenLifetimeHint = auth?.tokenLifetimeHint || null;
253
265
 
266
+ // A static token is a valid credential for every server except those whose
267
+ // endpoint declares it rejects them (`oauth_only`). This is the single source
268
+ // of truth the connect surfaces consult before offering manual entry.
269
+ const manualEntrySupported = !auth?.oauthOnly;
270
+ // Defensively force manual override off for oauth_only servers so no surface
271
+ // can enter a dead-end manual-entry state, even if a stale toggle is set.
272
+ const effectiveManualOverride = manualOverride && manualEntrySupported;
273
+
254
274
  const oauthStatus = mcpServer?.status?.oauthStatus;
255
275
  const isVendorApprovalPending =
256
276
  authMode === "oauth" &&
@@ -298,15 +318,15 @@ export function useMcpServerCredentials(
298
318
  );
299
319
 
300
320
  const missingVariables = useMemo(() => {
301
- if (!oauthTargetEnvVar || manualOverride) return requiredMissing;
321
+ if (!oauthTargetEnvVar || effectiveManualOverride) return requiredMissing;
302
322
  return requiredMissing.filter((v) => v.key !== oauthTargetEnvVar);
303
- }, [requiredMissing, oauthTargetEnvVar, manualOverride]);
323
+ }, [requiredMissing, oauthTargetEnvVar, effectiveManualOverride]);
304
324
 
305
325
  const isReady =
306
326
  !personalEnv.isLoading &&
307
327
  !grantStatus.isLoading &&
308
328
  missingVariables.length === 0 &&
309
- (authMode === "manual" || manualOverride || isOAuthConnected);
329
+ (authMode === "manual" || effectiveManualOverride || isOAuthConnected);
310
330
 
311
331
  const saveCredentials = useCallback(
312
332
  async (values: Record<string, EnvVarInput>): Promise<void> => {
@@ -335,6 +355,7 @@ export function useMcpServerCredentials(
335
355
  effectiveOAuthSource,
336
356
  isOrgOAuthApp,
337
357
  canBringOwnApp,
358
+ manualEntrySupported,
338
359
  missingVariables,
339
360
  isReady,
340
361
  isLoading: personalEnv.isLoading || grantStatus.isLoading,
@@ -342,7 +363,7 @@ export function useMcpServerCredentials(
342
363
  saveCredentials,
343
364
  isSaving: personalEnv.isMutating,
344
365
  refetch,
345
- manualOverride,
366
+ manualOverride: effectiveManualOverride,
346
367
  setManualOverride,
347
368
  };
348
369
  }