Reduce remaining MCP and persistence debug detail
This commit is contained in:
@@ -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',
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user