@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.
- package/mcp-server/McpServerConnectDialog.d.ts.map +1 -1
- package/mcp-server/McpServerConnectDialog.js +7 -3
- package/mcp-server/McpServerConnectDialog.js.map +1 -1
- package/mcp-server/McpServerDetailView.d.ts.map +1 -1
- package/mcp-server/McpServerDetailView.js +6 -3
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/mcp-server/McpServerPicker.d.ts.map +1 -1
- package/mcp-server/McpServerPicker.js +3 -1
- package/mcp-server/McpServerPicker.js.map +1 -1
- package/mcp-server/OAuthRequiredNotice.d.ts +29 -0
- package/mcp-server/OAuthRequiredNotice.d.ts.map +1 -0
- package/mcp-server/OAuthRequiredNotice.js +29 -0
- package/mcp-server/OAuthRequiredNotice.js.map +1 -0
- package/mcp-server/StdioSandboxNotice.d.ts +43 -0
- package/mcp-server/StdioSandboxNotice.d.ts.map +1 -0
- package/mcp-server/StdioSandboxNotice.js +45 -0
- package/mcp-server/StdioSandboxNotice.js.map +1 -0
- package/mcp-server/useMcpServerCredentials.d.ts +12 -0
- package/mcp-server/useMcpServerCredentials.d.ts.map +1 -1
- package/mcp-server/useMcpServerCredentials.js +12 -4
- package/mcp-server/useMcpServerCredentials.js.map +1 -1
- package/package.json +4 -4
- package/src/mcp-server/McpServerConnectDialog.tsx +23 -11
- package/src/mcp-server/McpServerDetailView.tsx +22 -9
- package/src/mcp-server/McpServerPicker.tsx +12 -9
- package/src/mcp-server/OAuthRequiredNotice.tsx +77 -0
- package/src/mcp-server/StdioSandboxNotice.tsx +98 -0
- package/src/mcp-server/__tests__/OAuthRequiredNotice.test.tsx +25 -0
- package/src/mcp-server/__tests__/StdioSandboxNotice.test.tsx +71 -0
- package/src/mcp-server/useMcpServerCredentials.ts +25 -4
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "@stigmer/theme";
|
|
4
|
+
/**
|
|
5
|
+
* Sets accurate expectations for connecting an **OAuth-only** MCP server —
|
|
6
|
+
* one whose hosted endpoint rejects manually-entered static tokens
|
|
7
|
+
* (`spec.auth.oauth_only`, e.g. Notion, Monday).
|
|
8
|
+
*
|
|
9
|
+
* Without this notice a user might expect the (now-hidden) manual token
|
|
10
|
+
* option and wonder why only "Sign in" is offered. It explains, in one line,
|
|
11
|
+
* that OAuth is required and a pasted API token would be rejected — turning a
|
|
12
|
+
* silent constraint into a clear one (Nielsen: visibility of system status).
|
|
13
|
+
*
|
|
14
|
+
* Self-gating: renders `null` unless the server is `oauth_only`. Callers
|
|
15
|
+
* render it unconditionally next to their Connect action, mirroring
|
|
16
|
+
* {@link StdioSandboxNotice}.
|
|
17
|
+
*
|
|
18
|
+
* All visual properties flow through `--stgm-*` design tokens. No
|
|
19
|
+
* Console-specific dependencies — safe for platform-builder embedding.
|
|
20
|
+
*/
|
|
21
|
+
export function OAuthRequiredNotice({ oauthOnly, className, }) {
|
|
22
|
+
if (!oauthOnly)
|
|
23
|
+
return null;
|
|
24
|
+
return (_jsxs("div", { role: "status", className: cn("bg-muted-subtle text-muted-foreground flex items-start gap-2.5 rounded-lg border border-transparent px-4 py-3", className), children: [_jsx(KeyIcon, { className: "mt-0.5 size-4 shrink-0" }), _jsx("p", { className: "text-xs leading-relaxed", children: "This server requires OAuth. Sign in to connect \u2014 its hosted endpoint rejects manually-entered API tokens, so a pasted token won't work here." })] }));
|
|
25
|
+
}
|
|
26
|
+
function KeyIcon({ className }) {
|
|
27
|
+
return (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": "true", children: [_jsx("circle", { cx: "5", cy: "11", r: "2.5" }), _jsx("path", { d: "M6.8 9.2 13 3" }), _jsx("path", { d: "M11 5l1.5 1.5" }), _jsx("path", { d: "M9 7l1.5 1.5" })] }));
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=OAuthRequiredNotice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthRequiredNotice.js","sourceRoot":"","sources":["../../src/mcp-server/OAuthRequiredNotice.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAapC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,SAAS,EACT,SAAS,GACgB;IACzB,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,OAAO,CACL,eACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAE,EAAE,CACX,+GAA+G,EAC/G,SAAS,CACV,aAED,KAAC,OAAO,IAAC,SAAS,EAAC,wBAAwB,GAAG,EAC9C,YAAG,SAAS,EAAC,yBAAyB,kKAIlC,IACA,CACP,CAAC;AACJ,CAAC;AAED,SAAS,OAAO,CAAC,EAAE,SAAS,EAA0B;IACpD,OAAO,CACL,eACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,KAAK,EACjB,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EACtB,SAAS,EAAE,SAAS,iBACR,MAAM,aAElB,iBAAQ,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,KAAK,GAAG,EACjC,eAAM,CAAC,EAAC,eAAe,GAAG,EAC1B,eAAM,CAAC,EAAC,eAAe,GAAG,EAC1B,eAAM,CAAC,EAAC,cAAc,GAAG,IACrB,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { DeploymentMode } from "@stigmer/sdk";
|
|
2
|
+
import type { McpServerSpec } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/spec_pb";
|
|
3
|
+
/** The `server_type` oneof discriminator from an MCP server's spec. */
|
|
4
|
+
type McpServerType = McpServerSpec["serverType"];
|
|
5
|
+
/**
|
|
6
|
+
* Whether an MCP server's transport is stdio while connected to Stigmer Cloud.
|
|
7
|
+
*
|
|
8
|
+
* stdio servers run as a subprocess inside an isolated Daytona sandbox in the
|
|
9
|
+
* cloud — not on the user's own computer. This predicate identifies exactly
|
|
10
|
+
* that combination so the connect UI can set accurate expectations. HTTP
|
|
11
|
+
* servers and any transport in local mode return `false`.
|
|
12
|
+
*
|
|
13
|
+
* Pure and framework-free so it can be unit-tested without rendering.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isStdioInCloud(mode: DeploymentMode, serverType: McpServerType | undefined): boolean;
|
|
16
|
+
/** Props for {@link StdioSandboxNotice}. */
|
|
17
|
+
export interface StdioSandboxNoticeProps {
|
|
18
|
+
/** The MCP server's `spec.serverType` oneof. */
|
|
19
|
+
readonly serverType: McpServerType | undefined;
|
|
20
|
+
/** Additional CSS class names for the root container. */
|
|
21
|
+
readonly className?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sets accurate expectations for connecting an **stdio** MCP server from
|
|
25
|
+
* **Stigmer Cloud**.
|
|
26
|
+
*
|
|
27
|
+
* stdio servers are a supported cloud capability — Stigmer runs them in an
|
|
28
|
+
* isolated Daytona sandbox. But that sandbox is not the user's own machine,
|
|
29
|
+
* so a server that expects access to local files, applications, or a private
|
|
30
|
+
* network (e.g. a filesystem server) won't behave as the user intends. This
|
|
31
|
+
* notice explains that up front; Connect itself stays enabled because
|
|
32
|
+
* self-contained servers work fine.
|
|
33
|
+
*
|
|
34
|
+
* Self-gating: reads the deployment mode from context and renders `null`
|
|
35
|
+
* unless the server is stdio and the backend is cloud. Callers render it
|
|
36
|
+
* unconditionally next to their Connect action.
|
|
37
|
+
*
|
|
38
|
+
* All visual properties flow through `--stgm-*` design tokens. No
|
|
39
|
+
* Console-specific dependencies — safe for platform-builder embedding.
|
|
40
|
+
*/
|
|
41
|
+
export declare function StdioSandboxNotice({ serverType, className, }: StdioSandboxNoticeProps): import("react/jsx-runtime").JSX.Element | null;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=StdioSandboxNotice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StdioSandboxNotice.d.ts","sourceRoot":"","sources":["../../src/mcp-server/StdioSandboxNotice.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yDAAyD,CAAC;AAG7F,uEAAuE;AACvE,KAAK,aAAa,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;AAEjD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,cAAc,EACpB,UAAU,EAAE,aAAa,GAAG,SAAS,GACpC,OAAO,CAET;AAED,4CAA4C;AAC5C,MAAM,WAAW,uBAAuB;IACtC,gDAAgD;IAChD,QAAQ,CAAC,UAAU,EAAE,aAAa,GAAG,SAAS,CAAC;IAC/C,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,UAAU,EACV,SAAS,GACV,EAAE,uBAAuB,kDAoBzB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "@stigmer/theme";
|
|
4
|
+
import { useDeploymentMode } from "../deployment-mode.js";
|
|
5
|
+
/**
|
|
6
|
+
* Whether an MCP server's transport is stdio while connected to Stigmer Cloud.
|
|
7
|
+
*
|
|
8
|
+
* stdio servers run as a subprocess inside an isolated Daytona sandbox in the
|
|
9
|
+
* cloud — not on the user's own computer. This predicate identifies exactly
|
|
10
|
+
* that combination so the connect UI can set accurate expectations. HTTP
|
|
11
|
+
* servers and any transport in local mode return `false`.
|
|
12
|
+
*
|
|
13
|
+
* Pure and framework-free so it can be unit-tested without rendering.
|
|
14
|
+
*/
|
|
15
|
+
export function isStdioInCloud(mode, serverType) {
|
|
16
|
+
return mode === "cloud" && serverType?.case === "stdio";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sets accurate expectations for connecting an **stdio** MCP server from
|
|
20
|
+
* **Stigmer Cloud**.
|
|
21
|
+
*
|
|
22
|
+
* stdio servers are a supported cloud capability — Stigmer runs them in an
|
|
23
|
+
* isolated Daytona sandbox. But that sandbox is not the user's own machine,
|
|
24
|
+
* so a server that expects access to local files, applications, or a private
|
|
25
|
+
* network (e.g. a filesystem server) won't behave as the user intends. This
|
|
26
|
+
* notice explains that up front; Connect itself stays enabled because
|
|
27
|
+
* self-contained servers work fine.
|
|
28
|
+
*
|
|
29
|
+
* Self-gating: reads the deployment mode from context and renders `null`
|
|
30
|
+
* unless the server is stdio and the backend is cloud. Callers render it
|
|
31
|
+
* unconditionally next to their Connect action.
|
|
32
|
+
*
|
|
33
|
+
* All visual properties flow through `--stgm-*` design tokens. No
|
|
34
|
+
* Console-specific dependencies — safe for platform-builder embedding.
|
|
35
|
+
*/
|
|
36
|
+
export function StdioSandboxNotice({ serverType, className, }) {
|
|
37
|
+
const mode = useDeploymentMode();
|
|
38
|
+
if (!isStdioInCloud(mode, serverType))
|
|
39
|
+
return null;
|
|
40
|
+
return (_jsxs("div", { role: "status", className: cn("bg-muted-subtle text-muted-foreground flex items-start gap-2.5 rounded-lg border border-transparent px-4 py-3", className), children: [_jsx(SandboxIcon, { className: "mt-0.5 size-4 shrink-0" }), _jsx("p", { className: "text-xs leading-relaxed", children: "This stdio server runs in an isolated cloud sandbox, not on your own computer. Tools that need access to your local files, applications, or private network won't be available here." })] }));
|
|
41
|
+
}
|
|
42
|
+
function SandboxIcon({ className }) {
|
|
43
|
+
return (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": "true", children: [_jsx("path", { d: "M2 5.5 8 2.5l6 3-6 3-6-3Z" }), _jsx("path", { d: "M2 5.5v5l6 3 6-3v-5" }), _jsx("path", { d: "M8 8.5v5" })] }));
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=StdioSandboxNotice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StdioSandboxNotice.js","sourceRoot":"","sources":["../../src/mcp-server/StdioSandboxNotice.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAGpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAK1D;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAoB,EACpB,UAAqC;IAErC,OAAO,IAAI,KAAK,OAAO,IAAI,UAAU,EAAE,IAAI,KAAK,OAAO,CAAC;AAC1D,CAAC;AAUD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,kBAAkB,CAAC,EACjC,UAAU,EACV,SAAS,GACe;IACxB,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAC;IACjC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnD,OAAO,CACL,eACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAE,EAAE,CACX,+GAA+G,EAC/G,SAAS,CACV,aAED,KAAC,WAAW,IAAC,SAAS,EAAC,wBAAwB,GAAG,EAClD,YAAG,SAAS,EAAC,yBAAyB,qMAIlC,IACA,CACP,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,EAAE,SAAS,EAA0B;IACxD,OAAO,CACL,eACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,KAAK,EACjB,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EACtB,SAAS,EAAE,SAAS,iBACR,MAAM,aAElB,eAAM,CAAC,EAAC,2BAA2B,GAAG,EACtC,eAAM,CAAC,EAAC,qBAAqB,GAAG,EAChC,eAAM,CAAC,EAAC,UAAU,GAAG,IACjB,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -141,6 +141,18 @@ export interface UseMcpServerCredentialsReturn {
|
|
|
141
141
|
* link (when vendor approval is granted).
|
|
142
142
|
*/
|
|
143
143
|
readonly canBringOwnApp: boolean;
|
|
144
|
+
/**
|
|
145
|
+
* `true` when a manually-entered static token is a valid way to
|
|
146
|
+
* authenticate this server. `false` for `oauth_only` servers whose
|
|
147
|
+
* hosted endpoint rejects static tokens (`spec.auth.oauth_only`), where
|
|
148
|
+
* OAuth is the sole credential path.
|
|
149
|
+
*
|
|
150
|
+
* Connect surfaces use this to decide whether to offer the "enter token
|
|
151
|
+
* manually" affordance — offering it on an `oauth_only` server would send
|
|
152
|
+
* the user down a path that cannot succeed. Always `true` for manual-only
|
|
153
|
+
* and PAT-capable servers.
|
|
154
|
+
*/
|
|
155
|
+
readonly manualEntrySupported: boolean;
|
|
144
156
|
/**
|
|
145
157
|
* When `true`, the user has opted to bypass OAuth and enter the
|
|
146
158
|
* `target_env_var` token manually. In this state:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMcpServerCredentials.d.ts","sourceRoot":"","sources":["../../src/mcp-server/useMcpServerCredentials.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wDAAwD,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uDAAuD,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,MAAM,2DAA2D,CAAC;AAK3F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEnD,uDAAuD;AACvD,MAAM,WAAW,6BAA6B;IAC5C;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,gBAAgB,EAAE,kBAAkB,EAAE,CAAC;IAChD;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,eAAe,EAAE,CACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,KAChC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C;;;;;;;OAOG;IACH,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC1C;;;;;;OAMG;IACH,QAAQ,CAAC,oBAAoB,EAAE,cAAc,CAAC;IAC9C;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;;;;OASG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,SAAS,EAAE,SAAS,GAAG,IAAI,GAC1B,6BAA6B,
|
|
1
|
+
{"version":3,"file":"useMcpServerCredentials.d.ts","sourceRoot":"","sources":["../../src/mcp-server/useMcpServerCredentials.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wDAAwD,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uDAAuD,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,MAAM,2DAA2D,CAAC;AAK3F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEnD,uDAAuD;AACvD,MAAM,WAAW,6BAA6B;IAC5C;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,gBAAgB,EAAE,kBAAkB,EAAE,CAAC;IAChD;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,eAAe,EAAE,CACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,KAChC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C;;;;;;;OAOG;IACH,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC1C;;;;;;OAMG;IACH,QAAQ,CAAC,oBAAoB,EAAE,cAAc,CAAC;IAC9C;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;IACvC;;;;;;;;;OASG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,SAAS,EAAE,SAAS,GAAG,IAAI,GAC1B,6BAA6B,CAgH/B"}
|
|
@@ -81,6 +81,13 @@ export function useMcpServerCredentials(org, mcpServer) {
|
|
|
81
81
|
const authMode = auth ? "oauth" : "manual";
|
|
82
82
|
const oauthTargetEnvVar = auth?.targetEnvVar || null;
|
|
83
83
|
const tokenLifetimeHint = auth?.tokenLifetimeHint || null;
|
|
84
|
+
// A static token is a valid credential for every server except those whose
|
|
85
|
+
// endpoint declares it rejects them (`oauth_only`). This is the single source
|
|
86
|
+
// of truth the connect surfaces consult before offering manual entry.
|
|
87
|
+
const manualEntrySupported = !auth?.oauthOnly;
|
|
88
|
+
// Defensively force manual override off for oauth_only servers so no surface
|
|
89
|
+
// can enter a dead-end manual-entry state, even if a stale toggle is set.
|
|
90
|
+
const effectiveManualOverride = manualOverride && manualEntrySupported;
|
|
84
91
|
const oauthStatus = mcpServer?.status?.oauthStatus;
|
|
85
92
|
const isVendorApprovalPending = authMode === "oauth" &&
|
|
86
93
|
oauthStatus?.vendorApprovalStatus === VendorApprovalStatus.PENDING;
|
|
@@ -106,14 +113,14 @@ export function useMcpServerCredentials(org, mcpServer) {
|
|
|
106
113
|
}, [mcpServer, existingKeys]);
|
|
107
114
|
const requiredMissing = useMemo(() => allMissingVariables.filter((v) => !v.optional), [allMissingVariables]);
|
|
108
115
|
const missingVariables = useMemo(() => {
|
|
109
|
-
if (!oauthTargetEnvVar ||
|
|
116
|
+
if (!oauthTargetEnvVar || effectiveManualOverride)
|
|
110
117
|
return requiredMissing;
|
|
111
118
|
return requiredMissing.filter((v) => v.key !== oauthTargetEnvVar);
|
|
112
|
-
}, [requiredMissing, oauthTargetEnvVar,
|
|
119
|
+
}, [requiredMissing, oauthTargetEnvVar, effectiveManualOverride]);
|
|
113
120
|
const isReady = !personalEnv.isLoading &&
|
|
114
121
|
!grantStatus.isLoading &&
|
|
115
122
|
missingVariables.length === 0 &&
|
|
116
|
-
(authMode === "manual" ||
|
|
123
|
+
(authMode === "manual" || effectiveManualOverride || isOAuthConnected);
|
|
117
124
|
const saveCredentials = useCallback(async (values) => {
|
|
118
125
|
await personalEnv.getOrCreate();
|
|
119
126
|
await personalEnv.addVariables(values);
|
|
@@ -136,6 +143,7 @@ export function useMcpServerCredentials(org, mcpServer) {
|
|
|
136
143
|
effectiveOAuthSource,
|
|
137
144
|
isOrgOAuthApp,
|
|
138
145
|
canBringOwnApp,
|
|
146
|
+
manualEntrySupported,
|
|
139
147
|
missingVariables,
|
|
140
148
|
isReady,
|
|
141
149
|
isLoading: personalEnv.isLoading || grantStatus.isLoading,
|
|
@@ -143,7 +151,7 @@ export function useMcpServerCredentials(org, mcpServer) {
|
|
|
143
151
|
saveCredentials,
|
|
144
152
|
isSaving: personalEnv.isMutating,
|
|
145
153
|
refetch,
|
|
146
|
-
manualOverride,
|
|
154
|
+
manualOverride: effectiveManualOverride,
|
|
147
155
|
setManualOverride,
|
|
148
156
|
};
|
|
149
157
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMcpServerCredentials.js","sourceRoot":"","sources":["../../src/mcp-server/useMcpServerCredentials.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAIvD,OAAO,EAAE,cAAc,EAAE,MAAM,2DAA2D,CAAC;AAC3F,OAAO,EAAE,oBAAoB,EAAE,MAAM,oDAAoD,CAAC;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAC;AAClF,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"useMcpServerCredentials.js","sourceRoot":"","sources":["../../src/mcp-server/useMcpServerCredentials.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stigmer/react",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.7",
|
|
4
4
|
"description": "React provider and client hook for the Stigmer platform SDK",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@bufbuild/protovalidate": "^1.1.1",
|
|
39
39
|
"@dagrejs/dagre": "^1.1.4",
|
|
40
|
-
"@stigmer/theme": "3.1.
|
|
40
|
+
"@stigmer/theme": "3.1.7",
|
|
41
41
|
"diff": "^8.0.3",
|
|
42
42
|
"fflate": "^0.8.2",
|
|
43
43
|
"hast-util-to-jsx-runtime": "^2.3.6",
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"@codemirror/state": "^6.0.0",
|
|
61
61
|
"@codemirror/view": "^6.0.0",
|
|
62
62
|
"@lezer/highlight": "^1.0.0",
|
|
63
|
-
"@stigmer/protos": "3.1.
|
|
64
|
-
"@stigmer/sdk": "3.1.
|
|
63
|
+
"@stigmer/protos": "3.1.7",
|
|
64
|
+
"@stigmer/sdk": "3.1.7",
|
|
65
65
|
"@tanstack/react-table": "^8.20.0",
|
|
66
66
|
"@xyflow/react": "^12.0.0",
|
|
67
67
|
"elkjs": "^0.9.0",
|
|
@@ -11,6 +11,8 @@ import { useMcpServerConnect } from "./useMcpServerConnect.js";
|
|
|
11
11
|
import { useDisconnectOAuth } from "./useDisconnectOAuth.js";
|
|
12
12
|
import { EnvVarForm } from "../environment/EnvVarForm.js";
|
|
13
13
|
import { ErrorMessage } from "../error/ErrorMessage.js";
|
|
14
|
+
import { StdioSandboxNotice } from "./StdioSandboxNotice.js";
|
|
15
|
+
import { OAuthRequiredNotice } from "./OAuthRequiredNotice.js";
|
|
14
16
|
|
|
15
17
|
/** Props for {@link McpServerConnectDialog}. */
|
|
16
18
|
export interface McpServerConnectDialogProps {
|
|
@@ -276,6 +278,9 @@ function ConnectDialogContent({
|
|
|
276
278
|
</p>
|
|
277
279
|
)}
|
|
278
280
|
|
|
281
|
+
<StdioSandboxNotice serverType={mcpServer.spec?.serverType} className="mb-4" />
|
|
282
|
+
<OAuthRequiredNotice oauthOnly={mcpServer.spec?.auth?.oauthOnly} className="mb-4" />
|
|
283
|
+
|
|
279
284
|
{activeError && (
|
|
280
285
|
<div className="mb-4">
|
|
281
286
|
<ErrorMessage
|
|
@@ -295,7 +300,11 @@ function ConnectDialogContent({
|
|
|
295
300
|
phase={oauth.phase}
|
|
296
301
|
onSignIn={handleOAuthSignIn}
|
|
297
302
|
isVendorApprovalBlocked={creds.isVendorApprovalBlocked}
|
|
298
|
-
onSwitchToManual={
|
|
303
|
+
onSwitchToManual={
|
|
304
|
+
creds.manualEntrySupported
|
|
305
|
+
? () => creds.setManualOverride(true)
|
|
306
|
+
: undefined
|
|
307
|
+
}
|
|
299
308
|
disabled={isConnectingPhase}
|
|
300
309
|
/>
|
|
301
310
|
)}
|
|
@@ -408,7 +417,8 @@ function OAuthSection({
|
|
|
408
417
|
readonly phase: OAuthConnectPhase;
|
|
409
418
|
readonly onSignIn: () => void;
|
|
410
419
|
readonly isVendorApprovalBlocked: boolean;
|
|
411
|
-
|
|
420
|
+
/** When omitted (e.g. `oauth_only` servers), no manual-entry link is shown. */
|
|
421
|
+
readonly onSwitchToManual?: () => void;
|
|
412
422
|
readonly disabled?: boolean;
|
|
413
423
|
}) {
|
|
414
424
|
if (isConnected) {
|
|
@@ -445,19 +455,21 @@ function OAuthSection({
|
|
|
445
455
|
{isInProgress && <LoadingSpinner size="sm" />}
|
|
446
456
|
{isInProgress ? (phaseLabel[phase] ?? "Connecting...") : "Sign in with OAuth"}
|
|
447
457
|
</button>
|
|
448
|
-
{isVendorApprovalBlocked && (
|
|
458
|
+
{isVendorApprovalBlocked && onSwitchToManual && (
|
|
449
459
|
<p className="text-xs text-muted-foreground">
|
|
450
460
|
OAuth sign-in is pending vendor approval. You can enter your token manually instead.
|
|
451
461
|
</p>
|
|
452
462
|
)}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
463
|
+
{onSwitchToManual && (
|
|
464
|
+
<button
|
|
465
|
+
type="button"
|
|
466
|
+
onClick={onSwitchToManual}
|
|
467
|
+
disabled={disabled || isInProgress}
|
|
468
|
+
className="text-xs text-muted-foreground underline hover:text-foreground"
|
|
469
|
+
>
|
|
470
|
+
Enter token manually
|
|
471
|
+
</button>
|
|
472
|
+
)}
|
|
461
473
|
</div>
|
|
462
474
|
);
|
|
463
475
|
}
|
|
@@ -20,6 +20,8 @@ import { useMcpServerConnect } from "./useMcpServerConnect.js";
|
|
|
20
20
|
import { useMcpServerCredentials } from "./useMcpServerCredentials.js";
|
|
21
21
|
import { useMcpServerOAuthConnect } from "./useMcpServerOAuthConnect.js";
|
|
22
22
|
import type { OAuthConnectPhase } from "./useMcpServerOAuthConnect.js";
|
|
23
|
+
import { StdioSandboxNotice } from "./StdioSandboxNotice.js";
|
|
24
|
+
import { OAuthRequiredNotice } from "./OAuthRequiredNotice.js";
|
|
23
25
|
import { useDisconnectOAuth } from "./useDisconnectOAuth.js";
|
|
24
26
|
import { useOrgOAuthApp } from "./useOrgOAuthApp.js";
|
|
25
27
|
import { OAuthAppForm } from "./OAuthAppForm.js";
|
|
@@ -488,7 +490,10 @@ export function McpServerDetailView({
|
|
|
488
490
|
)}
|
|
489
491
|
|
|
490
492
|
<Section title="Connection">
|
|
493
|
+
<StdioSandboxNotice serverType={spec?.serverType} className="mb-3" />
|
|
494
|
+
<OAuthRequiredNotice oauthOnly={spec?.auth?.oauthOnly} className="mb-3" />
|
|
491
495
|
<ConnectBar
|
|
496
|
+
manualEntrySupported={credentials.manualEntrySupported}
|
|
492
497
|
isConnecting={connection.isConnecting || oauth.isInProgress}
|
|
493
498
|
connectionError={combinedError}
|
|
494
499
|
onConnect={handleConnectClick}
|
|
@@ -709,6 +714,7 @@ function ConnectBar({
|
|
|
709
714
|
isRemovingOrgApp,
|
|
710
715
|
removeOrgAppError,
|
|
711
716
|
onClearRemoveOrgAppError,
|
|
717
|
+
manualEntrySupported,
|
|
712
718
|
manualOverride,
|
|
713
719
|
onManualOverride,
|
|
714
720
|
onBackToOAuth,
|
|
@@ -742,6 +748,8 @@ function ConnectBar({
|
|
|
742
748
|
readonly isRemovingOrgApp: boolean;
|
|
743
749
|
readonly removeOrgAppError: Error | null;
|
|
744
750
|
readonly onClearRemoveOrgAppError: () => void;
|
|
751
|
+
/** `false` for `oauth_only` servers — the manual-entry link is suppressed. */
|
|
752
|
+
readonly manualEntrySupported: boolean;
|
|
745
753
|
readonly manualOverride: boolean;
|
|
746
754
|
readonly onManualOverride: () => void;
|
|
747
755
|
readonly onBackToOAuth: () => void;
|
|
@@ -1047,8 +1055,11 @@ function ConnectBar({
|
|
|
1047
1055
|
</div>
|
|
1048
1056
|
)}
|
|
1049
1057
|
|
|
1050
|
-
{/* Secondary actions: manual entry, BYOA, back to OAuth
|
|
1051
|
-
|
|
1058
|
+
{/* Secondary actions: manual entry, BYOA, back to OAuth. Suppressed
|
|
1059
|
+
entirely for oauth_only servers with no BYOA option, so no empty
|
|
1060
|
+
action bar renders. */}
|
|
1061
|
+
{authMode === "oauth" && !isOAuthConnected && !isOAuthBusy && !isConnecting &&
|
|
1062
|
+
(manualOverride || manualEntrySupported || canBringOwnApp) && (
|
|
1052
1063
|
<div className="flex items-center gap-3 border-t border-border px-3 py-1.5">
|
|
1053
1064
|
{manualOverride ? (
|
|
1054
1065
|
<button
|
|
@@ -1060,13 +1071,15 @@ function ConnectBar({
|
|
|
1060
1071
|
</button>
|
|
1061
1072
|
) : (
|
|
1062
1073
|
<>
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1074
|
+
{manualEntrySupported && (
|
|
1075
|
+
<button
|
|
1076
|
+
type="button"
|
|
1077
|
+
onClick={onManualOverride}
|
|
1078
|
+
className="text-[11px] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground"
|
|
1079
|
+
>
|
|
1080
|
+
Enter token manually
|
|
1081
|
+
</button>
|
|
1082
|
+
)}
|
|
1070
1083
|
{canBringOwnApp && !isVendorApprovalBlocked && (
|
|
1071
1084
|
<button
|
|
1072
1085
|
type="button"
|
|
@@ -404,6 +404,8 @@ export function McpServerPicker({
|
|
|
404
404
|
const auth = entry.mcpServer.spec?.auth;
|
|
405
405
|
const oauthTargetEnvVar = auth?.targetEnvVar || null;
|
|
406
406
|
const hasOAuth = !!auth;
|
|
407
|
+
// oauth_only endpoints reject static tokens — never offer manual entry.
|
|
408
|
+
const manualEntrySupported = !auth?.oauthOnly;
|
|
407
409
|
const isManualOverride = manualOverrideKeys.has(view.serverKey);
|
|
408
410
|
|
|
409
411
|
const entryMissingVars =
|
|
@@ -454,15 +456,16 @@ export function McpServerPicker({
|
|
|
454
456
|
}
|
|
455
457
|
: undefined;
|
|
456
458
|
|
|
457
|
-
const handleSwitchToManual =
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
459
|
+
const handleSwitchToManual =
|
|
460
|
+
hasOAuth && manualEntrySupported
|
|
461
|
+
? () => {
|
|
462
|
+
setManualOverrideKeys((prev) => {
|
|
463
|
+
const next = new Set(prev);
|
|
464
|
+
next.add(view.serverKey);
|
|
465
|
+
return next;
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
: undefined;
|
|
466
469
|
|
|
467
470
|
const handleSwitchToOAuth =
|
|
468
471
|
hasOAuth && isManualOverride
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@stigmer/theme";
|
|
4
|
+
|
|
5
|
+
/** Props for {@link OAuthRequiredNotice}. */
|
|
6
|
+
export interface OAuthRequiredNoticeProps {
|
|
7
|
+
/**
|
|
8
|
+
* The server's `spec.auth.oauth_only` flag. When `true`, the endpoint
|
|
9
|
+
* rejects manually-entered static tokens and OAuth is the only path.
|
|
10
|
+
*/
|
|
11
|
+
readonly oauthOnly: boolean | undefined;
|
|
12
|
+
/** Additional CSS class names for the root container. */
|
|
13
|
+
readonly className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Sets accurate expectations for connecting an **OAuth-only** MCP server —
|
|
18
|
+
* one whose hosted endpoint rejects manually-entered static tokens
|
|
19
|
+
* (`spec.auth.oauth_only`, e.g. Notion, Monday).
|
|
20
|
+
*
|
|
21
|
+
* Without this notice a user might expect the (now-hidden) manual token
|
|
22
|
+
* option and wonder why only "Sign in" is offered. It explains, in one line,
|
|
23
|
+
* that OAuth is required and a pasted API token would be rejected — turning a
|
|
24
|
+
* silent constraint into a clear one (Nielsen: visibility of system status).
|
|
25
|
+
*
|
|
26
|
+
* Self-gating: renders `null` unless the server is `oauth_only`. Callers
|
|
27
|
+
* render it unconditionally next to their Connect action, mirroring
|
|
28
|
+
* {@link StdioSandboxNotice}.
|
|
29
|
+
*
|
|
30
|
+
* All visual properties flow through `--stgm-*` design tokens. No
|
|
31
|
+
* Console-specific dependencies — safe for platform-builder embedding.
|
|
32
|
+
*/
|
|
33
|
+
export function OAuthRequiredNotice({
|
|
34
|
+
oauthOnly,
|
|
35
|
+
className,
|
|
36
|
+
}: OAuthRequiredNoticeProps) {
|
|
37
|
+
if (!oauthOnly) return null;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div
|
|
41
|
+
role="status"
|
|
42
|
+
className={cn(
|
|
43
|
+
"bg-muted-subtle text-muted-foreground flex items-start gap-2.5 rounded-lg border border-transparent px-4 py-3",
|
|
44
|
+
className,
|
|
45
|
+
)}
|
|
46
|
+
>
|
|
47
|
+
<KeyIcon className="mt-0.5 size-4 shrink-0" />
|
|
48
|
+
<p className="text-xs leading-relaxed">
|
|
49
|
+
This server requires OAuth. Sign in to connect — its hosted endpoint
|
|
50
|
+
rejects manually-entered API tokens, so a pasted token won't work
|
|
51
|
+
here.
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function KeyIcon({ className }: { className?: string }) {
|
|
58
|
+
return (
|
|
59
|
+
<svg
|
|
60
|
+
width="16"
|
|
61
|
+
height="16"
|
|
62
|
+
viewBox="0 0 16 16"
|
|
63
|
+
fill="none"
|
|
64
|
+
stroke="currentColor"
|
|
65
|
+
strokeWidth="1.5"
|
|
66
|
+
strokeLinecap="round"
|
|
67
|
+
strokeLinejoin="round"
|
|
68
|
+
className={className}
|
|
69
|
+
aria-hidden="true"
|
|
70
|
+
>
|
|
71
|
+
<circle cx="5" cy="11" r="2.5" />
|
|
72
|
+
<path d="M6.8 9.2 13 3" />
|
|
73
|
+
<path d="M11 5l1.5 1.5" />
|
|
74
|
+
<path d="M9 7l1.5 1.5" />
|
|
75
|
+
</svg>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@stigmer/theme";
|
|
4
|
+
import type { DeploymentMode } from "@stigmer/sdk";
|
|
5
|
+
import type { McpServerSpec } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/spec_pb";
|
|
6
|
+
import { useDeploymentMode } from "../deployment-mode.js";
|
|
7
|
+
|
|
8
|
+
/** The `server_type` oneof discriminator from an MCP server's spec. */
|
|
9
|
+
type McpServerType = McpServerSpec["serverType"];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Whether an MCP server's transport is stdio while connected to Stigmer Cloud.
|
|
13
|
+
*
|
|
14
|
+
* stdio servers run as a subprocess inside an isolated Daytona sandbox in the
|
|
15
|
+
* cloud — not on the user's own computer. This predicate identifies exactly
|
|
16
|
+
* that combination so the connect UI can set accurate expectations. HTTP
|
|
17
|
+
* servers and any transport in local mode return `false`.
|
|
18
|
+
*
|
|
19
|
+
* Pure and framework-free so it can be unit-tested without rendering.
|
|
20
|
+
*/
|
|
21
|
+
export function isStdioInCloud(
|
|
22
|
+
mode: DeploymentMode,
|
|
23
|
+
serverType: McpServerType | undefined,
|
|
24
|
+
): boolean {
|
|
25
|
+
return mode === "cloud" && serverType?.case === "stdio";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Props for {@link StdioSandboxNotice}. */
|
|
29
|
+
export interface StdioSandboxNoticeProps {
|
|
30
|
+
/** The MCP server's `spec.serverType` oneof. */
|
|
31
|
+
readonly serverType: McpServerType | undefined;
|
|
32
|
+
/** Additional CSS class names for the root container. */
|
|
33
|
+
readonly className?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Sets accurate expectations for connecting an **stdio** MCP server from
|
|
38
|
+
* **Stigmer Cloud**.
|
|
39
|
+
*
|
|
40
|
+
* stdio servers are a supported cloud capability — Stigmer runs them in an
|
|
41
|
+
* isolated Daytona sandbox. But that sandbox is not the user's own machine,
|
|
42
|
+
* so a server that expects access to local files, applications, or a private
|
|
43
|
+
* network (e.g. a filesystem server) won't behave as the user intends. This
|
|
44
|
+
* notice explains that up front; Connect itself stays enabled because
|
|
45
|
+
* self-contained servers work fine.
|
|
46
|
+
*
|
|
47
|
+
* Self-gating: reads the deployment mode from context and renders `null`
|
|
48
|
+
* unless the server is stdio and the backend is cloud. Callers render it
|
|
49
|
+
* unconditionally next to their Connect action.
|
|
50
|
+
*
|
|
51
|
+
* All visual properties flow through `--stgm-*` design tokens. No
|
|
52
|
+
* Console-specific dependencies — safe for platform-builder embedding.
|
|
53
|
+
*/
|
|
54
|
+
export function StdioSandboxNotice({
|
|
55
|
+
serverType,
|
|
56
|
+
className,
|
|
57
|
+
}: StdioSandboxNoticeProps) {
|
|
58
|
+
const mode = useDeploymentMode();
|
|
59
|
+
if (!isStdioInCloud(mode, serverType)) return null;
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div
|
|
63
|
+
role="status"
|
|
64
|
+
className={cn(
|
|
65
|
+
"bg-muted-subtle text-muted-foreground flex items-start gap-2.5 rounded-lg border border-transparent px-4 py-3",
|
|
66
|
+
className,
|
|
67
|
+
)}
|
|
68
|
+
>
|
|
69
|
+
<SandboxIcon className="mt-0.5 size-4 shrink-0" />
|
|
70
|
+
<p className="text-xs leading-relaxed">
|
|
71
|
+
This stdio server runs in an isolated cloud sandbox, not on your own
|
|
72
|
+
computer. Tools that need access to your local files, applications, or
|
|
73
|
+
private network won't be available here.
|
|
74
|
+
</p>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function SandboxIcon({ className }: { className?: string }) {
|
|
80
|
+
return (
|
|
81
|
+
<svg
|
|
82
|
+
width="16"
|
|
83
|
+
height="16"
|
|
84
|
+
viewBox="0 0 16 16"
|
|
85
|
+
fill="none"
|
|
86
|
+
stroke="currentColor"
|
|
87
|
+
strokeWidth="1.5"
|
|
88
|
+
strokeLinecap="round"
|
|
89
|
+
strokeLinejoin="round"
|
|
90
|
+
className={className}
|
|
91
|
+
aria-hidden="true"
|
|
92
|
+
>
|
|
93
|
+
<path d="M2 5.5 8 2.5l6 3-6 3-6-3Z" />
|
|
94
|
+
<path d="M2 5.5v5l6 3 6-3v-5" />
|
|
95
|
+
<path d="M8 8.5v5" />
|
|
96
|
+
</svg>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup } from "@testing-library/react";
|
|
3
|
+
import { OAuthRequiredNotice } from "../OAuthRequiredNotice";
|
|
4
|
+
|
|
5
|
+
afterEach(cleanup);
|
|
6
|
+
|
|
7
|
+
describe("OAuthRequiredNotice", () => {
|
|
8
|
+
it("renders the OAuth-required caveat when oauth_only is true", () => {
|
|
9
|
+
render(<OAuthRequiredNotice oauthOnly={true} />);
|
|
10
|
+
expect(screen.getByRole("status")).toBeTruthy();
|
|
11
|
+
expect(screen.getByText(/requires OAuth/i)).toBeTruthy();
|
|
12
|
+
// It explains why manual entry is gone, not just that OAuth is used.
|
|
13
|
+
expect(screen.getByText(/rejects manually-entered API tokens/i)).toBeTruthy();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("renders nothing when oauth_only is false", () => {
|
|
17
|
+
const { container } = render(<OAuthRequiredNotice oauthOnly={false} />);
|
|
18
|
+
expect(container.firstChild).toBeNull();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("renders nothing when oauth_only is undefined", () => {
|
|
22
|
+
const { container } = render(<OAuthRequiredNotice oauthOnly={undefined} />);
|
|
23
|
+
expect(container.firstChild).toBeNull();
|
|
24
|
+
});
|
|
25
|
+
});
|