fix(extensions): hide runtime-injected mcp diagnostics
This commit is contained in:
parent
27a54f7f32
commit
12f6f90701
2 changed files with 44 additions and 18 deletions
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { isInstalledMcpScope } from '@shared/utils/mcpScopes';
|
||||||
|
|
||||||
import type { McpServerDiagnostic, McpServerHealthStatus } from '@shared/types/extensions';
|
import type { McpServerDiagnostic, McpServerHealthStatus } from '@shared/types/extensions';
|
||||||
|
|
||||||
interface McpDiagnoseJsonEntry {
|
interface McpDiagnoseJsonEntry {
|
||||||
|
|
@ -18,6 +20,21 @@ const EMBEDDED_HTTP_URL_PATTERN = /https?:\/\/[^\s"'`]+/gi;
|
||||||
const SENSITIVE_FLAG_VALUE_PATTERN =
|
const SENSITIVE_FLAG_VALUE_PATTERN =
|
||||||
/(--(?:api[-_]?key|access[-_]?token|auth[-_]?token|token|secret|password|client[-_]?secret))(?:=([^\s]+)|\s+([^\s]+))/gi;
|
/(--(?:api[-_]?key|access[-_]?token|auth[-_]?token|token|secret|password|client[-_]?secret))(?:=([^\s]+)|\s+([^\s]+))/gi;
|
||||||
|
|
||||||
|
function isPluginInjectedDiagnosticName(name: string): boolean {
|
||||||
|
return name.startsWith('plugin:');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isExtensionsManagedDiagnosticEntry(entry: {
|
||||||
|
name: string;
|
||||||
|
scope?: 'local' | 'user' | 'project' | 'global' | 'dynamic' | 'managed';
|
||||||
|
}): boolean {
|
||||||
|
if (isPluginInjectedDiagnosticName(entry.name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.scope === undefined || isInstalledMcpScope(entry.scope);
|
||||||
|
}
|
||||||
|
|
||||||
function extractJsonObject<T>(raw: string): T {
|
function extractJsonObject<T>(raw: string): T {
|
||||||
const trimmed = raw.trim();
|
const trimmed = raw.trim();
|
||||||
try {
|
try {
|
||||||
|
|
@ -127,7 +144,8 @@ export function parseMcpDiagnosticsOutput(output: string): McpServerDiagnostic[]
|
||||||
.map((line) => line.trim())
|
.map((line) => line.trim())
|
||||||
.filter((line) => line.length > 0 && !line.startsWith('Checking MCP server health'))
|
.filter((line) => line.length > 0 && !line.startsWith('Checking MCP server health'))
|
||||||
.map((line) => parseDiagnosticLine(line, checkedAt))
|
.map((line) => parseDiagnosticLine(line, checkedAt))
|
||||||
.filter((entry): entry is McpServerDiagnostic => entry !== null);
|
.filter((entry): entry is McpServerDiagnostic => entry !== null)
|
||||||
|
.filter((entry) => isExtensionsManagedDiagnosticEntry(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseMcpDiagnosticsJsonOutput(output: string): McpServerDiagnostic[] {
|
export function parseMcpDiagnosticsJsonOutput(output: string): McpServerDiagnostic[] {
|
||||||
|
|
@ -155,17 +173,17 @@ export function parseMcpDiagnosticsJsonOutput(output: string): McpServerDiagnost
|
||||||
: 'unknown';
|
: 'unknown';
|
||||||
|
|
||||||
const rawLine = `${entry.name}: ${redactedTarget} - ${entry.statusLabel}`;
|
const rawLine = `${entry.name}: ${redactedTarget} - ${entry.statusLabel}`;
|
||||||
return [
|
const diagnostic = {
|
||||||
{
|
name: entry.name,
|
||||||
name: entry.name,
|
target: redactedTarget,
|
||||||
target: redactedTarget,
|
scope: entry.scope,
|
||||||
scope: entry.scope,
|
transport: entry.transport,
|
||||||
transport: entry.transport,
|
status: normalizedStatus,
|
||||||
status: normalizedStatus,
|
statusLabel: entry.statusLabel,
|
||||||
statusLabel: entry.statusLabel,
|
rawLine,
|
||||||
rawLine,
|
checkedAt,
|
||||||
checkedAt,
|
} satisfies McpServerDiagnostic;
|
||||||
},
|
|
||||||
];
|
return isExtensionsManagedDiagnosticEntry(diagnostic) ? [diagnostic] : [];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,20 +16,20 @@ browsermcp: npx @browsermcp/mcp@latest - ✓ Connected
|
||||||
tavily-remote-mcp: npx -y mcp-remote https://mcp.tavily.com/mcp/?tavilyApiKey=test - ✗ Failed to connect
|
tavily-remote-mcp: npx -y mcp-remote https://mcp.tavily.com/mcp/?tavilyApiKey=test - ✗ Failed to connect
|
||||||
alpic: https://mcp.alpic.ai (HTTP) - ! Needs authentication`);
|
alpic: https://mcp.alpic.ai (HTTP) - ! Needs authentication`);
|
||||||
|
|
||||||
expect(diagnostics).toHaveLength(5);
|
expect(diagnostics).toHaveLength(3);
|
||||||
expect(diagnostics[0]).toMatchObject({
|
expect(diagnostics[0]).toMatchObject({
|
||||||
name: 'plugin:context7:context7',
|
name: 'browsermcp',
|
||||||
target: 'npx -y @upstash/context7-mcp',
|
target: 'npx @browsermcp/mcp@latest',
|
||||||
status: 'connected',
|
status: 'connected',
|
||||||
statusLabel: 'Connected',
|
statusLabel: 'Connected',
|
||||||
});
|
});
|
||||||
expect(diagnostics[3]).toMatchObject({
|
expect(diagnostics[1]).toMatchObject({
|
||||||
name: 'tavily-remote-mcp',
|
name: 'tavily-remote-mcp',
|
||||||
target: 'npx -y mcp-remote https://mcp.tavily.com/mcp/?tavilyApiKey=REDACTED',
|
target: 'npx -y mcp-remote https://mcp.tavily.com/mcp/?tavilyApiKey=REDACTED',
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
statusLabel: 'Failed to connect',
|
statusLabel: 'Failed to connect',
|
||||||
});
|
});
|
||||||
expect(diagnostics[4]).toMatchObject({
|
expect(diagnostics[2]).toMatchObject({
|
||||||
name: 'alpic',
|
name: 'alpic',
|
||||||
target: 'https://mcp.alpic.ai (HTTP)',
|
target: 'https://mcp.alpic.ai (HTTP)',
|
||||||
status: 'needs-authentication',
|
status: 'needs-authentication',
|
||||||
|
|
@ -64,6 +64,14 @@ another log line`);
|
||||||
status: 'timeout',
|
status: 'timeout',
|
||||||
statusLabel: 'Timed out',
|
statusLabel: 'Timed out',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin:context7:context7',
|
||||||
|
target: 'npx -y @upstash/context7-mcp',
|
||||||
|
scope: 'dynamic',
|
||||||
|
transport: 'stdio',
|
||||||
|
status: 'connected',
|
||||||
|
statusLabel: 'Connected',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue