Skip to content

Commit b31bbe7

Browse files
feat: adds an export tool
also makes SessionExportManager available on MongoDBToolBase
1 parent 88c52f1 commit b31bbe7

File tree

4 files changed

+97
-1
lines changed

4 files changed

+97
-1
lines changed

src/server.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,29 @@ import { type ServerCommand } from "./telemetry/types.js";
1313
import { CallToolRequestSchema, CallToolResult } from "@modelcontextprotocol/sdk/types.js";
1414
import assert from "assert";
1515
import { ToolBase } from "./tools/tool.js";
16+
import { SessionExportsManager } from "./common/sessionExportsManager.js";
1617

1718
export interface ServerOptions {
1819
session: Session;
20+
exportsManager: SessionExportsManager;
1921
userConfig: UserConfig;
2022
mcpServer: McpServer;
2123
telemetry: Telemetry;
2224
}
2325

2426
export class Server {
2527
public readonly session: Session;
28+
public readonly exportsManager: SessionExportsManager;
2629
public readonly mcpServer: McpServer;
2730
private readonly telemetry: Telemetry;
2831
public readonly userConfig: UserConfig;
2932
public readonly tools: ToolBase[] = [];
3033
private readonly startTime: number;
3134

32-
constructor({ session, mcpServer, userConfig, telemetry }: ServerOptions) {
35+
constructor({ session, exportsManager, mcpServer, userConfig, telemetry }: ServerOptions) {
3336
this.startTime = Date.now();
3437
this.session = session;
38+
this.exportsManager = exportsManager;
3539
this.telemetry = telemetry;
3640
this.mcpServer = mcpServer;
3741
this.userConfig = userConfig;

src/tools/mongodb/mongodbTool.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
55
import { ErrorCodes, MongoDBError } from "../../common/errors.js";
66
import logger, { LogId } from "../../common/logger.js";
77
import { Server } from "../../server.js";
8+
import { SessionExportsManager } from "../../common/sessionExportsManager.js";
89

910
export const DbOperationArgs = {
1011
database: z.string().describe("Database name"),
@@ -13,6 +14,7 @@ export const DbOperationArgs = {
1314

1415
export abstract class MongoDBToolBase extends ToolBase {
1516
private server?: Server;
17+
public exportsManager?: SessionExportsManager;
1618
public category: ToolCategory = "mongodb";
1719

1820
protected async ensureConnected(): Promise<NodeDriverServiceProvider> {
@@ -47,6 +49,7 @@ export abstract class MongoDBToolBase extends ToolBase {
4749

4850
public register(server: Server): boolean {
4951
this.server = server;
52+
this.exportsManager = server.exportsManager;
5053
return super.register(server);
5154
}
5255

src/tools/mongodb/read/export.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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+
}

src/transports/base.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Server } from "../server.js";
44
import { Session } from "../common/session.js";
55
import { Telemetry } from "../telemetry/telemetry.js";
66
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7+
import { SessionExportsManager } from "../common/sessionExportsManager.js";
78

89
export abstract class TransportRunnerBase {
910
protected setupServer(userConfig: UserConfig): Server {
@@ -13,6 +14,8 @@ export abstract class TransportRunnerBase {
1314
apiClientSecret: userConfig.apiClientSecret,
1415
});
1516

17+
const exportsManager = new SessionExportsManager(session, userConfig);
18+
1619
const telemetry = Telemetry.create(session, userConfig);
1720

1821
const mcpServer = new McpServer({
@@ -23,6 +26,7 @@ export abstract class TransportRunnerBase {
2326
return new Server({
2427
mcpServer,
2528
session,
29+
exportsManager,
2630
telemetry,
2731
userConfig,
2832
});

0 commit comments

Comments
 (0)