From 5fa620f1d3ba5ff98f379c6eb1482dfaa2efb74f Mon Sep 17 00:00:00 2001 From: nirinchev Date: Fri, 24 Oct 2025 12:40:43 +0200 Subject: [PATCH 1/4] feat: add ability to specify enabled preview features --- README.md | 13 +++++++++++++ src/common/config.ts | 11 ++++++++--- src/common/search/embeddingsProvider.ts | 4 ++-- src/tools/mongodb/create/createIndex.ts | 4 ++-- src/tools/mongodb/delete/dropIndex.ts | 4 ++-- src/tools/mongodb/metadata/collectionIndexes.ts | 4 ++-- src/tools/tool.ts | 16 +++------------- tests/accuracy/aggregate.test.ts | 4 ++-- tests/accuracy/createIndex.test.ts | 2 +- .../createIndex.vectorSearchDisabled.test.ts | 2 +- tests/accuracy/dropIndex.test.ts | 2 +- .../dropIndex.vectorSearchDisabled.test.ts | 2 +- .../tools/mongodb/create/createIndex.test.ts | 10 +++++----- .../tools/mongodb/delete/dropIndex.test.ts | 12 ++++++------ .../mongodb/metadata/collectionIndexes.test.ts | 2 +- .../tools/mongodb/read/aggregate.test.ts | 1 + 16 files changed, 51 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 152f34120..793e9c8b0 100644 --- a/README.md +++ b/README.md @@ -369,6 +369,7 @@ The MongoDB MCP Server can be configured using multiple methods, with the follow | `exportCleanupIntervalMs` | `MDB_MCP_EXPORT_CLEANUP_INTERVAL_MS` | 120000 | Time in milliseconds between export cleanup cycles that remove expired export files. | | `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. | | `voyageApiKey` | `MDB_VOYAGE_API_KEY` | | API key for communicating with Voyage AI. Used for generating embeddings for Vector search. | +| `previewFeatures` | `MDB_MCP_PREVIEW_FEATURES` | `[]` | An array of preview features to opt into. | #### Logger Options @@ -490,6 +491,18 @@ You can disable telemetry using: > **💡 Platform Note:** For Windows users, see [Environment Variables](#environment-variables) for platform-specific instructions. +#### Opting into Preview Features + +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. + +- For **environment variable** configuration, use a comma-separated string: `export MDB_MCP_PREVIEW_FEATURES="vectorSearch,dedicatedClusters"`. +- For **command-line argument** configuration, use a space-separated string: `--previewFeatures vectorSearch dedicatedClusters`. + +List of available preview features: +- `vectorSearch` - Enables tools or functionality related to Vector Search in MongoDB Atlas: + - Index management, such as creating, listing, and dropping vector search indexes. + - Querying collections using vector search capabilities. This requires a configured embedding model that will be used to generate vector representations of the query data. + ### Atlas API Access To use the Atlas API tools, you'll need to create a service account in MongoDB Atlas: diff --git a/src/common/config.ts b/src/common/config.ts index 9565e1d07..e1ee13c0a 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -10,6 +10,9 @@ import type { Similarity } from "./search/vectorSearchEmbeddingsManager.js"; import { z } from "zod"; const levenshtein = levenshteinModule.default; +const previewFeatures = z.enum(["vectorSearch"]); +export type PreviewFeatures = z.infer; + // From: https://github.com/mongodb-js/mongosh/blob/main/packages/cli-repl/src/arg-parser.ts export const OPTIONS = { number: ["maxDocumentsPerQuery", "maxBytesPerQuery"], @@ -81,7 +84,7 @@ export const OPTIONS = { "tlsFIPSMode", "version", ], - array: ["disabledTools", "loggers", "confirmationRequiredTools"], + array: ["disabledTools", "loggers", "confirmationRequiredTools", "previewFeatures"], alias: { h: "help", p: "password", @@ -119,7 +122,7 @@ export const ALL_CONFIG_KEYS = new Set( .concat(Object.keys(OPTIONS.alias)) ); -export function validateConfigKey(key: string): { valid: boolean; suggestion?: string } { +function validateConfigKey(key: string): { valid: boolean; suggestion?: string } { if (ALL_CONFIG_KEYS.has(key)) { return { valid: true }; } @@ -282,6 +285,7 @@ export const UserConfigSchema = z.object({ .optional() .default("euclidean") .describe("Default similarity function for vector search: 'euclidean', 'cosine', or 'dotProduct'."), + previewFeatures: z.array(previewFeatures).default([]).describe("An array of preview features that are enabled."), }); export type UserConfig = z.infer & CliOptions; @@ -318,6 +322,7 @@ export const defaultUserConfig: UserConfig = { disableEmbeddingsValidation: false, vectorSearchDimensions: 1024, vectorSearchSimilarityFunction: "euclidean", + previewFeatures: [], }; export const config = setupUserConfig({ @@ -556,7 +561,7 @@ export function setupUserConfig({ env: Record; defaults: Partial; }): UserConfig { - const userConfig: UserConfig = { + const userConfig = { ...defaults, ...parseEnvConfig(env), ...parseCliConfig(cli), diff --git a/src/common/search/embeddingsProvider.ts b/src/common/search/embeddingsProvider.ts index efc93e436..c8c912d07 100644 --- a/src/common/search/embeddingsProvider.ts +++ b/src/common/search/embeddingsProvider.ts @@ -53,8 +53,8 @@ class VoyageEmbeddingsProvider implements EmbeddingsProvider( diff --git a/src/tools/mongodb/create/createIndex.ts b/src/tools/mongodb/create/createIndex.ts index e535f4fe3..252d8c4c4 100644 --- a/src/tools/mongodb/create/createIndex.ts +++ b/src/tools/mongodb/create/createIndex.ts @@ -1,7 +1,7 @@ import { z } from "zod"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; -import { type ToolArgs, type OperationType, FeatureFlags } from "../../tool.js"; +import { type ToolArgs, type OperationType } from "../../tool.js"; import type { IndexDirection } from "mongodb"; import { quantizationEnum, similarityEnum } from "../../../common/search/vectorSearchEmbeddingsManager.js"; @@ -74,7 +74,7 @@ export class CreateIndexTool extends MongoDBToolBase { type: z.literal("classic"), keys: z.object({}).catchall(z.custom()).describe("The index definition"), }), - ...(this.isFeatureFlagEnabled(FeatureFlags.VectorSearch) ? [this.vectorSearchIndexDefinition] : []), + ...(this.isFeatureEnabled("vectorSearch") ? [this.vectorSearchIndexDefinition] : []), ]) ) .describe( diff --git a/src/tools/mongodb/delete/dropIndex.ts b/src/tools/mongodb/delete/dropIndex.ts index dea72bf83..a6f28d0db 100644 --- a/src/tools/mongodb/delete/dropIndex.ts +++ b/src/tools/mongodb/delete/dropIndex.ts @@ -2,7 +2,7 @@ import z from "zod"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import type { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; -import { type ToolArgs, type OperationType, formatUntrustedData, FeatureFlags } from "../../tool.js"; +import { type ToolArgs, type OperationType, formatUntrustedData } from "../../tool.js"; export class DropIndexTool extends MongoDBToolBase { public name = "drop-index"; @@ -10,7 +10,7 @@ export class DropIndexTool extends MongoDBToolBase { protected argsShape = { ...DbOperationArgs, indexName: z.string().nonempty().describe("The name of the index to be dropped."), - type: this.isFeatureFlagEnabled(FeatureFlags.VectorSearch) + type: this.isFeatureEnabled("vectorSearch") ? z .enum(["classic", "search"]) .describe( diff --git a/src/tools/mongodb/metadata/collectionIndexes.ts b/src/tools/mongodb/metadata/collectionIndexes.ts index a04596b9b..19007c4f1 100644 --- a/src/tools/mongodb/metadata/collectionIndexes.ts +++ b/src/tools/mongodb/metadata/collectionIndexes.ts @@ -1,7 +1,7 @@ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; import type { ToolArgs, OperationType } from "../../tool.js"; -import { FeatureFlags, formatUntrustedData } from "../../tool.js"; +import { formatUntrustedData } from "../../tool.js"; type SearchIndexStatus = { name: string; @@ -31,7 +31,7 @@ export class CollectionIndexesTool extends MongoDBToolBase { })); const searchIndexDefinitions: SearchIndexStatus[] = []; - if (this.isFeatureFlagEnabled(FeatureFlags.VectorSearch) && (await this.session.isSearchSupported())) { + if (this.isFeatureEnabled("vectorSearch") && (await this.session.isSearchSupported())) { const searchIndexes = await provider.getSearchIndexes(database, collection); searchIndexDefinitions.push(...this.extractSearchIndexDetails(searchIndexes)); } diff --git a/src/tools/tool.ts b/src/tools/tool.ts index 8c8d2436f..97934250a 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -6,7 +6,7 @@ import type { Session } from "../common/session.js"; import { LogId } from "../common/logger.js"; import type { Telemetry } from "../telemetry/telemetry.js"; import { type ToolEvent } from "../telemetry/types.js"; -import type { UserConfig } from "../common/config.js"; +import type { PreviewFeatures, UserConfig } from "../common/config.js"; import type { Server } from "../server.js"; import type { Elicitation } from "../elicitation.js"; @@ -15,10 +15,6 @@ export type ToolCallbackArgs = Parameters = Parameters>[1]; -export const enum FeatureFlags { - VectorSearch = "vectorSearch", -} - /** * The type of operation the tool performs. This is used when evaluating if a tool is allowed to run based on * the config's `disabledTools` and `readOnly` settings. @@ -325,14 +321,8 @@ export abstract class ToolBase { this.telemetry.emitEvents([event]); } - // TODO: Move this to a separate file - protected isFeatureFlagEnabled(flag: FeatureFlags): boolean { - switch (flag) { - case FeatureFlags.VectorSearch: - return this.config.voyageApiKey !== ""; - default: - return false; - } + protected isFeatureEnabled(flag: PreviewFeatures): boolean { + return this.config.previewFeatures.includes(flag); } } diff --git a/tests/accuracy/aggregate.test.ts b/tests/accuracy/aggregate.test.ts index 85340a331..e48761060 100644 --- a/tests/accuracy/aggregate.test.ts +++ b/tests/accuracy/aggregate.test.ts @@ -153,7 +153,7 @@ describeAccuracyTests([ }, }, { - prompt: "Run an approximate vectorSearch query on mflix.movies on path 'plot_embeddings' with the model voyage-3-large to find all 'sci-fy' movies.", + prompt: "Run an approximate vectorSearch query on mflix.movies on path 'plot_embeddings' with the model voyage-3-large to find all 'sci-fi' movies.", expectedToolCalls: [ { toolName: "collection-indexes", @@ -173,7 +173,7 @@ describeAccuracyTests([ exact: Matcher.anyOf(Matcher.undefined, Matcher.boolean(false)), index: "my-index", path: "plot_embeddings", - queryVector: "sci-fy", + queryVector: "sci-fi", embeddingParameters: { model: "voyage-3-large", outputDimension: Matcher.anyOf( diff --git a/tests/accuracy/createIndex.test.ts b/tests/accuracy/createIndex.test.ts index 66f330148..9a3a4f90c 100644 --- a/tests/accuracy/createIndex.test.ts +++ b/tests/accuracy/createIndex.test.ts @@ -137,7 +137,7 @@ describeAccuracyTests( }, ], { - userConfig: { voyageApiKey: "valid-key" }, + userConfig: { previewFeatures: "vectorSearch" }, clusterConfig: { search: true, }, diff --git a/tests/accuracy/createIndex.vectorSearchDisabled.test.ts b/tests/accuracy/createIndex.vectorSearchDisabled.test.ts index eb5fd3ebe..320e2d1e1 100644 --- a/tests/accuracy/createIndex.vectorSearchDisabled.test.ts +++ b/tests/accuracy/createIndex.vectorSearchDisabled.test.ts @@ -52,6 +52,6 @@ describeAccuracyTests( }, ], { - userConfig: { voyageApiKey: "" }, + userConfig: { previewFeatures: "" }, } ); diff --git a/tests/accuracy/dropIndex.test.ts b/tests/accuracy/dropIndex.test.ts index d5df1182b..0c9b6803d 100644 --- a/tests/accuracy/dropIndex.test.ts +++ b/tests/accuracy/dropIndex.test.ts @@ -127,7 +127,7 @@ describeAccuracyTests( ], { userConfig: { - voyageApiKey: "voyage-api-key", + previewFeatures: "vectorSearch", }, clusterConfig: { search: true }, } diff --git a/tests/accuracy/dropIndex.vectorSearchDisabled.test.ts b/tests/accuracy/dropIndex.vectorSearchDisabled.test.ts index eca250907..6649b4fcc 100644 --- a/tests/accuracy/dropIndex.vectorSearchDisabled.test.ts +++ b/tests/accuracy/dropIndex.vectorSearchDisabled.test.ts @@ -90,7 +90,7 @@ describeAccuracyTests( ], { userConfig: { - voyageApiKey: "", + previewFeatures: "", }, } ); diff --git a/tests/integration/tools/mongodb/create/createIndex.test.ts b/tests/integration/tools/mongodb/create/createIndex.test.ts index d273554c4..f76bb5ba1 100644 --- a/tests/integration/tools/mongodb/create/createIndex.test.ts +++ b/tests/integration/tools/mongodb/create/createIndex.test.ts @@ -14,7 +14,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; describeWithMongoDB("createIndex tool when search is not enabled", (integration) => { it("doesn't allow creating vector search indexes", async () => { - expect(integration.mcpServer().userConfig.voyageApiKey).toEqual(""); + expect(integration.mcpServer().userConfig.previewFeatures).to.not.include("vectorSearch"); const { tools } = await integration.mcpClient().listTools(); const createIndexTool = tools.find((tool) => tool.name === "create-index"); @@ -38,7 +38,7 @@ describeWithMongoDB( "createIndex tool when search is enabled", (integration) => { it("allows creating vector search indexes", async () => { - expect(integration.mcpServer().userConfig.voyageApiKey).not.toEqual(""); + expect(integration.mcpServer().userConfig.previewFeatures).includes("vectorSearch"); const { tools } = await integration.mcpClient().listTools(); const createIndexTool = tools.find((tool) => tool.name === "create-index"); @@ -84,7 +84,7 @@ describeWithMongoDB( getUserConfig: () => { return { ...defaultTestConfig, - voyageApiKey: "valid_key", + previewFeatures: ["vectorSearch"], }; }, } @@ -392,7 +392,7 @@ describeWithMongoDB( getUserConfig: () => { return { ...defaultTestConfig, - voyageApiKey: "valid_key", + previewFeatures: ["vectorSearch"], }; }, } @@ -613,7 +613,7 @@ describeWithMongoDB( { getUserConfig: () => ({ ...defaultTestConfig, - voyageApiKey: "valid_key", + previewFeatures: ["vectorSearch"], }), downloadOptions: { search: true, diff --git a/tests/integration/tools/mongodb/delete/dropIndex.test.ts b/tests/integration/tools/mongodb/delete/dropIndex.test.ts index e18f260cf..9bee2457e 100644 --- a/tests/integration/tools/mongodb/delete/dropIndex.test.ts +++ b/tests/integration/tools/mongodb/delete/dropIndex.test.ts @@ -157,7 +157,7 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])( { getUserConfig: () => ({ ...defaultTestConfig, - voyageApiKey: vectorSearchEnabled ? "test-api-key" : "", + previewFeatures: vectorSearchEnabled ? ["vectorSearch"] : [], }), } ); @@ -243,7 +243,7 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])( { getUserConfig: () => ({ ...defaultTestConfig, - voyageApiKey: vectorSearchEnabled ? "test-api-key" : "", + previewFeatures: vectorSearchEnabled ? ["vectorSearch"] : [], }), } ); @@ -310,7 +310,7 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])( { getUserConfig: () => ({ ...defaultTestConfig, - voyageApiKey: vectorSearchEnabled ? "test-api-key" : "", + previewFeatures: vectorSearchEnabled ? ["vectorSearch"] : [], }), getMockElicitationInput: () => mockElicitInput, } @@ -334,7 +334,7 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])( }); }, { - getUserConfig: () => ({ ...defaultTestConfig, voyageApiKey: "test-api-key" }), + getUserConfig: () => ({ ...defaultTestConfig, previewFeatures: ["vectorSearch"] }), } ); @@ -408,7 +408,7 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])( }); }, { - getUserConfig: () => ({ ...defaultTestConfig, voyageApiKey: "test-api-key" }), + getUserConfig: () => ({ ...defaultTestConfig, previewFeatures: ["vectorSearch"] }), downloadOptions: { search: true }, } ); @@ -484,7 +484,7 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])( }); }, { - getUserConfig: () => ({ ...defaultTestConfig, voyageApiKey: "test-api-key" }), + getUserConfig: () => ({ ...defaultTestConfig, previewFeatures: ["vectorSearch"] }), downloadOptions: { search: true }, getMockElicitationInput: () => mockElicitInput, } diff --git a/tests/integration/tools/mongodb/metadata/collectionIndexes.test.ts b/tests/integration/tools/mongodb/metadata/collectionIndexes.test.ts index 868d8d0a1..59a801055 100644 --- a/tests/integration/tools/mongodb/metadata/collectionIndexes.test.ts +++ b/tests/integration/tools/mongodb/metadata/collectionIndexes.test.ts @@ -315,7 +315,7 @@ describeWithMongoDB( { getUserConfig: () => ({ ...defaultTestConfig, - voyageApiKey: "valid_key", + previewFeatures: ["vectorSearch"], }), downloadOptions: { search: true }, } diff --git a/tests/integration/tools/mongodb/read/aggregate.test.ts b/tests/integration/tools/mongodb/read/aggregate.test.ts index d71ab4d91..83e0470c0 100644 --- a/tests/integration/tools/mongodb/read/aggregate.test.ts +++ b/tests/integration/tools/mongodb/read/aggregate.test.ts @@ -672,6 +672,7 @@ describeWithMongoDB( getUserConfig: () => ({ ...defaultTestConfig, voyageApiKey: process.env.TEST_MDB_MCP_VOYAGE_API_KEY ?? "", + previewFeatures: ["vectorSearch"], maxDocumentsPerQuery: -1, maxBytesPerQuery: -1, }), From 1162d4c4e36d3433dc93111a73e74db1308fd489 Mon Sep 17 00:00:00 2001 From: nirinchev Date: Fri, 24 Oct 2025 15:00:34 +0200 Subject: [PATCH 2/4] fix readme --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 793e9c8b0..9c0675738 100644 --- a/README.md +++ b/README.md @@ -369,7 +369,7 @@ The MongoDB MCP Server can be configured using multiple methods, with the follow | `exportCleanupIntervalMs` | `MDB_MCP_EXPORT_CLEANUP_INTERVAL_MS` | 120000 | Time in milliseconds between export cleanup cycles that remove expired export files. | | `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. | | `voyageApiKey` | `MDB_VOYAGE_API_KEY` | | API key for communicating with Voyage AI. Used for generating embeddings for Vector search. | -| `previewFeatures` | `MDB_MCP_PREVIEW_FEATURES` | `[]` | An array of preview features to opt into. | +| `previewFeatures` | `MDB_MCP_PREVIEW_FEATURES` | `[]` | An array of preview features to opt into. | #### Logger Options @@ -495,10 +495,11 @@ You can disable telemetry using: 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. -- For **environment variable** configuration, use a comma-separated string: `export MDB_MCP_PREVIEW_FEATURES="vectorSearch,dedicatedClusters"`. -- For **command-line argument** configuration, use a space-separated string: `--previewFeatures vectorSearch dedicatedClusters`. +- For **environment variable** configuration, use a comma-separated string: `export MDB_MCP_PREVIEW_FEATURES="vectorSearch,feature1,feature2"`. +- For **command-line argument** configuration, use a space-separated string: `--previewFeatures vectorSearch feature1 feature2`. List of available preview features: + - `vectorSearch` - Enables tools or functionality related to Vector Search in MongoDB Atlas: - Index management, such as creating, listing, and dropping vector search indexes. - Querying collections using vector search capabilities. This requires a configured embedding model that will be used to generate vector representations of the query data. From 150a59eb9ff815d9fe0fb6f3ad8d01239f783104 Mon Sep 17 00:00:00 2001 From: nirinchev Date: Mon, 27 Oct 2025 13:14:46 +0100 Subject: [PATCH 3/4] add unit tests --- src/common/config.ts | 2 +- src/tools/tool.ts | 6 +++--- tests/unit/toolBase.test.ts | 18 +++++++++++++++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index e1ee13c0a..ba7c14eb8 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -11,7 +11,7 @@ import { z } from "zod"; const levenshtein = levenshteinModule.default; const previewFeatures = z.enum(["vectorSearch"]); -export type PreviewFeatures = z.infer; +export type PreviewFeature = z.infer; // From: https://github.com/mongodb-js/mongosh/blob/main/packages/cli-repl/src/arg-parser.ts export const OPTIONS = { diff --git a/src/tools/tool.ts b/src/tools/tool.ts index 97934250a..ec9f01a61 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -6,7 +6,7 @@ import type { Session } from "../common/session.js"; import { LogId } from "../common/logger.js"; import type { Telemetry } from "../telemetry/telemetry.js"; import { type ToolEvent } from "../telemetry/types.js"; -import type { PreviewFeatures, UserConfig } from "../common/config.js"; +import type { PreviewFeature, UserConfig } from "../common/config.js"; import type { Server } from "../server.js"; import type { Elicitation } from "../elicitation.js"; @@ -321,8 +321,8 @@ export abstract class ToolBase { this.telemetry.emitEvents([event]); } - protected isFeatureEnabled(flag: PreviewFeatures): boolean { - return this.config.previewFeatures.includes(flag); + protected isFeatureEnabled(feature: PreviewFeature): boolean { + return this.config.previewFeatures.includes(feature); } } diff --git a/tests/unit/toolBase.test.ts b/tests/unit/toolBase.test.ts index 0e7d958c8..984aa5bfb 100644 --- a/tests/unit/toolBase.test.ts +++ b/tests/unit/toolBase.test.ts @@ -3,7 +3,7 @@ import { z } from "zod"; import { ToolBase, type OperationType, type ToolCategory, type ToolConstructorParams } from "../../src/tools/tool.js"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import type { Session } from "../../src/common/session.js"; -import type { UserConfig } from "../../src/common/config.js"; +import type { PreviewFeature, UserConfig } from "../../src/common/config.js"; import type { Telemetry } from "../../src/telemetry/telemetry.js"; import type { Elicitation } from "../../src/elicitation.js"; import type { CompositeLogger } from "../../src/common/logger.js"; @@ -32,6 +32,7 @@ describe("ToolBase", () => { mockConfig = { confirmationRequiredTools: [], + previewFeatures: [], } as unknown as UserConfig; mockTelemetry = {} as Telemetry; @@ -100,6 +101,21 @@ describe("ToolBase", () => { expect(mockRequestConfirmation).toHaveBeenCalledTimes(1); }); }); + + describe("isFeatureEnabled", () => { + it("should return false for any feature by default", () => { + expect(testTool["isFeatureEnabled"]("vectorSearch")).to.equal(false); + expect(testTool["isFeatureEnabled"]("someOtherFeature" as PreviewFeature)).to.equal(false); + }); + + it("should return true for enabled features", () => { + mockConfig.previewFeatures = ["vectorSearch", "someOtherFeature" as PreviewFeature]; + expect(testTool["isFeatureEnabled"]("vectorSearch")).to.equal(true); + expect(testTool["isFeatureEnabled"]("someOtherFeature" as PreviewFeature)).to.equal(true); + + expect(testTool["isFeatureEnabled"]("anotherFeature" as PreviewFeature)).to.equal(false); + }); + }); }); class TestTool extends ToolBase { From ec6348bf36278ddc87b3f692bf1633dea9b31f30 Mon Sep 17 00:00:00 2001 From: nirinchev Date: Mon, 27 Oct 2025 13:44:49 +0100 Subject: [PATCH 4/4] as -> satisfies --- src/common/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index ba7c14eb8..03bcddf8c 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -559,13 +559,13 @@ export function setupUserConfig({ }: { cli: string[]; env: Record; - defaults: Partial; + defaults: UserConfig; }): UserConfig { const userConfig = { ...defaults, ...parseEnvConfig(env), ...parseCliConfig(cli), - } as UserConfig; + } satisfies UserConfig; userConfig.disabledTools = commaSeparatedToArray(userConfig.disabledTools); userConfig.loggers = commaSeparatedToArray(userConfig.loggers);