Skip to content

Conversation

@vibegui
Copy link
Contributor

@vibegui vibegui commented Dec 27, 2025

  • Added support for STDIO transport in the proxy, allowing for the management of stdio-based MCP connections.
  • Introduced a new StdioConnectionManager to handle the lifecycle of stdio connections, including spawning and monitoring processes.
  • Updated connection schema to include STDIO as a valid connection type and modified related tools for fetching and managing connections.
  • Enhanced the UI to support STDIO connections, including new form fields for NPX package management.
  • Improved error handling and logging for stdio connections to facilitate better debugging and monitoring.
CleanShot 2025-12-27 at 11 16 35 CleanShot 2025-12-27 at 11 18 09 CleanShot 2025-12-27 at 11 17 00

Summary by cubic

Adds STDIO transport with a managed process runner for MCP servers, plus NPX-driven setup and a status/logs panel. Also improves OAuth by rewriting the resource parameter and making scopes configurable.

  • New Features

    • STDIO transport: proxy spawns/manages processes via stdioManager; supports stdio:// URLs (e.g., npx) and injects connection_token into env via header X-Stdio-Env-Var (default MCP_API_TOKEN).
    • API: /api/stdio for logs, info, list, and process controls (start/restart/stop); stderr piping and buffered logs.
    • Tool fetching supports STDIO with timeouts; HTTP path unchanged.
    • UI: NPX flow maps to STDIO with fields for package, env var name, and API key; settings include a status panel with live logs and start/restart/stop.
    • Tools/registry: added STDIO_LIST, STDIO_STOP, STDIO_STOP_ALL, STDIO_RESTART, STDIO_LOGS.
    • Schema/storage: connection_type now includes STDIO.
    • OAuth: rewrite resource on authorize/token to origin MCP URL; client scopes are optional/configurable.
    • Diagnostics: structured auth error logs and a 30s connect timeout.
    • Dev: DISABLE_RATE_LIMIT=true disables auth rate limiting.
  • Migration

    • No changes needed for existing HTTP/SSE/Websocket connections.
    • To add STDIO:
      • In UI: pick NPX, enter package, env var name, and API key.
      • Via API: set connection_type=STDIO, connection_url=stdio://..., put API key in connection_token, and set connection_headers["X-Stdio-Env-Var"] if not using MCP_API_TOKEN.
    • Managed processes persist; control via STDIO_STOP/STDIO_RESTART/STDIO_STOP_ALL or /api/stdio; fetch logs via STDIO_LOGS or /api/stdio; start via /api/stdio/:id/start or on first use.

Written for commit 86340c7. Summary will update automatically on new commits.

@github-actions
Copy link
Contributor

Release Options

Should a new version be published when this PR is merged?

React with an emoji to vote on the release type:

Reaction Type Next Version
👍 Prerelease 1.0.0-alpha.36
🎉 Patch 1.0.1
❤️ Minor 1.1.0
🚀 Major 2.0.0

Current version: 1.0.0-alpha.35

Deployment

  • Deploy to production (triggers ArgoCD sync after Docker image is published)

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 12 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/mesh/src/web/routes/orgs/connections.tsx">

<violation number="1" location="apps/mesh/src/web/routes/orgs/connections.tsx:86">
P1: Schema validation doesn&#39;t enforce conditional required fields. `npx_package` should be required when `ui_type === &quot;NPX&quot;`, and `connection_url` should be required when `ui_type !== &quot;NPX&quot;`. Consider using Zod&#39;s `.refine()` or `.superRefine()` to add conditional validation.</violation>

<violation number="2" location="apps/mesh/src/web/routes/orgs/connections.tsx:226">
P1: Unsafe type assertion: If a STDIO connection&#39;s URL fails to parse as NPX format, `connection_type` (&quot;STDIO&quot;) is incorrectly cast to `&quot;HTTP&quot; | &quot;SSE&quot; | &quot;Websocket&quot;`. Consider handling this edge case explicitly, perhaps defaulting to a sensible `ui_type` or showing an error.</violation>
</file>

<file name="apps/mesh/src/api/routes/proxy.ts">

<violation number="1" location="apps/mesh/src/api/routes/proxy.ts:299">
P1: STDIO clients from `stdioManager.spawn()` are pooled and reused, but `executeToolCall` closes the client in its `finally` block (line 419). This will close the managed STDIO connection after the first tool call, leaving the manager in an inconsistent state where subsequent requests return a broken client. Consider conditionally skipping the close for STDIO connections, or having the manager return a wrapper that ignores close calls.</violation>
</file>

Reply to cubic to teach it or ask questions. Tag @cubic-dev-ai to re-run a review.

form.reset({
title: editingConnection.title,
description: editingConnection.description,
ui_type: editingConnection.connection_type as "HTTP" | "SSE" | "Websocket",
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Unsafe type assertion: If a STDIO connection's URL fails to parse as NPX format, connection_type ("STDIO") is incorrectly cast to "HTTP" | "SSE" | "Websocket". Consider handling this edge case explicitly, perhaps defaulting to a sensible ui_type or showing an error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/routes/orgs/connections.tsx, line 226:

<comment>Unsafe type assertion: If a STDIO connection&#39;s URL fails to parse as NPX format, `connection_type` (&quot;STDIO&quot;) is incorrectly cast to `&quot;HTTP&quot; | &quot;SSE&quot; | &quot;Websocket&quot;`. Consider handling this edge case explicitly, perhaps defaulting to a sensible `ui_type` or showing an error.</comment>

<file context>
@@ -148,33 +180,67 @@ function OrgMcpsContent() {
+        form.reset({
+          title: editingConnection.title,
+          description: editingConnection.description,
+          ui_type: editingConnection.connection_type as &quot;HTTP&quot; | &quot;SSE&quot; | &quot;Websocket&quot;,
+          connection_url: editingConnection.connection_url,
+          connection_token: null, // Don&#39;t pre-fill token for security
</file context>
Suggested change
ui_type: editingConnection.connection_type as "HTTP" | "SSE" | "Websocket",
ui_type: ["HTTP", "SSE", "Websocket"].includes(editingConnection.connection_type)
? (editingConnection.connection_type as "HTTP" | "SSE" | "Websocket")
: "HTTP",
Fix with Cubic

connection_url: z.string().optional(),
connection_token: z.string().nullable().optional(),
// For NPX
npx_package: z.string().optional(),
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Schema validation doesn't enforce conditional required fields. npx_package should be required when ui_type === "NPX", and connection_url should be required when ui_type !== "NPX". Consider using Zod's .refine() or .superRefine() to add conditional validation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/routes/orgs/connections.tsx, line 86:

<comment>Schema validation doesn&#39;t enforce conditional required fields. `npx_package` should be required when `ui_type === &quot;NPX&quot;`, and `connection_url` should be required when `ui_type !== &quot;NPX&quot;`. Consider using Zod&#39;s `.refine()` or `.superRefine()` to add conditional validation.</comment>

<file context>
@@ -75,20 +74,53 @@ import { generatePrefixedId } from &quot;@/shared/utils/generate-id&quot;;
+  connection_url: z.string().optional(),
+  connection_token: z.string().nullable().optional(),
+  // For NPX
+  npx_package: z.string().optional(),
+  npx_env_var_name: z.string().optional(),
+  npx_api_key: z.string().optional(),
</file context>
Fix with Cubic

}

// Spawn or get existing client from the manager
return stdioManager.spawn({
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: STDIO clients from stdioManager.spawn() are pooled and reused, but executeToolCall closes the client in its finally block (line 419). This will close the managed STDIO connection after the first tool call, leaving the manager in an inconsistent state where subsequent requests return a broken client. Consider conditionally skipping the close for STDIO connections, or having the manager return a wrapper that ignores close calls.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/api/routes/proxy.ts, line 299:

<comment>STDIO clients from `stdioManager.spawn()` are pooled and reused, but `executeToolCall` closes the client in its `finally` block (line 419). This will close the managed STDIO connection after the first tool call, leaving the manager in an inconsistent state where subsequent requests return a broken client. Consider conditionally skipping the close for STDIO connections, or having the manager return a wrapper that ignores close calls.</comment>

<file context>
@@ -271,8 +275,35 @@ async function createMCPProxyDoNotUseDirectly(
+      }
+
+      // Spawn or get existing client from the manager
+      return stdioManager.spawn({
+        ...stdioConfig,
+        id: connectionId,
</file context>

✅ Addressed in d7acad1

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 issues found across 52 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/studio/src/components/widgets/dynamic-options.tsx">

<violation number="1" location="apps/studio/src/components/widgets/dynamic-options.tsx:120">
P1: Missing `fetchOptions` in the useEffect dependency array violates the React hooks exhaustive-deps rule. Since `fetchOptions` is a `useCallback` with its own dependencies, it should be included in this effect&#39;s deps. Without it, the effect may use a stale version of the callback.</violation>
</file>

<file name="apps/mesh/src/web/components/details/connection/settings-tab/stdio-status-panel.tsx">

<violation number="1" location="apps/mesh/src/web/components/details/connection/settings-tab/stdio-status-panel.tsx:86">
P1: The response from the `/restart` endpoint is not checked for errors. If the restart fails, the error is silently swallowed and the mutation proceeds to call `/start`, potentially masking the real failure from users.</violation>
</file>

<file name="apps/studio/src/components/json-preview.tsx">

<violation number="1" location="apps/studio/src/components/json-preview.tsx:59">
P2: The Copy button always copies `jsonString` regardless of which tab is active. When viewing the Schema tab, users would expect to copy the schema content. Consider tracking the active tab and copying the appropriate content:

```tsx
const [activeTab, setActiveTab] = useState(&#39;json&#39;);
// ...
&lt;Tabs defaultValue=&quot;json&quot; onValueChange={setActiveTab}&gt;
// ...
onClick={() =&gt; handleCopy(activeTab === &#39;json&#39; ? jsonString : schemaString)}
```</violation>
</file>

<file name="apps/studio/src/components/widgets/color-widget.tsx">

<violation number="1" location="apps/studio/src/components/widgets/color-widget.tsx:22">
P2: Inconsistent default values between color picker and text input. When `value` is undefined, the color picker shows `#000000` but the text input shows an empty string, creating a confusing user experience where the controls display different values.</violation>
</file>

<file name="apps/studio/src/server/index.ts">

<violation number="1" location="apps/studio/src/server/index.ts:50">
P2: Missing input validation for `name` and `schema`. Unlike the `/api/extract-schema` endpoint which validates required fields, this endpoint will create entries with potentially undefined values.</violation>
</file>

<file name="apps/studio/src/components/widgets/text-widget.tsx">

<violation number="1" location="apps/studio/src/components/widgets/text-widget.tsx:16">
P2: The `onBlur` handler uses `value` from props (captured at render time) instead of the current DOM value. This can cause stale values to be reported during race conditions. Use `e.target.value` to get the actual input value.</violation>
</file>

<file name="apps/mesh/src/tools/stdio/index.ts">

<violation number="1" location="apps/mesh/src/tools/stdio/index.ts:109">
P2: Handler always returns `success: true` even when the connection doesn&#39;t exist. Consider checking `stdioManager.isRunning()` first, similar to `stopStdioConnection`, to return an appropriate error message when the connection is not found.</violation>
</file>

<file name="apps/studio/src/lib/schema-extractor.ts">

<violation number="1" location="apps/studio/src/lib/schema-extractor.ts:20">
P1: Regex injection vulnerability: `typeName` is directly interpolated into RegExp without escaping special characters. If `typeName` contains regex metacharacters (e.g., `.`, `*`, `[`), it could cause unexpected matches or ReDoS attacks. Escape the input before using it in the regex.</violation>
</file>

<file name="apps/mesh/src/stdio/stdio-manager.ts">

<violation number="1" location="apps/mesh/src/stdio/stdio-manager.ts:115">
P1: The try-catch block is ineffective - `return existing.client` cannot throw. This means the connection health check doesn&#39;t work as intended. Consider actually performing a lightweight operation like `ping()` to verify the connection is alive, or remove the try-catch if the `onclose` handler is sufficient for detecting dead connections.</violation>
</file>

<file name="apps/mesh/src/api/app.ts">

<violation number="1" location="apps/mesh/src/api/app.ts:409">
P1: Missing authentication middleware for STDIO management routes. These endpoints expose sensitive operations (listing connections, viewing logs, stopping/starting processes) without requiring authentication. Apply `mcpAuth` middleware similar to `/mcp/*` routes.</violation>
</file>

Reply to cubic to teach it or ask questions. Tag @cubic-dev-ai to re-run a review.

if (isLoader || (isMustache && !isLoader)) {
fetchOptions(value as string | undefined);
}
}, [optionsUrl, JSON.stringify(parentData)]);
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Missing fetchOptions in the useEffect dependency array violates the React hooks exhaustive-deps rule. Since fetchOptions is a useCallback with its own dependencies, it should be included in this effect's deps. Without it, the effect may use a stale version of the callback.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/studio/src/components/widgets/dynamic-options.tsx, line 120:

<comment>Missing `fetchOptions` in the useEffect dependency array violates the React hooks exhaustive-deps rule. Since `fetchOptions` is a `useCallback` with its own dependencies, it should be included in this effect&#39;s deps. Without it, the effect may use a stale version of the callback.</comment>

<file context>
@@ -0,0 +1,170 @@
+    if (isLoader || (isMustache &amp;&amp; !isLoader)) {
+      fetchOptions(value as string | undefined);
+    }
+  }, [optionsUrl, JSON.stringify(parentData)]);
+
+  // Handle autocomplete search
</file context>
Fix with Cubic

const restartMutation = useMutation({
mutationFn: async () => {
// First stop, then start
await fetch(`/api/stdio/${connectionId}/restart`, { method: "POST" });
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The response from the /restart endpoint is not checked for errors. If the restart fails, the error is silently swallowed and the mutation proceeds to call /start, potentially masking the real failure from users.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/details/connection/settings-tab/stdio-status-panel.tsx, line 86:

<comment>The response from the `/restart` endpoint is not checked for errors. If the restart fails, the error is silently swallowed and the mutation proceeds to call `/start`, potentially masking the real failure from users.</comment>

<file context>
@@ -0,0 +1,306 @@
+  const restartMutation = useMutation({
+    mutationFn: async () =&gt; {
+      // First stop, then start
+      await fetch(`/api/stdio/${connectionId}/restart`, { method: &quot;POST&quot; });
+      // Then start it again
+      const response = await fetch(`/api/stdio/${connectionId}/start`, {
</file context>
Fix with Cubic

<Button
variant="outline"
size="sm"
onClick={() => handleCopy(jsonString)}
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The Copy button always copies jsonString regardless of which tab is active. When viewing the Schema tab, users would expect to copy the schema content. Consider tracking the active tab and copying the appropriate content:

const [activeTab, setActiveTab] = useState('json');
// ...
<Tabs defaultValue="json" onValueChange={setActiveTab}>
// ...
onClick={() => handleCopy(activeTab === 'json' ? jsonString : schemaString)}
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/studio/src/components/json-preview.tsx, line 59:

<comment>The Copy button always copies `jsonString` regardless of which tab is active. When viewing the Schema tab, users would expect to copy the schema content. Consider tracking the active tab and copying the appropriate content:

```tsx
const [activeTab, setActiveTab] = useState(&#39;json&#39;);
// ...
&lt;Tabs defaultValue=&quot;json&quot; onValueChange={setActiveTab}&gt;
// ...
onClick={() =&gt; handleCopy(activeTab === &#39;json&#39; ? jsonString : schemaString)}
```</comment>

<file context>
@@ -0,0 +1,130 @@
+            &lt;Button
+              variant=&quot;outline&quot;
+              size=&quot;sm&quot;
+              onClick={() =&gt; handleCopy(jsonString)}
+              className=&quot;gap-2&quot;
+            &gt;
</file context>
Fix with Cubic

<Input
id={id}
type="text"
value={value ?? ""}
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Inconsistent default values between color picker and text input. When value is undefined, the color picker shows #000000 but the text input shows an empty string, creating a confusing user experience where the controls display different values.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/studio/src/components/widgets/color-widget.tsx, line 22:

<comment>Inconsistent default values between color picker and text input. When `value` is undefined, the color picker shows `#000000` but the text input shows an empty string, creating a confusing user experience where the controls display different values.</comment>

<file context>
@@ -0,0 +1,32 @@
+      &lt;Input
+        id={id}
+        type=&quot;text&quot;
+        value={value ?? &quot;&quot;}
+        required={required}
+        disabled={disabled || readonly}
</file context>
Fix with Cubic


app.post("/api/schemas", async (c) => {
try {
const { name, schema } = await c.req.json();
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Missing input validation for name and schema. Unlike the /api/extract-schema endpoint which validates required fields, this endpoint will create entries with potentially undefined values.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/studio/src/server/index.ts, line 50:

<comment>Missing input validation for `name` and `schema`. Unlike the `/api/extract-schema` endpoint which validates required fields, this endpoint will create entries with potentially undefined values.</comment>

<file context>
@@ -0,0 +1,76 @@
+
+app.post(&quot;/api/schemas&quot;, async (c) =&gt; {
+  try {
+    const { name, schema } = await c.req.json();
+    const id = crypto.randomUUID();
+    const entry = { id, name, schema, createdAt: new Date().toISOString() };
</file context>
Fix with Cubic

disabled={disabled || readonly}
placeholder={placeholder}
onChange={(e) => onChange(e.target.value === "" ? undefined : e.target.value)}
onBlur={() => onBlur(id, value)}
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The onBlur handler uses value from props (captured at render time) instead of the current DOM value. This can cause stale values to be reported during race conditions. Use e.target.value to get the actual input value.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/studio/src/components/widgets/text-widget.tsx, line 16:

<comment>The `onBlur` handler uses `value` from props (captured at render time) instead of the current DOM value. This can cause stale values to be reported during race conditions. Use `e.target.value` to get the actual input value.</comment>

<file context>
@@ -0,0 +1,22 @@
+      disabled={disabled || readonly}
+      placeholder={placeholder}
+      onChange={(e) =&gt; onChange(e.target.value === &quot;&quot; ? undefined : e.target.value)}
+      onBlur={() =&gt; onBlur(id, value)}
+      onFocus={() =&gt; onFocus(id, value)}
+      className=&quot;w-full&quot;
</file context>
Fix with Cubic

Comment on lines +109 to +116
handler: async (input: { connectionId: string }) => {
// Stop the connection - this marks it as stopped
await stdioManager.stop(input.connectionId);
// The next spawn() call will automatically restart it
return {
success: true,
message: `Connection ${input.connectionId} stopped. Will restart on next request.`,
};
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Handler always returns success: true even when the connection doesn't exist. Consider checking stdioManager.isRunning() first, similar to stopStdioConnection, to return an appropriate error message when the connection is not found.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/tools/stdio/index.ts, line 109:

<comment>Handler always returns `success: true` even when the connection doesn&#39;t exist. Consider checking `stdioManager.isRunning()` first, similar to `stopStdioConnection`, to return an appropriate error message when the connection is not found.</comment>

<file context>
@@ -92,9 +92,76 @@ export const stopAllStdioConnections = defineTool({
+    success: z.boolean(),
+    message: z.string(),
+  }),
+  handler: async (input: { connectionId: string }) =&gt; {
+    // Stop the connection - this marks it as stopped
+    await stdioManager.stop(input.connectionId);
</file context>
Suggested change
handler: async (input: { connectionId: string }) => {
// Stop the connection - this marks it as stopped
await stdioManager.stop(input.connectionId);
// The next spawn() call will automatically restart it
return {
success: true,
message: `Connection ${input.connectionId} stopped. Will restart on next request.`,
};
handler: async (input: { connectionId: string }) => {
const isRunning = stdioManager.isRunning(input.connectionId);
if (!isRunning) {
return {
success: false,
message: `Connection ${input.connectionId} is not running`,
};
}
// Stop the connection - this marks it as stopped
await stdioManager.stop(input.connectionId);
// The next spawn() call will automatically restart it
return {
success: true,
message: `Connection ${input.connectionId} stopped. Will restart on next request.`,
};
},

✅ Addressed in 6235cb0


function parseTypeScriptToSchema(code: string, typeName: string): JSONSchema7 {
// Find the interface or type definition
const interfaceMatch = new RegExp(
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Regex injection vulnerability: typeName is directly interpolated into RegExp without escaping special characters. If typeName contains regex metacharacters (e.g., ., *, [), it could cause unexpected matches or ReDoS attacks. Escape the input before using it in the regex.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/studio/src/lib/schema-extractor.ts, line 20:

<comment>Regex injection vulnerability: `typeName` is directly interpolated into RegExp without escaping special characters. If `typeName` contains regex metacharacters (e.g., `.`, `*`, `[`), it could cause unexpected matches or ReDoS attacks. Escape the input before using it in the regex.</comment>

<file context>
@@ -0,0 +1,199 @@
+
+function parseTypeScriptToSchema(code: string, typeName: string): JSONSchema7 {
+  // Find the interface or type definition
+  const interfaceMatch = new RegExp(
+    `(?:export\\s+)?interface\\s+${typeName}\\s*\\{([^}]+(?:\\{[^}]*\\}[^}]*)*)\\}`,
+    &quot;s&quot;
</file context>
Fix with Cubic

return;
}

try {
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The try-catch block is ineffective - return existing.client cannot throw. This means the connection health check doesn't work as intended. Consider actually performing a lightweight operation like ping() to verify the connection is alive, or remove the try-catch if the onclose handler is sufficient for detecting dead connections.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/stdio/stdio-manager.ts, line 115:

<comment>The try-catch block is ineffective - `return existing.client` cannot throw. This means the connection health check doesn&#39;t work as intended. Consider actually performing a lightweight operation like `ping()` to verify the connection is alive, or remove the try-catch if the `onclose` handler is sufficient for detecting dead connections.</comment>

<file context>
@@ -50,28 +66,152 @@ class StdioConnectionManager {
+        return;
+      }
+
+      try {
+        // Send a ping to keep the connection alive
+        // MCP SDK Client has a ping method
</file context>

✅ Addressed in 8f80c93

app.route("/api", modelsRoutes);

// STDIO connection management routes (logs, info, restart)
app.route("/api/stdio", stdioLogsRoutes);
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Missing authentication middleware for STDIO management routes. These endpoints expose sensitive operations (listing connections, viewing logs, stopping/starting processes) without requiring authentication. Apply mcpAuth middleware similar to /mcp/* routes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/api/app.ts, line 409:

<comment>Missing authentication middleware for STDIO management routes. These endpoints expose sensitive operations (listing connections, viewing logs, stopping/starting processes) without requiring authentication. Apply `mcpAuth` middleware similar to `/mcp/*` routes.</comment>

<file context>
@@ -404,6 +405,9 @@ export function createApp(options: CreateAppOptions = {}) {
   app.route(&quot;/api&quot;, modelsRoutes);
 
+  // STDIO connection management routes (logs, info, restart)
+  app.route(&quot;/api/stdio&quot;, stdioLogsRoutes);
+
   // ============================================================================
</file context>
Suggested change
app.route("/api/stdio", stdioLogsRoutes);
app.use("/api/stdio/*", mcpAuth);
app.route("/api/stdio", stdioLogsRoutes);
Fix with Cubic

@vibegui vibegui force-pushed the feat/stdio-npx-support branch from afb64fa to 92fde40 Compare December 29, 2025 13:56
- Added support for STDIO transport in the proxy, allowing for the management of stdio-based MCP connections.
- Introduced a new StdioConnectionManager to handle the lifecycle of stdio connections, including spawning and monitoring processes.
- Updated connection schema to include STDIO as a valid connection type and modified related tools for fetching and managing connections.
- Enhanced the UI to support STDIO connections, including new form fields for NPX package management.
- Improved error handling and logging for stdio connections to facilitate better debugging and monitoring.
- Added new routes for managing STDIO connections, including fetching logs, connection info, and starting/stopping processes.
- Introduced a `StdioStatusPanel` component in the UI to display process status and logs for STDIO connections.
- Enhanced the `StdioConnectionManager` to support logging of process events and connection status.
- Updated tools to include functionalities for restarting STDIO connections and retrieving logs.
- Improved error handling and user feedback for STDIO operations.
- Rewrite 'resource' parameter in authorize/token endpoints to origin MCP URL
- Some auth servers (like Supabase) validate resource matches their endpoint
- Make OAuth scope optional and configurable instead of hardcoded 'mcp'
- Let servers decide default scope when not explicitly provided
@vibegui vibegui force-pushed the feat/stdio-npx-support branch from 92fde40 to 86340c7 Compare December 29, 2025 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants