Skip to content

chore: enable noUncheckedIndexedAccess #285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 5, 2025
Merged
Show file tree
Hide file tree
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
8 changes: 4 additions & 4 deletions scripts/apply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ async function main() {
const httpCode = parseInt(code, 10);
if (httpCode >= 200 && httpCode < 300) {
const response = operation.responses[code];
const responseObject = findObjectFromRef(response, openapi);
if (responseObject.content) {
const responseObject = findObjectFromRef(response, openapi) as OpenAPIV3_1.ResponseObject;
if (responseObject && responseObject.content) {
for (const contentType in responseObject.content) {
const content = responseObject.content[contentType];
hasResponseBody = !!content.schema;
hasResponseBody = !!content?.schema;
}
}
}
Expand All @@ -84,7 +84,7 @@ async function main() {
operationId: operation.operationId || "",
requiredParams,
hasResponseBody,
tag: operation.tags[0],
tag: operation.tags?.[0] ?? "",
});
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/common/atlas/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ export function formatCluster(cluster: ClusterDescription20240805): Cluster {
};
});

const instanceSize = (regionConfigs.length <= 0 ? undefined : regionConfigs[0].instanceSize) || "UNKNOWN";

const instanceSize = regionConfigs[0]?.instanceSize ?? "UNKNOWN";
const clusterInstanceType = instanceSize == "M0" ? "FREE" : "DEDICATED";

return {
Expand Down
2 changes: 2 additions & 0 deletions src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export const LogId = {

mongodbConnectFailure: mongoLogId(1_004_001),
mongodbDisconnectFailure: mongoLogId(1_004_002),

toolUpdateFailure: mongoLogId(1_005_001),
} as const;

abstract class LoggerBase {
Expand Down
8 changes: 7 additions & 1 deletion src/tools/atlas/create/createProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ export class CreateProjectTool extends AtlasToolBase {
"No organizations were found in your MongoDB Atlas account. Please create an organization first."
);
}
organizationId = organizations.results[0].id;
const firstOrg = organizations.results[0];
if (!firstOrg?.id) {
throw new Error(
"The first organization found does not have an ID. Please check your Atlas account."
);
}
organizationId = firstOrg.id;
assumedOrg = true;
} catch {
throw new Error(
Expand Down
6 changes: 4 additions & 2 deletions src/tools/atlas/read/listProjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export class ListProjectsTool extends AtlasToolBase {

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

const data = orgId
? await this.session.apiClient.listOrganizationProjects({
Expand All @@ -41,7 +42,8 @@ export class ListProjectsTool extends AtlasToolBase {
const rows = data.results
.map((project) => {
const createdAt = project.created ? new Date(project.created).toLocaleString() : "N/A";
return `${project.name} | ${project.id} | ${orgs[project.orgId]} | ${project.orgId} | ${createdAt}`;
const orgName = orgs[project.orgId] ?? "N/A";
return `${project.name} | ${project.id} | ${orgName} | ${project.orgId} | ${createdAt}`;
})
.join("\n");
const formattedProjects = `Project Name | Project ID | Organization Name | Organization ID | Created At
Expand Down
6 changes: 6 additions & 0 deletions src/tools/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ export abstract class ToolBase {
this.update = (updates: { name?: string; description?: string; inputSchema?: AnyZodObject }) => {
const tools = server["_registeredTools"] as { [toolName: string]: RegisteredTool };
const existingTool = tools[this.name];

if (!existingTool) {
logger.warning(LogId.toolUpdateFailure, "tool", `Tool ${this.name} not found in update`);
return;
}

existingTool.annotations = this.annotations;

if (updates.name && updates.name !== this.name) {
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/tools/atlas/accessLists.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describeWithAtlas("ip access lists", (integration) => {
})) as CallToolResult;
expect(response.content).toBeArray();
expect(response.content).toHaveLength(1);
expect(response.content[0].text).toContain("IP/CIDR ranges added to access list");
expect(response.content[0]?.text).toContain("IP/CIDR ranges added to access list");
});
});

Expand All @@ -90,7 +90,7 @@ describeWithAtlas("ip access lists", (integration) => {
expect(response.content).toBeArray();
expect(response.content).toHaveLength(1);
for (const value of values) {
expect(response.content[0].text).toContain(value);
expect(response.content[0]?.text).toContain(value);
}
});
});
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/tools/atlas/alerts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describeWithAtlas("alerts", (integration) => {
expect(response.content).toBeArray();
expect(response.content).toHaveLength(1);

const data = parseTable(response.content[0].text as string);
const data = parseTable(response.content[0]?.text as string);
expect(data).toBeArray();

// Since we can't guarantee alerts will exist, we just verify the table structure
Expand Down
8 changes: 5 additions & 3 deletions tests/integration/tools/atlas/atlasHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ export function parseTable(text: string): Record<string, string>[] {
.map((cells) => {
const row: Record<string, string> = {};
cells.forEach((cell, index) => {
row[headers[index]] = cell;
if (headers) {
row[headers[index] ?? ""] = cell;
}
Comment on lines +78 to +80
Copy link
Preview

Copilot AI Jun 4, 2025

Choose a reason for hiding this comment

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

The headers array is always defined here; you can remove the runtime if check and use row[headers[index]!] = cell; to simplify the code.

Suggested change
if (headers) {
row[headers[index] ?? ""] = cell;
}
row[headers[index]!] = cell;

Copilot uses AI. Check for mistakes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

'headers' is possibly 'undefined'.ts(18048)

});
return row;
});
Expand All @@ -87,14 +89,14 @@ async function createProject(apiClient: ApiClient): Promise<Group> {
const projectName: string = `testProj-` + randomId;

const orgs = await apiClient.listOrganizations();
if (!orgs?.results?.length || !orgs.results[0].id) {
if (!orgs?.results?.length || !orgs.results[0]?.id) {
throw new Error("No orgs found");
}

const group = await apiClient.createProject({
body: {
name: projectName,
orgId: orgs.results[0].id,
orgId: orgs.results[0]?.id ?? "",
} as Group,
});

Expand Down
8 changes: 4 additions & 4 deletions tests/integration/tools/atlas/clusters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ describeWithAtlas("clusters", (integration) => {
})) as CallToolResult;
expect(response.content).toBeArray();
expect(response.content).toHaveLength(2);
expect(response.content[0].text).toContain("has been created");
expect(response.content[0]?.text).toContain("has been created");
});
});

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

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

Expand Down Expand Up @@ -178,7 +178,7 @@ describeWithAtlas("clusters", (integration) => {
})) as CallToolResult;
expect(response.content).toBeArray();
expect(response.content).toHaveLength(1);
expect(response.content[0].text).toContain(`Connected to cluster "${clusterName}"`);
expect(response.content[0]?.text).toContain(`Connected to cluster "${clusterName}"`);
});
});
});
Expand Down
14 changes: 7 additions & 7 deletions tests/integration/tools/atlas/dbUsers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,18 @@ describeWithAtlas("db users", (integration) => {

const elements = getResponseElements(response);
expect(elements).toHaveLength(1);
expect(elements[0].text).toContain("created successfully");
expect(elements[0].text).toContain(userName);
expect(elements[0].text).not.toContain("testpassword");
expect(elements[0]?.text).toContain("created successfully");
expect(elements[0]?.text).toContain(userName);
expect(elements[0]?.text).not.toContain("testpassword");
});

it("should create a database user with generated password", async () => {
const response = await createUserWithMCP();
const elements = getResponseElements(response);
expect(elements).toHaveLength(1);
expect(elements[0].text).toContain("created successfully");
expect(elements[0].text).toContain(userName);
expect(elements[0].text).toContain("with password: `");
expect(elements[0]?.text).toContain("created successfully");
expect(elements[0]?.text).toContain(userName);
expect(elements[0]?.text).toContain("with password: `");
});
});
describe("atlas-list-db-users", () => {
Expand All @@ -98,7 +98,7 @@ describeWithAtlas("db users", (integration) => {
.callTool({ name: "atlas-list-db-users", arguments: { projectId } })) as CallToolResult;
expect(response.content).toBeArray();
expect(response.content).toHaveLength(1);
expect(response.content[0].text).toContain(userName);
expect(response.content[0]?.text).toContain(userName);
});
});
});
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/tools/atlas/orgs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ describeWithAtlas("orgs", (integration) => {
.callTool({ name: "atlas-list-orgs", arguments: {} })) as CallToolResult;
expect(response.content).toBeArray();
expect(response.content).toHaveLength(1);
const data = parseTable(response.content[0].text as string);
const data = parseTable(response.content[0]?.text as string);
expect(data).toHaveLength(1);
expect(data[0]["Organization Name"]).toEqual("MongoDB MCP Test");
expect(data[0]?.["Organization Name"]).toEqual("MongoDB MCP Test");
});
});
});
6 changes: 3 additions & 3 deletions tests/integration/tools/atlas/projects.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describeWithAtlas("projects", (integration) => {
})) as CallToolResult;
expect(response.content).toBeArray();
expect(response.content).toHaveLength(1);
expect(response.content[0].text).toContain(projName);
expect(response.content[0]?.text).toContain(projName);
});
});
describe("atlas-list-projects", () => {
Expand All @@ -62,8 +62,8 @@ describeWithAtlas("projects", (integration) => {
.callTool({ name: "atlas-list-projects", arguments: {} })) as CallToolResult;
expect(response.content).toBeArray();
expect(response.content).toHaveLength(1);
expect(response.content[0].text).toContain(projName);
const data = parseTable(response.content[0].text as string);
expect(response.content[0]?.text).toContain(projName);
const data = parseTable(response.content[0]?.text as string);
expect(data).toBeArray();
expect(data.length).toBeGreaterThan(0);
let found = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describeWithMongoDB("createCollection tool", (integration) => {

collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
expect(collections).toHaveLength(1);
expect(collections[0].name).toEqual("bar");
expect(collections[0]?.name).toEqual("bar");
});
});

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

// Make sure we didn't drop the existing collection
documents = await mongoClient.db(integration.randomDbName()).collection("collection1").find({}).toArray();
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/tools/mongodb/create/createIndex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ describeWithMongoDB("createIndex tool", (integration) => {
const mongoClient = integration.mongoClient();
const collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
expect(collections).toHaveLength(1);
expect(collections[0].name).toEqual("coll1");
expect(collections[0]?.name).toEqual("coll1");
const indexes = await mongoClient.db(integration.randomDbName()).collection(collection).indexes();
expect(indexes).toHaveLength(expected.length + 1);
expect(indexes[0].name).toEqual("_id_");
expect(indexes[0]?.name).toEqual("_id_");
for (const index of expected) {
const foundIndex = indexes.find((i) => i.name === index.name);
expectDefined(foundIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ describeWithMongoDB("insertMany tool", (integration) => {
const content = getResponseContent(response.content);
expect(content).toContain("Error running insert-many");
expect(content).toContain("duplicate key error");
expect(content).toContain(insertedIds[0].toString());
expect(content).toContain(insertedIds[0]?.toString());
});

validateAutoConnectBehavior(integration, "insert-many", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describeWithMongoDB("dropCollection tool", (integration) => {
);
const collections = await integration.mongoClient().db(integration.randomDbName()).listCollections().toArray();
expect(collections).toHaveLength(1);
expect(collections[0].name).toBe("coll2");
expect(collections[0]?.name).toBe("coll2");
});

validateAutoConnectBehavior(integration, "drop-collection", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,11 @@ describeWithMongoDB("collectionSchema tool", (integration) => {
expect(items).toHaveLength(2);

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

const schema = JSON.parse(items[1].text) as SimplifiedSchema;
const schema = JSON.parse(items[1]?.text ?? "{}") as SimplifiedSchema;
expect(schema).toEqual(testCase.expectedSchema);
});
}
Expand Down
8 changes: 4 additions & 4 deletions tests/integration/tools/mongodb/metadata/dbStats.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ describeWithMongoDB("dbStats tool", (integration) => {
});
const elements = getResponseElements(response.content);
expect(elements).toHaveLength(2);
expect(elements[0].text).toBe(`Statistics for database ${integration.randomDbName()}`);
expect(elements[0]?.text).toBe(`Statistics for database ${integration.randomDbName()}`);

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

const stats = JSON.parse(elements[1].text) as {
const stats = JSON.parse(elements[1]?.text ?? "{}") as {
db: string;
collections: unknown;
storageSize: unknown;
Expand Down
20 changes: 10 additions & 10 deletions tests/integration/tools/mongodb/metadata/explain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ describeWithMongoDB("explain tool", (integration) => {

const content = getResponseElements(response.content);
expect(content).toHaveLength(2);
expect(content[0].text).toEqual(
expect(content[0]?.text).toEqual(
`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.`
);

expect(content[1].text).toContain("queryPlanner");
expect(content[1].text).toContain("winningPlan");
expect(content[1]?.text).toContain("queryPlanner");
expect(content[1]?.text).toContain("winningPlan");
});
}
});
Expand Down Expand Up @@ -139,22 +139,22 @@ describeWithMongoDB("explain tool", (integration) => {

const content = getResponseElements(response.content);
expect(content).toHaveLength(2);
expect(content[0].text).toEqual(
expect(content[0]?.text).toEqual(
`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.`
);

expect(content[1].text).toContain("queryPlanner");
expect(content[1].text).toContain("winningPlan");
expect(content[1]?.text).toContain("queryPlanner");
expect(content[1]?.text).toContain("winningPlan");

if (indexed) {
if (testCase.method === "count") {
expect(content[1].text).toContain("COUNT_SCAN");
expect(content[1]?.text).toContain("COUNT_SCAN");
} else {
expect(content[1].text).toContain("IXSCAN");
expect(content[1]?.text).toContain("IXSCAN");
}
expect(content[1].text).toContain("name_1");
expect(content[1]?.text).toContain("name_1");
} else {
expect(content[1].text).toContain("COLLSCAN");
expect(content[1]?.text).toContain("COLLSCAN");
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describeWithMongoDB("listCollections tool", (integration) => {
});
const items = getResponseElements(response.content);
expect(items).toHaveLength(1);
expect(items[0].text).toContain('Name: "collection-1"');
expect(items[0]?.text).toContain('Name: "collection-1"');

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

Expand Down
Loading
Loading