-
Notifications
You must be signed in to change notification settings - Fork 138
feat: adds support for dropping vector search indexes in drop-index tool MCP-239 #642
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
base: main
Are you sure you want to change the base?
Changes from all commits
8184f73
1065ee8
f26ae1a
8960ffa
eed4479
52dc7c5
746643b
00f2212
716d9cb
7d795ca
7db2402
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; | ||
import { type DbOperationArgs, MongoDBToolBase } from "../tools/mongodb/mongodbTool.js"; | ||
import type { ToolArgs } from "../tools/tool.js"; | ||
|
||
export abstract class MongoDBToolWithSearchErrorHandler extends MongoDBToolBase { | ||
protected handleError( | ||
error: unknown, | ||
args: ToolArgs<typeof DbOperationArgs> | ||
): Promise<CallToolResult> | CallToolResult { | ||
const CTA = this.server?.areLocalAtlasToolsAvailable() ? "`atlas-local` tools" : "Atlas CLI"; | ||
if (error instanceof Error && "codeName" in error && error.codeName === "SearchNotEnabled") { | ||
return { | ||
content: [ | ||
{ | ||
text: `The connected MongoDB deployment does not support vector search indexes. Either connect to a MongoDB Atlas cluster or use the ${CTA} to create and manage a local Atlas deployment.`, | ||
type: "text", | ||
}, | ||
], | ||
isError: true, | ||
}; | ||
} | ||
return super.handleError(error, args); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import type { Collection } from "mongodb"; | ||
import { CompositeLogger } from "../../src/common/logger.js"; | ||
import { ExportsManager } from "../../src/common/exportsManager.js"; | ||
import { Session } from "../../src/common/session.js"; | ||
|
@@ -22,6 +23,9 @@ import { Keychain } from "../../src/common/keychain.js"; | |
import { Elicitation } from "../../src/elicitation.js"; | ||
import type { MockClientCapabilities, createMockElicitInput } from "../utils/elicitationMocks.js"; | ||
|
||
export const DEFAULT_WAIT_TIMEOUT = 1000; | ||
export const DEFAULT_RETRY_INTERVAL = 100; | ||
|
||
export const driverOptions = setupDriverConfig({ | ||
config, | ||
defaults: defaultDriverOptionsFromConfig, | ||
|
@@ -417,3 +421,60 @@ export function getDataFromUntrustedContent(content: string): string { | |
export function sleep(ms: number): Promise<void> { | ||
return new Promise((resolve) => setTimeout(resolve, ms)); | ||
} | ||
|
||
export async function waitUntilSearchManagementServiceIsReady( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why aren't we using waitUntilSearchIsReady There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably you should take a look at mongodbHelpers.ts and see what can be reused and refactor. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are the same methods. I just renamed / refactored them to re-use parts of the logic. |
||
collection: Collection, | ||
timeout: number = DEFAULT_WAIT_TIMEOUT, | ||
interval: number = DEFAULT_RETRY_INTERVAL | ||
): Promise<void> { | ||
await vi.waitFor(async () => await collection.listSearchIndexes({}).toArray(), { timeout, interval }); | ||
} | ||
|
||
async function waitUntilSearchIndexIs( | ||
collection: Collection, | ||
searchIndex: string, | ||
indexValidator: (index: { name: string; queryable: boolean }) => boolean, | ||
timeout: number, | ||
interval: number | ||
): Promise<void> { | ||
await vi.waitFor( | ||
async () => { | ||
const searchIndexes = (await collection.listSearchIndexes(searchIndex).toArray()) as { | ||
name: string; | ||
queryable: boolean; | ||
}[]; | ||
|
||
if (!searchIndexes.some((index) => indexValidator(index))) { | ||
throw new Error("Search index did not pass validation"); | ||
} | ||
}, | ||
{ | ||
timeout, | ||
interval, | ||
} | ||
); | ||
} | ||
|
||
export async function waitUntilSearchIndexIsListed( | ||
collection: Collection, | ||
searchIndex: string, | ||
timeout: number = DEFAULT_WAIT_TIMEOUT, | ||
interval: number = DEFAULT_RETRY_INTERVAL | ||
): Promise<void> { | ||
return waitUntilSearchIndexIs(collection, searchIndex, (index) => index.name === searchIndex, timeout, interval); | ||
} | ||
|
||
export async function waitUntilSearchIndexIsQueryable( | ||
collection: Collection, | ||
searchIndex: string, | ||
timeout: number = DEFAULT_WAIT_TIMEOUT, | ||
interval: number = DEFAULT_RETRY_INTERVAL | ||
): Promise<void> { | ||
return waitUntilSearchIndexIs( | ||
collection, | ||
searchIndex, | ||
(index) => index.name === searchIndex && index.queryable, | ||
timeout, | ||
interval | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure we want this. In #626 we are using the MongoDBToolBase, and it would be best to have all errors centralised in one single place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just now stumbled upon it while I was going through your PR. Certainly that's more preferable.