From 9cba2e509ada012a70fb93a9772f36f80813760e Mon Sep 17 00:00:00 2001 From: Kacper Date: Sun, 28 Dec 2025 16:29:11 +0100 Subject: [PATCH] feat: add API key masking and 80+ tools warning for MCP servers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security improvements: - Mask sensitive values in URLs (api_key, token, auth, secret, etc.) - Prevents accidental API key leaks when sharing screen or screenshots Performance guidance: - Show warning banner when total MCP tools exceed 80 - Warns users that high tool count may degrade AI model performance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../mcp-servers/mcp-servers-section.tsx | 84 ++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/apps/ui/src/components/views/settings-view/mcp-servers/mcp-servers-section.tsx b/apps/ui/src/components/views/settings-view/mcp-servers/mcp-servers-section.tsx index 503aec46..d4c3d3c9 100644 --- a/apps/ui/src/components/views/settings-view/mcp-servers/mcp-servers-section.tsx +++ b/apps/ui/src/components/views/settings-view/mcp-servers/mcp-servers-section.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef, useCallback } from 'react'; +import { useState, useEffect, useRef, useCallback, useMemo } from 'react'; import { useAppStore } from '@/store/app-store'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -37,6 +37,7 @@ import { ChevronDown, ChevronRight, Code, + AlertTriangle, } from 'lucide-react'; import { Textarea } from '@/components/ui/textarea'; import { cn } from '@/lib/utils'; @@ -77,6 +78,51 @@ interface ServerTestState { connectionTime?: number; } +// Patterns that indicate sensitive values in URLs or config +const SENSITIVE_PARAM_PATTERNS = [ + /api[-_]?key/i, + /api[-_]?token/i, + /auth/i, + /token/i, + /secret/i, + /password/i, + /credential/i, + /bearer/i, +]; + +/** + * Mask sensitive values in URLs (query params with key-like names) + */ +function maskSensitiveUrl(url: string): string { + try { + const urlObj = new URL(url); + const params = new URLSearchParams(urlObj.search); + let hasSensitive = false; + + for (const [key] of params.entries()) { + if (SENSITIVE_PARAM_PATTERNS.some((pattern) => pattern.test(key))) { + params.set(key, '***'); + hasSensitive = true; + } + } + + if (hasSensitive) { + urlObj.search = params.toString(); + return urlObj.toString(); + } + return url; + } catch { + // If URL parsing fails, try simple regex replacement for common patterns + return url.replace( + /([?&])(api[-_]?key|auth|token|secret|password|credential)=([^&]*)/gi, + '$1$2=***' + ); + } +} + +// Maximum recommended MCP tools before performance degradation +const MAX_RECOMMENDED_TOOLS = 80; + export function MCPServersSection() { const { mcpServers, @@ -103,6 +149,22 @@ export function MCPServersSection() { const [globalJsonValue, setGlobalJsonValue] = useState(''); const autoTestedServersRef = useRef>(new Set()); + // Calculate total tools across all enabled servers + const totalToolsCount = useMemo(() => { + let count = 0; + for (const server of mcpServers) { + if (server.enabled !== false) { + const testState = serverTestStates[server.id]; + if (testState?.status === 'success' && testState.tools) { + count += testState.tools.length; + } + } + } + return count; + }, [mcpServers, serverTestStates]); + + const showToolsWarning = totalToolsCount > MAX_RECOMMENDED_TOOLS; + // Auto-load MCP servers from settings file on mount useEffect(() => { loadMCPServersFromServer().catch((error) => { @@ -806,6 +868,24 @@ export function MCPServersSection() { )} + {/* Tools Count Warning */} + {showToolsWarning && ( +
+
+ +
+

+ High tool count detected ({totalToolsCount} tools) +

+

+ Having more than {MAX_RECOMMENDED_TOOLS} MCP tools may degrade AI model performance. + Consider disabling unused servers or removing unnecessary tools. +

+
+
+
+ )} + {/* Server List */}
{mcpServers.length === 0 ? ( @@ -883,7 +963,7 @@ export function MCPServersSection() {
{server.type === 'stdio' ? `${server.command}${server.args?.length ? ' ' + server.args.join(' ') : ''}` - : server.url} + : maskSensitiveUrl(server.url || '')}
{testState?.status === 'error' && testState.error && (