-
Notifications
You must be signed in to change notification settings - Fork 169
Add support for private registries #974
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughThis pull request introduces multi-registry support with authentication handling. New components enable users to select from and add custom registries. A Zustand-based store manages registry sources and state. The registry API layer now detects authentication requirements and surfaces them as distinct responses rather than throwing errors. The registry store supports per-registry caching and URL awareness. Server endpoints now accept optional registry URLs and forward authorization headers, returning authentication challenges when required. The RegistryTab component integrates the new registry selector and displays authentication prompts when needed. Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. 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. Comment |
There was a problem hiding this 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
🧹 Nitpick comments (7)
client/src/components/registry/AddRegistryDialog.tsx (1)
61-65: URL comparison may miss duplicates with trailing slash variance.
new URL(url).toString()preserves trailing slashes, sohttps://registry.example.comandhttps://registry.example.com/are treated as distinct. Consider normalizing before comparison.- const existingSource = sources.find((s) => s.url === registryUrl.toString()); + const normalizedUrl = registryUrl.toString().replace(/\/+$/, ''); + const existingSource = sources.find((s) => s.url.replace(/\/+$/, '') === normalizedUrl);client/src/stores/registry/registry-sources-store.ts (1)
72-76: Consider adding persist migration for future-proofing.The store uses
zustand/middlewarepersist correctly. For resilience against schema evolution, consider adding aversionandmigratefunction to the persist config. This prevents stale localStorage data from causing issues ifRegistrySourcegains new required fields.{ name: "mcp-registry-sources", + version: 1, + migrate: (persistedState, version) => { + // Handle future migrations here + return persistedState as RegistrySourcesState; + }, }client/src/components/RegistryTab.tsx (2)
184-184: Dependency array includesallServersbut body useslatestServers.The
filteredServersmemo depends onallServersin its dependency array, yet the filtering logic operates onlatestServers. This is functionally correct sincelatestServersderives fromallServers, but the explicit dependency should belatestServersfor clarity.- }, [allServers, searchQuery, fuseInstance]); + }, [latestServers, searchQuery, fuseInstance]);
31-34: Inline selector duplicatesgetActiveSource()logic.The store exposes
getActiveSource(), yet here we inline equivalent logic. Consider using the store method for consistency, though this inline approach avoids a function call on each render.server/routes/mcp/registry.ts (1)
36-47: Consider extracting the 401 response handling into a helper.The identical 401 handling pattern appears thrice. A small helper would reduce duplication and ensure consistency:
function authRequiredResponse(c: Context, wwwAuthenticate: string | null, registryUrl: string) { return c.json({ requiresAuth: true, wwwAuthenticate, registryUrl }, 401); }Also applies to: 80-90, 125-135
client/src/stores/registry/registry-store.ts (1)
191-196: Closure capturesregistryChangedbefore async IIFE executes.The
registryChangedboolean is captured at function start, but the async IIFE runs later. If multiplefetchAllPagescalls occur rapidly, the captured value may not reflect current state when the error handler executes at line 312.Consider re-checking against current state inside the error handler:
- if (currentState.allServers.length > 0 && !registryChanged) { + const stillSameRegistry = currentState.currentRegistryUrl === targetUrl; + if (currentState.allServers.length > 0 && stillSameRegistry) {client/src/lib/registry-api.ts (1)
29-32: Consider extracting header construction into a shared helper.The Authorization header logic repeats across all three functions:
function buildHeaders(accessToken?: string): HeadersInit { const headers: HeadersInit = {}; if (accessToken) { headers["Authorization"] = `Bearer ${accessToken}`; } return headers; }This mirrors the server-side
getRegistryHeaderspattern and would reduce duplication.Also applies to: 69-72, 101-104
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
client/src/components/RegistryTab.tsx(5 hunks)client/src/components/registry/AddRegistryDialog.tsx(1 hunks)client/src/components/registry/RegistrySelector.tsx(1 hunks)client/src/lib/registry-api.ts(3 hunks)client/src/stores/registry/registry-sources-store.ts(1 hunks)client/src/stores/registry/registry-store.ts(13 hunks)server/routes/mcp/registry.ts(3 hunks)shared/types.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
client/**/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (client/CLAUDE.md)
client/**/components/**/*.{ts,tsx}: Use functional React components with React.FC typing
Implement strict TypeScript types for React component props with Props interface definitions
Follow React 19 patterns including hooks, Suspense boundaries, error boundaries, and concurrent features
Use Radix UI components for Dialog, Dropdown menus, Form controls, and Tooltips
Design responsive layouts using Tailwind breakpoint system, grid layouts, flex containers, and container queries
Ensure accessibility compliance with ARIA attributes, keyboard navigation, focus management, and screen reader support
Files:
client/src/components/registry/AddRegistryDialog.tsxclient/src/components/registry/RegistrySelector.tsxclient/src/components/RegistryTab.tsx
client/**/{components,hooks}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (client/CLAUDE.md)
Use React hooks (useState, useReducer) for local state management with proper effect cleanup
Files:
client/src/components/registry/AddRegistryDialog.tsxclient/src/components/registry/RegistrySelector.tsxclient/src/components/RegistryTab.tsx
client/**/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (client/CLAUDE.md)
Use Zustand for global state management with proper store creation, action definitions, selector optimization, and middleware usage
Files:
client/src/stores/registry/registry-sources-store.tsclient/src/stores/registry/registry-store.ts
server/**/*.ts
📄 CodeRabbit inference engine (server/CLAUDE.md)
Implement TypeScript for type safety throughout the codebase
Files:
server/routes/mcp/registry.ts
🧠 Learnings (14)
📚 Learning: 2025-11-24T17:54:18.309Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.309Z
Learning: Applies to client/**/components/**/*.{ts,tsx} : Use Radix UI components for Dialog, Dropdown menus, Form controls, and Tooltips
Applied to files:
client/src/components/registry/AddRegistryDialog.tsxclient/src/components/registry/RegistrySelector.tsxclient/src/components/RegistryTab.tsx
📚 Learning: 2025-11-24T17:54:18.309Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.309Z
Learning: Applies to client/**/stores/**/*.{ts,tsx} : Use Zustand for global state management with proper store creation, action definitions, selector optimization, and middleware usage
Applied to files:
client/src/stores/registry/registry-sources-store.tsclient/src/stores/registry/registry-store.tsclient/src/components/RegistryTab.tsx
📚 Learning: 2025-11-24T17:54:18.309Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.309Z
Learning: Applies to client/**/stores/servers/**/*.{ts,tsx} : Synchronize MCP connection state, request tracking, response handling, and error management with global state
Applied to files:
client/src/stores/registry/registry-sources-store.tsclient/src/stores/registry/registry-store.tsclient/src/components/RegistryTab.tsx
📚 Learning: 2025-11-24T17:54:18.309Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.309Z
Learning: Applies to client/**/{stores/servers,lib/api}/**/*.{ts,tsx} : Persist server configurations with local storage, export/import functionality, sync options, and backup/restore capabilities
Applied to files:
client/src/stores/registry/registry-sources-store.tsclient/src/stores/registry/registry-store.tsclient/src/lib/registry-api.ts
📚 Learning: 2025-11-24T17:54:18.309Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.309Z
Learning: Applies to client/**/stores/chat/**/*.{ts,tsx} : Implement AI model state handling including model selection state, generation parameters, stream management, and history persistence
Applied to files:
client/src/stores/registry/registry-sources-store.ts
📚 Learning: 2025-11-24T17:54:18.310Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.310Z
Learning: Use Zustand for lightweight, flexible global state management
Applied to files:
client/src/stores/registry/registry-sources-store.ts
📚 Learning: 2025-11-24T17:54:18.309Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.309Z
Learning: Applies to client/**/components/servers/**/*.{ts,tsx} : Provide transport protocol selection UI with protocol options, configuration forms, validation rules, and default presets
Applied to files:
client/src/components/registry/RegistrySelector.tsx
📚 Learning: 2025-11-25T19:59:26.527Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: server/CLAUDE.md:0-0
Timestamp: 2025-11-25T19:59:26.527Z
Learning: Applies to server/**/types/mcp.ts : Define MCP protocol types in TypeScript type definition files
Applied to files:
server/routes/mcp/registry.tsclient/src/lib/registry-api.ts
📚 Learning: 2025-11-25T19:59:26.526Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: server/CLAUDE.md:0-0
Timestamp: 2025-11-25T19:59:26.526Z
Learning: Applies to server/**/{app,index,routes/**}.ts : Use Hono.js for API routing and middleware in the backend
Applied to files:
server/routes/mcp/registry.ts
📚 Learning: 2025-11-24T17:54:18.309Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.309Z
Learning: Applies to client/**/{components/servers,stores/servers}/**/*.{ts,tsx} : Implement authentication setup with OAuth configuration, token management, scope selection, and refresh handling
Applied to files:
client/src/stores/registry/registry-store.tsclient/src/lib/registry-api.ts
📚 Learning: 2025-11-24T17:54:18.309Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.309Z
Learning: Applies to client/**/components/**/*.{ts,tsx} : Follow React 19 patterns including hooks, Suspense boundaries, error boundaries, and concurrent features
Applied to files:
client/src/components/RegistryTab.tsx
📚 Learning: 2025-11-24T17:54:18.309Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.309Z
Learning: Applies to client/**/{components,hooks}/**/*.{ts,tsx} : Use React hooks (useState, useReducer) for local state management with proper effect cleanup
Applied to files:
client/src/components/RegistryTab.tsx
📚 Learning: 2025-11-24T17:54:18.309Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.309Z
Learning: Applies to client/**/components/**/*.{ts,tsx} : Use functional React components with React.FC typing
Applied to files:
client/src/components/RegistryTab.tsx
📚 Learning: 2025-11-24T17:54:18.309Z
Learnt from: CR
Repo: MCPJam/inspector PR: 0
File: client/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:54:18.309Z
Learning: Applies to client/**/components/servers/**/*.{ts,tsx} : Implement MCP server management UI with connection list, status indicators, quick actions, and group management
Applied to files:
client/src/components/RegistryTab.tsx
🧬 Code graph analysis (4)
client/src/stores/registry/registry-sources-store.ts (1)
shared/types.ts (1)
RegistrySource(815-821)
server/routes/mcp/registry.ts (1)
bin/start.js (1)
url(606-606)
client/src/stores/registry/registry-store.ts (2)
shared/types.ts (1)
RegistryServer(760-782)client/src/lib/registry-api.ts (2)
listRegistryServers(18-47)isAuthRequired(52-56)
client/src/lib/registry-api.ts (1)
shared/types.ts (4)
RegistryServerListResponse(799-805)RegistryAuthRequiredResponse(823-827)RegistryVersionListResponse(807-812)RegistryServerVersion(784-792)
🔍 Remote MCP
Summary: Additional Context for PR #974 - Private Registries Support
Based on my research, here's the relevant context and background information for reviewing this pull request:
MCP Registry Ecosystem Context
The Registry API has entered an API freeze (v0.1) in October 2025, with the API remaining stable with no breaking changes for at least the next month, allowing integrators to confidently implement support. Private subregistries will exist within enterprises that have strict privacy and security requirements, but the MCP Registry gives these enterprises a single upstream data source they can build upon, aiming to share API schemas with these private implementations so that associated SDKs and tooling can be shared across the ecosystem.
Architecture for Private Registries
The registry embraces federation where the upstream MCP Registry is not the only registry, but rather serves as the canonical source of public MCP server metadata that subregistries (public or private) can ingest, augment, or mirror. Private subregistries within enterprises can combine published servers with internal ones, apply custom policies, and share a consistent API surface with MCP clients, with subregistries expected to reuse the OpenAPI schema and metadata contracts defined by the upstream registry.
Authentication Handling
The registry stores metadata about server endpoints (including transport protocol support, authentication requirements, etc.), and server metadata may include declarations of required authentication headers or credentials, so clients know in advance which auth scheme to use. This aligns with the PR's RegistryAuthRequiredResponse type and authentication detection logic.
API Design Principles
The MCP Registry API is designed with "Reusability: API shapes designed for reuse, supporting private/internal registries" as a core principle. The API shapes and data formats are intentionally designed for reuse by subregistries.
Expected Client Behavior
It's highly recommended that clients use a subregistry rather than fetching data from the official registry directly, and clients might want to make this configurable so that users of the client can choose their preferred registry, as some enterprise users may have their own registry.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Cursor Bugbot
🔇 Additional comments (18)
shared/types.ts (1)
814-827: Well-structured type definitions for multi-registry support.The
RegistrySourceinterface cleanly captures registry metadata, andRegistryAuthRequiredResponseemploys a literaltruetype forrequiresAuth, enabling precise type narrowing via theisAuthRequiredtype guard inregistry-api.ts.client/src/components/registry/AddRegistryDialog.tsx (2)
112-116: Enter-to-submit from name field is reasonable UX.The
handleKeyDownlistener triggers submission from both inputs when Enter is pressed and a URL exists. This is intuitive behavior for a two-field form.
25-110: Clean implementation with proper state management and error handling.The component follows React 19 patterns with hooks, integrates Radix UI Dialog per coding guidelines, and provides clear user feedback through toast notifications. The async flow handles validation, duplicate detection, and auth requirement discovery elegantly.
client/src/stores/registry/registry-sources-store.ts (1)
25-71: Solid Zustand store with appropriate immutability guards.The store safeguards the official registry from modification or removal, uses
crypto.randomUUID()for IDs, and gracefully falls back when the active source is removed. The action signatures are well-typed.client/src/components/RegistryTab.tsx (2)
397-428: Auth-required state renders a clear, actionable UI.The
Lockicon and messaging communicate authentication needs effectively. IncludingRegistrySelectorin the header ensures users can switch registries without navigating away.
207-217: Registry-aware refresh and change handlers are correctly wired.
handleRefreshpasses the active registry URL, andhandleRegistryChangeusesuseCallbackwith appropriate dependencies. This ensures registry context flows through data fetching.client/src/components/registry/RegistrySelector.tsx (3)
78-91: Interactive element within SelectItem may cause focus/click conflicts.Placing a Button inside a SelectItem can lead to unexpected behavior—clicking the trash icon might also trigger item selection despite
stopPropagation. Test thoroughly, and consider moving delete actions to a separate context menu if issues arise.
96-101: Magic value"__add__"is safe given UUID-based source IDs.Since
addSourcegenerates UUIDs, collision with"__add__"is impossible. This pattern cleanly separates the "add" action from actual registry sources.
24-50: Selector integrates cleanly with registry sources store.State management follows Zustand patterns, and the
onRegistryChangecallback propagates URL changes to parent components. Event handling inhandleRemoveSourcecorrectly prevents bubbling.server/routes/mcp/registry.ts (2)
5-16: Well-structured helper and constant extraction.The
getRegistryHeadershelper elegantly centralizes header construction, and extractingDEFAULT_REGISTRY_URLas a constant promotes maintainability.
49-54: Appropriate error handling for non-401 failures.The throw-and-catch pattern preserves the original behavior while the new 401 logic integrates cleanly above it.
client/src/stores/registry/registry-store.ts (3)
103-117: Thoughtful initialization with cache preloading.Loading cached data for the default registry on store creation provides an excellent UX—users see data immediately while fresh data loads in the background. The fallback to set
currentRegistryUrleven without cache is correct.
138-146: Clean auth-required detection flow.The early return upon
isAuthRequiredcheck prevents further processing and correctly setsauthRequiredstate. This integrates well with the type guard fromregistry-api.ts.
280-301: Solid deduplication and cache persistence.Using a
Mapkeyed by${name}@${version}for deduplication is efficient. Persisting to localStorage per-registry enables offline resilience. The state update is atomic and comprehensive.client/src/lib/registry-api.ts (4)
10-13: Clean interface for request options.
RegistryRequestOptionsprovides a reusable contract for registry-aware API calls. The optional fields allow flexible composition.
52-56: Elegant type guard implementation.The
isAuthRequiredfunction serves as a proper TypeScript type guard, enabling callers to narrow the union type cleanly. Checking both"requiresAuth" in responseand the value beingtrueis defensive and correct.
37-40: Consistent 401 handling across all API functions.Returning the parsed
RegistryAuthRequiredResponseinstead of throwing enables callers to handle authentication challenges gracefully. The pattern is uniform and aligns with the shared type definition.Also applies to: 77-79, 109-111
61-86: Signature change verified as backward-compatible.The function has one existing call site in
client/src/components/registry/ServerDetailModal.tsx(line 68), which callslistServerVersions(initialServer.name)with only theserverNameargument. Since the newoptionsparameter is optional, this call site remains compatible with the updated signature. No unexpected arguments are being passed, and no breaking changes exist.
| function getCacheKey(registryUrl: string): string { | ||
| // Use a hash-like identifier for the registry URL | ||
| const urlId = btoa(registryUrl).replace(/[^a-zA-Z0-9]/g, "").slice(0, 20); | ||
| return `${CACHE_KEY_PREFIX}-${urlId}`; | ||
| } | ||
|
|
||
| function getCacheTimestampKey(registryUrl: string): string { | ||
| const urlId = btoa(registryUrl).replace(/[^a-zA-Z0-9]/g, "").slice(0, 20); | ||
| return `${CACHE_TIMESTAMP_KEY_PREFIX}-${urlId}`; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btoa() may throw for URLs containing non-ASCII characters.
Registry URLs with internationalized domain names or encoded paths could contain characters outside the Latin1 range, causing btoa() to throw.
function getCacheKey(registryUrl: string): string {
- const urlId = btoa(registryUrl).replace(/[^a-zA-Z0-9]/g, "").slice(0, 20);
+ // Use encodeURIComponent to safely handle Unicode before base64 encoding
+ const urlId = btoa(encodeURIComponent(registryUrl))
+ .replace(/[^a-zA-Z0-9]/g, "")
+ .slice(0, 32);
return `${CACHE_KEY_PREFIX}-${urlId}`;
}Also consider increasing the slice length to reduce collision probability across registries.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function getCacheKey(registryUrl: string): string { | |
| // Use a hash-like identifier for the registry URL | |
| const urlId = btoa(registryUrl).replace(/[^a-zA-Z0-9]/g, "").slice(0, 20); | |
| return `${CACHE_KEY_PREFIX}-${urlId}`; | |
| } | |
| function getCacheTimestampKey(registryUrl: string): string { | |
| const urlId = btoa(registryUrl).replace(/[^a-zA-Z0-9]/g, "").slice(0, 20); | |
| return `${CACHE_TIMESTAMP_KEY_PREFIX}-${urlId}`; | |
| } | |
| function getCacheKey(registryUrl: string): string { | |
| // Use encodeURIComponent to safely handle Unicode before base64 encoding | |
| const urlId = btoa(encodeURIComponent(registryUrl)) | |
| .replace(/[^a-zA-Z0-9]/g, "") | |
| .slice(0, 32); | |
| return `${CACHE_KEY_PREFIX}-${urlId}`; | |
| } | |
| function getCacheTimestampKey(registryUrl: string): string { | |
| const urlId = btoa(registryUrl).replace(/[^a-zA-Z0-9]/g, "").slice(0, 20); | |
| return `${CACHE_TIMESTAMP_KEY_PREFIX}-${urlId}`; | |
| } |
| const registryUrl = c.req.query("registryUrl") || DEFAULT_REGISTRY_URL; | ||
|
|
||
| let url = `${REGISTRY_BASE_URL}/servers?limit=${limit}`; | ||
| let url = `${registryUrl}/servers?limit=${limit}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential SSRF vulnerability with user-supplied registry URL.
The registryUrl query parameter flows directly into fetch() without validation. A malicious actor could probe internal services (e.g., http://localhost:6379, http://169.254.169.254/).
Consider an allowlist or URL validation:
+const ALLOWED_REGISTRY_HOSTS = new Set([
+ "registry.modelcontextprotocol.io",
+ // Add other trusted hosts
+]);
+
+function isValidRegistryUrl(url: string): boolean {
+ try {
+ const parsed = new URL(url);
+ return ALLOWED_REGISTRY_HOSTS.has(parsed.hostname) ||
+ parsed.hostname.endsWith(".modelcontextprotocol.io");
+ } catch {
+ return false;
+ }
+}Then validate before use:
const registryUrl = c.req.query("registryUrl") || DEFAULT_REGISTRY_URL;
+if (!isValidRegistryUrl(registryUrl)) {
+ return c.json({ success: false, error: "Invalid registry URL" }, 400);
+}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In server/routes/mcp/registry.ts around lines 23–25, the code uses the
user-supplied registryUrl directly to build an outbound URL which can enable
SSRF; instead, parse and validate registryUrl before use and fall back to
DEFAULT_REGISTRY_URL on failure. Implement one of: (preferred) an allowlist of
permitted hostnames/domains and only accept registryUrl whose hostname matches
the allowlist and whose protocol is http or https; or (minimum) parse the URL
and reject any with disallowed hosts/ranges (localhost, 127.0.0.0/8, ::1,
link-local, 169.254.0.0/16, metadata IPs) and non-http(s) schemes. If validation
fails, use DEFAULT_REGISTRY_URL; also ensure proper URL encoding when appending
query params.
| function getCacheTimestampKey(registryUrl: string): string { | ||
| const urlId = btoa(registryUrl).replace(/[^a-zA-Z0-9]/g, "").slice(0, 20); | ||
| return `${CACHE_TIMESTAMP_KEY_PREFIX}-${urlId}`; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Cache key collision risk with truncated URLs
The cache key generation truncates base64-encoded registry URLs to only 20 characters, creating a high collision risk between different registry URLs. When multiple private registries are added with similar URLs, their cache keys could collide, causing one registry's cached data to overwrite another's. This defeats the purpose of per-registry caching and can show incorrect server lists when switching between registries.
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| removeSource(sourceId); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Registry data not refreshed when removing active source
When removing the currently active registry source, the store correctly resets to the official registry, but handleRemoveSource doesn't call onRegistryChange to notify the parent component. This leaves the UI displaying stale data from the removed registry instead of fetching and showing servers from the official registry that's now active.
| authRequired: true, | ||
| error: "Authentication required for this registry", | ||
| }); | ||
| return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Registry URL not updated on auth failure
In fetchServers, when authentication is required or an error occurs, currentRegistryUrl isn't updated to reflect the target registry. This causes subsequent calls without an explicit registryUrl parameter to use the wrong registry, and breaks registry change detection logic that compares against currentRegistryUrl. The same function correctly updates this field on successful responses.
Additional Locations (1)
01d6e36 to
ff5fe45
Compare
Based on modelcontextprotocol/registry#756

Note
Adds multi-registry support with a selector and add dialog, per-registry caching, and auth-required handling across client and server.
RegistryTab: integratesRegistrySelector, per-registry refresh, and an auth-required empty state; updates header/status UI.registry/RegistrySelectorwith add/remove sources and auth indicator; opensAddRegistryDialog.registry/AddRegistryDialogto add custom registries (validates URL, checks connectivity/auth, updates store).stores/registry/registry-sources-store: persistable list ofRegistrySources with active source management.stores/registry/registry-store: per-registry cache keys,currentRegistryUrl,authRequired; fetch methods acceptregistryUrl, handle 401 auth responses, and cache per registry.lib/registry-api: requests acceptregistryUrl/accessToken;isAuthRequiredhelper; all endpoints return typed auth-required responses on 401.server/routes/mcp/registry: acceptregistryUrlparam, forwardAuthorization, and return{ requiresAuth, wwwAuthenticate, registryUrl }on 401 for servers, versions, and specific version endpoints.shared/types: addRegistrySourceandRegistryAuthRequiredResponsetypes.Written by Cursor Bugbot for commit 01d6e36. This will update automatically on new commits. Configure here.