|
| 1 | +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; |
| 2 | +import { OperationType, ToolArgs } from "../../tool.js"; |
| 3 | +import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; |
| 4 | +import { FindArgs } from "./find.js"; |
| 5 | +import { jsonExportFormat } from "../../../common/sessionExportsManager.js"; |
| 6 | +import z from "zod"; |
| 7 | + |
| 8 | +export class ExportTool extends MongoDBToolBase { |
| 9 | + public name = "export"; |
| 10 | + protected description = "Export a collection data or query results in the specified json format."; |
| 11 | + protected argsShape = { |
| 12 | + ...DbOperationArgs, |
| 13 | + ...FindArgs, |
| 14 | + limit: z.number().optional().describe("The maximum number of documents to return"), |
| 15 | + jsonExportFormat: jsonExportFormat |
| 16 | + .default("relaxed") |
| 17 | + .describe( |
| 18 | + [ |
| 19 | + "The format to be used when exporting collection data as JSON with default being relaxed.", |
| 20 | + "relaxed: A string format that emphasizes readability and interoperability at the expense of type preservation. That is, conversion from relaxed format to BSON can lose type information.", |
| 21 | + "canonical: A string format that emphasizes type preservation at the expense of readability and interoperability. That is, conversion from canonical to BSON will generally preserve type information except in certain specific cases.", |
| 22 | + ].join("\n") |
| 23 | + ), |
| 24 | + }; |
| 25 | + public operationType: OperationType = "read"; |
| 26 | + |
| 27 | + protected async execute({ |
| 28 | + database, |
| 29 | + collection, |
| 30 | + jsonExportFormat, |
| 31 | + filter, |
| 32 | + projection, |
| 33 | + sort, |
| 34 | + limit, |
| 35 | + }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> { |
| 36 | + const provider = await this.ensureConnected(); |
| 37 | + const findCursor = provider.find(database, collection, filter ?? {}, { |
| 38 | + projection, |
| 39 | + sort, |
| 40 | + limit, |
| 41 | + promoteValues: false, |
| 42 | + bsonRegExp: true, |
| 43 | + }); |
| 44 | + const exportName = `${database}.${collection}.json`; |
| 45 | + if (!this.exportsManager) { |
| 46 | + throw new Error("Incorrect server configuration, export not possible!"); |
| 47 | + } |
| 48 | + |
| 49 | + await this.exportsManager.createJSONExport({ input: findCursor, exportName, jsonExportFormat }); |
| 50 | + const exportedResourceURI = this.exportsManager.exportNameToResourceURI(exportName); |
| 51 | + const exportedResourcePath = this.exportsManager.exportFilePath( |
| 52 | + this.exportsManager.exportsDirectoryPath(), |
| 53 | + exportName |
| 54 | + ); |
| 55 | + const toolCallContent: CallToolResult["content"] = [ |
| 56 | + // Not all the clients as of this commit understands how to |
| 57 | + // parse a resource_link so we provide a text result for them to |
| 58 | + // understand what to do with the result. |
| 59 | + { |
| 60 | + type: "text", |
| 61 | + text: `Exported data for namespace ${database}.${collection} is available under resource URI - "${exportedResourceURI}".`, |
| 62 | + }, |
| 63 | + { |
| 64 | + type: "resource_link", |
| 65 | + name: exportName, |
| 66 | + uri: exportedResourceURI, |
| 67 | + description: "Resource URI for fetching exported data.", |
| 68 | + mimeType: "application/json", |
| 69 | + }, |
| 70 | + ]; |
| 71 | + |
| 72 | + // This special case is to make it easier to work with exported data for |
| 73 | + // stdio transport. |
| 74 | + if (this.config.transport === "stdio") { |
| 75 | + toolCallContent.push({ |
| 76 | + type: "text", |
| 77 | + text: `Optionally, the exported data can also be accessed under path - "${exportedResourcePath}"`, |
| 78 | + }); |
| 79 | + } |
| 80 | + |
| 81 | + return { |
| 82 | + content: toolCallContent, |
| 83 | + }; |
| 84 | + } |
| 85 | +} |
0 commit comments