Skip to content

Commit adfe4b5

Browse files
authored
chore: support schema overrides and custom parserOptions MCP-338 (#800)
1 parent cced0c7 commit adfe4b5

File tree

7 files changed

+163
-85
lines changed

7 files changed

+163
-85
lines changed

MCP_SERVER_LIBRARY.md

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import {
4949
Session,
5050
UserConfig,
5151
UserConfigSchema,
52-
parseCliArgumentsAsUserConfig,
52+
parseUserConfig,
5353
StreamableHttpRunner,
5454
StdioRunner,
5555
TransportRunnerBase,
@@ -811,26 +811,30 @@ const customConfig = UserConfigSchema.parse({
811811

812812
This approach ensures you get all the default values without having to specify every configuration key manually.
813813

814-
### parseCliArgumentsAsUserConfig
814+
### parseUserConfig
815815

816816
Utility function to parse command-line arguments and environment variables into a UserConfig object, using the same parsing logic as the MongoDB MCP server CLI.
817817

818818
_Note: This is what MongoDB MCP server uses internally._
819819

820-
```typescript
821-
function parseCliArgumentsAsUserConfig(options?: {
822-
args?: string[];
823-
helpers?: CreateUserConfigHelpers;
824-
}): UserConfig;
825-
```
826-
827820
**Example:**
828821

829822
```typescript
830-
import { parseCliArgumentsAsUserConfig, StdioRunner } from "mongodb-mcp-server";
823+
import {
824+
parseUserConfig,
825+
StdioRunner,
826+
UserConfigSchema,
827+
} from "mongodb-mcp-server";
831828

832829
// Parse config from process.argv and environment variables
833-
const config = parseCliArgumentsAsUserConfig();
830+
const config = parseUserConfig({
831+
args: process.argv.slice(2),
832+
// You can optionally specify overrides for the config
833+
// This can be used, for example, to set new defaults.
834+
overrides: {
835+
readOnly: UserConfigSchema.shape.readOnly.default(true),
836+
},
837+
});
834838

835839
const runner = new StdioRunner({ userConfig: config });
836840
await runner.start();

eslint-rules/enforce-zod-v4.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import path from "path";
44
// The file that is allowed to import from zod/v4
55
const allowedFilePaths = [
66
path.resolve(import.meta.dirname, "../src/common/config/userConfig.ts"),
7-
path.resolve(import.meta.dirname, "../src/common/config/createUserConfig.ts"),
7+
path.resolve(import.meta.dirname, "../src/common/config/parseUserConfig.ts"),
88
];
99

1010
// Ref: https://eslint.org/docs/latest/extend/custom-rules
Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,51 @@ import type { Secret } from "../keychain.js";
44
import { matchingConfigKey } from "./configUtils.js";
55
import { UserConfigSchema, type UserConfig } from "./userConfig.js";
66
import {
7-
defaultParserOptions,
7+
defaultParserOptions as defaultArgParserOptions,
88
parseArgsWithCliOptions,
99
CliOptionsSchema,
1010
UnknownArgumentError,
1111
} from "@mongosh/arg-parser/arg-parser";
12-
import type { z as z4 } from "zod/v4";
13-
14-
export function createUserConfig({ args }: { args: string[] }): {
12+
import { z as z4 } from "zod/v4";
13+
14+
export type ParserOptions = typeof defaultArgParserOptions;
15+
16+
export const defaultParserOptions = {
17+
// This is the name of key that yargs-parser will look up in CLI
18+
// arguments (--config) and ENV variables (MDB_MCP_CONFIG) to load an
19+
// initial configuration from.
20+
config: "config",
21+
// This helps parse the relevant environment variables.
22+
envPrefix: "MDB_MCP_",
23+
configuration: {
24+
...defaultArgParserOptions.configuration,
25+
// To avoid populating `_` with end-of-flag arguments we explicitly
26+
// populate `--` variable and altogether ignore them later.
27+
"populate--": true,
28+
},
29+
} satisfies ParserOptions;
30+
31+
export function parseUserConfig({
32+
args,
33+
overrides,
34+
parserOptions = defaultParserOptions,
35+
}: {
36+
args: string[];
37+
overrides?: z4.ZodRawShape;
38+
parserOptions?: ParserOptions;
39+
}): {
1540
warnings: string[];
1641
parsed: UserConfig | undefined;
1742
error: string | undefined;
1843
} {
19-
const { error: parseError, warnings, parsed } = parseUserConfigSources(args);
44+
const schema = overrides
45+
? z4.object({
46+
...UserConfigSchema.shape,
47+
...overrides,
48+
})
49+
: UserConfigSchema;
50+
51+
const { error: parseError, warnings, parsed } = parseUserConfigSources({ args, schema, parserOptions });
2052

2153
if (parseError) {
2254
return { error: parseError, warnings, parsed: undefined };
@@ -40,7 +72,7 @@ export function createUserConfig({ args }: { args: string[] }): {
4072
parsed.connectionString = connectionInfo.connectionString;
4173
}
4274

43-
const configParseResult = UserConfigSchema.safeParse(parsed);
75+
const configParseResult = schema.safeParse(parsed);
4476
const mongoshArguments = CliOptionsSchema.safeParse(parsed);
4577
const error = configParseResult.error || mongoshArguments.error;
4678
if (error) {
@@ -62,34 +94,29 @@ export function createUserConfig({ args }: { args: string[] }): {
6294
};
6395
}
6496

65-
function parseUserConfigSources(cliArguments: string[]): {
97+
function parseUserConfigSources<T extends typeof UserConfigSchema>({
98+
args,
99+
schema = UserConfigSchema as T,
100+
parserOptions,
101+
}: {
102+
args: string[];
103+
schema: T;
104+
parserOptions: ParserOptions;
105+
}): {
66106
error: string | undefined;
67107
warnings: string[];
68-
parsed: Partial<CliOptions & z4.infer<typeof UserConfigSchema>>;
108+
parsed: Partial<CliOptions & z4.infer<T>>;
69109
} {
70-
let parsed: Partial<CliOptions & z4.infer<typeof UserConfigSchema>>;
71-
let deprecated: Record<string, keyof UserConfig>;
110+
let parsed: Partial<CliOptions & z4.infer<T>>;
111+
let deprecated: Record<string, string>;
72112
try {
73113
const { parsed: parsedResult, deprecated: deprecatedResult } = parseArgsWithCliOptions({
74-
args: cliArguments,
75-
schema: UserConfigSchema,
76-
parserOptions: {
77-
// This is the name of key that yargs-parser will look up in CLI
78-
// arguments (--config) and ENV variables (MDB_MCP_CONFIG) to load an
79-
// initial configuration from.
80-
config: "config",
81-
// This helps parse the relevant environment variables.
82-
envPrefix: "MDB_MCP_",
83-
configuration: {
84-
...defaultParserOptions.configuration,
85-
// To avoid populating `_` with end-of-flag arguments we explicitly
86-
// populate `--` variable and altogether ignore them later.
87-
"populate--": true,
88-
},
89-
},
114+
args,
115+
schema,
116+
parserOptions,
90117
});
91118
parsed = parsedResult;
92-
deprecated = deprecatedResult;
119+
deprecated = deprecatedResult as Record<string, string>;
93120

94121
// Delete fileNames - this is a field populated by mongosh but not used by us.
95122
delete parsed.fileNames;
@@ -112,7 +139,7 @@ function parseUserConfigSources(cliArguments: string[]): {
112139
}
113140

114141
const deprecationWarnings = [
115-
...getWarnings(parsed, cliArguments),
142+
...getWarnings(parsed, args),
116143
...Object.entries(deprecated).map(([deprecated, replacement]) => {
117144
return `Warning: The --${deprecated} argument is deprecated. Use --${replacement} instead.`;
118145
}),

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ enableFipsIfRequested();
3737

3838
import crypto from "crypto";
3939
import { ConsoleLogger, LogId } from "./common/logger.js";
40-
import { createUserConfig } from "./common/config/createUserConfig.js";
40+
import { parseUserConfig } from "./common/config/parseUserConfig.js";
4141
import { type UserConfig } from "./common/config/userConfig.js";
4242
import { packageInfo } from "./common/packageInfo.js";
4343
import { StdioRunner } from "./transports/stdio.js";
@@ -53,7 +53,7 @@ async function main(): Promise<void> {
5353
error,
5454
warnings,
5555
parsed: config,
56-
} = createUserConfig({
56+
} = parseUserConfig({
5757
args: process.argv.slice(2),
5858
});
5959

src/lib.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
export { Server, type ServerOptions } from "./server.js";
22
export { Session, type SessionOptions } from "./common/session.js";
33
export { type UserConfig, UserConfigSchema } from "./common/config/userConfig.js";
4-
export { createUserConfig as parseCliArgumentsAsUserConfig } from "./common/config/createUserConfig.js";
4+
export { parseUserConfig, defaultParserOptions, type ParserOptions } from "./common/config/parseUserConfig.js";
5+
6+
import { parseUserConfig } from "./common/config/parseUserConfig.js";
7+
import type { UserConfig } from "./common/config/userConfig.js";
8+
9+
/** @deprecated Use `parseUserConfig` instead. */
10+
export function parseArgsWithCliOptions(cliArguments: string[]): {
11+
warnings: string[];
12+
parsed: UserConfig | undefined;
13+
error: string | undefined;
14+
} {
15+
return parseUserConfig({
16+
args: cliArguments,
17+
});
18+
}
19+
520
export { LoggerBase, type LogPayload, type LoggerType, type LogLevel } from "./common/logger.js";
621
export { StreamableHttpRunner } from "./transports/streamableHttp.js";
722
export { StdioRunner } from "./transports/stdio.js";

src/transports/base.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ export type TransportRunnerConfig = {
7474
* `UserConfig`.
7575
*
7676
* To parse CLI arguments and environment variables in order to generate a
77-
* `UserConfig` object, you can use `createUserConfig` function, also
78-
* exported as `parseCliArgumentsAsUserConfig` through MCP server library
77+
* `UserConfig` object, you can use `parseUserConfig` function, also
78+
* exported as `parseUserConfig` through MCP server library
7979
* exports.
8080
*
8181
* Optionally, you can also use `UserConfigSchema` (available through MCP

0 commit comments

Comments
 (0)