Skip to content

Commit 57e5265

Browse files
blvaCopilot
andauthored
chore: enable noUncheckedIndexedAccess (#285)
Co-authored-by: Copilot <[email protected]>
1 parent fcd62b6 commit 57e5265

30 files changed

+124
-87
lines changed

scripts/apply.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ async function main() {
5858
const httpCode = parseInt(code, 10);
5959
if (httpCode >= 200 && httpCode < 300) {
6060
const response = operation.responses[code];
61-
const responseObject = findObjectFromRef(response, openapi);
62-
if (responseObject.content) {
61+
const responseObject = findObjectFromRef(response, openapi) as OpenAPIV3_1.ResponseObject;
62+
if (responseObject && responseObject.content) {
6363
for (const contentType in responseObject.content) {
6464
const content = responseObject.content[contentType];
65-
hasResponseBody = !!content.schema;
65+
hasResponseBody = !!content?.schema;
6666
}
6767
}
6868
}
@@ -84,7 +84,7 @@ async function main() {
8484
operationId: operation.operationId || "",
8585
requiredParams,
8686
hasResponseBody,
87-
tag: operation.tags[0],
87+
tag: operation.tags?.[0] ?? "",
8888
});
8989
}
9090
}

src/common/atlas/cluster.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ export function formatCluster(cluster: ClusterDescription20240805): Cluster {
5050
};
5151
});
5252

53-
const instanceSize = (regionConfigs.length <= 0 ? undefined : regionConfigs[0].instanceSize) || "UNKNOWN";
54-
53+
const instanceSize = regionConfigs[0]?.instanceSize ?? "UNKNOWN";
5554
const clusterInstanceType = instanceSize == "M0" ? "FREE" : "DEDICATED";
5655

5756
return {

src/logger.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ export const LogId = {
3232

3333
mongodbConnectFailure: mongoLogId(1_004_001),
3434
mongodbDisconnectFailure: mongoLogId(1_004_002),
35+
36+
toolUpdateFailure: mongoLogId(1_005_001),
3537
} as const;
3638

3739
abstract class LoggerBase {

src/tools/atlas/create/createProject.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ export class CreateProjectTool extends AtlasToolBase {
2828
"No organizations were found in your MongoDB Atlas account. Please create an organization first."
2929
);
3030
}
31-
organizationId = organizations.results[0].id;
31+
const firstOrg = organizations.results[0];
32+
if (!firstOrg?.id) {
33+
throw new Error(
34+
"The first organization found does not have an ID. Please check your Atlas account."
35+
);
36+
}
37+
organizationId = firstOrg.id;
3238
assumedOrg = true;
3339
} catch {
3440
throw new Error(

src/tools/atlas/read/listProjects.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export class ListProjectsTool extends AtlasToolBase {
2121

2222
const orgs: Record<string, string> = orgData.results
2323
.map((org) => [org.id || "", org.name])
24-
.reduce((acc, [id, name]) => ({ ...acc, [id]: name }), {});
24+
.filter(([id]) => id)
25+
.reduce((acc, [id, name]) => ({ ...acc, [id as string]: name }), {});
2526

2627
const data = orgId
2728
? await this.session.apiClient.listOrganizationProjects({
@@ -41,7 +42,8 @@ export class ListProjectsTool extends AtlasToolBase {
4142
const rows = data.results
4243
.map((project) => {
4344
const createdAt = project.created ? new Date(project.created).toLocaleString() : "N/A";
44-
return `${project.name} | ${project.id} | ${orgs[project.orgId]} | ${project.orgId} | ${createdAt}`;
45+
const orgName = orgs[project.orgId] ?? "N/A";
46+
return `${project.name} | ${project.id} | ${orgName} | ${project.orgId} | ${createdAt}`;
4547
})
4648
.join("\n");
4749
const formattedProjects = `Project Name | Project ID | Organization Name | Organization ID | Created At

src/tools/tool.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ export abstract class ToolBase {
9393
this.update = (updates: { name?: string; description?: string; inputSchema?: AnyZodObject }) => {
9494
const tools = server["_registeredTools"] as { [toolName: string]: RegisteredTool };
9595
const existingTool = tools[this.name];
96+
97+
if (!existingTool) {
98+
logger.warning(LogId.toolUpdateFailure, "tool", `Tool ${this.name} not found in update`);
99+
return;
100+
}
101+
96102
existingTool.annotations = this.annotations;
97103

98104
if (updates.name && updates.name !== this.name) {

tests/integration/tools/atlas/accessLists.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ describeWithAtlas("ip access lists", (integration) => {
6767
})) as CallToolResult;
6868
expect(response.content).toBeArray();
6969
expect(response.content).toHaveLength(1);
70-
expect(response.content[0].text).toContain("IP/CIDR ranges added to access list");
70+
expect(response.content[0]?.text).toContain("IP/CIDR ranges added to access list");
7171
});
7272
});
7373

@@ -90,7 +90,7 @@ describeWithAtlas("ip access lists", (integration) => {
9090
expect(response.content).toBeArray();
9191
expect(response.content).toHaveLength(1);
9292
for (const value of values) {
93-
expect(response.content[0].text).toContain(value);
93+
expect(response.content[0]?.text).toContain(value);
9494
}
9595
});
9696
});

tests/integration/tools/atlas/alerts.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describeWithAtlas("alerts", (integration) => {
2323
expect(response.content).toBeArray();
2424
expect(response.content).toHaveLength(1);
2525

26-
const data = parseTable(response.content[0].text as string);
26+
const data = parseTable(response.content[0]?.text as string);
2727
expect(data).toBeArray();
2828

2929
// Since we can't guarantee alerts will exist, we just verify the table structure

tests/integration/tools/atlas/atlasHelpers.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ export function parseTable(text: string): Record<string, string>[] {
7575
.map((cells) => {
7676
const row: Record<string, string> = {};
7777
cells.forEach((cell, index) => {
78-
row[headers[index]] = cell;
78+
if (headers) {
79+
row[headers[index] ?? ""] = cell;
80+
}
7981
});
8082
return row;
8183
});
@@ -87,14 +89,14 @@ async function createProject(apiClient: ApiClient): Promise<Group> {
8789
const projectName: string = `testProj-` + randomId;
8890

8991
const orgs = await apiClient.listOrganizations();
90-
if (!orgs?.results?.length || !orgs.results[0].id) {
92+
if (!orgs?.results?.length || !orgs.results[0]?.id) {
9193
throw new Error("No orgs found");
9294
}
9395

9496
const group = await apiClient.createProject({
9597
body: {
9698
name: projectName,
97-
orgId: orgs.results[0].id,
99+
orgId: orgs.results[0]?.id ?? "",
98100
} as Group,
99101
});
100102

tests/integration/tools/atlas/clusters.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ describeWithAtlas("clusters", (integration) => {
8888
})) as CallToolResult;
8989
expect(response.content).toBeArray();
9090
expect(response.content).toHaveLength(2);
91-
expect(response.content[0].text).toContain("has been created");
91+
expect(response.content[0]?.text).toContain("has been created");
9292
});
9393
});
9494

@@ -113,7 +113,7 @@ describeWithAtlas("clusters", (integration) => {
113113
})) as CallToolResult;
114114
expect(response.content).toBeArray();
115115
expect(response.content).toHaveLength(1);
116-
expect(response.content[0].text).toContain(`${clusterName} | `);
116+
expect(response.content[0]?.text).toContain(`${clusterName} | `);
117117
});
118118
});
119119

@@ -135,7 +135,7 @@ describeWithAtlas("clusters", (integration) => {
135135
.callTool({ name: "atlas-list-clusters", arguments: { projectId } })) as CallToolResult;
136136
expect(response.content).toBeArray();
137137
expect(response.content).toHaveLength(2);
138-
expect(response.content[1].text).toContain(`${clusterName} | `);
138+
expect(response.content[1]?.text).toContain(`${clusterName} | `);
139139
});
140140
});
141141

@@ -178,7 +178,7 @@ describeWithAtlas("clusters", (integration) => {
178178
})) as CallToolResult;
179179
expect(response.content).toBeArray();
180180
expect(response.content).toHaveLength(1);
181-
expect(response.content[0].text).toContain(`Connected to cluster "${clusterName}"`);
181+
expect(response.content[0]?.text).toContain(`Connected to cluster "${clusterName}"`);
182182
});
183183
});
184184
});

tests/integration/tools/atlas/dbUsers.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,18 @@ describeWithAtlas("db users", (integration) => {
6565

6666
const elements = getResponseElements(response);
6767
expect(elements).toHaveLength(1);
68-
expect(elements[0].text).toContain("created successfully");
69-
expect(elements[0].text).toContain(userName);
70-
expect(elements[0].text).not.toContain("testpassword");
68+
expect(elements[0]?.text).toContain("created successfully");
69+
expect(elements[0]?.text).toContain(userName);
70+
expect(elements[0]?.text).not.toContain("testpassword");
7171
});
7272

7373
it("should create a database user with generated password", async () => {
7474
const response = await createUserWithMCP();
7575
const elements = getResponseElements(response);
7676
expect(elements).toHaveLength(1);
77-
expect(elements[0].text).toContain("created successfully");
78-
expect(elements[0].text).toContain(userName);
79-
expect(elements[0].text).toContain("with password: `");
77+
expect(elements[0]?.text).toContain("created successfully");
78+
expect(elements[0]?.text).toContain(userName);
79+
expect(elements[0]?.text).toContain("with password: `");
8080
});
8181
});
8282
describe("atlas-list-db-users", () => {
@@ -98,7 +98,7 @@ describeWithAtlas("db users", (integration) => {
9898
.callTool({ name: "atlas-list-db-users", arguments: { projectId } })) as CallToolResult;
9999
expect(response.content).toBeArray();
100100
expect(response.content).toHaveLength(1);
101-
expect(response.content[0].text).toContain(userName);
101+
expect(response.content[0]?.text).toContain(userName);
102102
});
103103
});
104104
});

tests/integration/tools/atlas/orgs.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ describeWithAtlas("orgs", (integration) => {
1616
.callTool({ name: "atlas-list-orgs", arguments: {} })) as CallToolResult;
1717
expect(response.content).toBeArray();
1818
expect(response.content).toHaveLength(1);
19-
const data = parseTable(response.content[0].text as string);
19+
const data = parseTable(response.content[0]?.text as string);
2020
expect(data).toHaveLength(1);
21-
expect(data[0]["Organization Name"]).toEqual("MongoDB MCP Test");
21+
expect(data[0]?.["Organization Name"]).toEqual("MongoDB MCP Test");
2222
});
2323
});
2424
});

tests/integration/tools/atlas/projects.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describeWithAtlas("projects", (integration) => {
4343
})) as CallToolResult;
4444
expect(response.content).toBeArray();
4545
expect(response.content).toHaveLength(1);
46-
expect(response.content[0].text).toContain(projName);
46+
expect(response.content[0]?.text).toContain(projName);
4747
});
4848
});
4949
describe("atlas-list-projects", () => {
@@ -62,8 +62,8 @@ describeWithAtlas("projects", (integration) => {
6262
.callTool({ name: "atlas-list-projects", arguments: {} })) as CallToolResult;
6363
expect(response.content).toBeArray();
6464
expect(response.content).toHaveLength(1);
65-
expect(response.content[0].text).toContain(projName);
66-
const data = parseTable(response.content[0].text as string);
65+
expect(response.content[0]?.text).toContain(projName);
66+
const data = parseTable(response.content[0]?.text as string);
6767
expect(data).toBeArray();
6868
expect(data.length).toBeGreaterThan(0);
6969
let found = false;

tests/integration/tools/mongodb/create/createCollection.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describeWithMongoDB("createCollection tool", (integration) => {
3434

3535
collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
3636
expect(collections).toHaveLength(1);
37-
expect(collections[0].name).toEqual("bar");
37+
expect(collections[0]?.name).toEqual("bar");
3838
});
3939
});
4040

@@ -78,7 +78,7 @@ describeWithMongoDB("createCollection tool", (integration) => {
7878
expect(content).toEqual(`Collection "collection1" created in database "${integration.randomDbName()}".`);
7979
collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
8080
expect(collections).toHaveLength(1);
81-
expect(collections[0].name).toEqual("collection1");
81+
expect(collections[0]?.name).toEqual("collection1");
8282

8383
// Make sure we didn't drop the existing collection
8484
documents = await mongoClient.db(integration.randomDbName()).collection("collection1").find({}).toArray();

tests/integration/tools/mongodb/create/createIndex.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ describeWithMongoDB("createIndex tool", (integration) => {
3838
const mongoClient = integration.mongoClient();
3939
const collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
4040
expect(collections).toHaveLength(1);
41-
expect(collections[0].name).toEqual("coll1");
41+
expect(collections[0]?.name).toEqual("coll1");
4242
const indexes = await mongoClient.db(integration.randomDbName()).collection(collection).indexes();
4343
expect(indexes).toHaveLength(expected.length + 1);
44-
expect(indexes[0].name).toEqual("_id_");
44+
expect(indexes[0]?.name).toEqual("_id_");
4545
for (const index of expected) {
4646
const foundIndex = indexes.find((i) => i.name === index.name);
4747
expectDefined(foundIndex);

tests/integration/tools/mongodb/create/insertMany.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ describeWithMongoDB("insertMany tool", (integration) => {
8282
const content = getResponseContent(response.content);
8383
expect(content).toContain("Error running insert-many");
8484
expect(content).toContain("duplicate key error");
85-
expect(content).toContain(insertedIds[0].toString());
85+
expect(content).toContain(insertedIds[0]?.toString());
8686
});
8787

8888
validateAutoConnectBehavior(integration, "insert-many", () => {

tests/integration/tools/mongodb/delete/dropCollection.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describeWithMongoDB("dropCollection tool", (integration) => {
5454
);
5555
const collections = await integration.mongoClient().db(integration.randomDbName()).listCollections().toArray();
5656
expect(collections).toHaveLength(1);
57-
expect(collections[0].name).toBe("coll2");
57+
expect(collections[0]?.name).toBe("coll2");
5858
});
5959

6060
validateAutoConnectBehavior(integration, "drop-collection", () => {

tests/integration/tools/mongodb/metadata/collectionSchema.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,11 @@ describeWithMongoDB("collectionSchema tool", (integration) => {
132132
expect(items).toHaveLength(2);
133133

134134
// Expect to find _id, name, age
135-
expect(items[0].text).toEqual(
135+
expect(items[0]?.text).toEqual(
136136
`Found ${Object.entries(testCase.expectedSchema).length} fields in the schema for "${integration.randomDbName()}.foo"`
137137
);
138138

139-
const schema = JSON.parse(items[1].text) as SimplifiedSchema;
139+
const schema = JSON.parse(items[1]?.text ?? "{}") as SimplifiedSchema;
140140
expect(schema).toEqual(testCase.expectedSchema);
141141
});
142142
}

tests/integration/tools/mongodb/metadata/dbStats.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ describeWithMongoDB("dbStats tool", (integration) => {
2828
});
2929
const elements = getResponseElements(response.content);
3030
expect(elements).toHaveLength(2);
31-
expect(elements[0].text).toBe(`Statistics for database ${integration.randomDbName()}`);
31+
expect(elements[0]?.text).toBe(`Statistics for database ${integration.randomDbName()}`);
3232

33-
const stats = JSON.parse(elements[1].text) as {
33+
const stats = JSON.parse(elements[1]?.text ?? "{}") as {
3434
db: string;
3535
collections: number;
3636
storageSize: number;
@@ -75,9 +75,9 @@ describeWithMongoDB("dbStats tool", (integration) => {
7575
});
7676
const elements = getResponseElements(response.content);
7777
expect(elements).toHaveLength(2);
78-
expect(elements[0].text).toBe(`Statistics for database ${integration.randomDbName()}`);
78+
expect(elements[0]?.text).toBe(`Statistics for database ${integration.randomDbName()}`);
7979

80-
const stats = JSON.parse(elements[1].text) as {
80+
const stats = JSON.parse(elements[1]?.text ?? "{}") as {
8181
db: string;
8282
collections: unknown;
8383
storageSize: unknown;

tests/integration/tools/mongodb/metadata/explain.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,12 @@ describeWithMongoDB("explain tool", (integration) => {
8989

9090
const content = getResponseElements(response.content);
9191
expect(content).toHaveLength(2);
92-
expect(content[0].text).toEqual(
92+
expect(content[0]?.text).toEqual(
9393
`Here is some information about the winning plan chosen by the query optimizer for running the given \`${testCase.method}\` operation in "${integration.randomDbName()}.coll1". This information can be used to understand how the query was executed and to optimize the query performance.`
9494
);
9595

96-
expect(content[1].text).toContain("queryPlanner");
97-
expect(content[1].text).toContain("winningPlan");
96+
expect(content[1]?.text).toContain("queryPlanner");
97+
expect(content[1]?.text).toContain("winningPlan");
9898
});
9999
}
100100
});
@@ -139,22 +139,22 @@ describeWithMongoDB("explain tool", (integration) => {
139139

140140
const content = getResponseElements(response.content);
141141
expect(content).toHaveLength(2);
142-
expect(content[0].text).toEqual(
142+
expect(content[0]?.text).toEqual(
143143
`Here is some information about the winning plan chosen by the query optimizer for running the given \`${testCase.method}\` operation in "${integration.randomDbName()}.people". This information can be used to understand how the query was executed and to optimize the query performance.`
144144
);
145145

146-
expect(content[1].text).toContain("queryPlanner");
147-
expect(content[1].text).toContain("winningPlan");
146+
expect(content[1]?.text).toContain("queryPlanner");
147+
expect(content[1]?.text).toContain("winningPlan");
148148

149149
if (indexed) {
150150
if (testCase.method === "count") {
151-
expect(content[1].text).toContain("COUNT_SCAN");
151+
expect(content[1]?.text).toContain("COUNT_SCAN");
152152
} else {
153-
expect(content[1].text).toContain("IXSCAN");
153+
expect(content[1]?.text).toContain("IXSCAN");
154154
}
155-
expect(content[1].text).toContain("name_1");
155+
expect(content[1]?.text).toContain("name_1");
156156
} else {
157-
expect(content[1].text).toContain("COLLSCAN");
157+
expect(content[1]?.text).toContain("COLLSCAN");
158158
}
159159
});
160160
}

tests/integration/tools/mongodb/metadata/listCollections.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describeWithMongoDB("listCollections tool", (integration) => {
4545
});
4646
const items = getResponseElements(response.content);
4747
expect(items).toHaveLength(1);
48-
expect(items[0].text).toContain('Name: "collection-1"');
48+
expect(items[0]?.text).toContain('Name: "collection-1"');
4949

5050
await mongoClient.db(integration.randomDbName()).createCollection("collection-2");
5151

0 commit comments

Comments
 (0)