Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 25 additions & 14 deletions packages/core/src/ide/ide-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,23 +495,13 @@ export class IdeClient {
}
}

private registerClientHandlers() {
// Must be invoked before client.connect so that errors surfaced during
// connection setup or by a long-lived SSE stream are routed through
// onerror instead of becoming unhandled promise rejections.
private registerTransportHandlers() {
if (!this.client) {
return;
}

this.client.setNotificationHandler(
IdeContextNotificationSchema,
(notification) => {
ideContextStore.set(notification.params);
const isTrusted = notification.params.workspaceState?.isTrusted;
if (isTrusted !== undefined) {
for (const listener of this.trustChangeListeners) {
listener(isTrusted);
}
}
},
);
this.client.onerror = (_error) => {
const errorMessage = _error instanceof Error ? _error.message : `_error`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The fallback value _error is currently a string literal, which prevents detailed error logging. It should be updated to capture the actual error details for debugging. Additionally, ensure the resulting string is trimmed and falls back to a non-empty default to avoid uninformative messages from whitespace-only strings.

Suggested change
const errorMessage = _error instanceof Error ? _error.message : `_error`;
const errorMessage = (_error instanceof Error ? _error.message : String(_error)).trim() || "Unknown error";
References
  1. When catching exceptions, log the detailed error for debugging instead of providing only a generic error message.
  2. When using an optional string with a fallback value, trim the optional string and use the fallback if the result is empty to avoid uninformative messages from whitespace-only strings.

this.setState(
Expand All @@ -527,6 +517,25 @@ export class IdeClient {
true,
);
};
}

private registerClientHandlers() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Registering handlers after connection introduces a race condition where early notifications might be missed. Since the MCP SDK allows registering handlers before connection, moving registerClientHandlers() before client.connect(transport) ensures the startup sequence correctly handles incoming messages without needing additional state management for race conditions.

References
  1. Avoid redundant internal state management for race conditions if the application's startup sequence already guarantees the correct order of operations.

if (!this.client) {
return;
}

this.client.setNotificationHandler(
IdeContextNotificationSchema,
(notification) => {
ideContextStore.set(notification.params);
const isTrusted = notification.params.workspaceState?.isTrusted;
if (isTrusted !== undefined) {
for (const listener of this.trustChangeListeners) {
listener(isTrusted);
}
}
},
);
this.client.setNotificationHandler(
IdeDiffAcceptedNotificationSchema,
(notification) => {
Expand Down Expand Up @@ -597,6 +606,7 @@ export class IdeClient {
headers: authToken ? { Authorization: `Bearer ${authToken}` } : {},
},
});
this.registerTransportHandlers();
await this.client.connect(transport);
this.registerClientHandlers();
await this.discoverTools();
Expand Down Expand Up @@ -630,6 +640,7 @@ export class IdeClient {
command,
args,
});
this.registerTransportHandlers();
await this.client.connect(transport);
this.registerClientHandlers();
await this.discoverTools();
Expand Down