Skip to content

fix(mcp): make provider clients Worker-safe - #238

Merged
l1shen merged 1 commit into
oomol-lab:mainfrom
l1shen:grim-rooster
Jul 30, 2026
Merged

fix(mcp): make provider clients Worker-safe#238
l1shen merged 1 commit into
oomol-lab:mainfrom
l1shen:grim-rooster

Conversation

@l1shen

@l1shen l1shen commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Summary by CodeRabbit

  • New Features

    • JumpServer integrations now support Streamable HTTP endpoints, while retaining compatibility with legacy SSE endpoints.
    • Automatic fallback to SSE is provided when Streamable HTTP is unavailable.
  • Bug Fixes

    • Improved connection cleanup and error handling for JumpServer MCP connections.
    • Added stronger validation for tool responses and payloads across supported integrations, helping prevent malformed data from being processed.

Walkthrough

MCP clients for Excalidraw, Flomo, Jin10, Lingxing, and Luckin now use CfWorkerJsonSchemaValidator. JumpServer now attempts Streamable HTTP first and falls back to legacy SSE for unsupported 404/405 responses, with explicit cleanup and expanded error mapping. JumpServer documentation and runtime tests now cover both transport modes and worker-safe schema validation.

Sequence Diagram(s)

sequenceDiagram
  participant JumpServerRuntime
  participant StreamableHTTPTransport
  participant SSETransport
  participant MCPClient
  JumpServerRuntime->>MCPClient: Create validated client
  JumpServerRuntime->>StreamableHTTPTransport: Attempt connection
  StreamableHTTPTransport-->>JumpServerRuntime: Return result or 404/405
  JumpServerRuntime->>SSETransport: Retry unsupported endpoint
  SSETransport->>MCPClient: Establish SSE session
  MCPClient-->>JumpServerRuntime: Execute request
  JumpServerRuntime->>MCPClient: Close client
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided, so the intent cannot be assessed from the author text. Add a brief description of the MCP Worker-safety and schema validation changes.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the required type(scope): subject format and accurately reflects the Worker-safe MCP client changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/providers/jumpserver/runtime.ts (1)

121-181: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider caching the discovered transport per endpoint.

Every call to withJumpServerMcpClient re-attempts Streamable HTTP first, and for servers that only support legacy SSE, this means every single request (not just initial validation) pays for a failed Streamable HTTP round trip before falling back. The MCP SDK itself is moving toward a discovery-caching pattern for exactly this scenario in its v2 API (ConnectOptions.prior/PriorDiscovery), confirming this cost is real and worth avoiding on the hot path.

Consider caching the successful transport kind keyed by context.endpoint.href (e.g. a module-level Map<string, "streamable" | "sse">) so subsequent calls to a known-legacy endpoint skip straight to SSEClientTransport.

♻️ Example caching approach
+const transportKindCache = new Map<string, "streamable" | "sse">();
+
 async function connectJumpServerMcpClient(context: JumpServerMcpContext, headers: Headers): Promise<Client> {
+  const cacheKey = context.endpoint.href;
+  if (transportKindCache.get(cacheKey) === "sse") {
+    return connectLegacyClient(context, headers, cacheKey);
+  }
   const streamableClient = createJumpServerMcpClient();
   const streamableTransport = new StreamableHTTPClientTransport(context.endpoint, {
     fetch: context.fetcher,
     requestInit: { headers, signal: context.signal },
   });

   try {
     await streamableClient.connect(streamableTransport, { timeout: requestTimeoutMs });
+    transportKindCache.set(cacheKey, "streamable");
     return streamableClient;
   } catch (error) {
     await streamableClient.close().catch(() => undefined);
     if (!isUnsupportedStreamableHttp(error)) {
       throw error;
     }
   }

-  const legacyClient = createJumpServerMcpClient();
-  const legacyTransport = new SSEClientTransport(context.endpoint, {
-    fetch: context.fetcher,
-    requestInit: { headers, signal: context.signal },
-  });
-  try {
-    await legacyClient.connect(legacyTransport, { timeout: requestTimeoutMs });
-    return legacyClient;
-  } catch (error) {
-    await legacyClient.close().catch(() => undefined);
-    throw error;
-  }
+  transportKindCache.set(cacheKey, "sse");
+  return connectLegacyClient(context, headers, cacheKey);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/providers/jumpserver/runtime.ts` around lines 121 - 181, Cache the
successful transport type per endpoint in the connection flow. Update
connectJumpServerMcpClient to consult a module-level map keyed by
context.endpoint.href, skip StreamableHTTPClientTransport for endpoints
previously discovered as SSE, and record the transport kind after each
successful connection while preserving the existing fallback behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/providers/jumpserver/definition.ts`:
- Around line 21-27: Update the placeholder in the MCP endpoint definition to
use a `/mcp`-style HTTPS example instead of the legacy `/sse` path, aligning it
with the preferred Streamable HTTP transport. Leave the surrounding label,
description, and validation settings unchanged.

---

Nitpick comments:
In `@src/providers/jumpserver/runtime.ts`:
- Around line 121-181: Cache the successful transport type per endpoint in the
connection flow. Update connectJumpServerMcpClient to consult a module-level map
keyed by context.endpoint.href, skip StreamableHTTPClientTransport for endpoints
previously discovered as SSE, and record the transport kind after each
successful connection while preserving the existing fallback behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55b31022-ea69-4200-b29c-9ca354a7c9de

📥 Commits

Reviewing files that changed from the base of the PR and between 22b5946 and ea99443.

📒 Files selected for processing (8)
  • src/providers/excalidraw_mcp/runtime.ts
  • src/providers/flomo/executors.ts
  • src/providers/jin10/executors.ts
  • src/providers/jumpserver/definition.ts
  • src/providers/jumpserver/runtime.test.ts
  • src/providers/jumpserver/runtime.ts
  • src/providers/lingxing/runtime.ts
  • src/providers/luckin_coffee/executors.ts

Comment on lines +21 to +27
label: "MCP Endpoint",
inputType: "text",
required: true,
secret: false,
placeholder: "https://jumpserver-mcp.example.com/sse",
description:
"The SSE endpoint of the official jumpserver/mcp server. Public HTTPS endpoints are supported by default. Private-network, Tailscale, and NetBird endpoints require OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK. Loopback endpoints remain blocked. See https://github.com/jumpserver/mcp.",
"The Streamable HTTP endpoint, or the legacy SSE endpoint exposed by official jumpserver/mcp deployments. Public HTTPS endpoints are supported by default. Private-network, Tailscale, and NetBird endpoints require OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK. Loopback endpoints remain blocked. See https://github.com/jumpserver/mcp.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Placeholder still models the legacy /sse endpoint.

The description now presents Streamable HTTP as primary, but placeholder (Line 25) still shows https://jumpserver-mcp.example.com/sse, which could nudge users toward configuring the legacy endpoint first. Consider using a /mcp-style example to match the now-preferred transport.

✏️ Suggested placeholder update
-          placeholder: "https://jumpserver-mcp.example.com/sse",
+          placeholder: "https://jumpserver-mcp.example.com/mcp",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
label: "MCP Endpoint",
inputType: "text",
required: true,
secret: false,
placeholder: "https://jumpserver-mcp.example.com/sse",
description:
"The SSE endpoint of the official jumpserver/mcp server. Public HTTPS endpoints are supported by default. Private-network, Tailscale, and NetBird endpoints require OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK. Loopback endpoints remain blocked. See https://github.com/jumpserver/mcp.",
"The Streamable HTTP endpoint, or the legacy SSE endpoint exposed by official jumpserver/mcp deployments. Public HTTPS endpoints are supported by default. Private-network, Tailscale, and NetBird endpoints require OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK. Loopback endpoints remain blocked. See https://github.com/jumpserver/mcp.",
label: "MCP Endpoint",
inputType: "text",
required: true,
secret: false,
placeholder: "https://jumpserver-mcp.example.com/mcp",
description:
"The Streamable HTTP endpoint, or the legacy SSE endpoint exposed by official jumpserver/mcp deployments. Public HTTPS endpoints are supported by default. Private-network, Tailscale, and NetBird endpoints require OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK. Loopback endpoints remain blocked. See https://github.com/jumpserver/mcp.",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/providers/jumpserver/definition.ts` around lines 21 - 27, Update the
placeholder in the MCP endpoint definition to use a `/mcp`-style HTTPS example
instead of the legacy `/sse` path, aligning it with the preferred Streamable
HTTP transport. Leave the surrounding label, description, and validation
settings unchanged.

@l1shen
l1shen merged commit 8e5501c into oomol-lab:main Jul 30, 2026
4 checks passed
@l1shen
l1shen deleted the grim-rooster branch July 30, 2026 11:27
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.

1 participant