mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 21:23:07 +00:00
feat: add API key masking and 80+ tools warning for MCP servers
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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 { useAppStore } from '@/store/app-store';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Code,
|
Code,
|
||||||
|
AlertTriangle,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
@@ -77,6 +78,51 @@ interface ServerTestState {
|
|||||||
connectionTime?: number;
|
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() {
|
export function MCPServersSection() {
|
||||||
const {
|
const {
|
||||||
mcpServers,
|
mcpServers,
|
||||||
@@ -103,6 +149,22 @@ export function MCPServersSection() {
|
|||||||
const [globalJsonValue, setGlobalJsonValue] = useState('');
|
const [globalJsonValue, setGlobalJsonValue] = useState('');
|
||||||
const autoTestedServersRef = useRef<Set<string>>(new Set());
|
const autoTestedServersRef = useRef<Set<string>>(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
|
// Auto-load MCP servers from settings file on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadMCPServersFromServer().catch((error) => {
|
loadMCPServersFromServer().catch((error) => {
|
||||||
@@ -806,6 +868,24 @@ export function MCPServersSection() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Tools Count Warning */}
|
||||||
|
{showToolsWarning && (
|
||||||
|
<div className="mx-6 mt-4 p-3 rounded-lg border border-yellow-500/50 bg-yellow-500/10">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<AlertTriangle className="w-5 h-5 text-yellow-500 shrink-0 mt-0.5" />
|
||||||
|
<div className="text-sm">
|
||||||
|
<p className="font-medium text-yellow-600 dark:text-yellow-400">
|
||||||
|
High tool count detected ({totalToolsCount} tools)
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground mt-1">
|
||||||
|
Having more than {MAX_RECOMMENDED_TOOLS} MCP tools may degrade AI model performance.
|
||||||
|
Consider disabling unused servers or removing unnecessary tools.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Server List */}
|
{/* Server List */}
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
{mcpServers.length === 0 ? (
|
{mcpServers.length === 0 ? (
|
||||||
@@ -883,7 +963,7 @@ export function MCPServersSection() {
|
|||||||
<div className="text-xs text-muted-foreground/60 mt-0.5 truncate">
|
<div className="text-xs text-muted-foreground/60 mt-0.5 truncate">
|
||||||
{server.type === 'stdio'
|
{server.type === 'stdio'
|
||||||
? `${server.command}${server.args?.length ? ' ' + server.args.join(' ') : ''}`
|
? `${server.command}${server.args?.length ? ' ' + server.args.join(' ') : ''}`
|
||||||
: server.url}
|
: maskSensitiveUrl(server.url || '')}
|
||||||
</div>
|
</div>
|
||||||
{testState?.status === 'error' && testState.error && (
|
{testState?.status === 'error' && testState.error && (
|
||||||
<div className="text-xs text-destructive mt-1 line-clamp-2 break-words">
|
<div className="text-xs text-destructive mt-1 line-clamp-2 break-words">
|
||||||
|
|||||||
Reference in New Issue
Block a user