Two critical issues with the health check endpoint:
- Environment Variable Exposure: The
/healthendpoint was exposing sensitive credentials (API keys, client secrets) in plaintext - Field Name Inconsistency: Circular remapping between
env→environment→envcausing confusion
The health check endpoint (GET /health) was returning full, unmasked environment variables from MCP server configurations:
{
"HELPSCOUT_CLIENT_ID": "IKA8CezyOi2TD5stGBJFrZ376tNBdksp", // ❌ EXPOSED
"AUTOMEM_API_KEY": "mem_7163ec31424b3e9d74b986811fd310aa" // ❌ EXPOSED
}The codebase had inconsistent field naming:
.mcp.jsonfiles useenv(MCP standard format)- Internal
MCPServerConfigtype usedenvironment - SDK
StdioClientTransportusesenv - Unnecessary remapping:
env→environment→env
This created circular logic with no benefit.
Changed Files:
src/types/core.ts: UpdatedMCPServerConfiginterface to useenvinstead ofenvironmentsrc/executor.ts: Removed unnecessaryenv→environmentremappingsrc/mcp/aggregator.ts: Updated all references to useconfig.env
Rationale:
- Matches
.mcp.jsonformat (.mcp.json 的格式) - Matches SDK expectations (SDK 的期望)
- Eliminates circular remapping (消除循环映射)
Added to src/mcp/aggregator.ts:
private maskEnvironmentVariables(env: Record<string, any>): Record<string, string> {
const masked: Record<string, string> = {};
for (const key of Object.keys(env)) {
const value = String(env[key]);
if (value.length <= 4) {
masked[key] = '***';
} else {
// Show first 4 chars for debugging
masked[key] = value.substring(0, 4) + '***';
}
}
return masked;
}Integration in updateRegistry():
private updateRegistry(connection: MCPConnection): void {
const sanitizedConfig = {
...connection.config,
env: this.maskEnvironmentVariables(connection.config.env || {})
};
const newServerInfo: ServerInfo = {
name: connection.name,
status: connection.status,
config: sanitizedConfig, // Masked config for health endpoint
// ...
};
this.registry.servers.set(connection.name, newServerInfo);
}$ curl -s http://localhost:3001/health | jq '.components.mcp.servers[0].config.env'
{
"AUTOMEM_API_KEY": "mem_7163ec31424b3e9d74b986811fd310aa", # ❌ EXPOSED
"OPENAI_API_KEY": "sk-proj-ixuUd404wuQP..." # ❌ EXPOSED
}$ curl -s http://localhost:3001/health | jq '.components.mcp.servers[0].config.env'
{
"AUTOMEM_ENDPOINT": "http***", # ✅ MASKED
"AUTOMEM_API_KEY": "mem_***", # ✅ MASKED
"OPENAI_API_KEY": "sk-p***" # ✅ MASKED
}-
src/types/core.ts (line 117)
- Changed
environment?: Record<string, string>→env?: Record<string, string>
- Changed
-
src/executor.ts (lines 70-87)
- Removed
env→environmentremapping - Simplified config normalization to only handle
type→transport
- Removed
-
src/mcp/aggregator.ts
- Lines 144, 149-154: Updated to use
config.env - Lines 258-271: Added
maskEnvironmentVariables()method - Lines 273-281: Integrated masking in
updateRegistry()
- Lines 144, 149-154: Updated to use
- Security: Credentials no longer exposed via health endpoint
- Consistency: Single source of truth for field naming (
env) - Simplicity: Removed unnecessary circular remapping
- Debuggability: First 4 chars preserved for debugging while maintaining security
All 5 MCP servers connect successfully and health check shows masked values:
- ✅ automem (7 tools)
- ✅ sequential-thinking (1 tool)
- ✅ context7 (2 tools)
- ✅ WordPressAPI (12 tools)
- ✅ helpscout (8 tools)
Total: 30 MCP tools available with secure health monitoring
- Type System Alignment: Internal types should match external formats to avoid confusion
- Registry Updates: Registry must be updated immediately when connections change for accurate health reporting
- Hot Reload Caveat:
tsx watchcan create stale executor instances - full restart required to verify changes - Security by Default: Sensitive data should be masked at the source, not at display time