-
Notifications
You must be signed in to change notification settings - Fork 246
Expand file tree
/
Copy pathuserConfig.ts
More file actions
310 lines (305 loc) · 13.9 KB
/
userConfig.ts
File metadata and controls
310 lines (305 loc) · 13.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
import { z as z4 } from "zod/v4";
import {
type ConfigFieldMeta,
commaSeparatedToArray,
getExportsPath,
getLogPath,
oneWayOverride,
onlyLowerThanBaseValueOverride,
onlySubsetOfBaseValueOverride,
parseBoolean,
} from "./configUtils.js";
import { monitoringServerFeatureValues, previewFeatureValues, similarityValues } from "../schemas.js";
import { argMetadata, CliOptionsSchema as MongoshCliOptionsSchema } from "@mongosh/arg-parser/arg-parser";
import { TRANSPORT_PAYLOAD_LIMITS } from "../../transports/constants.js";
export const configRegistry = z4.registry<ConfigFieldMeta>();
const ServerConfigSchema = z4.object({
apiBaseUrl: z4
.string()
.default("https://cloud.mongodb.com/")
.register(configRegistry, { overrideBehavior: "not-allowed" }),
assistantBaseUrl: z4
.string()
.default("https://knowledge.mongodb.com/api/v1/")
.describe("Base URL for the MongoDB Assistant API.")
.register(configRegistry, { overrideBehavior: "not-allowed" }),
apiClientId: z4
.string()
.optional()
.describe("Atlas API client ID for authentication. Required for running Atlas tools.")
.register(configRegistry, { isSecret: true, overrideBehavior: "not-allowed" }),
apiClientSecret: z4
.string()
.optional()
.describe("Atlas API client secret for authentication. Required for running Atlas tools.")
.register(configRegistry, { isSecret: true, overrideBehavior: "not-allowed" }),
connectionString: z4
.string()
.optional()
.describe(
"MongoDB connection string for direct database connections. Optional, if not set, you'll need to call the connect tool before interacting with MongoDB data."
)
.register(configRegistry, { isSecret: true, overrideBehavior: "not-allowed" }),
loggers: z4
.preprocess(
(val: string | string[] | undefined) => commaSeparatedToArray(val),
z4.array(z4.enum(["stderr", "disk", "mcp"]))
)
.check(
z4.minLength(1, "Cannot be an empty array"),
z4.refine((val) => new Set(val).size === val.length, {
message: "Duplicate loggers found in config",
})
)
.default(["disk", "mcp"])
.describe("An array of logger types.")
.register(configRegistry, {
defaultValueDescription: '`"disk,mcp"` see below*',
overrideBehavior: "not-allowed",
}),
logPath: z4
.string()
.default(getLogPath())
.describe("Folder to store logs.")
.register(configRegistry, { defaultValueDescription: "see below*", overrideBehavior: "not-allowed" }),
disabledTools: z4
.preprocess((val: string | string[] | undefined) => commaSeparatedToArray(val), z4.array(z4.string()))
.default([])
.describe("An array of tool names, operation types, and/or categories of tools that will be disabled.")
.register(configRegistry, { overrideBehavior: "merge" }),
confirmationRequiredTools: z4
.preprocess((val: string | string[] | undefined) => commaSeparatedToArray(val), z4.array(z4.string()))
.default([
"atlas-create-access-list",
"atlas-create-db-user",
"drop-database",
"drop-collection",
"delete-many",
"drop-index",
"atlas-streams-manage",
"atlas-streams-teardown",
])
.describe(
"An array of tool names that require user confirmation before execution. Requires the client to support elicitation."
)
.register(configRegistry, { overrideBehavior: "merge" }),
readOnly: z4
.preprocess(parseBoolean, z4.boolean())
.default(false)
.describe(
"When set to true, only allows read, connect, and metadata operation types, disabling create/update/delete operations."
)
.register(configRegistry, {
overrideBehavior: oneWayOverride(true),
}),
indexCheck: z4
.preprocess(parseBoolean, z4.boolean())
.default(false)
.describe(
"When set to true, enforces that query operations must use an index, rejecting queries that perform a collection scan."
)
.register(configRegistry, {
overrideBehavior: oneWayOverride(true),
}),
telemetry: z4
.enum(["enabled", "disabled"])
.default("enabled")
.describe("When set to disabled, disables telemetry collection.")
.register(configRegistry, { overrideBehavior: "not-allowed" }),
transport: z4
.enum(["stdio", "http"])
.default("stdio")
.describe("Either 'stdio' or 'http'.")
.register(configRegistry, { overrideBehavior: "not-allowed" }),
httpPort: z4.coerce
.number()
.int()
.min(0, "Invalid httpPort: must be at least 0")
.max(65535, "Invalid httpPort: must be at most 65535")
.default(3000)
.describe("Port number for the HTTP server (only used when transport is 'http'). Use 0 for a random port.")
.register(configRegistry, { overrideBehavior: "not-allowed" }),
httpHost: z4
.string()
.default("127.0.0.1")
.describe("Host address to bind the HTTP server to (only used when transport is 'http').")
.register(configRegistry, { overrideBehavior: "not-allowed" }),
httpHeaders: z4
.object({})
.loose()
.default({})
.describe(
"Header that the HTTP server will validate when making requests (only used when transport is 'http')."
)
.register(configRegistry, { overrideBehavior: "not-allowed" }),
httpBodyLimit: z4.coerce
.number()
.int()
.min(
TRANSPORT_PAYLOAD_LIMITS.http,
`Invalid httpBodyLimit: must be at least ${TRANSPORT_PAYLOAD_LIMITS.http} bytes`
)
.default(TRANSPORT_PAYLOAD_LIMITS.http)
.describe(
"Maximum size of the HTTP request body in bytes (only used when transport is 'http'). This value is passed as the optional limit parameter to the Express.js json() middleware."
)
.register(configRegistry, { overrideBehavior: "not-allowed" }),
idleTimeoutMs: z4.coerce
.number()
.default(600_000)
.describe("Idle timeout for a client to disconnect (only applies to http transport).")
.register(configRegistry, { overrideBehavior: onlyLowerThanBaseValueOverride() }),
notificationTimeoutMs: z4.coerce
.number()
.default(540_000)
.describe("Notification timeout for a client to be aware of disconnect (only applies to http transport).")
.register(configRegistry, { overrideBehavior: onlyLowerThanBaseValueOverride() }),
maxBytesPerQuery: z4.coerce
.number()
.default(16_777_216)
.describe(
"The maximum size in bytes for results from a find or aggregate tool call. This serves as an upper bound for the responseBytesLimit parameter in those tools."
)
.register(configRegistry, { overrideBehavior: "not-allowed" }),
maxDocumentsPerQuery: z4.coerce
.number()
.default(100)
.describe(
"The maximum number of documents that can be returned by a find or aggregate tool call. For the find tool, the effective limit will be the smaller of this value and the tool's limit parameter."
)
.register(configRegistry, { overrideBehavior: "not-allowed" }),
exportsPath: z4
.string()
.default(getExportsPath())
.describe("Folder to store exported data files.")
.register(configRegistry, { defaultValueDescription: "see below*", overrideBehavior: "not-allowed" }),
exportTimeoutMs: z4.coerce
.number()
.default(300_000)
.describe("Time in milliseconds after which an export is considered expired and eligible for cleanup.")
.register(configRegistry, { overrideBehavior: onlyLowerThanBaseValueOverride() }),
exportCleanupIntervalMs: z4.coerce
.number()
.default(120_000)
.describe("Time in milliseconds between export cleanup cycles that remove expired export files.")
.register(configRegistry, { overrideBehavior: "not-allowed" }),
atlasTemporaryDatabaseUserLifetimeMs: z4.coerce
.number()
.default(14_400_000)
.describe(
"Time in milliseconds that temporary database users created when connecting to MongoDB Atlas clusters will remain active before being automatically deleted."
)
.register(configRegistry, { overrideBehavior: onlyLowerThanBaseValueOverride() }),
voyageApiKey: z4
.string()
.default("")
.describe(
"API key for Voyage AI embeddings service (required for vector search operations with text-to-embedding conversion)."
)
.register(configRegistry, { isSecret: true, overrideBehavior: "not-allowed" }),
embeddingsValidation: z4
.preprocess(parseBoolean, z4.boolean())
.default(true)
.describe("When set to false, disables validation of embeddings dimensions.")
.register(configRegistry, { overrideBehavior: oneWayOverride(true) }),
vectorSearchDimensions: z4.coerce
.number()
.default(1024)
.describe("Default number of dimensions for vector search embeddings.")
.register(configRegistry, { overrideBehavior: "override" }),
vectorSearchSimilarityFunction: z4
.enum(similarityValues)
.default("euclidean")
.describe("Default similarity function for vector search: 'euclidean', 'cosine', or 'dotProduct'.")
.register(configRegistry, { overrideBehavior: "override" }),
previewFeatures: z4
.preprocess(
(val: string | string[] | undefined) => commaSeparatedToArray(val),
z4.array(z4.enum(previewFeatureValues))
)
.default([])
.describe("An array of preview features that are enabled.")
.register(configRegistry, { overrideBehavior: onlySubsetOfBaseValueOverride() }),
allowRequestOverrides: z4
.preprocess(parseBoolean, z4.boolean())
.default(false)
.describe(
"When set to true, allows configuration values to be overridden via request headers and query parameters."
)
.register(configRegistry, { overrideBehavior: "not-allowed" }),
dryRun: z4
.boolean()
.default(false)
.describe(
"When true, runs the server in dry mode: dumps configuration and enabled tools, then exits without starting the server."
)
.register(configRegistry, { overrideBehavior: "not-allowed" }),
externallyManagedSessions: z4
.boolean()
.default(false)
.describe(
"When true, the HTTP transport allows requests with a session ID supplied externally through the 'mcp-session-id' header. When an external ID is supplied, the initialization request is optional."
)
.register(configRegistry, { overrideBehavior: "not-allowed" }),
httpResponseType: z4
.enum(["sse", "json"])
.default("sse")
.describe(
"The HTTP response type for tool responses: 'sse' for Server-Sent Events, 'json' for standard JSON responses."
)
.register(configRegistry, { overrideBehavior: "not-allowed" }),
/** @deprecated Use `monitoringServerPort` instead. */
healthCheckPort: z4
.number()
.int()
.min(0, "Invalid healthCheckPort: must be at least 0")
.max(65535, "Invalid healthCheckPort: must be at most 65535")
.optional()
.describe(
"Deprecated. Use `monitoringServerPort` instead. Port number for the healthCheck HTTP server (only used when transport is 'http'). If provided, `healthCheckHost` must also be set."
)
.register(configRegistry, { overrideBehavior: "not-allowed" })
.register(argMetadata, { deprecationReplacement: "monitoringServerPort" }),
/** @deprecated Use `monitoringServerHost` instead. */
healthCheckHost: z4
.string()
.optional()
.describe(
"Deprecated. Use `monitoringServerHost` instead. Host address to bind the healthCheck HTTP server to (only used when transport is 'http'). If provided, `healthCheckPort` must also be set."
)
.register(configRegistry, { overrideBehavior: "not-allowed" })
.register(argMetadata, { deprecationReplacement: "monitoringServerHost" }),
monitoringServerPort: z4
.number()
.int()
.min(0, "Invalid monitoringServerPort: must be at least 0")
.max(65535, "Invalid monitoringServerPort: must be at most 65535")
.optional()
.describe(
"Port number for the monitoring HTTP server (only used when transport is 'http'). If provided, `monitoringServerHost` must also be set."
)
.register(configRegistry, { overrideBehavior: "not-allowed" }),
monitoringServerHost: z4
.string()
.optional()
.describe(
"Host address to bind the monitoring HTTP server to (only used when transport is 'http'). If provided, `monitoringServerPort` must also be set."
)
.register(configRegistry, { overrideBehavior: "not-allowed" }),
monitoringServerFeatures: z4
.preprocess(
(val: string | string[] | undefined) => commaSeparatedToArray(val),
z4.array(z4.enum(monitoringServerFeatureValues))
)
.default(["health-check"])
.describe(
"Features to expose on the monitoring server (only used when transport is 'http' and monitoringServerHost/monitoringServerPort are set)."
)
.register(configRegistry, { overrideBehavior: "not-allowed" }),
});
export const UserConfigSchema = z4.object({
...MongoshCliOptionsSchema.shape,
...ServerConfigSchema.shape,
});
export type UserConfig = z4.infer<typeof UserConfigSchema>;
export const ALL_CONFIG_KEYS = Object.keys(UserConfigSchema.shape) as (keyof UserConfig)[];