find
Query MongoDB collections to retrieve documents matching specific criteria, with options to filter, sort, limit results, and control response size.
Instructions
Run a find query against a MongoDB collection
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| database | Yes | Database name | |
| collection | Yes | Collection name | |
| filter | No | The query filter, matching the syntax of the query argument of db.collection.find() | |
| projection | No | The projection, matching the syntax of the projection argument of db.collection.find() | |
| limit | No | The maximum number of documents to return | |
| sort | No | A document, describing the sort order, matching the syntax of the sort argument of cursor.sort(). The keys of the object are the fields to sort on, while the values are the sort directions (1 for ascending, -1 for descending). | |
| responseBytesLimit | No | The maximum number of bytes to return in the response. This value is capped by the server's configured maxBytesPerQuery and cannot be exceeded. Note to LLM: If the entire query result is required, use the "export" tool instead of increasing this limit. |
Implementation Reference
- src/tools/mongodb/read/find.ts:38-170 (handler)The FindTool class is the core implementation of the 'find' tool. It extends MongoDBToolBase, sets name to 'find', defines argsShape using FindArgs, and implements the execute method to run find queries on MongoDB collections with limits, index checks, and result formatting.export class FindTool extends MongoDBToolBase { public name = "find"; protected description = "Run a find query against a MongoDB collection"; protected argsShape = { ...DbOperationArgs, ...FindArgs, }; static operationType: OperationType = "read"; protected async execute( { database, collection, filter, projection, limit, sort, responseBytesLimit }: ToolArgs<typeof this.argsShape>, { signal }: ToolExecutionContext ): Promise<CallToolResult> { let findCursor: FindCursor<unknown> | undefined = undefined; try { const provider = await this.ensureConnected(); // Check if find operation uses an index if enabled if (this.config.indexCheck) { await checkIndexUsage(provider, database, collection, "find", async () => { return provider .find(database, collection, filter, { projection, limit, sort }) .explain("queryPlanner"); }); } const limitOnFindCursor = this.getLimitForFindCursor(limit); findCursor = provider.find(database, collection, filter, { projection, limit: limitOnFindCursor.limit, sort, }); const [queryResultsCount, cursorResults] = await Promise.all([ operationWithFallback( () => provider.countDocuments(database, collection, filter, { // We should be counting documents that the original // query would have yielded which is why we don't // use `limitOnFindCursor` calculated above, only // the limit provided to the tool. limit, maxTimeMS: QUERY_COUNT_MAX_TIME_MS_CAP, }), undefined ), collectCursorUntilMaxBytesLimit({ cursor: findCursor, configuredMaxBytesPerQuery: this.config.maxBytesPerQuery, toolResponseBytesLimit: responseBytesLimit, abortSignal: signal, }), ]); return { content: formatUntrustedData( this.generateMessage({ collection, queryResultsCount, documents: cursorResults.documents, appliedLimits: [limitOnFindCursor.cappedBy, cursorResults.cappedBy].filter((limit) => !!limit), }), ...(cursorResults.documents.length > 0 ? [EJSON.stringify(cursorResults.documents)] : []) ), }; } finally { if (findCursor) { void this.safeCloseCursor(findCursor); } } } private async safeCloseCursor(cursor: FindCursor<unknown>): Promise<void> { try { await cursor.close(); } catch (error) { this.session.logger.warning({ id: LogId.mongodbCursorCloseError, context: "find tool", message: `Error when closing the cursor - ${error instanceof Error ? error.message : String(error)}`, }); } } private generateMessage({ collection, queryResultsCount, documents, appliedLimits, }: { collection: string; queryResultsCount: number | undefined; documents: unknown[]; appliedLimits: (keyof typeof CURSOR_LIMITS_TO_LLM_TEXT)[]; }): string { const appliedLimitsText = appliedLimits.length ? `\ while respecting the applied limits of ${appliedLimits.map((limit) => CURSOR_LIMITS_TO_LLM_TEXT[limit]).join(", ")}. \ Note to LLM: If the entire query result is required then use "export" tool to export the query results.\ ` : ""; return `\ Query on collection "${collection}" resulted in ${queryResultsCount === undefined ? "indeterminable number of" : queryResultsCount} documents. \ Returning ${documents.length} documents${appliedLimitsText ? ` ${appliedLimitsText}` : "."}\ `; } private getLimitForFindCursor(providedLimit: number | undefined | null): { cappedBy: "config.maxDocumentsPerQuery" | undefined; limit: number | undefined; } { const configuredLimit: number = parseInt(String(this.config.maxDocumentsPerQuery), 10); // Setting configured maxDocumentsPerQuery to negative, zero or nullish // is equivalent to disabling the max limit applied on documents const configuredLimitIsNotApplicable = Number.isNaN(configuredLimit) || configuredLimit <= 0; if (configuredLimitIsNotApplicable) { return { cappedBy: undefined, limit: providedLimit ?? undefined }; } const providedLimitIsNotApplicable = providedLimit === null || providedLimit === undefined; if (providedLimitIsNotApplicable) { return { cappedBy: "config.maxDocumentsPerQuery", limit: configuredLimit }; } return { cappedBy: configuredLimit < providedLimit ? "config.maxDocumentsPerQuery" : undefined, limit: Math.min(providedLimit, configuredLimit), }; } }
- src/tools/mongodb/read/find.ts:15-36 (schema)Zod schema definition for the input arguments of the 'find' tool, including filter, projection, limit, sort, and responseBytesLimit.export const FindArgs = { filter: zEJSON() .optional() .describe("The query filter, matching the syntax of the query argument of db.collection.find()"), projection: z .object({}) .passthrough() .optional() .describe("The projection, matching the syntax of the projection argument of db.collection.find()"), limit: z.number().optional().default(10).describe("The maximum number of documents to return"), sort: z .object({}) .catchall(z.custom<SortDirection>()) .optional() .describe( "A document, describing the sort order, matching the syntax of the sort argument of cursor.sort(). The keys of the object are the fields to sort on, while the values are the sort directions (1 for ascending, -1 for descending)." ), responseBytesLimit: z.number().optional().default(ONE_MB).describe(`\ The maximum number of bytes to return in the response. This value is capped by the server's configured maxBytesPerQuery and cannot be exceeded. \ Note to LLM: If the entire query result is required, use the "export" tool instead of increasing this limit.\ `), };
- src/tools/mongodb/tools.ts:7-7 (registration)The FindTool is exported from src/tools/mongodb/tools.ts, which serves as the central registry exporting all MongoDB tools for use in the MCP server transports.export { FindTool } from "./read/find.js";
- The 'find' tool schema (FindArgs) is reused in the export tool for defining find-based exports.import { FindArgs } from "./find.js"; import { jsonExportFormat } from "../../../common/exportsManager.js"; import { getAggregateArgs } from "./aggregate.js"; export class ExportTool extends MongoDBToolBase { public name = "export"; protected description = "Export a query or aggregation results in the specified EJSON format."; protected argsShape = { ...DbOperationArgs, exportTitle: z.string().describe("A short description to uniquely identify the export."), exportTarget: z .array( z.discriminatedUnion("name", [ z.object({ name: z .literal("find") .describe("The literal name 'find' to represent a find cursor as target."), arguments: z .object({ ...FindArgs, limit: FindArgs.limit.removeDefault(), }) .describe("The arguments for 'find' operation."), }), z.object({ name: z .literal("aggregate") .describe("The literal name 'aggregate' to represent an aggregation cursor as target."), arguments: z .object(getAggregateArgs(this.isFeatureEnabled("search")))
- Helper methods within FindTool for message generation, limit calculation, and cursor management.private async safeCloseCursor(cursor: FindCursor<unknown>): Promise<void> { try { await cursor.close(); } catch (error) { this.session.logger.warning({ id: LogId.mongodbCursorCloseError, context: "find tool", message: `Error when closing the cursor - ${error instanceof Error ? error.message : String(error)}`, }); } } private generateMessage({ collection, queryResultsCount, documents, appliedLimits, }: { collection: string; queryResultsCount: number | undefined; documents: unknown[]; appliedLimits: (keyof typeof CURSOR_LIMITS_TO_LLM_TEXT)[]; }): string { const appliedLimitsText = appliedLimits.length ? `\ while respecting the applied limits of ${appliedLimits.map((limit) => CURSOR_LIMITS_TO_LLM_TEXT[limit]).join(", ")}. \ Note to LLM: If the entire query result is required then use "export" tool to export the query results.\ ` : ""; return `\ Query on collection "${collection}" resulted in ${queryResultsCount === undefined ? "indeterminable number of" : queryResultsCount} documents. \ Returning ${documents.length} documents${appliedLimitsText ? ` ${appliedLimitsText}` : "."}\ `; } private getLimitForFindCursor(providedLimit: number | undefined | null): { cappedBy: "config.maxDocumentsPerQuery" | undefined; limit: number | undefined; } { const configuredLimit: number = parseInt(String(this.config.maxDocumentsPerQuery), 10); // Setting configured maxDocumentsPerQuery to negative, zero or nullish // is equivalent to disabling the max limit applied on documents const configuredLimitIsNotApplicable = Number.isNaN(configuredLimit) || configuredLimit <= 0; if (configuredLimitIsNotApplicable) { return { cappedBy: undefined, limit: providedLimit ?? undefined }; } const providedLimitIsNotApplicable = providedLimit === null || providedLimit === undefined; if (providedLimitIsNotApplicable) { return { cappedBy: "config.maxDocumentsPerQuery", limit: configuredLimit }; } return { cappedBy: configuredLimit < providedLimit ? "config.maxDocumentsPerQuery" : undefined, limit: Math.min(providedLimit, configuredLimit), }; } }