Skip to content

Commit 5ce70e4

Browse files
committed
chore: add more information when there is a failure connecting
This adds more information to the debug resource whenever there is an issue connecting.
1 parent 46a93cc commit 5ce70e4

File tree

5 files changed

+116
-19
lines changed

5 files changed

+116
-19
lines changed

src/common/connectionManager.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
109109

110110
let serviceProvider: NodeDriverServiceProvider;
111111
let connectionInfo: ConnectionInfo;
112+
let connectionStringAuthType: ConnectionStringAuthType = "scram";
112113

113114
try {
114115
settings = { ...settings };
@@ -137,6 +138,11 @@ export class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
137138
connectionInfo.driverOptions.proxy ??= { useEnvironmentVariableProxies: true };
138139
connectionInfo.driverOptions.applyProxyToOIDC ??= true;
139140

141+
connectionStringAuthType = ConnectionManager.inferConnectionTypeFromSettings(
142+
this.userConfig,
143+
connectionInfo
144+
);
145+
140146
serviceProvider = await NodeDriverServiceProvider.connect(
141147
connectionInfo.connectionString,
142148
{
@@ -152,6 +158,7 @@ export class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
152158
this.changeState("connection-errored", {
153159
tag: "errored",
154160
errorReason,
161+
connectionStringAuthType,
155162
connectedAtlasCluster: settings.atlas,
156163
});
157164
throw new MongoDBError(ErrorCodes.MisconfiguredConnectionString, errorReason);
@@ -184,6 +191,7 @@ export class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
184191
this.changeState("connection-errored", {
185192
tag: "errored",
186193
errorReason,
194+
connectionStringAuthType,
187195
connectedAtlasCluster: settings.atlas,
188196
});
189197
throw new MongoDBError(ErrorCodes.NotConnectedToMongoDB, errorReason);

src/common/session.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
ConnectionManager,
1111
ConnectionSettings,
1212
ConnectionStateConnected,
13+
ConnectionStateErrored,
1314
} from "./connectionManager.js";
1415
import type { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
1516
import { ErrorCodes, MongoDBError } from "./errors.js";
@@ -28,7 +29,7 @@ export type SessionEvents = {
2829
connect: [];
2930
close: [];
3031
disconnect: [];
31-
"connection-error": [string];
32+
"connection-error": [ConnectionStateErrored];
3233
};
3334

3435
export class Session extends EventEmitter<SessionEvents> {
@@ -67,9 +68,9 @@ export class Session extends EventEmitter<SessionEvents> {
6768
this.exportsManager = exportsManager;
6869
this.connectionManager = connectionManager;
6970
this.connectionManager.on("connection-succeeded", () => this.emit("connect"));
70-
this.connectionManager.on("connection-timed-out", (error) => this.emit("connection-error", error.errorReason));
71+
this.connectionManager.on("connection-timed-out", (error) => this.emit("connection-error", error));
7172
this.connectionManager.on("connection-closed", () => this.emit("disconnect"));
72-
this.connectionManager.on("connection-errored", (error) => this.emit("connection-error", error.errorReason));
73+
this.connectionManager.on("connection-errored", (error) => this.emit("connection-error", error));
7374
}
7475

7576
setMcpClient(mcpClient: Implementation | undefined): void {
@@ -136,13 +137,7 @@ export class Session extends EventEmitter<SessionEvents> {
136137
}
137138

138139
async connectToMongoDB(settings: ConnectionSettings): Promise<void> {
139-
try {
140-
await this.connectionManager.connect({ ...settings });
141-
} catch (error: unknown) {
142-
const message = error instanceof Error ? error.message : (error as string);
143-
this.emit("connection-error", message);
144-
throw error;
145-
}
140+
await this.connectionManager.connect({ ...settings });
146141
}
147142

148143
get isConnectedToMongoDB(): boolean {

src/resources/common/debug.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { ReactiveResource } from "../resource.js";
22
import type { Telemetry } from "../../telemetry/telemetry.js";
33
import type { Session, UserConfig } from "../../lib.js";
4+
import { AtlasClusterConnectionInfo, ConnectionStateErrored } from "../../common/connectionManager.js";
45

56
type ConnectionStateDebuggingInformation = {
67
readonly tag: "connected" | "connecting" | "disconnected" | "errored";
78
readonly connectionStringAuthType?: "scram" | "ldap" | "kerberos" | "oidc-auth-flow" | "oidc-device-flow" | "x.509";
8-
readonly oidcLoginUrl?: string;
9-
readonly oidcUserCode?: string;
109
readonly errorReason?: string;
10+
readonly connectedAtlasCluster?: AtlasClusterConnectionInfo;
1111
};
1212

1313
export class DebugResource extends ReactiveResource<
@@ -35,15 +35,21 @@ export class DebugResource extends ReactiveResource<
3535
}
3636
reduce(
3737
eventName: "connect" | "disconnect" | "close" | "connection-error",
38-
event: string | undefined
38+
event: ConnectionStateErrored | undefined
3939
): ConnectionStateDebuggingInformation {
40-
void event;
41-
4240
switch (eventName) {
4341
case "connect":
4442
return { tag: "connected" };
45-
case "connection-error":
46-
return { tag: "errored", errorReason: event };
43+
case "connection-error": {
44+
return {
45+
tag: "errored",
46+
connectionStringAuthType: event?.connectionStringAuthType,
47+
connectedAtlasCluster: event?.connectedAtlasCluster,
48+
errorReason:
49+
event?.errorReason ??
50+
"Could not find a reason. This might be a bug in the MCP Server. Please open an issue in https://github.com/mongodb-js/mongodb-mcp-server.",
51+
};
52+
}
4753
case "disconnect":
4854
case "close":
4955
return { tag: "disconnected" };
@@ -59,6 +65,11 @@ export class DebugResource extends ReactiveResource<
5965
break;
6066
case "errored":
6167
result += `The user is not connected to a MongoDB cluster because of an error.\n`;
68+
if (this.current.connectedAtlasCluster) {
69+
result += `Attempted connecting to Atlas Cluster "${this.current.connectedAtlasCluster.clusterName}" in project with id "${this.current.connectedAtlasCluster.projectId}".\n`;
70+
}
71+
72+
result += `The inferred authentication mechanism is "${this.current.connectionStringAuthType ?? "could-not-infer"}".\n`;
6273
result += `<error>${this.current.errorReason}</error>`;
6374
break;
6475
case "connecting":

tests/integration/common/connectionManager.test.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,49 @@ describeWithMongoDB("Connection Manager", (integration) => {
119119
});
120120

121121
it("should notify that it failed connecting", () => {
122-
expect(connectionManagerSpies["connection-errored"]).toHaveBeenCalled();
122+
expect(connectionManagerSpies["connection-errored"]).toHaveBeenCalledWith({
123+
tag: "errored",
124+
connectedAtlasCluster: undefined,
125+
connectionStringAuthType: "scram",
126+
errorReason: "Unable to parse localhost:xxxxx with URL",
127+
});
128+
});
129+
130+
it("should be marked explicitly as connected", () => {
131+
expect(connectionManager().currentConnectionState.tag).toEqual("errored");
132+
});
133+
});
134+
135+
describe("when fails to connect to a new atlas cluster", () => {
136+
const atlas = {
137+
username: "",
138+
projectId: "",
139+
clusterName: "My Atlas Cluster",
140+
expiryDate: new Date(),
141+
};
142+
143+
beforeEach(async () => {
144+
try {
145+
await connectionManager().connect({
146+
connectionString: "mongodb://localhost:xxxxx",
147+
atlas,
148+
});
149+
} catch (_error: unknown) {
150+
void _error;
151+
}
152+
});
153+
154+
it("should notify that it was disconnected before connecting", () => {
155+
expect(connectionManagerSpies["connection-closed"]).toHaveBeenCalled();
156+
});
157+
158+
it("should notify that it failed connecting", () => {
159+
expect(connectionManagerSpies["connection-errored"]).toHaveBeenCalledWith({
160+
tag: "errored",
161+
connectedAtlasCluster: atlas,
162+
connectionStringAuthType: "scram",
163+
errorReason: "Unable to parse localhost:xxxxx with URL",
164+
});
123165
});
124166

125167
it("should be marked explicitly as connected", () => {

tests/unit/resources/common/debug.test.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,51 @@ describe("debug resource", () => {
4747
});
4848

4949
it("should be disconnected and contain an error when an error event occurred", () => {
50-
debugResource.reduceApply("connection-error", "Error message from the server");
50+
debugResource.reduceApply("connection-error", {
51+
tag: "errored",
52+
errorReason: "Error message from the server",
53+
});
54+
55+
const output = debugResource.toOutput();
56+
57+
expect(output).toContain(`The user is not connected to a MongoDB cluster because of an error.`);
58+
expect(output).toContain(`<error>Error message from the server</error>`);
59+
});
60+
61+
it("should show the inferred authentication type", () => {
62+
debugResource.reduceApply("connection-error", {
63+
tag: "errored",
64+
connectionStringAuthType: "scram",
65+
errorReason: "Error message from the server",
66+
});
67+
68+
const output = debugResource.toOutput();
69+
70+
expect(output).toContain(`The user is not connected to a MongoDB cluster because of an error.`);
71+
expect(output).toContain(`The inferred authentication mechanism is "scram".`);
72+
expect(output).toContain(`<error>Error message from the server</error>`);
73+
});
74+
75+
it("should show the atlas cluster information when provided", () => {
76+
debugResource.reduceApply("connection-error", {
77+
tag: "errored",
78+
connectionStringAuthType: "scram",
79+
errorReason: "Error message from the server",
80+
connectedAtlasCluster: {
81+
clusterName: "My Test Cluster",
82+
projectId: "COFFEEFABADA",
83+
username: "",
84+
expiryDate: new Date(),
85+
},
86+
});
87+
5188
const output = debugResource.toOutput();
5289

5390
expect(output).toContain(`The user is not connected to a MongoDB cluster because of an error.`);
91+
expect(output).toContain(
92+
`Attempted connecting to Atlas Cluster "My Test Cluster" in project with id "COFFEEFABADA".`
93+
);
94+
expect(output).toContain(`The inferred authentication mechanism is "scram".`);
5495
expect(output).toContain(`<error>Error message from the server</error>`);
5596
});
5697
});

0 commit comments

Comments
 (0)