Skip to content

Commit 80f9e7e

Browse files
chore: add mcp auto-embed usage property
1 parent 0b60d8a commit 80f9e7e

File tree

5 files changed

+137
-6
lines changed

5 files changed

+137
-6
lines changed

src/telemetry/types.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,22 +143,30 @@ export type CommonProperties = {
143143
* For MongoDB tools, this is typically empty, while for Atlas tools, this should include
144144
* the project and organization IDs if available.
145145
*/
146-
export type TelemetryToolMetadata = AtlasMetadata | PerfAdvisorToolMetadata | ConnectionMetadata;
146+
export type TelemetryToolMetadata =
147+
| AtlasMetadata
148+
| ConnectionMetadata
149+
| PerfAdvisorToolMetadata
150+
| AutoEmbeddingsUsageMetadata;
147151

148152
export type AtlasMetadata = {
149153
project_id?: string;
150154
org_id?: string;
151155
};
152156

153-
export type PerfAdvisorToolMetadata = AtlasMetadata & {
154-
operations: string[];
157+
type AtlasLocalToolMetadata = {
158+
atlas_local_deployment_id?: string;
155159
};
156160

157161
export type ConnectionMetadata = AtlasMetadata &
158162
AtlasLocalToolMetadata & {
159163
connection_auth_type?: string;
160164
};
161165

162-
type AtlasLocalToolMetadata = {
163-
atlas_local_deployment_id?: string;
166+
export type PerfAdvisorToolMetadata = AtlasMetadata & {
167+
operations: string[];
168+
};
169+
170+
export type AutoEmbeddingsUsageMetadata = ConnectionMetadata & {
171+
embeddingsGeneratedBy: "mcp" | "mongot";
164172
};

src/tools/mongodb/create/insertMany.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { zEJSON } from "../../args.js";
66
import { type Document } from "bson";
77
import { zSupportedEmbeddingParameters } from "../mongodbSchemas.js";
88
import { ErrorCodes, MongoDBError } from "../../../common/errors.js";
9+
import type { ConnectionMetadata, AutoEmbeddingsUsageMetadata } from "../../../telemetry/types.js";
910

1011
const zSupportedEmbeddingParametersWithInput = zSupportedEmbeddingParameters.extend({
1112
input: z
@@ -155,4 +156,18 @@ export class InsertManyTool extends MongoDBToolBase {
155156
}
156157
}
157158
}
159+
160+
protected resolveTelemetryMetadata(
161+
args: ToolArgs<typeof this.argsShape>,
162+
{ result }: { result: CallToolResult }
163+
): ConnectionMetadata | AutoEmbeddingsUsageMetadata {
164+
if ("embeddingParameters" in args && this.config.voyageApiKey) {
165+
return {
166+
...super.resolveTelemetryMetadata(args, { result }),
167+
embeddingsGeneratedBy: "mcp",
168+
};
169+
} else {
170+
return super.resolveTelemetryMetadata(args, { result });
171+
}
172+
}
158173
}

src/tools/mongodb/read/aggregate.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
assertVectorSearchFilterFieldsAreIndexed,
1818
type SearchIndex,
1919
} from "../../../helpers/assertVectorSearchFilterFieldsAreIndexed.js";
20+
import type { AutoEmbeddingsUsageMetadata, ConnectionMetadata } from "../../../telemetry/types.js";
2021

2122
const pipelineDescriptionWithVectorSearch = `\
2223
An array of aggregation stages to execute.
@@ -344,4 +345,23 @@ The aggregation resulted in ${aggResultsCount === undefined ? "indeterminable nu
344345
Returning ${documents.length} documents${appliedLimitText ? ` ${appliedLimitText}` : "."}\
345346
`;
346347
}
348+
349+
protected resolveTelemetryMetadata(
350+
args: ToolArgs<typeof this.argsShape>,
351+
{ result }: { result: CallToolResult }
352+
): ConnectionMetadata | AutoEmbeddingsUsageMetadata {
353+
const [maybeVectorStage] = args.pipeline;
354+
if (
355+
maybeVectorStage &&
356+
(maybeVectorStage as z.infer<typeof VectorSearchStage>)?.["$vectorSearch"]?.embeddingParameters &&
357+
this.config.voyageApiKey
358+
) {
359+
return {
360+
...super.resolveTelemetryMetadata(args, { result }),
361+
embeddingsGeneratedBy: "mcp",
362+
};
363+
} else {
364+
return super.resolveTelemetryMetadata(args, { result });
365+
}
366+
}
347367
}

tests/integration/tools/mongodb/create/insertMany.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ import {
1414
getDataFromUntrustedContent,
1515
defaultTestConfig,
1616
} from "../../../helpers.js";
17-
import { beforeEach, afterEach, expect, it, describe } from "vitest";
17+
import { beforeEach, afterEach, expect, it, describe, vi } from "vitest";
1818
import { ObjectId } from "bson";
1919
import type { Collection } from "mongodb";
20+
import type { ToolEvent } from "../../../../../src/telemetry/types.js";
2021

2122
describeWithMongoDB("insertMany tool when search is disabled", (integration) => {
2223
validateToolMetadata(
@@ -237,6 +238,7 @@ describeWithMongoDB(
237238

238239
afterEach(async () => {
239240
await collection.drop();
241+
vi.clearAllMocks();
240242
});
241243

242244
it("generates embeddings for a single document with one field", async () => {
@@ -627,6 +629,42 @@ describeWithMongoDB(
627629
// Verify embeddings are different for different text
628630
expect(doc?.titleEmbeddings).not.toEqual(doc?.plotEmbeddings);
629631
});
632+
633+
it("should emit tool event with auto-embedding usage metadata", async () => {
634+
const mockEmitEvents = vi.spyOn(integration.mcpServer()["telemetry"], "emitEvents");
635+
vi.spyOn(integration.mcpServer()["telemetry"], "isTelemetryEnabled").mockReturnValue(true);
636+
637+
await createVectorSearchIndexAndWait(integration.mongoClient(), database, "test", [
638+
{
639+
type: "vector",
640+
path: "titleEmbeddings",
641+
numDimensions: 1024,
642+
similarity: "cosine",
643+
quantization: "scalar",
644+
},
645+
]);
646+
647+
const response = await integration.mcpClient().callTool({
648+
name: "insert-many",
649+
arguments: {
650+
database,
651+
collection: "test",
652+
documents: [{ title: "The Matrix" }],
653+
embeddingParameters: {
654+
model: "voyage-3.5-lite",
655+
input: [{ titleEmbeddings: "The Matrix" }],
656+
},
657+
},
658+
});
659+
660+
const content = getResponseContent(response.content);
661+
expect(content).toContain("Documents were inserted successfully.");
662+
663+
expect(mockEmitEvents).toHaveBeenCalled();
664+
const emittedEvent = mockEmitEvents.mock.lastCall?.[0][0] as ToolEvent;
665+
expectDefined(emittedEvent);
666+
expect(emittedEvent.properties.embeddingsGeneratedBy).toBe("mcp");
667+
});
630668
});
631669
},
632670
{

tests/integration/tools/mongodb/read/aggregate.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
validateThrowsForInvalidArguments,
55
getResponseContent,
66
defaultTestConfig,
7+
expectDefined,
78
} from "../../../helpers.js";
89
import { beforeEach, describe, expect, it, vi, afterEach } from "vitest";
910
import {
@@ -17,6 +18,7 @@ import * as constants from "../../../../../src/helpers/constants.js";
1718
import { freshInsertDocuments } from "./find.test.js";
1819
import { BSON } from "bson";
1920
import { DOCUMENT_EMBEDDINGS } from "./vyai/embeddings.js";
21+
import type { ToolEvent } from "../../../../../src/telemetry/types.js";
2022

2123
describeWithMongoDB("aggregate tool", (integration) => {
2224
afterEach(() => {
@@ -393,6 +395,10 @@ describeWithMongoDB(
393395
await integration.mongoClient().db(integration.randomDbName()).collection("databases").drop();
394396
});
395397

398+
afterEach(() => {
399+
vi.clearAllMocks();
400+
});
401+
396402
validateToolMetadata(integration, "aggregate", "Run an aggregation against a MongoDB collection", "read", [
397403
...databaseCollectionParameters,
398404
{
@@ -462,6 +468,50 @@ If the user requests additional filtering, include filters in \`$vectorSearch.fi
462468
);
463469
});
464470

471+
it("should emit tool event with auto-embedding usage metadata", async () => {
472+
const mockEmitEvents = vi.spyOn(integration.mcpServer()["telemetry"], "emitEvents");
473+
vi.spyOn(integration.mcpServer()["telemetry"], "isTelemetryEnabled").mockReturnValue(true);
474+
475+
await waitUntilSearchIsReady(integration.mongoClient());
476+
await createVectorSearchIndexAndWait(integration.mongoClient(), integration.randomDbName(), "databases", [
477+
{
478+
type: "vector",
479+
path: "description_embedding",
480+
numDimensions: 256,
481+
similarity: "cosine",
482+
quantization: "none",
483+
},
484+
]);
485+
486+
await integration.mcpClient().callTool({
487+
name: "aggregate",
488+
arguments: {
489+
database: integration.randomDbName(),
490+
collection: "databases",
491+
pipeline: [
492+
{
493+
$vectorSearch: {
494+
index: "default",
495+
path: "description_embedding",
496+
queryVector: "some data",
497+
numCandidates: 10,
498+
limit: 10,
499+
embeddingParameters: {
500+
model: "voyage-3-large",
501+
outputDimension: "256",
502+
},
503+
},
504+
},
505+
],
506+
},
507+
});
508+
509+
expect(mockEmitEvents).toHaveBeenCalled();
510+
const emittedEvent = mockEmitEvents.mock.lastCall?.[0][0] as ToolEvent;
511+
expectDefined(emittedEvent);
512+
expect(emittedEvent.properties.embeddingsGeneratedBy).toBe("mcp");
513+
});
514+
465515
for (const [dataType, embedding] of Object.entries(DOCUMENT_EMBEDDINGS)) {
466516
for (const similarity of ["euclidean", "cosine", "dotProduct"]) {
467517
describe(`querying with dataType ${dataType} and similarity ${similarity}`, () => {

0 commit comments

Comments
 (0)