Reduce remaining MCP and persistence debug detail

This commit is contained in:
2026-04-04 09:52:38 +08:00
parent 02f22d80bd
commit 010ded8476
4 changed files with 240 additions and 72 deletions

View File

@@ -118,6 +118,54 @@ function summarizeHeadersForDebug(
} }
} }
function extractHttpStatusFromErrorMessage(message: string): number | undefined {
const statusMatch = message.match(/^HTTP (\d{3}):/)
if (!statusMatch) {
return undefined
}
return Number(statusMatch[1])
}
function summarizeOAuthErrorForDebug(error: unknown): string {
const summary: Record<string, boolean | number | string> = {}
if (error instanceof Error) {
summary.errorType = error.constructor.name
summary.errorName = error.name
summary.hasMessage = error.message.length > 0
const httpStatus = extractHttpStatusFromErrorMessage(error.message)
if (httpStatus !== undefined) {
summary.httpStatus = httpStatus
}
if (error instanceof OAuthError) {
summary.oauthErrorCode = error.errorCode
}
} else {
summary.errorType = typeof error
summary.hasValue = error !== undefined && error !== null
}
const errno = getErrnoCode(error)
if (errno) {
summary.errno = errno
}
if (axios.isAxiosError(error)) {
summary.errorType = 'AxiosError'
if (error.code) {
summary.axiosCode = error.code
}
if (typeof error.response?.status === 'number') {
summary.httpStatus = error.response.status
}
summary.hasResponseData = error.response?.data !== undefined
}
return jsonStringify(summary)
}
/** /**
* Some OAuth servers (notably Slack) return HTTP 200 for all responses, * Some OAuth servers (notably Slack) return HTTP 200 for all responses,
* signaling errors via the JSON body instead. The SDK's executeTokenRequest * signaling errors via the JSON body instead. The SDK's executeTokenRequest
@@ -289,7 +337,9 @@ async function fetchAuthServerMetadata(
// to the legacy path-aware retry. // to the legacy path-aware retry.
logMCPDebug( logMCPDebug(
serverName, serverName,
`RFC 9728 discovery failed, falling back: ${errorMessage(err)}`, `RFC 9728 discovery failed, falling back: ${summarizeOAuthErrorForDebug(
err,
)}`,
) )
} }
@@ -511,7 +561,7 @@ export async function revokeServerTokens(
: 'client_secret_basic' : 'client_secret_basic'
logMCPDebug( logMCPDebug(
serverName, serverName,
`Revoking tokens via ${revocationEndpointStr} (${authMethod})`, `Revoking tokens via discovered OAuth revocation endpoint (${authMethod})`,
) )
// Revoke refresh token first (more important - prevents future access token generation) // Revoke refresh token first (more important - prevents future access token generation)
@@ -531,7 +581,9 @@ export async function revokeServerTokens(
// Log but continue // Log but continue
logMCPDebug( logMCPDebug(
serverName, serverName,
`Failed to revoke refresh token: ${errorMessage(error)}`, `Failed to revoke refresh token: ${summarizeOAuthErrorForDebug(
error,
)}`,
) )
} }
} }
@@ -552,7 +604,9 @@ export async function revokeServerTokens(
} catch (error: unknown) { } catch (error: unknown) {
logMCPDebug( logMCPDebug(
serverName, serverName,
`Failed to revoke access token: ${errorMessage(error)}`, `Failed to revoke access token: ${summarizeOAuthErrorForDebug(
error,
)}`,
) )
} }
} }
@@ -560,7 +614,10 @@ export async function revokeServerTokens(
} }
} catch (error: unknown) { } catch (error: unknown) {
// Log error but don't throw - revocation is best-effort // Log error but don't throw - revocation is best-effort
logMCPDebug(serverName, `Failed to revoke tokens: ${errorMessage(error)}`) logMCPDebug(
serverName,
`Failed to revoke tokens: ${summarizeOAuthErrorForDebug(error)}`,
)
} }
} else { } else {
logMCPDebug(serverName, 'No tokens to revoke') logMCPDebug(serverName, 'No tokens to revoke')
@@ -914,10 +971,7 @@ export async function performMCPOAuthFlow(
try { try {
resourceMetadataUrl = new URL(cachedResourceMetadataUrl) resourceMetadataUrl = new URL(cachedResourceMetadataUrl)
} catch { } catch {
logMCPDebug( logMCPDebug(serverName, 'Invalid cached resource metadata URL')
serverName,
`Invalid cached resourceMetadataUrl: ${cachedResourceMetadataUrl}`,
)
} }
} }
const wwwAuthParams: WWWAuthenticateParams = { const wwwAuthParams: WWWAuthenticateParams = {
@@ -987,7 +1041,7 @@ export async function performMCPOAuthFlow(
} catch (error) { } catch (error) {
logMCPDebug( logMCPDebug(
serverName, serverName,
`Failed to fetch OAuth metadata: ${errorMessage(error)}`, `Failed to fetch OAuth metadata: ${summarizeOAuthErrorForDebug(error)}`,
) )
} }
@@ -1184,7 +1238,10 @@ export async function performMCPOAuthFlow(
) )
} }
} catch (error) { } catch (error) {
logMCPDebug(serverName, `SDK auth error: ${errorMessage(error)}`) logMCPDebug(
serverName,
`SDK auth error: ${summarizeOAuthErrorForDebug(error)}`,
)
cleanup() cleanup()
rejectOnce(new Error(`SDK auth failed: ${errorMessage(error)}`)) rejectOnce(new Error(`SDK auth failed: ${errorMessage(error)}`))
} }
@@ -1258,7 +1315,7 @@ export async function performMCPOAuthFlow(
} catch (error) { } catch (error) {
logMCPDebug( logMCPDebug(
serverName, serverName,
`Error during auth completion: ${errorMessage(error)}`, `Error during auth completion: ${summarizeOAuthErrorForDebug(error)}`,
) )
// Determine failure reason for attribution telemetry. The try block covers // Determine failure reason for attribution telemetry. The try block covers
@@ -1300,9 +1357,9 @@ export async function performMCPOAuthFlow(
// SDK does not attach HTTP status as a property, but the fallback ServerError // SDK does not attach HTTP status as a property, but the fallback ServerError
// embeds it in the message as "HTTP {status}:" when the response body was // embeds it in the message as "HTTP {status}:" when the response body was
// unparseable. Best-effort extraction. // unparseable. Best-effort extraction.
const statusMatch = error.message.match(/^HTTP (\d{3}):/) const parsedStatus = extractHttpStatusFromErrorMessage(error.message)
if (statusMatch) { if (parsedStatus !== undefined) {
httpStatus = Number(statusMatch[1]) httpStatus = parsedStatus
} }
// If client not found, clear the stored client ID and suggest retry // If client not found, clear the stored client ID and suggest retry
if ( if (
@@ -1608,7 +1665,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
} catch (e) { } catch (e) {
logMCPDebug( logMCPDebug(
this.serverName, this.serverName,
`XAA silent exchange failed: ${errorMessage(e)}`, `XAA silent exchange failed: ${summarizeOAuthErrorForDebug(e)}`,
) )
} }
// Fall through. Either id_token isn't cached (xaaRefresh returned // Fall through. Either id_token isn't cached (xaaRefresh returned
@@ -1681,7 +1738,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
} catch (error) { } catch (error) {
logMCPDebug( logMCPDebug(
this.serverName, this.serverName,
`Token refresh error: ${errorMessage(error)}`, `Token refresh error: ${summarizeOAuthErrorForDebug(error)}`,
) )
} }
} }
@@ -1796,7 +1853,9 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
} catch (e) { } catch (e) {
logMCPDebug( logMCPDebug(
this.serverName, this.serverName,
`XAA: OIDC discovery failed in silent refresh: ${errorMessage(e)}`, `XAA: OIDC discovery failed in silent refresh: ${summarizeOAuthErrorForDebug(
e,
)}`,
) )
return undefined return undefined
} }
@@ -2055,7 +2114,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
if (metadataUrl) { if (metadataUrl) {
logMCPDebug( logMCPDebug(
this.serverName, this.serverName,
`Fetching metadata from configured URL: ${metadataUrl}`, 'Fetching metadata from configured override URL',
) )
try { try {
const metadata = await fetchAuthServerMetadata( const metadata = await fetchAuthServerMetadata(
@@ -2073,7 +2132,9 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
} catch (error) { } catch (error) {
logMCPDebug( logMCPDebug(
this.serverName, this.serverName,
`Failed to fetch from configured metadata URL: ${errorMessage(error)}`, `Failed to fetch from configured metadata URL: ${summarizeOAuthErrorForDebug(
error,
)}`,
) )
} }
} }
@@ -2225,7 +2286,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
} else if (cached?.authorizationServerUrl) { } else if (cached?.authorizationServerUrl) {
logMCPDebug( logMCPDebug(
this.serverName, this.serverName,
`Re-discovering metadata from persisted auth server URL: ${cached.authorizationServerUrl}`, 'Re-discovering metadata from persisted auth server URL',
) )
metadata = await discoverAuthorizationServerMetadata( metadata = await discoverAuthorizationServerMetadata(
cached.authorizationServerUrl, cached.authorizationServerUrl,
@@ -2281,10 +2342,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
// Invalid grant means the refresh token itself is invalid/revoked/expired. // Invalid grant means the refresh token itself is invalid/revoked/expired.
// But another process may have already refreshed successfully — check first. // But another process may have already refreshed successfully — check first.
if (error instanceof InvalidGrantError) { if (error instanceof InvalidGrantError) {
logMCPDebug( logMCPDebug(this.serverName, 'Token refresh failed with invalid_grant')
this.serverName,
`Token refresh failed with invalid_grant: ${error.message}`,
)
clearKeychainCache() clearKeychainCache()
const storage = getSecureStorage() const storage = getSecureStorage()
const data = storage.read() const data = storage.read()
@@ -2331,7 +2389,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
if (!isRetryable || attempt >= MAX_ATTEMPTS) { if (!isRetryable || attempt >= MAX_ATTEMPTS) {
logMCPDebug( logMCPDebug(
this.serverName, this.serverName,
`Token refresh failed: ${errorMessage(error)}`, `Token refresh failed: ${summarizeOAuthErrorForDebug(error)}`,
) )
emitRefreshEvent( emitRefreshEvent(
'failure', 'failure',

View File

@@ -377,6 +377,49 @@ function summarizeStderrForDebug(stderrOutput: string): string {
return `Server stderr captured (${trimmed.length} chars, ${lineCount} lines)` return `Server stderr captured (${trimmed.length} chars, ${lineCount} lines)`
} }
function summarizeMcpErrorForDebug(error: unknown): string {
const summary: Record<string, boolean | number | string> = {}
if (error instanceof Error) {
summary.errorType = error.constructor.name
summary.errorName = error.name
summary.hasMessage = error.message.length > 0
summary.hasStack = Boolean(error.stack)
const errorObj = error as Error & {
code?: unknown
errno?: unknown
syscall?: unknown
status?: unknown
cause?: unknown
}
if (typeof errorObj.code === 'string' || typeof errorObj.code === 'number') {
summary.code = errorObj.code
}
if (
typeof errorObj.errno === 'string' ||
typeof errorObj.errno === 'number'
) {
summary.errno = errorObj.errno
}
if (typeof errorObj.syscall === 'string') {
summary.syscall = errorObj.syscall
}
if (typeof errorObj.status === 'number') {
summary.status = errorObj.status
}
if (errorObj.cause !== undefined) {
summary.hasCause = true
}
} else {
summary.errorType = typeof error
summary.hasValue = error !== undefined && error !== null
}
return jsonStringify(summary)
}
/** /**
* Shared handler for sse/http/claudeai-proxy auth failures during connect: * Shared handler for sse/http/claudeai-proxy auth failures during connect:
* emits tengu_mcp_server_needs_auth, caches the needs-auth entry, and returns * emits tengu_mcp_server_needs_auth, caches the needs-auth entry, and returns
@@ -1077,20 +1120,22 @@ export const connectToServer = memoize(
) )
try { try {
const testUrl = new URL(serverRef.url) const testUrl = new URL(serverRef.url)
logMCPDebug( logMCPDebug(name, 'Parsed HTTP endpoint for preflight checks')
name,
`Parsed URL: host=${testUrl.hostname}, port=${testUrl.port || 'default'}, protocol=${testUrl.protocol}`,
)
// Log DNS resolution attempt // Log DNS resolution attempt
if ( if (
testUrl.hostname === '127.0.0.1' || testUrl.hostname === '127.0.0.1' ||
testUrl.hostname === 'localhost' testUrl.hostname === 'localhost'
) { ) {
logMCPDebug(name, `Using loopback address: ${testUrl.hostname}`) logMCPDebug(name, 'Using loopback HTTP endpoint')
} }
} catch (urlError) { } catch (urlError) {
logMCPDebug(name, `Failed to parse URL: ${urlError}`) logMCPDebug(
name,
`Failed to parse HTTP endpoint for preflight: ${summarizeMcpErrorForDebug(
urlError,
)}`,
)
} }
} }
@@ -1142,30 +1187,29 @@ export const connectToServer = memoize(
if (serverRef.type === 'sse' && error instanceof Error) { if (serverRef.type === 'sse' && error instanceof Error) {
logMCPDebug( logMCPDebug(
name, name,
`SSE Connection failed after ${elapsed}ms: ${jsonStringify({ `SSE connection failed after ${elapsed}ms: ${summarizeMcpErrorForDebug(
url: serverRef.url, error,
error: error.message, )}`,
errorType: error.constructor.name, )
stack: error.stack, logMCPError(
})}`, name,
`SSE connection failed: ${summarizeMcpErrorForDebug(error)}`,
) )
logMCPError(name, error)
if (error instanceof UnauthorizedError) { if (error instanceof UnauthorizedError) {
return handleRemoteAuthFailure(name, serverRef, 'sse') return handleRemoteAuthFailure(name, serverRef, 'sse')
} }
} else if (serverRef.type === 'http' && error instanceof Error) { } else if (serverRef.type === 'http' && error instanceof Error) {
const errorObj = error as Error & {
cause?: unknown
code?: string
errno?: string | number
syscall?: string
}
logMCPDebug( logMCPDebug(
name, name,
`HTTP Connection failed after ${elapsed}ms: ${error.message} (code: ${errorObj.code || 'none'}, errno: ${errorObj.errno || 'none'})`, `HTTP connection failed after ${elapsed}ms: ${summarizeMcpErrorForDebug(
error,
)}`,
)
logMCPError(
name,
`HTTP connection failed: ${summarizeMcpErrorForDebug(error)}`,
) )
logMCPError(name, error)
if (error instanceof UnauthorizedError) { if (error instanceof UnauthorizedError) {
return handleRemoteAuthFailure(name, serverRef, 'http') return handleRemoteAuthFailure(name, serverRef, 'http')
@@ -1176,9 +1220,16 @@ export const connectToServer = memoize(
) { ) {
logMCPDebug( logMCPDebug(
name, name,
`claude.ai proxy connection failed after ${elapsed}ms: ${error.message}`, `claude.ai proxy connection failed after ${elapsed}ms: ${summarizeMcpErrorForDebug(
error,
)}`,
)
logMCPError(
name,
`claude.ai proxy connection failed: ${summarizeMcpErrorForDebug(
error,
)}`,
) )
logMCPError(name, error)
// StreamableHTTPError has a `code` property with the HTTP status // StreamableHTTPError has a `code` property with the HTTP status
const errorCode = (error as Error & { code?: number }).code const errorCode = (error as Error & { code?: number }).code
@@ -1257,7 +1308,9 @@ export const connectToServer = memoize(
} catch (error) { } catch (error) {
logMCPError( logMCPError(
name, name,
`Failed to send ide_connected notification: ${error}`, `Failed to send ide_connected notification: ${summarizeMcpErrorForDebug(
error,
)}`,
) )
} }
} }
@@ -1291,7 +1344,10 @@ export const connectToServer = memoize(
hasTriggeredClose = true hasTriggeredClose = true
logMCPDebug(name, `Closing transport (${reason})`) logMCPDebug(name, `Closing transport (${reason})`)
void client.close().catch(e => { void client.close().catch(e => {
logMCPDebug(name, `Error during close: ${errorMessage(e)}`) logMCPDebug(
name,
`Error during close: ${summarizeMcpErrorForDebug(e)}`,
)
}) })
} }
@@ -1355,7 +1411,10 @@ export const connectToServer = memoize(
`Failed to spawn process - check command and permissions`, `Failed to spawn process - check command and permissions`,
) )
} else { } else {
logMCPDebug(name, `Connection error: ${error.message}`) logMCPDebug(
name,
`Connection error: ${summarizeMcpErrorForDebug(error)}`,
)
} }
} }
@@ -1456,12 +1515,20 @@ export const connectToServer = memoize(
try { try {
await inProcessServer.close() await inProcessServer.close()
} catch (error) { } catch (error) {
logMCPDebug(name, `Error closing in-process server: ${error}`) logMCPDebug(
name,
`Error closing in-process server: ${summarizeMcpErrorForDebug(
error,
)}`,
)
} }
try { try {
await client.close() await client.close()
} catch (error) { } catch (error) {
logMCPDebug(name, `Error closing client: ${error}`) logMCPDebug(
name,
`Error closing client: ${summarizeMcpErrorForDebug(error)}`,
)
} }
return return
} }
@@ -1487,7 +1554,10 @@ export const connectToServer = memoize(
try { try {
process.kill(childPid, 'SIGINT') process.kill(childPid, 'SIGINT')
} catch (error) { } catch (error) {
logMCPDebug(name, `Error sending SIGINT: ${error}`) logMCPDebug(
name,
`Error sending SIGINT: ${summarizeMcpErrorForDebug(error)}`,
)
return return
} }
@@ -1541,7 +1611,12 @@ export const connectToServer = memoize(
try { try {
process.kill(childPid, 'SIGTERM') process.kill(childPid, 'SIGTERM')
} catch (termError) { } catch (termError) {
logMCPDebug(name, `Error sending SIGTERM: ${termError}`) logMCPDebug(
name,
`Error sending SIGTERM: ${summarizeMcpErrorForDebug(
termError,
)}`,
)
resolved = true resolved = true
clearInterval(checkInterval) clearInterval(checkInterval)
clearTimeout(failsafeTimeout) clearTimeout(failsafeTimeout)
@@ -1574,7 +1649,9 @@ export const connectToServer = memoize(
} catch (killError) { } catch (killError) {
logMCPDebug( logMCPDebug(
name, name,
`Error sending SIGKILL: ${killError}`, `Error sending SIGKILL: ${summarizeMcpErrorForDebug(
killError,
)}`,
) )
} }
} catch { } catch {
@@ -1606,7 +1683,12 @@ export const connectToServer = memoize(
}) })
} }
} catch (processError) { } catch (processError) {
logMCPDebug(name, `Error terminating process: ${processError}`) logMCPDebug(
name,
`Error terminating process: ${summarizeMcpErrorForDebug(
processError,
)}`,
)
} }
} }
@@ -1614,7 +1696,10 @@ export const connectToServer = memoize(
try { try {
await client.close() await client.close()
} catch (error) { } catch (error) {
logMCPDebug(name, `Error closing client: ${error}`) logMCPDebug(
name,
`Error closing client: ${summarizeMcpErrorForDebug(error)}`,
)
} }
} }
@@ -1671,9 +1756,14 @@ export const connectToServer = memoize(
}) })
logMCPDebug( logMCPDebug(
name, name,
`Connection failed after ${connectionDurationMs}ms: ${errorMessage(error)}`, `Connection failed after ${connectionDurationMs}ms: ${summarizeMcpErrorForDebug(
error,
)}`,
)
logMCPError(
name,
`Connection failed: ${summarizeMcpErrorForDebug(error)}`,
) )
logMCPError(name, `Connection failed: ${errorMessage(error)}`)
if (inProcessServer) { if (inProcessServer) {
inProcessServer.close().catch(() => {}) inProcessServer.close().catch(() => {})
@@ -2038,7 +2128,10 @@ export const fetchToolsForClient = memoizeWithLRU(
}) })
.filter(isIncludedMcpTool) .filter(isIncludedMcpTool)
} catch (error) { } catch (error) {
logMCPError(client.name, `Failed to fetch tools: ${errorMessage(error)}`) logMCPError(
client.name,
`Failed to fetch tools: ${summarizeMcpErrorForDebug(error)}`,
)
return [] return []
} }
}, },
@@ -2070,7 +2163,7 @@ export const fetchResourcesForClient = memoizeWithLRU(
} catch (error) { } catch (error) {
logMCPError( logMCPError(
client.name, client.name,
`Failed to fetch resources: ${errorMessage(error)}`, `Failed to fetch resources: ${summarizeMcpErrorForDebug(error)}`,
) )
return [] return []
} }
@@ -2136,7 +2229,9 @@ export const fetchCommandsForClient = memoizeWithLRU(
} catch (error) { } catch (error) {
logMCPError( logMCPError(
client.name, client.name,
`Error running command '${prompt.name}': ${errorMessage(error)}`, `Error running command '${prompt.name}': ${summarizeMcpErrorForDebug(
error,
)}`,
) )
throw error throw error
} }
@@ -2146,7 +2241,7 @@ export const fetchCommandsForClient = memoizeWithLRU(
} catch (error) { } catch (error) {
logMCPError( logMCPError(
client.name, client.name,
`Failed to fetch commands: ${errorMessage(error)}`, `Failed to fetch commands: ${summarizeMcpErrorForDebug(error)}`,
) )
return [] return []
} }
@@ -2247,7 +2342,10 @@ export async function reconnectMcpServerImpl(
} }
} catch (error) { } catch (error) {
// Handle errors gracefully - connection might have closed during fetch // Handle errors gracefully - connection might have closed during fetch
logMCPError(name, `Error during reconnection: ${errorMessage(error)}`) logMCPError(
name,
`Error during reconnection: ${summarizeMcpErrorForDebug(error)}`,
)
// Return with failed status // Return with failed status
return { return {
@@ -2422,7 +2520,9 @@ export async function getMcpToolsCommandsAndResources(
// Handle errors gracefully - connection might have closed during fetch // Handle errors gracefully - connection might have closed during fetch
logMCPError( logMCPError(
name, name,
`Error fetching tools/commands/resources: ${errorMessage(error)}`, `Error fetching tools/commands/resources: ${summarizeMcpErrorForDebug(
error,
)}`,
) )
// Still update with the client but no tools/commands // Still update with the client but no tools/commands
@@ -2509,7 +2609,7 @@ export function prefetchAllMcpResources(
}, mcpConfigs).catch(error => { }, mcpConfigs).catch(error => {
logMCPError( logMCPError(
'prefetchAllMcpResources', 'prefetchAllMcpResources',
`Failed to get MCP resources: ${errorMessage(error)}`, `Failed to get MCP resources: ${summarizeMcpErrorForDebug(error)}`,
) )
// Still resolve with empty results // Still resolve with empty results
void resolve({ void resolve({
@@ -3371,7 +3471,12 @@ export async function setupSdkMcpClients(
} }
} catch (error) { } catch (error) {
// If connection fails, return failed server // If connection fails, return failed server
logMCPError(name, `Failed to connect SDK MCP server: ${error}`) logMCPError(
name,
`Failed to connect SDK MCP server: ${summarizeMcpErrorForDebug(
error,
)}`,
)
return { return {
client: { client: {
type: 'failed' as const, type: 'failed' as const,

View File

@@ -1397,6 +1397,7 @@ export function parseMcpConfigFromFilePath(params: {
configContent = fs.readFileSync(filePath, { encoding: 'utf8' }) configContent = fs.readFileSync(filePath, { encoding: 'utf8' })
} catch (error: unknown) { } catch (error: unknown) {
const code = getErrnoCode(error) const code = getErrnoCode(error)
const fileName = parse(filePath).base
if (code === 'ENOENT') { if (code === 'ENOENT') {
return { return {
config: null, config: null,
@@ -1415,7 +1416,7 @@ export function parseMcpConfigFromFilePath(params: {
} }
} }
logForDebugging( logForDebugging(
`MCP config read error for ${filePath} (scope=${scope}): ${error}`, `MCP config read error (scope=${scope}, file=${fileName}, errno=${code ?? 'none'}, errorType=${error instanceof Error ? error.name : typeof error})`,
{ level: 'error' }, { level: 'error' },
) )
return { return {
@@ -1439,7 +1440,7 @@ export function parseMcpConfigFromFilePath(params: {
if (!parsedJson) { if (!parsedJson) {
logForDebugging( logForDebugging(
`MCP config is not valid JSON: ${filePath} (scope=${scope}, length=${configContent.length}, first100=${jsonStringify(configContent.slice(0, 100))})`, `MCP config is not valid JSON (scope=${scope}, file=${parse(filePath).base}, length=${configContent.length})`,
{ level: 'error' }, { level: 'error' },
) )
return { return {

View File

@@ -1344,7 +1344,11 @@ class Project {
setRemoteIngressUrl(url: string): void { setRemoteIngressUrl(url: string): void {
this.remoteIngressUrl = url this.remoteIngressUrl = url
logForDebugging(`Remote persistence enabled with URL: ${url}`) logForDebugging(
url
? 'Remote persistence enabled (remote ingress configured)'
: 'Remote persistence disabled',
)
if (url) { if (url) {
// If using CCR, don't delay messages by any more than 10ms. // If using CCR, don't delay messages by any more than 10ms.
this.FLUSH_INTERVAL_MS = REMOTE_FLUSH_INTERVAL_MS this.FLUSH_INTERVAL_MS = REMOTE_FLUSH_INTERVAL_MS