Skip to content

Commit 132876e

Browse files
committed
chore: use custom overrides
1 parent 0f2a594 commit 132876e

File tree

5 files changed

+48
-40
lines changed

5 files changed

+48
-40
lines changed

src/common/config/configOverrides.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,15 @@ function applyOverride(
132132
behavior: OverrideBehavior
133133
): unknown {
134134
if (typeof behavior === "function") {
135-
const shouldApply = behavior(baseValue, overrideValue);
136-
if (!shouldApply) {
135+
// Custom logic function returns the value to use (potentially transformed)
136+
// or throws an error if the override cannot be applied
137+
try {
138+
return behavior(baseValue, overrideValue);
139+
} catch (error) {
137140
throw new Error(
138-
`Config override validation failed for ${key}: cannot override from ${JSON.stringify(baseValue)} to ${JSON.stringify(overrideValue)}`
141+
`Cannot apply override for ${key} from ${JSON.stringify(baseValue)} to ${JSON.stringify(overrideValue)}: ${error instanceof Error ? error.message : String(error)}`
139142
);
140143
}
141-
return overrideValue;
142144
}
143145
switch (behavior) {
144146
case "override":

src/common/config/configUtils.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import * as levenshteinModule from "ts-levenshtein";
55
const levenshtein = levenshteinModule.default;
66

77
/// Custom logic function to apply the override value.
8-
/// Returns true if the override should be applied, false otherwise.
9-
export type CustomOverrideLogic = (oldValue: unknown, newValue: unknown) => boolean;
8+
/// Returns the value to use (which may be transformed from newValue).
9+
/// Should throw an error if the override cannot be applied.
10+
export type CustomOverrideLogic = (oldValue: unknown, newValue: unknown) => unknown;
1011

1112
/**
1213
* Defines how a config field can be overridden via HTTP headers or query parameters.

src/common/config/userConfig.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ export const UserConfigSchema = z4.object({
8989
overrideBehavior: (oldValue, newValue) => {
9090
// Only allow override if setting to true from false
9191
if (oldValue === false && newValue === true) {
92-
return true;
92+
return newValue;
9393
}
94-
return false;
94+
throw new Error("Cannot disable readOnly mode");
9595
},
9696
}),
9797
indexCheck: z4
@@ -103,10 +103,10 @@ export const UserConfigSchema = z4.object({
103103
.register(configRegistry, {
104104
overrideBehavior: (oldValue, newValue) => {
105105
// Only allow override if setting to true from false
106-
if (oldValue === false && newValue === true) {
107-
return true;
106+
if (newValue === true) {
107+
return newValue;
108108
}
109-
return false;
109+
throw new Error("Cannot disable indexCheck mode");
110110
},
111111
}),
112112
telemetry: z4
@@ -200,10 +200,10 @@ export const UserConfigSchema = z4.object({
200200
.register(configRegistry, {
201201
overrideBehavior: (oldValue, newValue) => {
202202
// Only allow override if setting to false from true (making more restrictive)
203-
if (oldValue === true && newValue === false) {
204-
return true;
203+
if (newValue === false) {
204+
return newValue;
205205
}
206-
return false;
206+
throw new Error("Cannot disable disableEmbeddingsValidation");
207207
},
208208
}),
209209
vectorSearchDimensions: z4.coerce

tests/integration/transports/configOverrides.test.ts

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -68,27 +68,6 @@ describe("Config Overrides via HTTP", () => {
6868
expect(readTools.length).toBe(1);
6969
});
7070

71-
it("should NOT override readOnly from true to false", async () => {
72-
await startRunner({
73-
...defaultTestConfig,
74-
httpPort: 0,
75-
readOnly: true,
76-
});
77-
78-
try {
79-
await connectClient({
80-
["x-mongodb-mcp-read-only"]: "false",
81-
});
82-
expect.fail("Expected an error to be thrown");
83-
} catch (error) {
84-
if (!(error instanceof Error)) {
85-
throw new Error("Expected an error to be thrown");
86-
}
87-
expect(error.message).toContain("Error POSTing to endpoint (HTTP 400)");
88-
expect(error.message).toContain(`Config override validation failed for readOnly`);
89-
}
90-
});
91-
9271
it("should override connectionString via header", async () => {
9372
await startRunner({
9473
...defaultTestConfig,
@@ -350,7 +329,9 @@ describe("Config Overrides via HTTP", () => {
350329
throw new Error("Expected an error to be thrown");
351330
}
352331
expect(error.message).toContain("Error POSTing to endpoint (HTTP 400)");
353-
expect(error.message).toContain(`Config override validation failed for readOnly`);
332+
expect(error.message).toContain(
333+
`Cannot apply override for readOnly from true to false: Cannot disable readOnly mode`
334+
);
354335
}
355336
});
356337
});

tests/unit/common/config/configOverrides.test.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,28 @@ describe("configOverrides", () => {
141141
});
142142

143143
describe("not-allowed behavior", () => {
144+
it("shoud have some not-allowed fields", () => {
145+
expect(
146+
Object.keys(UserConfigSchema.shape).filter(
147+
(key) => getConfigMeta(key as any)?.overrideBehavior === "not-allowed"
148+
)
149+
).toEqual([
150+
"apiBaseUrl",
151+
"apiClientId",
152+
"apiClientSecret",
153+
"logPath",
154+
"telemetry",
155+
"transport",
156+
"httpPort",
157+
"httpHost",
158+
"httpHeaders",
159+
"maxBytesPerQuery",
160+
"maxDocumentsPerQuery",
161+
"exportsPath",
162+
"voyageApiKey",
163+
]);
164+
});
165+
144166
it("should throw an error for not-allowed fields", () => {
145167
const request: RequestContext = {
146168
headers: {
@@ -157,7 +179,7 @@ describe("configOverrides", () => {
157179
});
158180
});
159181

160-
describe("conditional overrides", () => {
182+
describe("custom overrides", () => {
161183
it("should have certain config keys to be conditionally overridden", () => {
162184
expect(
163185
Object.keys(UserConfigSchema.shape)
@@ -180,7 +202,7 @@ describe("configOverrides", () => {
180202
const request: RequestContext = { headers: { "x-mongodb-mcp-read-only": "false" } };
181203
expect(() =>
182204
applyConfigOverrides({ baseConfig: { ...baseConfig, readOnly: true } as UserConfig, request })
183-
).toThrow("Config override validation failed for readOnly");
205+
).toThrow("Cannot apply override for readOnly from true to false: Cannot disable readOnly mode");
184206
});
185207

186208
it("should allow indexCheck override from false to true", () => {
@@ -196,7 +218,7 @@ describe("configOverrides", () => {
196218
const request: RequestContext = { headers: { "x-mongodb-mcp-index-check": "false" } };
197219
expect(() =>
198220
applyConfigOverrides({ baseConfig: { ...baseConfig, indexCheck: true } as UserConfig, request })
199-
).toThrow("Config override validation failed for indexCheck");
221+
).toThrow("Cannot apply override for indexCheck from true to false: Cannot disable indexCheck mode");
200222
});
201223

202224
it("should allow disableEmbeddingsValidation override from true to false", () => {
@@ -215,7 +237,9 @@ describe("configOverrides", () => {
215237
baseConfig: { ...baseConfig, disableEmbeddingsValidation: false } as UserConfig,
216238
request,
217239
})
218-
).toThrow("Config override validation failed for disableEmbeddingsValidation");
240+
).toThrow(
241+
"Cannot apply override for disableEmbeddingsValidation from false to true: Cannot disable disableEmbeddingsValidation"
242+
);
219243
});
220244
});
221245

0 commit comments

Comments
 (0)