Skip to content

Commit 34c9c68

Browse files
authored
feat: add ability to specify enabled preview features (#686)
1 parent 64ac05e commit 34c9c68

File tree

16 files changed

+69
-43
lines changed

16 files changed

+69
-43
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ The MongoDB MCP Server can be configured using multiple methods, with the follow
369369
| `exportCleanupIntervalMs` | `MDB_MCP_EXPORT_CLEANUP_INTERVAL_MS` | 120000 | Time in milliseconds between export cleanup cycles that remove expired export files. |
370370
| `atlasTemporaryDatabaseUserLifetimeMs` | `MDB_MCP_ATLAS_TEMPORARY_DATABASE_USER_LIFETIME_MS` | 14400000 | Time in milliseconds that temporary database users created when connecting to MongoDB Atlas clusters will remain active before being automatically deleted. |
371371
| `voyageApiKey` | `MDB_VOYAGE_API_KEY` | <not set> | API key for communicating with Voyage AI. Used for generating embeddings for Vector search. |
372+
| `previewFeatures` | `MDB_MCP_PREVIEW_FEATURES` | `[]` | An array of preview features to opt into. |
372373

373374
#### Logger Options
374375

@@ -490,6 +491,19 @@ You can disable telemetry using:
490491

491492
> **💡 Platform Note:** For Windows users, see [Environment Variables](#environment-variables) for platform-specific instructions.
492493
494+
#### Opting into Preview Features
495+
496+
The MongoDB MCP Server may offer functionality that is still in development and may change in future releases. These features are considered "preview features" and are not enabled by default. Generally, these features are well tested, but may not offer the complete functionality we intend to provide in the final release or we'd like to gather feedback before making them generally available. To enable one or more preview features, use the `previewFeatures` configuration option.
497+
498+
- For **environment variable** configuration, use a comma-separated string: `export MDB_MCP_PREVIEW_FEATURES="vectorSearch,feature1,feature2"`.
499+
- For **command-line argument** configuration, use a space-separated string: `--previewFeatures vectorSearch feature1 feature2`.
500+
501+
List of available preview features:
502+
503+
- `vectorSearch` - Enables tools or functionality related to Vector Search in MongoDB Atlas:
504+
- Index management, such as creating, listing, and dropping vector search indexes.
505+
- Querying collections using vector search capabilities. This requires a configured embedding model that will be used to generate vector representations of the query data.
506+
493507
### Atlas API Access
494508

495509
To use the Atlas API tools, you'll need to create a service account in MongoDB Atlas:

src/common/config.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import type { Similarity } from "./search/vectorSearchEmbeddingsManager.js";
1010
import { z } from "zod";
1111
const levenshtein = levenshteinModule.default;
1212

13+
const previewFeatures = z.enum(["vectorSearch"]);
14+
export type PreviewFeature = z.infer<typeof previewFeatures>;
15+
1316
// From: https://github.com/mongodb-js/mongosh/blob/main/packages/cli-repl/src/arg-parser.ts
1417
export const OPTIONS = {
1518
number: ["maxDocumentsPerQuery", "maxBytesPerQuery"],
@@ -81,7 +84,7 @@ export const OPTIONS = {
8184
"tlsFIPSMode",
8285
"version",
8386
],
84-
array: ["disabledTools", "loggers", "confirmationRequiredTools"],
87+
array: ["disabledTools", "loggers", "confirmationRequiredTools", "previewFeatures"],
8588
alias: {
8689
h: "help",
8790
p: "password",
@@ -119,7 +122,7 @@ export const ALL_CONFIG_KEYS = new Set(
119122
.concat(Object.keys(OPTIONS.alias))
120123
);
121124

122-
export function validateConfigKey(key: string): { valid: boolean; suggestion?: string } {
125+
function validateConfigKey(key: string): { valid: boolean; suggestion?: string } {
123126
if (ALL_CONFIG_KEYS.has(key)) {
124127
return { valid: true };
125128
}
@@ -282,6 +285,7 @@ export const UserConfigSchema = z.object({
282285
.optional()
283286
.default("euclidean")
284287
.describe("Default similarity function for vector search: 'euclidean', 'cosine', or 'dotProduct'."),
288+
previewFeatures: z.array(previewFeatures).default([]).describe("An array of preview features that are enabled."),
285289
});
286290

287291
export type UserConfig = z.infer<typeof UserConfigSchema> & CliOptions;
@@ -318,6 +322,7 @@ export const defaultUserConfig: UserConfig = {
318322
disableEmbeddingsValidation: false,
319323
vectorSearchDimensions: 1024,
320324
vectorSearchSimilarityFunction: "euclidean",
325+
previewFeatures: [],
321326
};
322327

323328
export const config = setupUserConfig({
@@ -554,13 +559,13 @@ export function setupUserConfig({
554559
}: {
555560
cli: string[];
556561
env: Record<string, unknown>;
557-
defaults: Partial<UserConfig>;
562+
defaults: UserConfig;
558563
}): UserConfig {
559-
const userConfig: UserConfig = {
564+
const userConfig = {
560565
...defaults,
561566
...parseEnvConfig(env),
562567
...parseCliConfig(cli),
563-
} as UserConfig;
568+
} satisfies UserConfig;
564569

565570
userConfig.disabledTools = commaSeparatedToArray(userConfig.disabledTools);
566571
userConfig.loggers = commaSeparatedToArray(userConfig.loggers);

src/common/search/embeddingsProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ class VoyageEmbeddingsProvider implements EmbeddingsProvider<VoyageModels, Voyag
7272
this.voyage = createVoyage({ apiKey: voyageApiKey, fetch: customFetch });
7373
}
7474

75-
static isConfiguredIn({ voyageApiKey }: UserConfig): boolean {
76-
return !!voyageApiKey;
75+
static isConfiguredIn({ voyageApiKey, previewFeatures }: UserConfig): boolean {
76+
return previewFeatures.includes("vectorSearch") && !!voyageApiKey;
7777
}
7878

7979
async embed<Model extends VoyageModels>(

src/tools/mongodb/create/createIndex.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { z } from "zod";
22
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
33
import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
4-
import { type ToolArgs, type OperationType, FeatureFlags } from "../../tool.js";
4+
import { type ToolArgs, type OperationType } from "../../tool.js";
55
import type { IndexDirection } from "mongodb";
66
import { quantizationEnum, similarityEnum } from "../../../common/search/vectorSearchEmbeddingsManager.js";
77

@@ -74,7 +74,7 @@ export class CreateIndexTool extends MongoDBToolBase {
7474
type: z.literal("classic"),
7575
keys: z.object({}).catchall(z.custom<IndexDirection>()).describe("The index definition"),
7676
}),
77-
...(this.isFeatureFlagEnabled(FeatureFlags.VectorSearch) ? [this.vectorSearchIndexDefinition] : []),
77+
...(this.isFeatureEnabled("vectorSearch") ? [this.vectorSearchIndexDefinition] : []),
7878
])
7979
)
8080
.describe(

src/tools/mongodb/delete/dropIndex.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import z from "zod";
22
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
33
import type { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
44
import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
5-
import { type ToolArgs, type OperationType, formatUntrustedData, FeatureFlags } from "../../tool.js";
5+
import { type ToolArgs, type OperationType, formatUntrustedData } from "../../tool.js";
66

77
export class DropIndexTool extends MongoDBToolBase {
88
public name = "drop-index";
99
protected description = "Drop an index for the provided database and collection.";
1010
protected argsShape = {
1111
...DbOperationArgs,
1212
indexName: z.string().nonempty().describe("The name of the index to be dropped."),
13-
type: this.isFeatureFlagEnabled(FeatureFlags.VectorSearch)
13+
type: this.isFeatureEnabled("vectorSearch")
1414
? z
1515
.enum(["classic", "search"])
1616
.describe(

src/tools/mongodb/metadata/collectionIndexes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
22
import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
33
import type { ToolArgs, OperationType } from "../../tool.js";
4-
import { FeatureFlags, formatUntrustedData } from "../../tool.js";
4+
import { formatUntrustedData } from "../../tool.js";
55

66
type SearchIndexStatus = {
77
name: string;
@@ -31,7 +31,7 @@ export class CollectionIndexesTool extends MongoDBToolBase {
3131
}));
3232

3333
const searchIndexDefinitions: SearchIndexStatus[] = [];
34-
if (this.isFeatureFlagEnabled(FeatureFlags.VectorSearch) && (await this.session.isSearchSupported())) {
34+
if (this.isFeatureEnabled("vectorSearch") && (await this.session.isSearchSupported())) {
3535
const searchIndexes = await provider.getSearchIndexes(database, collection);
3636
searchIndexDefinitions.push(...this.extractSearchIndexDetails(searchIndexes));
3737
}

src/tools/tool.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { Session } from "../common/session.js";
66
import { LogId } from "../common/logger.js";
77
import type { Telemetry } from "../telemetry/telemetry.js";
88
import { type ToolEvent } from "../telemetry/types.js";
9-
import type { UserConfig } from "../common/config.js";
9+
import type { PreviewFeature, UserConfig } from "../common/config.js";
1010
import type { Server } from "../server.js";
1111
import type { Elicitation } from "../elicitation.js";
1212

@@ -15,10 +15,6 @@ export type ToolCallbackArgs<Args extends ZodRawShape> = Parameters<ToolCallback
1515

1616
export type ToolExecutionContext<Args extends ZodRawShape = ZodRawShape> = Parameters<ToolCallback<Args>>[1];
1717

18-
export const enum FeatureFlags {
19-
VectorSearch = "vectorSearch",
20-
}
21-
2218
/**
2319
* The type of operation the tool performs. This is used when evaluating if a tool is allowed to run based on
2420
* the config's `disabledTools` and `readOnly` settings.
@@ -325,14 +321,8 @@ export abstract class ToolBase {
325321
this.telemetry.emitEvents([event]);
326322
}
327323

328-
// TODO: Move this to a separate file
329-
protected isFeatureFlagEnabled(flag: FeatureFlags): boolean {
330-
switch (flag) {
331-
case FeatureFlags.VectorSearch:
332-
return this.config.voyageApiKey !== "";
333-
default:
334-
return false;
335-
}
324+
protected isFeatureEnabled(feature: PreviewFeature): boolean {
325+
return this.config.previewFeatures.includes(feature);
336326
}
337327
}
338328

tests/accuracy/createIndex.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ describeAccuracyTests(
137137
},
138138
],
139139
{
140-
userConfig: { voyageApiKey: "valid-key" },
140+
userConfig: { previewFeatures: "vectorSearch" },
141141
clusterConfig: {
142142
search: true,
143143
},

tests/accuracy/createIndex.vectorSearchDisabled.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@ describeAccuracyTests(
5252
},
5353
],
5454
{
55-
userConfig: { voyageApiKey: "" },
55+
userConfig: { previewFeatures: "" },
5656
}
5757
);

tests/accuracy/dropIndex.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ describeAccuracyTests(
127127
],
128128
{
129129
userConfig: {
130-
voyageApiKey: "voyage-api-key",
130+
previewFeatures: "vectorSearch",
131131
},
132132
clusterConfig: { search: true },
133133
}

0 commit comments

Comments
 (0)