Skip to content

feat: add an MCP service that supports converting Swagger documents i…#175

Open
gargameljyh wants to merge 11 commits into
devfrom
jyh/swagger-mcp-server
Open

feat: add an MCP service that supports converting Swagger documents i…#175
gargameljyh wants to merge 11 commits into
devfrom
jyh/swagger-mcp-server

Conversation

@gargameljyh
Copy link
Copy Markdown
Collaborator

@gargameljyh gargameljyh commented May 21, 2026

Add an MCP service that supports converting Swagger documents into MCP tools

Summary by CodeRabbit

  • New Features

    • Adds a Swagger/OpenAPI MCP server that parses specs into dynamic MCP tools and AI-SDK tools, plus meta-tools to parse/list/manage them.
    • HTTP & stdio transports with session limits, idle eviction, optional health endpoint, and an Assets API to browse/serve Swagger assets.
    • Integrates a built-in Swagger system prompt into chat flows; supports cookie params and request timeouts for API calls.
  • Chores

    • Adds example env (Chinese translations), TypeScript config, package metadata, server registration, and static asset copy for builds.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a playground Swagger→MCP service: project scaffolding, env/config loader and bootstrap, OpenAPI input security and parser, operation extraction and schema→Zod conversion, API executor with timeout/cookie handling, MCP HTTP/stdio transports with per-session registry and assets API, meta-tools and dynamic registry, AI-SDK adapters, and playground integration.

Changes

Swagger MCP Server

Layer / File(s) Summary
Project setup and environment
sites/playground/swagger-mcp-server/package.json, sites/playground/swagger-mcp-server/tsconfig.json, sites/playground/swagger-mcp-server/.env.example, sites/playground/server/package.json, sites/playground/server/vite.config.ts
New package manifest, TypeScript config, example env templates, workspace dependency, and Vite static-copy for Swagger assets.
Core bootstrap and shared instance
sites/playground/swagger-mcp-server/src/config.ts, sites/playground/swagger-mcp-server/src/types.ts, sites/playground/swagger-mcp-server/src/main.ts, sites/playground/swagger-mcp-server/src/index.ts, sites/playground/swagger-mcp-server/src/instance.ts
Environment loader loadServerConfigFromEnv(), core TypeScript types, main.ts to start HTTP or stdio transports, lazy singletons exposing an in-process McpServer and SwaggerToolRegistry, and barrel exports.
Swagger input security & parsing
sites/playground/swagger-mcp-server/src/swagger/swagger-input-security.ts, sites/playground/swagger-mcp-server/src/swagger/parse-swagger-input.ts
Policy-driven controls for inline/URL/file inputs, hostname/IP allowlist/blocks, redirect-guarded fetch with timeout, local-file root enforcement, and parsing into OpenAPIV3.Document with base-URL resolution.
Extract operations & schema→Zod
sites/playground/swagger-mcp-server/src/swagger/extract-operations.ts, sites/playground/swagger-mcp-server/src/swagger/schema-to-zod.ts, sites/playground/swagger-mcp-server/src/swagger/index.ts
Sanitizes tool names, merges path/operation parameters, selects JSON request bodies, extracts ApiOperation records, and converts OpenAPI schemas/parameters/request bodies into Zod validators and shapes.
API execution: URL, headers, cookies, timeouts
sites/playground/swagger-mcp-server/src/swagger/http-executor.ts
Fills path templates, builds URLs and query strings, merges cookie parameters into a single Cookie header, applies AbortSignal timeouts, parses JSON/text responses, and returns structured status/headers/body.
HTTP transport, session management, and assets API
sites/playground/swagger-mcp-server/src/server/http-transport.ts, sites/playground/swagger-mcp-server/src/server/assets-api.ts, sites/playground/swagger-mcp-server/src/server/mcp-session-registry.ts, sites/playground/swagger-mcp-server/src/server/index.ts
Express-based MCP /mcp POST/GET/DELETE endpoints managing per-session transports, optional /health, session registry with eviction and idle TTL, and safe assets listing/serving via registerAssetsApi().
Meta tools, registry, and registration
sites/playground/swagger-mcp-server/src/server/register-meta-tools.ts, sites/playground/swagger-mcp-server/src/server/swagger-tool-registry.ts, sites/playground/swagger-mcp-server/src/server/register-swagger-tools.ts
Adds parse_swagger and list_tools meta tools, SwaggerToolRegistry for dynamic registration/clearing/listing, and registration of OpenAPI operations as MCP tools with Zod input schemas and execute handlers.
AI SDK adapter and playground integration
sites/playground/swagger-mcp-server/src/build-ai-sdk-tools.ts, sites/playground/server/src/swagger-mcp/*, sites/playground/server/src/chat-genui.ts, sites/playground/server/src/chat-template.ts
Wraps registered MCP tools as AI SDK tool() instances, includes built-in Swagger tools and system prompt in playground flows, excludes built-in Swagger MCP URLs when generating external MCP tools, and copies Swagger assets into the client build.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ExpressApp
  participant SessionMgr
  participant McpServer
  participant SwaggerRegistry
  Client->>ExpressApp: POST /mcp (init or tool call)
  ExpressApp->>SessionMgr: lookup or create session (mcp-session-id)
  SessionMgr->>McpServer: forward request via transport
  McpServer->>SwaggerRegistry: parse_swagger / list_tools / tool handler
  SwaggerRegistry->>SwaggerRegistry: parse spec, extract ops, register tools
  SwaggerRegistry-->>McpServer: registration result or tool output
  McpServer-->>SessionMgr: execution result
  SessionMgr-->>ExpressApp: response
  ExpressApp-->>Client: final response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • rhlin
  • gimmyhehe

Poem

🐰 I hop through specs and lines of YAML bright,
Tools grow from paths and methods in the night,
Sessions keep their tokens, transports hum light,
Prompts and assets mingle, the playground's alight,
A rabbit nods: MCP tools feel just right!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly describes the main feature being added: an MCP service that converts Swagger documents. It accurately reflects the primary objective of the changeset.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jyh/swagger-mcp-server

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🤖 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 `@sites/playground/swagger-mcp-server/.env.example`:
- Line 12: Fix the typo in the .env.example comment: change the misspelled
"assests" to "assets" in the comment that starts with "# Assets API(读取 assests
目录)" so it matches routes and API naming; ensure the updated comment reads "#
Assets API(读取 assets 目录)" to avoid confusion.

In `@sites/playground/swagger-mcp-server/src/config.ts`:
- Around line 11-13: The config currently sets port via Number(process.env.PORT
?? 3100) which can yield NaN or out-of-range values; update the port logic in
config.ts (the port export) to parse and validate process.env.PORT (e.g., use
parseInt, check isFinite and integer, and clamp/validate to the TCP port range
1–65535) and fall back to the default 3100 when invalid; ensure the resulting
port is a safe integer before returning it in the config so listeners never
receive NaN or an invalid port.

In `@sites/playground/swagger-mcp-server/src/server/assets-api.ts`:
- Around line 44-48: The API is leaking server internals by returning ASSETS_DIR
and raw error messages; update the response in the handler that currently does
res.json({ directory: ASSETS_DIR, files }) and the catch blocks (where
res.status(500).json({ error: message }) is used) to avoid exposing absolute
paths or full error details: return a sanitized directory identifier (e.g.,
path.basename(ASSETS_DIR) or a fixed logical name like "assets") and only return
a generic error message to clients (e.g., { error: "internal server error" });
log the full error server-side using the existing logger/console (preserve error
via console.error or processLogger.error) so debugging info is retained but not
sent to clients; make the same change for the other occurrence that uses the
same catch pattern.
- Line 6: ASSETS_DIR currently exposes an absolute path and raw error messages;
change the code so the server still resolves the assets folder (keep the
existing resolve(dirname(fileURLToPath(import.meta.url)), '../../assests')
logic) but never return that value to clients (remove any response field like
directory: ASSETS_DIR). In handlers that stat/read files (e.g., the routes that
use fs.stat/fs.readFile), validate and sanitize requested paths to prevent path
traversal (use path.join with the user-supplied name and assert
path.relative(ASSETS_DIR, joined) does not start with '..'), catch filesystem
errors and map them to generic HTTP responses (404 for not found, 400 for
invalid request, 500 for other errors) and return non-sensitive messages (e.g.,
"file not found" / "internal server error") instead of error.message. Also keep
the folder name exactly as "assests" to match disk and ensure any logging of
errors stays server-side (log full error with processLogger or console) without
sending details to the client.

In `@sites/playground/swagger-mcp-server/src/server/http-transport.ts`:
- Around line 14-37: transports is unbounded; add a max session limit and
TTL-based expiry to avoid memory growth: introduce a MaxSessions constant and a
per-session metadata map (e.g., lastAccessed/created timestamps) and enforce
when creating a new StreamableHTTPServerTransport in mcpPostHandler — if
transports.size >= MaxSessions prune expired entries first and then the
oldest/least-recently-used sessions before inserting; also set a per-transport
inactivity timeout (or reuse transport.onclose) to delete its entry when TTL
elapses; apply the same pruning/expiry logic where transports is
accessed/created (including the code paths around the existing transport.onclose
and the referenced lines 81-83).

In `@sites/playground/swagger-mcp-server/src/server/register-meta-tools.ts`:
- Around line 89-93: Existing dynamic tools are being cleared before the new
Swagger spec is validated, so if parseSwaggerInput(swagger) fails the server
ends up with no dynamic tools; change the flow to call
parseSwaggerInput(swagger) first, verify it succeeds, and only then call
registry.clearDynamicTools() when replaceExisting is true and proceed to
register the new tools (e.g., in the same block that currently follows
parseSwaggerInput); ensure errors from parseSwaggerInput are caught and
returned/propagated without mutating registry state so existing tools remain
intact.

In `@sites/playground/swagger-mcp-server/src/swagger/extract-operations.ts`:
- Around line 48-58: The current logic picks jsonContent by searching content
entries but then picks contentType by blindly taking the first content key,
which can mismatch; change the selection to capture the matched content key when
finding the JSON entry (e.g., store the result of Object.entries(...).find(...)
as jsonEntry and set jsonContent = jsonEntry?.[1]) and then compute contentType
from that same key (jsonEntry?.[0]) falling back to application/json or the
first key only if no json entry was found; update references to jsonContent and
contentType accordingly so they always correspond to the same content entry from
requestBody.

In `@sites/playground/swagger-mcp-server/src/swagger/http-executor.ts`:
- Line 86: The fetch call using "url" and "init" (result assigned to "response")
needs timeout protection to avoid indefinite hangs—wrap the request with an
AbortController: create a controller, attach controller.signal to the fetch init
(or to a shallow-cloned init), start a timer (e.g., HTTP_TIMEOUT_MS) that calls
controller.abort() on expiry, then clear the timer after the await fetch
completes; ensure you catch AbortError and throw or convert to a useful timeout
error. Add a named timeout constant (e.g., HTTP_TIMEOUT_MS) and update the fetch
invocation to use the controller.signal so the request is canceled on timeout.
- Around line 49-61: The switch currently ignores cookie params; add
cookieArgs[param.name] = value in the 'cookie' case and then, in the
request-building code (where pathArgs, queryArgs and headerArgs are used to
create the outgoing HTTP request), serialize cookieArgs into a single "Cookie"
header (e.g., join name=value pairs with "; ") and merge it into headerArgs
(preserving any existing Cookie header). Use proper URI-encoding for cookie
values and ensure headerArgs['Cookie'] is set before the HTTP client is invoked.

In `@sites/playground/swagger-mcp-server/src/swagger/parse-swagger-input.ts`:
- Around line 31-46: The readSpecContent function presently allows arbitrary
HTTP(S) fetches and filesystem reads, enabling SSRF and local file access; fix
this by validating and restricting sources: for URL sources (detected by the
/^https?:\/\// check and fetch usage) enforce an allowlist of trusted
hostnames/origins and perform IP resolution to block private/reserved ranges
before calling fetch, and for file sources (code paths that call isAbsolute,
resolve and readFile) disallow absolute paths and paths that escape a safe base
directory (use a configured safeDocsDir and reject paths with ../ after path
normalization); also keep isInlineSwaggerContent handling as-is and return clear
errors when a source is rejected.
- Around line 36-41: The remote fetch can hang; wrap fetch(source) with an
AbortController and a timeout: create an AbortController, start a timer (e.g.
configurable constant like SWAGGER_FETCH_TIMEOUT_MS) that calls
controller.abort() after the timeout, pass controller.signal into fetch(source,
{ signal }), then clear the timer after fetch completes; catch the abort error
and throw a clear timeout/abort Error. Update the call site where fetch(source)
is used in parse-swagger-input (the fetch(...) block) to use this
AbortController pattern so remote Swagger fetches are aborted after the timeout.

In `@sites/playground/swagger-mcp-server/src/swagger/schema-to-zod.ts`:
- Around line 64-73: The code currently returns a z.record when
schema.additionalProperties is present, dropping explicit properties in shape;
instead, keep the base object (built from shape or a record when there are no
properties) and apply a catchall for additionalProperties: when
additionalProperties === true use .catchall(z.unknown()), and when it is an
object use .catchall(schemaToZod(schema.additionalProperties, true)); ensure you
still wrap the final result with withDescription and reference the symbols
schemaToZod, withDescription, shape, and schema.additionalProperties when making
the change.
🪄 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: 0272c1ed-9cd2-48c2-8bb0-cc924dc3ea71

📥 Commits

Reviewing files that changed from the base of the PR and between 12949d3 and 272090c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (20)
  • sites/playground/swagger-mcp-server/.env.example
  • sites/playground/swagger-mcp-server/assests/aad.json
  • sites/playground/swagger-mcp-server/package.json
  • sites/playground/swagger-mcp-server/src/config.ts
  • sites/playground/swagger-mcp-server/src/index.ts
  • sites/playground/swagger-mcp-server/src/main.ts
  • sites/playground/swagger-mcp-server/src/server/assets-api.ts
  • sites/playground/swagger-mcp-server/src/server/create-mcp-server.ts
  • sites/playground/swagger-mcp-server/src/server/http-transport.ts
  • sites/playground/swagger-mcp-server/src/server/index.ts
  • sites/playground/swagger-mcp-server/src/server/register-meta-tools.ts
  • sites/playground/swagger-mcp-server/src/server/register-swagger-tools.ts
  • sites/playground/swagger-mcp-server/src/server/swagger-tool-registry.ts
  • sites/playground/swagger-mcp-server/src/swagger/extract-operations.ts
  • sites/playground/swagger-mcp-server/src/swagger/http-executor.ts
  • sites/playground/swagger-mcp-server/src/swagger/index.ts
  • sites/playground/swagger-mcp-server/src/swagger/parse-swagger-input.ts
  • sites/playground/swagger-mcp-server/src/swagger/schema-to-zod.ts
  • sites/playground/swagger-mcp-server/src/types.ts
  • sites/playground/swagger-mcp-server/tsconfig.json

Comment thread sites/playground/swagger-mcp-server/.env.example Outdated
Comment thread sites/playground/swagger-mcp-server/src/config.ts Outdated
Comment thread sites/playground/swagger-mcp-server/src/server/assets-api.ts Outdated
Comment thread sites/playground/swagger-mcp-server/src/server/assets-api.ts Outdated
Comment thread sites/playground/swagger-mcp-server/src/server/http-transport.ts Outdated
Comment thread sites/playground/swagger-mcp-server/src/swagger/http-executor.ts
Comment thread sites/playground/swagger-mcp-server/src/swagger/http-executor.ts Outdated
Comment thread sites/playground/swagger-mcp-server/src/swagger/parse-swagger-input.ts Outdated
Comment thread sites/playground/swagger-mcp-server/src/swagger/parse-swagger-input.ts Outdated
Comment thread sites/playground/swagger-mcp-server/src/swagger/schema-to-zod.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sites/playground/swagger-mcp-server/src/server/http-transport.ts (1)

74-81: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Harden GET/DELETE handlers with explicit error handling.

handleRequest errors in these handlers are not wrapped, so failures can bypass consistent JSON-RPC error responses.

Proposed patch
   const mcpGetHandler = async (req: Request, res: Response) => {
-    const sessionId = req.headers['mcp-session-id'] as string | undefined;
-    if (!sessionId || !transports[sessionId]) {
-      res.status(400).send('Invalid or missing session ID');
-      return;
-    }
-    await transports[sessionId].handleRequest(req, res);
+    try {
+      const sessionId = req.headers['mcp-session-id'] as string | undefined;
+      if (!sessionId || !transports[sessionId]) {
+        res.status(400).send('Invalid or missing session ID');
+        return;
+      }
+      await transports[sessionId].handleRequest(req, res);
+    } catch (error) {
+      console.error('MCP GET request error:', error);
+      if (!res.headersSent) {
+        res.status(500).send('Internal server error');
+      }
+    }
   };
@@
   const mcpDeleteHandler = async (req: Request, res: Response) => {
-    const sessionId = req.headers['mcp-session-id'] as string | undefined;
-    if (!sessionId || !transports[sessionId]) {
-      res.status(400).send('Invalid or missing session ID');
-      return;
-    }
-    await transports[sessionId].handleRequest(req, res);
+    try {
+      const sessionId = req.headers['mcp-session-id'] as string | undefined;
+      if (!sessionId || !transports[sessionId]) {
+        res.status(400).send('Invalid or missing session ID');
+        return;
+      }
+      await transports[sessionId].handleRequest(req, res);
+    } catch (error) {
+      console.error('MCP DELETE request error:', error);
+      if (!res.headersSent) {
+        res.status(500).send('Internal server error');
+      }
+    }
   };

Also applies to: 83-90

🤖 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 `@sites/playground/swagger-mcp-server/src/server/http-transport.ts` around
lines 74 - 81, mcpGetHandler (and the analogous DELETE handler) calls
transports[sessionId].handleRequest without protection; wrap the await transport
call in a try/catch, log the caught error, and send a consistent JSON-RPC error
response (e.g., status 500 with { jsonrpc: "2.0", error: { code: -32000,
message: "Internal error", data: error.message }, id: null }) instead of letting
the exception propagate; ensure you still validate
sessionId/transports[sessionId] first and reference
transports[sessionId].handleRequest in your fix so the handler names are updated
consistently.
♻️ Duplicate comments (2)
sites/playground/swagger-mcp-server/src/server/http-transport.ts (1)

25-25: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add a session cap to avoid unbounded in-memory growth.

transports remains unbounded; repeated session initialization can grow memory indefinitely.

Proposed patch
 type TransportMap = Record<string, StreamableHTTPServerTransport>;
+const MAX_SESSIONS = 1000;
@@
       } else if (!sessionId && isInitializeRequest(req.body)) {
+        if (Object.keys(transports).length >= MAX_SESSIONS) {
+          res.status(503).json({
+            jsonrpc: '2.0',
+            error: { code: -32001, message: 'Server busy: session limit reached' },
+            id: null,
+          });
+          return;
+        }
+
         transport = new StreamableHTTPServerTransport({

Also applies to: 35-46

🤖 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 `@sites/playground/swagger-mcp-server/src/server/http-transport.ts` at line 25,
The global transports map (transports: TransportMap) is unbounded and can grow
indefinitely; add a session cap by replacing or wrapping transports with a
bounded structure (e.g., an LRU or capped Map) and a MAX_SESSIONS constant,
ensure every site where transports is mutated (places that create or set entries
into transports) performs eviction when the cap is exceeded (evict
oldest/least-recently-used entry and close/cleanup its transport), and update
access paths so reads update recency if using LRU—adjust logic around the
existing transports/TransportMap usage (including the blocks around lines 35-46
that create sessions) to use the capped container and perform proper cleanup on
removal.
sites/playground/swagger-mcp-server/src/server/assets-api.ts (1)

52-56: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stop exposing filesystem paths and internal error details in responses.

The API still returns assetsDir and raw exception messages, which leaks server internals.

Proposed hardening patch
-      res.json({ directory: assetsDir, files });
+      res.json({ files });
     } catch (error) {
-      const message = error instanceof Error ? error.message : String(error);
-      res.status(500).json({ error: message });
+      console.error('Failed to list assets:', error);
+      res.status(500).json({ error: 'Internal server error' });
     }
@@
-      const message = error instanceof Error ? error.message : String(error);
-      res.status(500).json({ error: message });
+      console.error('Failed to read asset file:', error);
+      res.status(500).json({ error: 'Internal server error' });
     }

Also applies to: 75-77

🤖 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 `@sites/playground/swagger-mcp-server/src/server/assets-api.ts` around lines 52
- 56, Remove exposure of internal filesystem paths and raw exceptions: stop
returning the assetsDir value in the JSON response and replace it with a
non-sensitive placeholder (e.g., omit directory or return a boolean/summary),
and change the catch block to send a generic error message like "Internal server
error" to clients while logging the full error server-side via console.error or
processLogger; specifically update the res.json({ directory: assetsDir, files
})/res.json({ files }) usage and the catch handling that builds message from
error (the assetsDir variable and the catch block in assets-api.ts) and apply
the same change to the similar response/catch at the other occurrence referenced
(lines 75-77).
🤖 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 `@sites/playground/server/src/chat-genui.ts`:
- Around line 295-299: The code currently only warns on duplicateToolNames and
then merges swaggerTools, mcpTools, agentTools, and skillTools into tools,
causing silent shadowing; change this to fail fast by checking
duplicateToolNames.size and throwing an Error (including the duplicate names)
instead of console.warn before the merge of swaggerTools, mcpTools, agentTools,
and skillTools so callers cannot proceed with ambiguous tool resolution.
- Around line 274-276: The filter treating MCP server entries as disabled when
`enabled` is omitted should be adjusted so unspecified `enabled` defaults to
true; update the predicate used to compute `externalMcpServers` (and the
identical predicate in the other usage in chat-template) from `s.enabled &&
!isBuiltinSwaggerMcpUrl(s.url)` to a check that treats undefined as true (e.g.,
use nullish-coalescing or explicit default logic for `s.enabled`) while
preserving the `!isBuiltinSwaggerMcpUrl(s.url)` part; apply this change to the
code that computes `externalMcpServers` in chat-genui and the corresponding
filter in chat-template.

In `@sites/playground/server/src/swagger-mcp/build-tools.ts`:
- Around line 27-29: The port matching logic incorrectly treats a URL with no
explicit port (target.port empty) as port 80 only when PORT=3008, causing
localhost without a port to be treated as builtin; fix by computing an effective
port for the parsed URL and comparing that to the configured port: derive
effectiveTargetPort = target.port || (target.protocol === 'https:' ? '443' :
'80') and then set portMatches = effectiveTargetPort === port (remove the
special-case clause that checks targetPort === '80' && port === '3008' &&
!target.port). Keep the rest of the builtin detection (isLocalHost and pathname
comparison using target.pathname and normalizedPath) unchanged so variables
targetPort, port, target.port, target.protocol, isLocalHost, portMatches,
target.pathname, and normalizedPath remain the reference points.

In `@sites/playground/server/vite.config.ts`:
- Around line 30-33: The static-copy config in vite.config.ts contains a typo:
both src and dest strings use "assests" which will break asset copying; update
the values referenced in the static-copy object (the src string
"../swagger-mcp-server/assests" and the dest string "./assests") to use the
correct folder name "assets" so the src becomes "../swagger-mcp-server/assets"
and the dest becomes "./assets".

In `@sites/playground/swagger-mcp-server/src/instance.ts`:
- Around line 10-33: The current getSwaggerMcpServer function returns
process-wide singletons (swaggerMcpServer, swaggerToolRegistry) causing
cross-user tool registration bleed; replace this pattern by removing the
module-level singletons and exposing a factory that creates a fresh McpServer
and SwaggerToolRegistry per call (e.g., createSwaggerMcpServer or change
getSwaggerMcpServer to accept a per-request context and always instantiate new
McpServer/SwaggerToolRegistry), instantiate McpServer (using the same
constructor args), new SwaggerToolRegistry(server), call
registerMetaTools(server, registry), and return the newly created server (and
make registry available to the caller if needed) so that parse_swagger
registrations are scoped to the caller instead of global state.
- Around line 47-51: listSwaggerMcpToolEntries currently reads the private
McpServer._registeredTools; change this to use an explicit registry you maintain
when calling server.registerTool (or intercept/override the protocol response
for "tools/list") instead of accessing _registeredTools directly: update
getSwaggerMcpServer usage to consult your own Map/array of registered tool
definitions (the source-of-truth you populate at register time) and have
listSwaggerMcpToolEntries return entries from that registry; alternatively
implement a wrapper around server.registerTool that records each tool and expose
a public getter used by listSwaggerMcpToolEntries rather than touching
_registeredTools.

In `@sites/playground/swagger-mcp-server/src/server/assets-api.ts`:
- Around line 59-63: The call to resolveSafeAssetPath is currently outside the
route's try/catch so any thrown error can bypass your 400/404/500 mapping; wrap
the path resolution and the subsequent file access logic inside the same
try/catch within the app.get handler (the route using resolveSafeAssetPath,
assetsDir and req.params.filename) and map errors consistently—return 400 for
invalid filename results, 404 when file not found, and 500 for unexpected
exceptions—so all thrown errors from resolveSafeAssetPath are caught and handled
by the existing error-response flow.

---

Outside diff comments:
In `@sites/playground/swagger-mcp-server/src/server/http-transport.ts`:
- Around line 74-81: mcpGetHandler (and the analogous DELETE handler) calls
transports[sessionId].handleRequest without protection; wrap the await transport
call in a try/catch, log the caught error, and send a consistent JSON-RPC error
response (e.g., status 500 with { jsonrpc: "2.0", error: { code: -32000,
message: "Internal error", data: error.message }, id: null }) instead of letting
the exception propagate; ensure you still validate
sessionId/transports[sessionId] first and reference
transports[sessionId].handleRequest in your fix so the handler names are updated
consistently.

---

Duplicate comments:
In `@sites/playground/swagger-mcp-server/src/server/assets-api.ts`:
- Around line 52-56: Remove exposure of internal filesystem paths and raw
exceptions: stop returning the assetsDir value in the JSON response and replace
it with a non-sensitive placeholder (e.g., omit directory or return a
boolean/summary), and change the catch block to send a generic error message
like "Internal server error" to clients while logging the full error server-side
via console.error or processLogger; specifically update the res.json({
directory: assetsDir, files })/res.json({ files }) usage and the catch handling
that builds message from error (the assetsDir variable and the catch block in
assets-api.ts) and apply the same change to the similar response/catch at the
other occurrence referenced (lines 75-77).

In `@sites/playground/swagger-mcp-server/src/server/http-transport.ts`:
- Line 25: The global transports map (transports: TransportMap) is unbounded and
can grow indefinitely; add a session cap by replacing or wrapping transports
with a bounded structure (e.g., an LRU or capped Map) and a MAX_SESSIONS
constant, ensure every site where transports is mutated (places that create or
set entries into transports) performs eviction when the cap is exceeded (evict
oldest/least-recently-used entry and close/cleanup its transport), and update
access paths so reads update recency if using LRU—adjust logic around the
existing transports/TransportMap usage (including the blocks around lines 35-46
that create sessions) to use the capped container and perform proper cleanup on
removal.
🪄 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: 324571f3-91a7-4604-b6ff-bb1b5b28916f

📥 Commits

Reviewing files that changed from the base of the PR and between 272090c and e9c1342.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • sites/playground/server/.env.example
  • sites/playground/server/index.ts
  • sites/playground/server/package.json
  • sites/playground/server/src/chat-genui.ts
  • sites/playground/server/src/chat-template.ts
  • sites/playground/server/src/swagger-mcp/build-tools.ts
  • sites/playground/server/src/swagger-mcp/index.ts
  • sites/playground/server/vite.config.ts
  • sites/playground/swagger-mcp-server/.env.example
  • sites/playground/swagger-mcp-server/package.json
  • sites/playground/swagger-mcp-server/src/build-ai-sdk-tools.ts
  • sites/playground/swagger-mcp-server/src/index.ts
  • sites/playground/swagger-mcp-server/src/instance.ts
  • sites/playground/swagger-mcp-server/src/server/assets-api.ts
  • sites/playground/swagger-mcp-server/src/server/create-mcp-server.ts
  • sites/playground/swagger-mcp-server/src/server/http-transport.ts
  • sites/playground/swagger-mcp-server/src/server/index.ts
✅ Files skipped from review due to trivial changes (2)
  • sites/playground/server/.env.example
  • sites/playground/swagger-mcp-server/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • sites/playground/swagger-mcp-server/.env.example

Comment thread sites/playground/server/src/chat-genui.ts
Comment thread sites/playground/server/src/chat-genui.ts
Comment thread sites/playground/server/src/swagger-mcp/build-tools.ts Outdated
Comment thread sites/playground/server/vite.config.ts Outdated
Comment thread sites/playground/swagger-mcp-server/src/instance.ts
Comment thread sites/playground/swagger-mcp-server/src/instance.ts Outdated
Comment thread sites/playground/swagger-mcp-server/src/server/assets-api.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
sites/playground/swagger-mcp-server/src/server/assets-api.ts (1)

52-56: ⚠️ Potential issue | 🟠 Major

Information disclosure issues remain unaddressed.

The security concerns flagged in previous reviews persist:

  • Line 52: directory: assetsDir exposes the absolute filesystem path to clients.
  • Lines 54-55, 75-76: Raw error.message values leak internal details.

These should be remediated as outlined in the earlier review comments (remove directory field, return generic error messages, log full errors server-side only).

Also applies to: 75-76

🤖 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 `@sites/playground/swagger-mcp-server/src/server/assets-api.ts` around lines 52
- 56, Remove the sensitive absolute path and replace detailed error messages
with generic responses: stop including the assetsDir field in the successful
response (only return files), and in the catch blocks that currently build
message from error (the catch that calls res.status(500).json and the similar
handler around lines 75-76) send a generic error payload like { error: "Internal
server error" } to clients while logging the full error server-side (use
console.error or the existing logger) so the original error.message/stack is not
leaked. Target the res.json call that currently returns { directory: assetsDir,
files } and the catch blocks that use error instanceof Error ? error.message :
String(error) to implement these changes.
🤖 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.

Duplicate comments:
In `@sites/playground/swagger-mcp-server/src/server/assets-api.ts`:
- Around line 52-56: Remove the sensitive absolute path and replace detailed
error messages with generic responses: stop including the assetsDir field in the
successful response (only return files), and in the catch blocks that currently
build message from error (the catch that calls res.status(500).json and the
similar handler around lines 75-76) send a generic error payload like { error:
"Internal server error" } to clients while logging the full error server-side
(use console.error or the existing logger) so the original error.message/stack
is not leaked. Target the res.json call that currently returns { directory:
assetsDir, files } and the catch blocks that use error instanceof Error ?
error.message : String(error) to implement these changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 07b68ba1-2dc4-4b75-9f12-b14d36371cb4

📥 Commits

Reviewing files that changed from the base of the PR and between e9c1342 and b1f5629.

📒 Files selected for processing (6)
  • sites/playground/server/src/swagger-mcp/index.ts
  • sites/playground/server/vite.config.ts
  • sites/playground/swagger-mcp-server/.env.example
  • sites/playground/swagger-mcp-server/assets/aad.json
  • sites/playground/swagger-mcp-server/src/config.ts
  • sites/playground/swagger-mcp-server/src/server/assets-api.ts
✅ Files skipped from review due to trivial changes (1)
  • sites/playground/swagger-mcp-server/.env.example
🚧 Files skipped from review as they are similar to previous changes (3)
  • sites/playground/server/vite.config.ts
  • sites/playground/swagger-mcp-server/src/config.ts
  • sites/playground/server/src/swagger-mcp/index.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
sites/playground/swagger-mcp-server/src/swagger/http-executor.ts (2)

120-120: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add timeout/abort protection to outbound fetch.

Line 120 can block indefinitely if the upstream hangs, which can stall MCP tool execution under load.

Proposed fix
+const HTTP_TIMEOUT_MS = 15_000;
@@
-  const response = await fetch(url, init);
+  const controller = new AbortController();
+  const timer = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS);
+  let response: Response;
+  try {
+    response = await fetch(url, { ...init, signal: controller.signal });
+  } catch (error) {
+    if (error instanceof Error && error.name === 'AbortError') {
+      throw new Error(`Request timed out after ${HTTP_TIMEOUT_MS}ms`);
+    }
+    throw error;
+  } finally {
+    clearTimeout(timer);
+  }
🤖 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 `@sites/playground/swagger-mcp-server/src/swagger/http-executor.ts` at line
120, The outbound fetch at "response = await fetch(url, init);" needs
abort/timeout protection: create an AbortController, set a timeout (e.g.
configurable TIMEOUT_MS) that calls controller.abort(), merge controller.signal
into the request init (respecting any existing init.signal by using
controller.signal as the effective signal or linking signals), call fetch with
that signal, clear the timeout (clearTimeout) after fetch completes, and ensure
downstream callers handle AbortError/DOMException appropriately; update the
function (where fetch is invoked in http-executor.ts) to implement this pattern
so hung upstreams cannot stall execution.

44-44: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Encode cookie names before composing the Cookie header.

Line 44 inserts raw parameter names into the cookie header. If a Swagger param name contains cookie delimiters (;, =), this can alter cookie semantics and send unintended cookie pairs.

Proposed fix
-    .map(([name, value]) => `${name}=${encodeCookieValue(value)}`);
+    .map(([name, value]) => `${encodeURIComponent(name)}=${encodeCookieValue(value)}`);
🤖 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 `@sites/playground/swagger-mcp-server/src/swagger/http-executor.ts` at line 44,
The cookie header builder currently inserts raw cookie names in the map callback
(.map(([name, value]) => `${name}=${encodeCookieValue(value)}`), which can allow
delimiters in names to break semantics; update this expression to encode the
cookie name as well (e.g., use encodeCookieValue(name) or a dedicated
encodeCookieName(name)) so the composed string becomes
`${encodeCookieName(name)}=${encodeCookieValue(value)}` and ensure any helper
you add/reuse follows the same encoding rules as encodeCookieValue.
🤖 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 `@sites/playground/swagger-mcp-server/src/server/http-transport.ts`:
- Around line 46-63: The current capacity check
(sessionRegistry.canAcceptSession()) is racy because registration happens later
in StreamableHTTPServerTransport. Fix by making session creation atomic:
generate a session id up front (using the same randomUUID() logic), then attempt
to reserve/register that id in sessionRegistry immediately (add or use a
register/reserve method that returns success/failure) before constructing or
returning the transport; if the immediate register/reserve fails respond with
503, otherwise create the StreamableHTTPServerTransport with that id and ensure
onsessioninitialized either confirms or finalizes the registration (or becomes a
no-op) so no two concurrent initializations can exceed maxSessions. Reference
the functions/objects: sessionRegistry.canAcceptSession(),
sessionRegistry.register(), StreamableHTTPServerTransport, sessionIdGenerator,
onsessioninitialized.
- Around line 130-133: The code currently disposes a caller-provided
sessionRegistry on SIGINT/SIGTERM; change this so only registries created
locally by this module are disposed on process signals: introduce a boolean like
createdLocalSessionRegistry that you set to true when you instantiate
sessionRegistry locally (where sessionRegistry is constructed), leave it false
when sessionRegistry is injected via options, and guard the
process.once('SIGINT'...) / process.once('SIGTERM'...) registration (and the
dispose callback) behind that flag so you do not attach signal handlers or call
sessionRegistry.dispose() for caller-owned registries.

---

Duplicate comments:
In `@sites/playground/swagger-mcp-server/src/swagger/http-executor.ts`:
- Line 120: The outbound fetch at "response = await fetch(url, init);" needs
abort/timeout protection: create an AbortController, set a timeout (e.g.
configurable TIMEOUT_MS) that calls controller.abort(), merge controller.signal
into the request init (respecting any existing init.signal by using
controller.signal as the effective signal or linking signals), call fetch with
that signal, clear the timeout (clearTimeout) after fetch completes, and ensure
downstream callers handle AbortError/DOMException appropriately; update the
function (where fetch is invoked in http-executor.ts) to implement this pattern
so hung upstreams cannot stall execution.
- Line 44: The cookie header builder currently inserts raw cookie names in the
map callback (.map(([name, value]) => `${name}=${encodeCookieValue(value)}`),
which can allow delimiters in names to break semantics; update this expression
to encode the cookie name as well (e.g., use encodeCookieValue(name) or a
dedicated encodeCookieName(name)) so the composed string becomes
`${encodeCookieName(name)}=${encodeCookieValue(value)}` and ensure any helper
you add/reuse follows the same encoding rules as encodeCookieValue.
🪄 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: 36b012ab-ba45-473f-b8a9-5daca65c10fc

📥 Commits

Reviewing files that changed from the base of the PR and between b1f5629 and 38927d9.

📒 Files selected for processing (7)
  • sites/playground/server/src/swagger-mcp/index.ts
  • sites/playground/swagger-mcp-server/.env.example
  • sites/playground/swagger-mcp-server/src/server/assets-api.ts
  • sites/playground/swagger-mcp-server/src/server/http-transport.ts
  • sites/playground/swagger-mcp-server/src/server/index.ts
  • sites/playground/swagger-mcp-server/src/server/mcp-session-registry.ts
  • sites/playground/swagger-mcp-server/src/swagger/http-executor.ts
✅ Files skipped from review due to trivial changes (1)
  • sites/playground/swagger-mcp-server/.env.example
🚧 Files skipped from review as they are similar to previous changes (3)
  • sites/playground/swagger-mcp-server/src/server/index.ts
  • sites/playground/server/src/swagger-mcp/index.ts
  • sites/playground/swagger-mcp-server/src/server/assets-api.ts

Comment thread sites/playground/swagger-mcp-server/src/server/http-transport.ts Outdated
Comment thread sites/playground/swagger-mcp-server/src/server/http-transport.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@sites/playground/swagger-mcp-server/src/swagger/parse-swagger-input.ts`:
- Around line 60-61: The call to SwaggerParser.validate(raw) can resolve
external $ref pointers; update the call in parse-swagger-input.ts so
SwaggerParser.validate(raw, { resolve: { external: false } }) is used to disable
external resolution. Locate the parseRawSpec(...) -> raw variable and replace
the existing SwaggerParser.validate(raw) invocation with
SwaggerParser.validate(raw, { resolve: { external: false } }) to ensure external
HTTP/file $refs are not fetched.

In `@sites/playground/swagger-mcp-server/src/swagger/swagger-input-security.ts`:
- Around line 85-105: isBlockedHostname currently only rejects literal IP hosts,
allowing hostnames that resolve to loopback/RFC1918/link-local addresses to
bypass SSRF checks; update isBlockedHostname (and the similar logic around lines
142-149) to perform a DNS resolution of the input hostname first (use
dns.promises.resolve or resolve4/resolve6 to get all addresses), normalize the
original hostname as you already do, then for each resolved IP run the existing
IP logic (isIP, isPrivateIpv4, isPrivateIpv6, and the metadata.google.internal
check) and return blocked if any resolved address is
private/loopback/link-local; keep the urlHostnameAllowlist check but still
resolve and validate the resolved IPs even when the hostname matches the
allowlist to avoid allowlist-based bypass. Ensure the function remains
synchronous/async consistent with callers (make isBlockedHostname async if
necessary) and update all call sites accordingly.
🪄 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: ac3132a9-06a8-4ba1-93c4-e924296762ff

📥 Commits

Reviewing files that changed from the base of the PR and between 38927d9 and 4548da2.

📒 Files selected for processing (9)
  • sites/playground/server/.env.example
  • sites/playground/swagger-mcp-server/.env.example
  • sites/playground/swagger-mcp-server/src/server/register-meta-tools.ts
  • sites/playground/swagger-mcp-server/src/server/register-swagger-tools.ts
  • sites/playground/swagger-mcp-server/src/swagger/http-executor.ts
  • sites/playground/swagger-mcp-server/src/swagger/index.ts
  • sites/playground/swagger-mcp-server/src/swagger/parse-swagger-input.ts
  • sites/playground/swagger-mcp-server/src/swagger/swagger-input-security.ts
  • sites/playground/swagger-mcp-server/src/types.ts
✅ Files skipped from review due to trivial changes (1)
  • sites/playground/swagger-mcp-server/.env.example
🚧 Files skipped from review as they are similar to previous changes (5)
  • sites/playground/server/.env.example
  • sites/playground/swagger-mcp-server/src/swagger/index.ts
  • sites/playground/swagger-mcp-server/src/types.ts
  • sites/playground/swagger-mcp-server/src/server/register-swagger-tools.ts
  • sites/playground/swagger-mcp-server/src/server/register-meta-tools.ts

Comment thread sites/playground/swagger-mcp-server/src/swagger/parse-swagger-input.ts Outdated
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