Skip to content

feat(mcp): embed read-only MCP server for agent access to starred repos#250

Merged
AmintaCCCP merged 13 commits into
mainfrom
feat/mcp-server
Jul 23, 2026
Merged

feat(mcp): embed read-only MCP server for agent access to starred repos#250
AmintaCCCP merged 13 commits into
mainfrom
feat/mcp-server

Conversation

@AmintaCCCP

@AmintaCCCP AmintaCCCP commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • Embed a read-only MCP server (Streamable HTTP + legacy SSE) so agents (Claude Code / Cursor) can search AI-enriched starred repos without a CLI product (#164).
  • Support frontend+backend, Docker (nginx /mcp), and Electron standalone (loopback HTTP + IPC snapshot). Pure SPA hides the MCP settings tab.
  • Settings → MCP服务: enable toggle, always-viewable token + reset, one-click agent JSON config.
  • gsm_vector_search is listed only when vector search is configured/enabled.
  • Data safety: additive settings / Zustand fields only; default off; no destructive migrations.

Audit fixes (this PR)

  • Restore helmet CSP (do not disable for MCP).
  • No token mint on app mount; token created only when enabling MCP.
  • Stateless Streamable HTTP (fresh transport/server per request; vector tools reflect live config).
  • Disabled MCP returns 404 (does not advertise surface).
  • Electron MCP forces loopback bind (reject 0.0.0.0).
  • nginx uses prefix /mcp covering /mcp/sse paths; token redaction in log sanitizer.
  • Cap in-memory repo load for MCP providers.

Test plan

  • server unit tests: tests/mcp/repoSearch + pure token tests
  • Frontend: repoSearch + SearchBar tests pass
  • Backend with working better-sqlite3 native (Node LTS): run server/tests/mcp/routes.test.ts (DB suite skips on Node 26 without rebuild)
  • Manual: enable MCP in Settings with backend, copy JSON into Claude Code, call gsm_status / gsm_search_repos
  • Docker compose: http://localhost:8080/mcp with Bearer token
  • Electron: toggle local MCP, confirm bind is 127.0.0.1 only
  • Upgrade/downgrade smoke: existing repos/AI fields unchanged with MCP off

Summary by CodeRabbit

  • New Features
    • Added an optional MCP Server for AI agent access via Streamable HTTP and legacy SSE (token-gated), with vector-search tool support when enabled.
    • Added MCP controls in the Settings UI, including a token and copyable endpoint/agent config.
    • Added a desktop-only Electron local MCP server and session-wide MCP lifecycle.
  • Documentation
    • Updated README and DOCKER.md with MCP endpoint paths, token behavior, and agent config examples.
  • Bug Fixes
    • Improved MCP token handling/masking and unified repository search/filtering used by UI and MCP.
  • Tests
    • Added MCP and repository search/projection test coverage.
  • Chores
    • Updated desktop build workflow to rely on committed Electron sources.

Add Streamable HTTP + legacy SSE MCP endpoints so agents can search
AI-enriched stars without a separate CLI. Backend mounts /mcp (token-gated,
config read per request); Electron hosts loopback-only local MCP via IPC
snapshot. Settings panel enables toggle, viewable token, and one-click agent
JSON. Vector tool is listed only when vector search is configured. Additive
settings only — no data-loss migrations.

Security/audit hardening: restore helmet CSP, no token mint on mount,
stateless Streamable HTTP, loopback bind enforcement, log sanitization for
mcp tokens, nginx prefix proxy for /mcp paths.
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

Walkthrough

Adds optional MCP access for backend and Electron deployments, including authenticated Streamable HTTP/SSE transports, read-only repository tools, vector-search integration, settings UI, persisted configuration, Docker/nginx exposure, and shared repository search utilities.

Changes

MCP repository search

Layer / File(s) Summary
Shared search and projections
src/utils/repoSearch.ts, src/utils/repoSearch.test.ts, server/src/mcp/repoSearch.ts, server/tests/mcp/repoSearch.test.ts, src/components/SearchBar.tsx
Centralizes repository text search, filtering, sorting, pagination, and agent projections; refactors component search logic to use shared utilities.
MCP host normalization
src/utils/mcpHost.ts, src/utils/mcpHost.test.ts
Adds loopback bind constants and host normalization for Electron MCP configuration validation and coercion.

MCP data layer

Layer / File(s) Summary
Repository data and MCP tools
server/src/mcp/provider.ts, server/src/mcp/server.ts, server/src/mcp/settings.ts, server/src/mcp/tools.ts, server/package.json, server/src/services/logSanitizer.ts
Adds encrypted MCP settings persistence, repository access with in-memory caching, aggregate statistics, vector-search integration, read-only MCP tools, and credential masking.

MCP transports and clients

Layer / File(s) Summary
Backend HTTP transports
server/src/mcp/http.ts, server/src/routes/mcp.ts, server/src/index.ts, nginx.conf.template
Adds authenticated Streamable HTTP and legacy SSE routes with per-request gating, admin configuration endpoints, CORS headers for MCP session/auth fields, and nginx reverse-proxy paths.
Electron local runtime
electron/main.js, electron/mcpLocalServer.js, electron/preload.js, electron/package.json, scripts/build-desktop.js, .gitignore
Adds the local HTTP MCP server, IPC lifecycle controls, preload API bridge, committed Electron metadata, and source validation during desktop builds.
Frontend configuration
src/types/index.ts, src/store/useAppStore.ts, src/services/backendAdapter.ts, src/services/electronProxy.ts, src/services/mcpElectronBridge.ts, src/App.tsx
Adds persisted MCP configuration, backend/Electron API contracts, store actions, and session lifecycle synchronization.
MCP settings panel
src/components/settings/McpSettingsPanel.tsx, src/components/settings/index.ts, src/components/SettingsPanel.tsx
Adds settings UI for enabling MCP, managing tokens, copying endpoints, displaying vector availability, and conditional tab navigation.
Documentation
README.md, README_zh.md, DOCKER.md
Documents MCP capabilities, Docker/nginx endpoints, client configuration, token behavior, and optional vector-search tools.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client as AI Agent
  participant AuthMiddleware
  participant MCPTransport
  participant MCPTools
  participant DataProvider
  Client->>AuthMiddleware: POST /mcp with Bearer token
  AuthMiddleware->>MCPTransport: Validate enabled & token match
  MCPTransport->>MCPTools: Forward JSON-RPC (initialize/tools/call)
  MCPTools->>DataProvider: Execute tool (search/stats/vector-search)
  DataProvider-->>MCPTools: Return filtered/projected results
  MCPTools-->>Client: JSON-RPC result or Streamable HTTP/SSE chunk
Loading

Possibly related PRs

  • AmintaCCCP/GithubStarsManager#81: Overlaps with settings tab infrastructure and MCP tab navigation changes in src/components/SettingsPanel.tsx.
  • AmintaCCCP/GithubStarsManager#163: Shares Electron proxy IPC, persistent proxy configuration, and preload/main-process bridge implementation in electron/main.js and electron/preload.js.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.84% 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 title clearly summarizes the main change: adding an embedded read-only MCP server for agent access to starred repositories.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (3)
src/services/electronProxy.ts (1)

3-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate type: reuse McpServiceConfig instead of redefining it.

McpLocalConfig has the exact same shape as McpServiceConfig (src/types/index.ts), which this file already imports from '../types'. Defining a second, structurally-identical type risks the two silently drifting apart on a future field change.

-import type { ProxyConfig, Repository, Category, VectorSearchConfig } from '../types';
-
-export interface McpLocalConfig {
-  enabled: boolean;
-  host: string;
-  port: number;
-  token: string;
-}
+import type { ProxyConfig, Repository, Category, VectorSearchConfig, McpServiceConfig } from '../types';
+
+export type McpLocalConfig = McpServiceConfig;
🤖 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/services/electronProxy.ts` around lines 3 - 8, Remove the duplicate
McpLocalConfig interface from electronProxy.ts and reuse the already imported
McpServiceConfig type from ../types wherever McpLocalConfig is referenced,
preserving the existing configuration behavior.
src/store/useAppStore.ts (1)

594-620: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Export the canonical host normalizer for reuse.

normalizeMcpHost/normalizeMcpConfig correctly enforce the loopback-only whitelist, but aren't exported. McpSettingsPanel.tsx (same renderer bundle) re-implements a weaker, ad-hoc version of this check inline in two places instead of importing this one. Exporting it would let consumers share one source of truth for a security-relevant rule instead of maintaining parallel implementations that can drift.

🤖 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/store/useAppStore.ts` around lines 594 - 620, Export normalizeMcpHost
from useAppStore so McpSettingsPanel.tsx can reuse the canonical loopback-only
validation. Update both inline host-normalization checks in McpSettingsPanel.tsx
to import and call normalizeMcpHost, removing the weaker duplicated logic while
preserving the existing configuration behavior.
src/components/settings/McpSettingsPanel.tsx (1)

51-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate, weaker host-normalization logic.

mcpConfig.host === '0.0.0.0' || !mcpConfig.host ? '127.0.0.1' : mcpConfig.host (repeated at line 121-122) only guards against the wildcard/empty case, unlike useAppStore.ts's normalizeMcpHost, which enforces the full loopback whitelist. In practice mcpConfig.host is already sanitized by the store's setMcpConfig/normalizeMcpConfig before it reaches this component, so this isn't exploitable today — but it's a second, weaker implementation of a security-relevant rule that could silently diverge.

🤖 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/components/settings/McpSettingsPanel.tsx` around lines 51 - 63, The
baseUrl logic in the useMemo duplicates weaker MCP host normalization. Reuse the
existing normalizeMcpHost helper from useAppStore.ts when determining the
Electron local MCP host, removing the inline wildcard/empty fallback while
preserving the loopback whitelist behavior and existing URL construction.
🤖 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 `@electron/main.js`:
- Around line 128-151: Update the mcp:setConfig handler to stop the currently
running mcpServer whenever the MCP configuration changes while enabled, not only
when the new configuration disables MCP. Ensure the existing mcpServer.stop()
lifecycle allows the renderer’s subsequent mcp:start call to bind the updated
host and port, while preserving the disabled configuration behavior.

In `@electron/mcpLocalServer.js`:
- Around line 418-429: Update getStatus to safely handle a missing state.config,
matching the optional-chaining guard used by start. Apply the guard to both host
and port access while preserving the existing localhost fallback, default port,
and returned status structure.

In `@nginx.conf.template`:
- Around line 53-74: Update the nginx MCP routing locations so only the exact
/mcp path and paths beginning with /mcp/ are proxied. Replace the broad location
/mcp rule with separate exact and /mcp/ prefix handling, or an equivalent
segment-anchored regex, while preserving the existing proxy settings and nested
SSE/message support.

In `@README.md`:
- Line 34: The MCP feature descriptions must state that MCP settings are
available only in backend or packaged/Electron deployments, not pure frontend
deployments. Update the English description in README.md lines 34-34 and add the
equivalent qualification to the Chinese description in README_zh.md lines 31-31;
keep the existing feature details unchanged.

In `@server/src/mcp/http.ts`:
- Around line 85-133: Update the legacy /sse handler’s res.on('close') cleanup
to close its SSEServerTransport after removing the session, matching the cleanup
in the /mcp/sse handler. Prefer extracting the duplicated SSE connection setup
into a shared helper used by both routes, preserving each route’s distinct
message endpoint while ensuring disconnected clients close their transport (and
connected server resources).

In `@server/src/mcp/provider.ts`:
- Around line 227-235: Add a shared fetchWithTimeout helper in the provider flow
that aborts requests after the configured timeout, then use it for both the
embedding provider request in embedQuery and the vector worker /query request in
vectorSearch. Preserve the existing request options and error handling while
ensuring neither outbound fetch can hang indefinitely.

In `@src/components/settings/McpSettingsPanel.tsx`:
- Around line 118-157: Move the MCP lifecycle and snapshot synchronization
currently handled by the useEffect in McpSettingsPanel into a subscription owned
by a long-lived app component. Keep start/stop driven by MCP configuration, and
refresh pushSnapshot whenever repositories, customCategories, or
vectorSearchConfig changes so switching tabs or unmounting McpSettingsPanel
cannot leave the Electron snapshot stale.

In `@src/utils/repoSearch.ts`:
- Around line 136-145: Update the guard in the isEdited filtering block to use
allCategories.length > 0, so the filter runs only after categories are loaded;
leave the existing customized filtering behavior unchanged.

---

Nitpick comments:
In `@src/components/settings/McpSettingsPanel.tsx`:
- Around line 51-63: The baseUrl logic in the useMemo duplicates weaker MCP host
normalization. Reuse the existing normalizeMcpHost helper from useAppStore.ts
when determining the Electron local MCP host, removing the inline wildcard/empty
fallback while preserving the loopback whitelist behavior and existing URL
construction.

In `@src/services/electronProxy.ts`:
- Around line 3-8: Remove the duplicate McpLocalConfig interface from
electronProxy.ts and reuse the already imported McpServiceConfig type from
../types wherever McpLocalConfig is referenced, preserving the existing
configuration behavior.

In `@src/store/useAppStore.ts`:
- Around line 594-620: Export normalizeMcpHost from useAppStore so
McpSettingsPanel.tsx can reuse the canonical loopback-only validation. Update
both inline host-normalization checks in McpSettingsPanel.tsx to import and call
normalizeMcpHost, removing the weaker duplicated logic while preserving the
existing configuration 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 86568fe3-8703-40f8-a8c7-c436c6857785

📥 Commits

Reviewing files that changed from the base of the PR and between bf01136 and a7b587e.

⛔ Files ignored due to path filters (1)
  • server/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (32)
  • .gitignore
  • DOCKER.md
  • README.md
  • README_zh.md
  • electron/main.js
  • electron/mcpLocalServer.js
  • electron/package.json
  • electron/preload.js
  • nginx.conf.template
  • scripts/build-desktop.js
  • server/package.json
  • server/src/index.ts
  • server/src/mcp/http.ts
  • server/src/mcp/provider.ts
  • server/src/mcp/repoSearch.ts
  • server/src/mcp/server.ts
  • server/src/mcp/settings.ts
  • server/src/mcp/tools.ts
  • server/src/routes/mcp.ts
  • server/src/services/logSanitizer.ts
  • server/tests/mcp/repoSearch.test.ts
  • server/tests/mcp/routes.test.ts
  • src/components/SearchBar.tsx
  • src/components/SettingsPanel.tsx
  • src/components/settings/McpSettingsPanel.tsx
  • src/components/settings/index.ts
  • src/services/backendAdapter.ts
  • src/services/electronProxy.ts
  • src/store/useAppStore.ts
  • src/types/index.ts
  • src/utils/repoSearch.test.ts
  • src/utils/repoSearch.ts

Comment thread electron/main.js
Comment thread electron/mcpLocalServer.js
Comment thread nginx.conf.template Outdated
Comment thread README.md Outdated
Comment thread server/src/mcp/http.ts Outdated
Comment thread server/src/mcp/provider.ts Outdated
Comment thread src/components/settings/McpSettingsPanel.tsx Outdated
Comment thread src/utils/repoSearch.ts Outdated
- Electron: stop MCP server when bind host/port changes so start rebinds
- Electron getStatus: null-safe config access
- nginx: exact /mcp + ^~ /mcp/ only (no /mcpxyz leak)
- SSE: shared connectSse helper closes transport on disconnect for all aliases
- provider: 15s timeout on embedding and vector worker fetch
- App-level mcpElectronBridge keeps snapshots live after leaving settings tab
- Reuse McpServiceConfig type; drop dead allCategories.length >= 0 guard
- README: document MCP requires backend or Electron

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/services/mcpElectronBridge.ts`:
- Around line 31-34: Normalize the displayed MCP endpoint’s host consistently
with the server host logic: update the endpoint construction in McpSettingsPanel
to map an unset host, 0.0.0.0, or :: to 127.0.0.1, preferably by reusing a
shared host-normalization helper. Preserve configured non-wildcard hosts
unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3c0497c8-6127-41e0-a12f-57925f9346fb

📥 Commits

Reviewing files that changed from the base of the PR and between a7b587e and 4fbf95b.

📒 Files selected for processing (12)
  • README.md
  • README_zh.md
  • electron/main.js
  • electron/mcpLocalServer.js
  • nginx.conf.template
  • server/src/mcp/http.ts
  • server/src/mcp/provider.ts
  • src/App.tsx
  • src/components/settings/McpSettingsPanel.tsx
  • src/services/electronProxy.ts
  • src/services/mcpElectronBridge.ts
  • src/utils/repoSearch.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • README_zh.md
  • src/utils/repoSearch.ts
  • src/services/electronProxy.ts
  • electron/main.js
  • server/src/mcp/provider.ts
  • server/src/mcp/http.ts
  • electron/mcpLocalServer.js

Comment thread src/services/mcpElectronBridge.ts Outdated
Unify MCP host handling for unset / 0.0.0.0 / :: / [::] → 127.0.0.1 across
settings UI copy URL, Electron bridge, and store persistence so agents never
see invalid http://:::port URLs.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/utils/mcpHost.ts`:
- Around line 14-15: Normalize the IPv6 loopback consistently: update
normalizeMcpHost in src/utils/mcpHost.ts#L14-L15 to return MCP_DEFAULT_HOST for
::1, and update the corresponding expectation in
src/utils/mcpHost.test.ts#L14-L18 so normalizeMcpHost('::1') is expected to
return MCP_DEFAULT_HOST.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a5da2af8-f19e-458b-b5ff-6d277810d74b

📥 Commits

Reviewing files that changed from the base of the PR and between 4fbf95b and 9dcb624.

📒 Files selected for processing (5)
  • src/components/settings/McpSettingsPanel.tsx
  • src/services/mcpElectronBridge.ts
  • src/store/useAppStore.ts
  • src/utils/mcpHost.test.ts
  • src/utils/mcpHost.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/services/mcpElectronBridge.ts
  • src/store/useAppStore.ts
  • src/components/settings/McpSettingsPanel.tsx

Comment thread src/utils/mcpHost.ts Outdated
IPv6 loopback was preserved as ::1, producing invalid unbracketed
http://::1:port URLs in the settings panel. Map ::1/[::1] to 127.0.0.1.
Installed client had mcpLocalServer.js but main.js/preload never exposed
IPC or started the listener, so Settings could show a token while nothing
bound 127.0.0.1:3927. Merge production proxy/menu shell with MCP
setConfig/start/stop/pushSnapshot handlers and preload electronAPI.mcp.
Electron mcpLocalServer previously only accepted /sse as a path alias
and returned JSON health on GET. Add real HTTP+SSE: GET /sse (or
/mcp/sse) emits endpoint event; POST /messages?sessionId= returns 202
and writes JSON-RPC results on the open stream. Settings UI shows SSE
copy helper; docs list desktop and docker SSE URLs.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
electron/main.js (1)

335-342: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

normalizeMcpHost logic is redundant.

The final return '127.0.0.1' (Line 341) already covers every input that isn't the exact literal '127.0.0.1', so the explicit alias checks for 0.0.0.0, ::, localhost, etc. never change the outcome — the function always returns '127.0.0.1' regardless of input. Functionally correct (loopback-only enforced), but the branching implies intent that isn't real.

♻️ Suggested simplification
-function normalizeMcpHost(rawHost) {
-  const host = typeof rawHost === 'string' && rawHost.trim() ? rawHost.trim() : '127.0.0.1';
-  if (host === '0.0.0.0' || host === '::' || host === '[::]' || host === '::1' || host === '[::1]' || host === 'localhost') {
-    return '127.0.0.1';
-  }
-  if (host === '127.0.0.1') return host;
-  return '127.0.0.1';
-}
+// Loopback-only: any value other than the exact '127.0.0.1' literal is coerced to it.
+function normalizeMcpHost(_rawHost) {
+  return '127.0.0.1';
+}
🤖 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 `@electron/main.js` around lines 335 - 342, Simplify normalizeMcpHost by
removing the redundant alias checks and conditional branches; since every input
must resolve to the loopback address, return '127.0.0.1' directly while
preserving the function’s current 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 `@electron/main.js`:
- Around line 218-239: Update applyProxy so the “[Proxy] Applied” log never
includes proxy credentials from config.username or config.password. Preserve the
full authenticated proxyUrl for setProxy, but log a redacted URL or equivalent
representation with credentials removed while retaining useful host and port
context.
- Around line 35-43: Update the did-fail-load handler on mainWindow.webContents
to avoid reloading the same fallbackPath when the failed validatedURL already
refers to it, preventing a self-triggered infinite loop. Preserve the existing
fallback load for failures from other paths, and use the existing
possiblePaths/fallbackPath values rather than introducing unrelated retry
behavior.
- Around line 15-23: Update the BrowserWindow webPreferences in createWindow()
so webSecurity, allowRunningInsecureContent, and devTools are enabled only when
isDev is true, or otherwise disabled/removed for production; preserve the
existing secure settings and preload configuration.
- Around line 262-295: Update the SOCKS5 handshake Promise in the config.type
=== 'socks5' branch to accumulate incoming data across data events and parse
each response only once the required bytes are available. Preserve the greeting
and authentication state transitions, and resolve success only after a complete
valid response; incomplete fragments must wait for more data rather than falling
through to success.

---

Nitpick comments:
In `@electron/main.js`:
- Around line 335-342: Simplify normalizeMcpHost by removing the redundant alias
checks and conditional branches; since every input must resolve to the loopback
address, return '127.0.0.1' directly while preserving the function’s current
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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2fab6151-e6fb-4481-bfe5-58c765dcc67c

📥 Commits

Reviewing files that changed from the base of the PR and between 474d633 and 0626b9d.

📒 Files selected for processing (2)
  • electron/main.js
  • electron/preload.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • electron/preload.js

Comment thread electron/main.js
Comment thread electron/main.js
Comment thread electron/main.js
Comment thread electron/main.js
- webSecurity true, no mixed content, DevTools only in isDev
- did-fail-load fallback only once (no reload loop)
- redact proxy credentials in console logs
- buffer SOCKS5 handshake TCP fragments before parse
- stop MCP server on quit; simplify loopback host normalizer
build-desktop.yml regenerated electron/main.js and preload.js without
MCP IPC, so workflow packages never listened on :3927 despite UI tokens.
Verify committed electron/* (main/preload/mcpLocalServer) instead.
@AmintaCCCP

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (4)
electron/mcpLocalServer.js (1)

315-317: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor: early-return URL skips the loopback normalization applied elsewhere.

The already-running branch reports state.config.host verbatim, whereas the bind path (Lines 320-324) and getStatus (Lines 508-511) normalize 0.0.0.0/:: to 127.0.0.1. In the Electron flow config.host is always 127.0.0.1, so there's no live bug, but since this module is standalone a non-loopback config value would produce a misleading URL here. Consider reusing the same normalization for consistency.

🤖 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 `@electron/mcpLocalServer.js` around lines 315 - 317, Update the
already-running branch in the server startup flow to normalize state.config.host
using the same 0.0.0.0/:: to 127.0.0.1 logic used by the bind path and getStatus
before constructing the returned URL. Keep the existing success response and
port/path unchanged.
src/utils/repoSearch.test.ts (1)

1-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add filter-branch coverage to both applyRepoFilters test suites. Both mirrored implementations of applyRepoFilters have several untested conditional branches (isAnalyzed, isSubscribed, isCategoryLocked, isEdited, analysisFailed, maxStars, sort variants for the frontend; the same set minus isEdited for the MCP server). This is the same class of gap that let a real bug (tautological isEdited guard) ship undetected previously.

  • src/utils/repoSearch.test.ts#L1-L122: add cases for isAnalyzed, isSubscribed, isCategoryLocked, isEdited (with allCategories), analysisFailed, maxStars, and sortBy = updated/name/starred.
  • server/tests/mcp/repoSearch.test.ts#L1-L56: import and directly test applyRepoFilters for languages, tags, platforms, isAnalyzed, isSubscribed, isCategoryLocked, analysisFailed, minStars/maxStars, since this function backs the externally-facing gsm_search_repos tool contract.
🤖 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/utils/repoSearch.test.ts` around lines 1 - 122, Add direct filter-branch
coverage to src/utils/repoSearch.test.ts lines 1-122 for applyRepoFilters: test
isAnalyzed, isSubscribed, isCategoryLocked, isEdited with allCategories,
analysisFailed, maxStars, and updated/name/starred sorting. In
server/tests/mcp/repoSearch.test.ts lines 1-56, import and test applyRepoFilters
for languages, tags, platforms, isAnalyzed, isSubscribed, isCategoryLocked,
analysisFailed, and minStars/maxStars; use fixtures that distinguish matching
and non-matching repositories.
server/src/mcp/tools.ts (1)

20-21: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

getVectorAvailability() re-queried on every stateless request.

Since createMcpServer()/registerMcpTools() runs fresh per-request under the stateless Streamable HTTP transport (per handleStreamable in server/src/mcp/http.ts, which uses sessionIdGenerator: undefined), this availability check (2 SQLite queries) executes on every MCP call, not just gsm_vector_search invocations. Consider memoizing the result briefly (e.g., short TTL cache) to avoid repeated DB round-trips on the hot path.

Also applies to: 147-169

🤖 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 `@server/src/mcp/tools.ts` around lines 20 - 21, Memoize the result of
getVectorAvailability() with a short TTL shared across fresh registerMcpTools()
invocations, so stateless requests do not repeat the two SQLite queries on every
MCP call. Update the availability checks in registerMcpTools, including the path
around lines 147-169, while preserving refresh behavior after the TTL expires.
nginx.conf.template (1)

53-126: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider de-duplicating the repeated proxy directives across the four MCP location blocks.

= /mcp, ^~ /mcp/, = /sse, and = /messages each repeat ~15 lines of near-identical proxy_set_header/timeout/buffering config, and have already drifted slightly (e.g. /messages omits Connection '', proxy_cache off, chunked_transfer_encoding on that the other blocks set). An nginx snippet file (include mcp_proxy_common.conf;) would keep these in sync going forward.

🤖 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 `@nginx.conf.template` around lines 53 - 126, The four MCP-related location
blocks duplicate shared proxy configuration and have drifted. Create an nginx
snippet containing the common proxy directives, include it from = /mcp, ^~
/mcp/, = /sse, and = /messages, and preserve any location-specific settings such
as differing timeouts or body-size limits.
🤖 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 `@server/src/mcp/http.ts`:
- Around line 55-72: The `/mcp` endpoint currently accepts methods beyond POST,
allowing stateless GET and DELETE requests into handleStreamable without session
validation. Replace the app.all('/mcp', ...) routing with POST-only handling,
and add method handling that returns HTTP 405 for GET and DELETE requests while
preserving the existing handleStreamable behavior for POST.

In `@server/src/routes/mcp.ts`:
- Around line 50-69: Update the resetToken branch in the MCP configuration
handler to call resetMcpToken() only when isMcpEnabled() is true; when MCP is
disabled, preserve the existing token without minting or rotating one, while
retaining the current enabled-path behavior.

In `@src/App.tsx`:
- Around line 133-138: The App-level bridge effect must wait for backend
discovery and hydration before starting the Electron MCP bridge. Coordinate
startMcpElectronBridge with the backend.init() completion or emit an explicit
backend-readiness transition that invokes pushLifecycle(), ensuring a
successfully initialized backend stops any locally started MCP bridge while
preserving session-wide cleanup via stopMcpElectronBridge.

In `@src/components/settings/McpSettingsPanel.tsx`:
- Around line 178-195: Update handleResetToken to return without generating or
resetting a token when mcpConfig.enabled is false, and disable the associated
reset action in the UI. In the backend updateMcpConfig/resetMcpToken flow,
reject or ignore resetToken requests while MCP is disabled so direct API calls
preserve the same behavior.
- Around line 403-424: Add accessible aria-labels to the icon-only copy buttons
in the MCP settings URL rows, identifying the HTTP endpoint for the button using
mcpHttpUrl and the SSE endpoint for the button using mcpSseUrl; leave their
existing click behavior unchanged.

In `@src/services/backendAdapter.ts`:
- Around line 770-800: Redact the token returned by getMcpStatus and
updateMcpConfig before it can reach fetchWithTimeout’s debug responseBody
logging. Update the shared response-preview/logging path to mask the token for
the /mcp/status and /mcp/config endpoints, or suppress response-body logging for
those requests, while preserving the methods’ returned response objects.

In `@src/services/mcpElectronBridge.ts`:
- Around line 22-57: Update pushLifecycle to serialize Electron MCP IPC
operations through an async queue or generation guard, ensuring each enabled
cycle awaits setConfig, pushSnapshot, then start, while disable/backend paths
await stop and supersede stale work. Catch and handle rejected IPC promises so
lifecycle failures do not become unhandled rejections.

In `@src/store/useAppStore.ts`:
- Around line 2233-2234: The persistence configuration around mcpConfig must
stop storing backend MCP bearer tokens. Update the partialize state selection so
only safe Electron-local preferences are persisted, while backend connection
settings and tokens remain memory-only; preserve any non-sensitive preferences
required by the existing flow.

---

Nitpick comments:
In `@electron/mcpLocalServer.js`:
- Around line 315-317: Update the already-running branch in the server startup
flow to normalize state.config.host using the same 0.0.0.0/:: to 127.0.0.1 logic
used by the bind path and getStatus before constructing the returned URL. Keep
the existing success response and port/path unchanged.

In `@nginx.conf.template`:
- Around line 53-126: The four MCP-related location blocks duplicate shared
proxy configuration and have drifted. Create an nginx snippet containing the
common proxy directives, include it from = /mcp, ^~ /mcp/, = /sse, and =
/messages, and preserve any location-specific settings such as differing
timeouts or body-size limits.

In `@server/src/mcp/tools.ts`:
- Around line 20-21: Memoize the result of getVectorAvailability() with a short
TTL shared across fresh registerMcpTools() invocations, so stateless requests do
not repeat the two SQLite queries on every MCP call. Update the availability
checks in registerMcpTools, including the path around lines 147-169, while
preserving refresh behavior after the TTL expires.

In `@src/utils/repoSearch.test.ts`:
- Around line 1-122: Add direct filter-branch coverage to
src/utils/repoSearch.test.ts lines 1-122 for applyRepoFilters: test isAnalyzed,
isSubscribed, isCategoryLocked, isEdited with allCategories, analysisFailed,
maxStars, and updated/name/starred sorting. In
server/tests/mcp/repoSearch.test.ts lines 1-56, import and test applyRepoFilters
for languages, tags, platforms, isAnalyzed, isSubscribed, isCategoryLocked,
analysisFailed, and minStars/maxStars; use fixtures that distinguish matching
and non-matching repositories.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a60866cd-e6b2-4bb9-a832-a713d3762e07

📥 Commits

Reviewing files that changed from the base of the PR and between bf01136 and b80f7e1.

⛔ Files ignored due to path filters (1)
  • server/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (37)
  • .github/workflows/build-desktop.yml
  • .gitignore
  • DOCKER.md
  • README.md
  • README_zh.md
  • electron/main.js
  • electron/mcpLocalServer.js
  • electron/package.json
  • electron/preload.js
  • nginx.conf.template
  • scripts/build-desktop.js
  • server/package.json
  • server/src/index.ts
  • server/src/mcp/http.ts
  • server/src/mcp/provider.ts
  • server/src/mcp/repoSearch.ts
  • server/src/mcp/server.ts
  • server/src/mcp/settings.ts
  • server/src/mcp/tools.ts
  • server/src/routes/mcp.ts
  • server/src/services/logSanitizer.ts
  • server/tests/mcp/repoSearch.test.ts
  • server/tests/mcp/routes.test.ts
  • src/App.tsx
  • src/components/SearchBar.tsx
  • src/components/SettingsPanel.tsx
  • src/components/settings/McpSettingsPanel.tsx
  • src/components/settings/index.ts
  • src/services/backendAdapter.ts
  • src/services/electronProxy.ts
  • src/services/mcpElectronBridge.ts
  • src/store/useAppStore.ts
  • src/types/index.ts
  • src/utils/mcpHost.test.ts
  • src/utils/mcpHost.ts
  • src/utils/repoSearch.test.ts
  • src/utils/repoSearch.ts

Comment thread server/src/mcp/http.ts
Comment thread server/src/routes/mcp.ts
Comment thread src/App.tsx Outdated
Comment thread src/components/settings/McpSettingsPanel.tsx
Comment thread src/components/settings/McpSettingsPanel.tsx
Comment thread src/services/backendAdapter.ts
Comment thread src/services/mcpElectronBridge.ts Outdated
Comment thread src/store/useAppStore.ts Outdated
- Streamable /mcp is POST-only; GET/DELETE return 405 (legacy SSE on /mcp/sse)
- Do not mint/reset MCP token while disabled (API + UI)
- Start Electron MCP bridge after backend.init; serialize IPC lifecycle
- Persist MCP prefs without token; keep desktop token in sessionStorage
- Redact gsm_mcp_* in frontend logs and backendAdapter debug bodies
- URL copy buttons get aria-labels; normalize already-running MCP URL
- Disable Reset Token button while MCP is off (handler + API already guard)
- Redact token fields and gsm_mcp_* values in backendAdapter responseBody
  debug previews so /mcp/status and /mcp/config cannot leak into logs
Local MCP previously always returned "vector search not supported".
Pass embedding + worker runtime config via IPC snapshot and implement
embed + Vectorize query in mcpLocalServer when Vector Search is enabled
in the app. tools/list only exposes gsm_vector_search when ready.
SiliconFlow/OpenAI baseUrl in the app is the host (e.g. api.siliconflow.cn);
the client appends /v1/embeddings. Local MCP was appending only /embeddings
(or wrongly defaulting base to .../v1), producing 404 page not found and
breaking gsm_vector_search. Match vectorSearchService.ts for all providers.
Desktop MCP tokens were session-only and auto-regenerated after restart,
forcing users to update agent configs silently. Persist full mcpConfig
(including token) via Zustand IndexedDB partialize. Backend already stores
encrypted mcp_token in SQLite; ensure paths only mint when missing and
document that tokens change solely on explicit Reset Token.
@AmintaCCCP
AmintaCCCP merged commit adff34b into main Jul 23, 2026
5 checks passed
@AmintaCCCP
AmintaCCCP deleted the feat/mcp-server branch July 23, 2026 16:32
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