• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mongodb-js / mongodb-mcp-server / 19933887292

04 Dec 2025 03:15PM UTC coverage: 80.313% (+0.5%) from 79.814%
19933887292

Pull #782

github

web-flow
Merge 90f90b1c5 into 7e32ef14a
Pull Request #782: chore: add support for loading config from file MCP-327

1454 of 1894 branches covered (76.77%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 2 files covered. (100.0%)

2 existing lines in 1 file now uncovered.

6656 of 8204 relevant lines covered (81.13%)

80.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.91
/src/common/config/configOverrides.ts
1
import type { UserConfig } from "./userConfig.js";
2✔
2
import { UserConfigSchema, configRegistry } from "./userConfig.js";
1✔
3
import type { RequestContext } from "../../transports/base.js";
4
import type { ConfigFieldMeta, OverrideBehavior } from "./configUtils.js";
5

6
export const CONFIG_HEADER_PREFIX = "x-mongodb-mcp-";
1✔
7
export const CONFIG_QUERY_PREFIX = "mongodbMcp";
1✔
8

9
/**
10
 * Applies config overrides from request context (headers and query parameters).
11
 * Query parameters take precedence over headers. Can be used within the createSessionConfig
12
 * hook to manually apply the overrides. Requires `allowRequestOverrides` to be enabled.
13
 *
14
 * @param baseConfig - The base user configuration
15
 * @param request - The request context containing headers and query parameters
16
 * @returns The configuration with overrides applied
17
 */
18
export function applyConfigOverrides({
1✔
19
    baseConfig,
71✔
20
    request,
71✔
21
}: {
71✔
22
    baseConfig: UserConfig;
23
    request?: RequestContext;
24
}): UserConfig {
71✔
25
    if (!request) {
71✔
26
        return baseConfig;
5✔
27
    }
5✔
28

29
    const result: UserConfig = { ...baseConfig };
66✔
30
    const overridesFromHeaders = extractConfigOverrides("header", request.headers);
66✔
31
    const overridesFromQuery = extractConfigOverrides("query", request.query);
66✔
32

33
    // Only apply overrides if allowRequestOverrides is enabled
34
    if (
66✔
35
        !baseConfig.allowRequestOverrides &&
66✔
36
        (Object.keys(overridesFromHeaders).length > 0 || Object.keys(overridesFromQuery).length > 0)
9✔
37
    ) {
71✔
38
        throw new Error("Request overrides are not enabled");
3✔
39
    }
3✔
40

41
    // Apply header overrides first
42
    for (const [key, overrideValue] of Object.entries(overridesFromHeaders)) {
68✔
43
        assertValidConfigKey(key);
51✔
44
        const meta = getConfigMeta(key);
51✔
45
        const behavior = meta?.overrideBehavior || "not-allowed";
51!
46
        const baseValue = baseConfig[key];
51✔
47
        const newValue = applyOverride(key, baseValue, overrideValue, behavior);
51✔
48
        (result as Record<keyof UserConfig, unknown>)[key] = newValue;
51✔
49
    }
51✔
50

51
    // Apply query overrides (with precedence), but block secret fields
52
    for (const [key, overrideValue] of Object.entries(overridesFromQuery)) {
68✔
53
        assertValidConfigKey(key);
5✔
54
        const meta = getConfigMeta(key);
5✔
55

56
        // Prevent overriding secret fields via query params
57
        if (meta?.isSecret) {
5!
UNCOV
58
            throw new Error(`Config key ${key} can only be overriden with headers.`);
×
UNCOV
59
        }
×
60

61
        const behavior = meta?.overrideBehavior || "not-allowed";
5!
62
        const baseValue = baseConfig[key];
5✔
63
        const newValue = applyOverride(key, baseValue, overrideValue, behavior);
5✔
64
        (result as Record<keyof UserConfig, unknown>)[key] = newValue;
5✔
65
    }
5✔
66

67
    return result;
33✔
68
}
33✔
69

70
/**
71
 * Extracts config overrides from HTTP headers or query parameters.
72
 */
73
function extractConfigOverrides(
124✔
74
    mode: "header" | "query",
124✔
75
    source: Record<string, string | string[] | undefined> | undefined
124✔
76
): Partial<Record<keyof typeof UserConfigSchema.shape, unknown>> {
124✔
77
    if (!source) {
124✔
78
        return {};
29✔
79
    }
29✔
80

81
    const overrides: Partial<Record<keyof typeof UserConfigSchema.shape, unknown>> = {};
95✔
82

83
    for (const [name, value] of Object.entries(source)) {
124✔
84
        const configKey = nameToConfigKey(mode, name);
331✔
85
        if (!configKey) {
331✔
86
            continue;
257✔
87
        }
257✔
88
        assertValidConfigKey(configKey);
74✔
89

90
        const parsedValue = parseConfigValue(configKey, value);
74✔
91
        if (parsedValue !== undefined) {
74✔
92
            overrides[configKey] = parsedValue;
66✔
93
        }
66✔
94
    }
331✔
95

96
    return overrides;
87✔
97
}
87✔
98

99
function assertValidConfigKey(key: string): asserts key is keyof typeof UserConfigSchema.shape {
130✔
100
    if (!(key in UserConfigSchema.shape)) {
130!
101
        throw new Error(`Invalid config key: ${key}`);
×
102
    }
×
103
}
130✔
104

105
/**
106
 * Gets the schema metadata for a config key.
107
 */
108
export function getConfigMeta(key: keyof typeof UserConfigSchema.shape): ConfigFieldMeta | undefined {
1✔
109
    return configRegistry.get(UserConfigSchema.shape[key]);
360✔
110
}
360✔
111

112
/**
113
 * Parses a string value to the appropriate type using the Zod schema.
114
 */
115
function parseConfigValue(key: keyof typeof UserConfigSchema.shape, value: unknown): unknown {
74✔
116
    const fieldSchema = UserConfigSchema.shape[key];
74✔
117
    if (!fieldSchema) {
74!
118
        throw new Error(`Invalid config key: ${key}`);
×
119
    }
×
120

121
    const result = fieldSchema.safeParse(value);
74✔
122
    if (!result.success) {
74✔
123
        throw new Error(
1✔
124
            `Invalid configuration for the following fields:\n${result.error.issues.map((issue) => `${key} - ${issue.message}`).join("\n")}`
1✔
125
        );
1✔
126
    }
1✔
127
    return result.data;
66✔
128
}
66✔
129

130
/**
131
 * Converts a header/query name to its config key format.
132
 * Example: "x-mongodb-mcp-read-only" -> "readOnly"
133
 * Example: "mongodbMcpReadOnly" -> "readOnly"
134
 */
135
export function nameToConfigKey(mode: "header" | "query", name: string): string | undefined {
1✔
136
    const lowerCaseName = name.toLowerCase();
342✔
137

138
    if (mode === "header" && lowerCaseName.startsWith(CONFIG_HEADER_PREFIX)) {
342✔
139
        const normalized = lowerCaseName.substring(CONFIG_HEADER_PREFIX.length);
72✔
140
        // Convert kebab-case to camelCase
141
        return normalized.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase());
72✔
142
    }
72✔
143
    if (mode === "query" && name.startsWith(CONFIG_QUERY_PREFIX)) {
342✔
144
        const withoutPrefix = name.substring(CONFIG_QUERY_PREFIX.length);
8✔
145
        // Convert first letter to lowercase to get config key
146
        return withoutPrefix.charAt(0).toLowerCase() + withoutPrefix.slice(1);
8✔
147
    }
8✔
148

149
    return undefined;
262✔
150
}
262✔
151

152
function applyOverride(
56✔
153
    key: keyof typeof UserConfigSchema.shape,
56✔
154
    baseValue: unknown,
56✔
155
    overrideValue: unknown,
56✔
156
    behavior: OverrideBehavior
56✔
157
): unknown {
56✔
158
    if (typeof behavior === "function") {
56✔
159
        // Custom logic function returns the value to use (potentially transformed)
160
        // or throws an error if the override cannot be applied
161
        try {
29✔
162
            return behavior(baseValue, overrideValue);
29✔
163
        } catch (error) {
29✔
164
            throw new Error(
7✔
165
                `Cannot apply override for ${key}: ${error instanceof Error ? error.message : String(error)}`
7!
166
            );
7✔
167
        }
7✔
168
    }
29✔
169
    switch (behavior) {
27✔
170
        case "override":
56✔
171
            return overrideValue;
1✔
172

173
        case "merge":
56✔
174
            if (Array.isArray(baseValue) && Array.isArray(overrideValue)) {
11✔
175
                return [...(baseValue as unknown[]), ...(overrideValue as unknown[])];
11✔
176
            }
11!
177
            throw new Error(`Cannot merge non-array values for ${key}`);
×
178

179
        case "not-allowed":
56✔
180
            throw new Error(`Config key ${key} is not allowed to be overridden`);
15✔
181
        default:
56!
182
            return baseValue;
×
183
    }
56✔
184
}
56✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc