-
Notifications
You must be signed in to change notification settings - Fork 171
feat: add structured content #780
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
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 |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| import { z } from "zod"; | ||
| import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; | ||
| import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; | ||
| import type { ToolResult } from "../../tool.js"; | ||
| import { type ToolArgs, type OperationType, formatUntrustedData } from "../../tool.js"; | ||
| import { zEJSON } from "../../args.js"; | ||
| import { type Document } from "bson"; | ||
|
|
@@ -37,14 +37,21 @@ export class InsertManyTool extends MongoDBToolBase { | |
| ), | ||
| } | ||
| : commonArgs; | ||
|
|
||
| protected outputShape = { | ||
| success: z.boolean(), | ||
| insertedCount: z.number(), | ||
| insertedIds: z.array(z.any()), | ||
nirinchev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| static operationType: OperationType = "create"; | ||
|
|
||
| protected async execute({ | ||
| database, | ||
| collection, | ||
| documents, | ||
| embeddingParameters: providedEmbeddingParameters, | ||
| }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> { | ||
| }: ToolArgs<typeof this.argsShape>): Promise<ToolResult<typeof this.outputShape>> { | ||
| const provider = await this.ensureConnected(); | ||
|
|
||
| const embeddingParameters = this.isFeatureEnabled("search") | ||
|
|
@@ -70,8 +77,14 @@ export class InsertManyTool extends MongoDBToolBase { | |
| `Inserted \`${result.insertedCount}\` document(s) into ${database}.${collection}.`, | ||
| `Inserted IDs: ${Object.values(result.insertedIds).join(", ")}` | ||
| ); | ||
|
|
||
| return { | ||
| content, | ||
| structuredContent: { | ||
|
Collaborator
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. FYI the spec says we should return the json serialised instead of formatted text at https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content, I don't know if you would be thinking about automating it as well
Collaborator
Author
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. I think we touched on it at some point in Slack, but in my opinion the target of the "SHOULD" here is the backwards compatibility part, not the exact shape of the response. That is, we want to make sure clients that don't support structured content still work as expected, but I don't believe those matching precisely is critical. I've decided to keep the prompt injection mitigation in place as, if a client does not support structured content, returning json with potentially malicious content is risky. Technically, automating it further is feasible - we can have |
||
| success: true, | ||
| insertedCount: result.insertedCount, | ||
| insertedIds: Object.values(result.insertedIds), | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,31 @@ | ||
| import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; | ||
| import { MongoDBToolBase } from "../mongodbTool.js"; | ||
| import type * as bson from "bson"; | ||
| import type { OperationType } from "../../tool.js"; | ||
| import type { OperationType, ToolResult } from "../../tool.js"; | ||
| import { formatUntrustedData } from "../../tool.js"; | ||
| import z, { type ZodNever } from "zod"; | ||
|
|
||
| export const ListDatabasesToolOutputShape = { | ||
| dbs: z.array(z.object({ name: z.string(), sizeOnDisk: z.string(), sizeUnit: z.literal("bytes") })), | ||
| }; | ||
|
|
||
| export type ListDatabasesToolOutput = z.objectOutputType<typeof ListDatabasesToolOutputShape, ZodNever>; | ||
|
|
||
| export class ListDatabasesTool extends MongoDBToolBase { | ||
| public name = "list-databases"; | ||
| protected description = "List all databases for a MongoDB connection"; | ||
| protected argsShape = {}; | ||
| protected outputShape = ListDatabasesToolOutputShape; | ||
| static operationType: OperationType = "metadata"; | ||
|
|
||
| protected async execute(): Promise<CallToolResult> { | ||
| protected async execute(): Promise<ToolResult<typeof this.outputShape>> { | ||
| const provider = await this.ensureConnected(); | ||
| const dbs = (await provider.listDatabases("")).databases as { name: string; sizeOnDisk: bson.Long }[]; | ||
| const dbs = ((await provider.listDatabases("")).databases as { name: string; sizeOnDisk: bson.Long }[]).map( | ||
| (db) => ({ name: db.name, sizeOnDisk: db.sizeOnDisk.toString(), sizeUnit: "bytes" as const }) | ||
| ); | ||
|
|
||
| return { | ||
| content: formatUntrustedData( | ||
| `Found ${dbs.length} databases`, | ||
| ...dbs.map((db) => `Name: ${db.name}, Size: ${db.sizeOnDisk.toString()} bytes`) | ||
| ), | ||
| content: formatUntrustedData(`Found ${dbs.length} databases`, JSON.stringify(dbs)), | ||
| structuredContent: { dbs }, | ||
| }; | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.