@cyanheads/calculator-mcp-server 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,333 @@
1
+ /**
2
+ * @fileoverview Hardened math.js wrapper for secure expression evaluation.
3
+ * Creates a restricted math.js instance with dangerous functions disabled in
4
+ * the expression scope, and wraps evaluation in a vm sandbox with timeout.
5
+ * @module services/math/math-service
6
+ */
7
+ import vm from 'node:vm';
8
+ import { invalidParams, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
9
+ import { all, create } from 'mathjs';
10
+ /**
11
+ * Custom simplification rules extending math.js defaults.
12
+ * math.js ships with algebraic rules only — these add common trig identities.
13
+ * The `n` wildcard matches any sub-expression (e.g., sin(2x+1)^2).
14
+ */
15
+ const TRIG_SIMPLIFY_RULES = [
16
+ // Pythagorean identities
17
+ 'sin(n)^2 + cos(n)^2 -> 1',
18
+ 'cos(n)^2 + sin(n)^2 -> 1',
19
+ '1 - sin(n)^2 -> cos(n)^2',
20
+ '1 - cos(n)^2 -> sin(n)^2',
21
+ // tan / sec / csc / cot relationships
22
+ 'tan(n)^2 + 1 -> sec(n)^2',
23
+ '1 + tan(n)^2 -> sec(n)^2',
24
+ 'sec(n)^2 - 1 -> tan(n)^2',
25
+ '1 + cot(n)^2 -> csc(n)^2',
26
+ 'cot(n)^2 + 1 -> csc(n)^2',
27
+ 'csc(n)^2 - 1 -> cot(n)^2',
28
+ // Double-angle identities
29
+ '2 * sin(n) * cos(n) -> sin(2 * n)',
30
+ 'cos(n)^2 - sin(n)^2 -> cos(2 * n)',
31
+ ];
32
+ /**
33
+ * Functions disabled in the expression scope for security.
34
+ * These are overridden via math.import() — expressions cannot call them.
35
+ * simplify/derivative are called programmatically by the tool handler, not from expressions.
36
+ */
37
+ const DISABLED_FUNCTIONS = [
38
+ 'import',
39
+ 'createUnit',
40
+ 'evaluate',
41
+ 'parse',
42
+ 'simplify',
43
+ 'derivative',
44
+ 'resolve',
45
+ 'reviver',
46
+ 'compile',
47
+ 'chain',
48
+ 'config',
49
+ 'parser',
50
+ ];
51
+ /**
52
+ * `typed` is NOT disabled here — it's used internally by math.js for function dispatch.
53
+ * Overriding it breaks trig/simplify/etc. Instead, `typed()` calls from expressions are
54
+ * caught by the BLOCKED_RESULT_TYPES check (resultType "function" is rejected).
55
+ */
56
+ /**
57
+ * Constants/properties overridden in the expression scope to prevent info leakage.
58
+ * `version` exposes the exact math.js version (enables targeted CVE research).
59
+ */
60
+ const REDACTED_CONSTANTS = {
61
+ version: 'redacted',
62
+ };
63
+ /**
64
+ * Result types that must never be returned to clients.
65
+ * Function references leak internal source code via toString().
66
+ * ResultSet indicates multi-expression evaluation (newline bypass).
67
+ */
68
+ const BLOCKED_RESULT_TYPES = new Set(['function', 'Function', 'ResultSet', 'Parser']);
69
+ /**
70
+ * Scope key names that could pollute the object prototype chain or shadow
71
+ * critical Object.prototype methods. Validated before passing to math.js.
72
+ */
73
+ const BLOCKED_SCOPE_KEYS = new Set([
74
+ '__proto__',
75
+ '__defineGetter__',
76
+ '__defineSetter__',
77
+ '__lookupGetter__',
78
+ '__lookupSetter__',
79
+ 'constructor',
80
+ 'prototype',
81
+ 'toString',
82
+ 'valueOf',
83
+ 'hasOwnProperty',
84
+ 'isPrototypeOf',
85
+ 'propertyIsEnumerable',
86
+ 'toLocaleString',
87
+ ]);
88
+ /**
89
+ * Check for expression separators outside square brackets.
90
+ * Semicolons inside `[...]` are valid matrix row separators.
91
+ * Newlines (`\n`, `\r`) are also expression separators in math.js.
92
+ */
93
+ function hasExpressionSeparator(expr) {
94
+ let depth = 0;
95
+ for (const ch of expr) {
96
+ if (ch === '[')
97
+ depth++;
98
+ else if (ch === ']')
99
+ depth--;
100
+ else if (ch === '\n' || ch === '\r')
101
+ return true;
102
+ else if (ch === ';' && depth === 0)
103
+ return true;
104
+ }
105
+ return false;
106
+ }
107
+ export class MathService {
108
+ evaluate;
109
+ simplify;
110
+ derivative;
111
+ simplifyRules;
112
+ format;
113
+ typeOf;
114
+ config;
115
+ constructor(config) {
116
+ this.config = config;
117
+ // biome-ignore lint/style/noNonNullAssertion: math.js types declare `all` as potentially undefined, but it's always defined at runtime
118
+ const math = create(all);
119
+ // Save references before overriding — these bypass the expression scope restrictions
120
+ this.evaluate = math.evaluate.bind(math);
121
+ this.simplify = math.simplify.bind(math);
122
+ this.derivative = math.derivative.bind(math);
123
+ this.simplifyRules = [...math.simplify.rules, ...TRIG_SIMPLIFY_RULES];
124
+ this.format = math.format.bind(math);
125
+ this.typeOf = math.typeOf.bind(math);
126
+ // Disable dangerous functions in expression scope
127
+ const disabled = {};
128
+ for (const fn of DISABLED_FUNCTIONS) {
129
+ disabled[fn] = () => {
130
+ throw new Error(`Function "${fn}" is disabled for security.`);
131
+ };
132
+ }
133
+ // Redact constants that leak implementation details
134
+ for (const [key, value] of Object.entries(REDACTED_CONSTANTS)) {
135
+ disabled[key] = value;
136
+ }
137
+ math.import(disabled, { override: true });
138
+ }
139
+ /** Evaluate a math expression with optional variable scope and precision. */
140
+ evaluateExpression(expression, scope, precision) {
141
+ this.validateInput(expression);
142
+ const sanitizedScope = scope ? this.sanitizeScope(scope) : undefined;
143
+ const raw = this.runWithTimeout(() => sanitizedScope ? this.evaluate(expression, sanitizedScope) : this.evaluate(expression));
144
+ if (typeof raw === 'number' && !Number.isFinite(raw)) {
145
+ throw invalidParams(`Expression evaluated to ${raw} — this typically means the operation is mathematically undefined (e.g., division by zero, log of zero). Check the expression.`);
146
+ }
147
+ const resultType = this.typeOf(raw);
148
+ this.validateResultType(resultType);
149
+ const result = precision != null ? this.format(raw, { precision }) : this.format(raw);
150
+ this.validateResultSize(result);
151
+ return { result, resultType };
152
+ }
153
+ /** Simplify an algebraic expression symbolically. */
154
+ simplifyExpression(expression) {
155
+ this.validateInput(expression);
156
+ const simplified = this.runWithTimeout(() => this.simplify(expression, this.simplifyRules));
157
+ const result = simplified.toString();
158
+ this.validateResultSize(result);
159
+ return { result, resultType: 'string' };
160
+ }
161
+ /** Compute the symbolic derivative of an expression with respect to a variable. */
162
+ differentiateExpression(expression, variable) {
163
+ this.validateInput(expression);
164
+ const derived = this.runWithTimeout(() => this.derivative(expression, variable));
165
+ const result = derived.toString();
166
+ this.validateResultSize(result);
167
+ return { result, resultType: 'string' };
168
+ }
169
+ /** Get formatted help content listing available functions, operators, and syntax. */
170
+ getHelpContent() {
171
+ return HELP_CONTENT;
172
+ }
173
+ validateInput(expression) {
174
+ if (!expression.trim()) {
175
+ throw invalidParams('Expression cannot be empty.');
176
+ }
177
+ if (expression.length > this.config.maxExpressionLength) {
178
+ throw invalidParams(`Expression exceeds maximum length of ${this.config.maxExpressionLength} characters.`);
179
+ }
180
+ if (hasExpressionSeparator(expression)) {
181
+ throw invalidParams('Multiple expressions are not allowed. Submit one expression per call.');
182
+ }
183
+ }
184
+ /** Reject scope keys that could pollute the object prototype chain. */
185
+ sanitizeScope(scope) {
186
+ for (const key of Object.keys(scope)) {
187
+ if (BLOCKED_SCOPE_KEYS.has(key)) {
188
+ throw invalidParams(`Scope key "${key}" is not allowed — it conflicts with a reserved property name.`);
189
+ }
190
+ }
191
+ return scope;
192
+ }
193
+ /** Reject result types that leak internals (functions, parsers, multi-expression ResultSets). */
194
+ validateResultType(resultType) {
195
+ if (BLOCKED_RESULT_TYPES.has(resultType)) {
196
+ throw invalidParams(`Expression produced a ${resultType} — only numeric, string, matrix, complex, unit, and boolean results are allowed.`);
197
+ }
198
+ }
199
+ /** Reject results that exceed the configured maximum size. */
200
+ validateResultSize(result) {
201
+ if (result.length > this.config.maxResultLength) {
202
+ throw invalidParams(`Result exceeds maximum size (${this.config.maxResultLength} characters). Reduce matrix dimensions or simplify the expression.`);
203
+ }
204
+ }
205
+ /** Runs a synchronous function inside a vm sandbox with timeout protection. */
206
+ runWithTimeout(fn) {
207
+ const sandbox = { fn, result: undefined };
208
+ const context = vm.createContext(sandbox);
209
+ try {
210
+ vm.runInNewContext('result = fn()', context, { timeout: this.config.evaluationTimeoutMs });
211
+ return sandbox.result;
212
+ }
213
+ catch (err) {
214
+ if (err instanceof Error && 'code' in err && err.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT') {
215
+ throw serviceUnavailable(`Expression evaluation timed out after ${this.config.evaluationTimeoutMs / 1000} seconds. Simplify the expression or reduce matrix dimensions.`);
216
+ }
217
+ // All non-timeout errors from the VM are expression-related
218
+ const message = err instanceof Error ? err.message : String(err);
219
+ throw invalidParams(`Invalid expression: ${message}`);
220
+ }
221
+ }
222
+ }
223
+ // --- Init/accessor pattern ---
224
+ let _service;
225
+ /** Initialize the math service. Call once from createApp setup(). */
226
+ export function initMathService(config) {
227
+ _service = new MathService(config);
228
+ }
229
+ /** Get the initialized math service instance. */
230
+ export function getMathService() {
231
+ if (!_service)
232
+ throw new Error('MathService not initialized — call initMathService() in setup()');
233
+ return _service;
234
+ }
235
+ // --- Help content ---
236
+ const HELP_CONTENT = `# Calculator Help
237
+
238
+ ## Operators
239
+
240
+ | Operator | Description | Example |
241
+ |:---------|:------------|:--------|
242
+ | + | Addition | 2 + 3 |
243
+ | - | Subtraction | 5 - 2 |
244
+ | * | Multiplication | 3 * 4 |
245
+ | / | Division | 10 / 3 |
246
+ | ^ | Exponentiation | 2 ^ 10 |
247
+ | % | Modulus | 17 % 5 |
248
+ | ! | Factorial | 5! |
249
+
250
+ ## Constants
251
+
252
+ | Name | Value | Description |
253
+ |:-----|:------|:------------|
254
+ | pi | 3.14159... | Ratio of circumference to diameter |
255
+ | e | 2.71828... | Euler's number |
256
+ | phi | 1.61803... | Golden ratio |
257
+ | i | sqrt(-1) | Imaginary unit |
258
+ | Infinity | Infinity | Positive infinity |
259
+ | NaN | NaN | Not a number |
260
+ | true | true | Boolean true |
261
+ | false | false | Boolean false |
262
+
263
+ ## Functions
264
+
265
+ ### Arithmetic
266
+ abs, ceil, floor, round, sign, sqrt, cbrt, exp, expm1, log, log2, log10, log1p, pow, mod, gcd, lcm, nthRoot, hypot, fix, cube, square, unaryMinus, unaryPlus
267
+
268
+ ### Trigonometry
269
+ sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh, asinh, acosh, atanh, sec, csc, cot, asec, acsc, acot, sech, csch, coth
270
+
271
+ ### Statistics
272
+ mean, median, mode, std, variance, min, max, sum, prod, quantileSeq, mad, count
273
+
274
+ ### Matrix
275
+ det, inv, transpose, trace, zeros, ones, identity, diag, size, reshape, flatten, concat, sort, cross, dot, eigs, expm, sqrtm, kron, pinv, range
276
+
277
+ ### Combinatorics
278
+ factorial, gamma, permutations, combinations, catalan, bellNumbers, stirlingS2, composition, multinomial
279
+
280
+ ### Complex Numbers
281
+ re, im, conj, arg, complex
282
+
283
+ ### Logical
284
+ and, or, xor, not
285
+
286
+ ### Comparison
287
+ equal, unequal, larger, largerEq, smaller, smallerEq, compare, deepEqual
288
+
289
+ ### Unit Conversion
290
+ Syntax: \`value unit to targetUnit\`
291
+
292
+ Common units: m, cm, mm, km, inch, ft, yard, mile, kg, g, lb, oz, s, min, hour, day, celsius, fahrenheit, kelvin, liter, gallon, joule, watt, newton, pascal, bar, psi, radian, degree
293
+
294
+ ## Syntax Examples
295
+
296
+ ### Basic arithmetic
297
+ \`2 + 3 * 4\` => 14
298
+
299
+ ### Functions
300
+ \`sqrt(144)\` => 12
301
+ \`sin(pi / 2)\` => 1
302
+ \`log(1000, 10)\` => 3
303
+
304
+ ### Variables (via scope parameter)
305
+ Provide scope: { "x": 5, "y": 3 }
306
+ Expression: \`x^2 + y\` => 28
307
+
308
+ ### Matrices
309
+ \`[1, 2; 3, 4]\` — 2x2 matrix
310
+ \`det([1, 2; 3, 4])\` => -2
311
+ \`inv([1, 2; 3, 4])\` => [[-2, 1], [1.5, -0.5]]
312
+
313
+ ### Complex numbers
314
+ \`2 + 3i\` => 2 + 3i
315
+ \`sqrt(-4)\` => 2i
316
+ \`abs(3 + 4i)\` => 5
317
+
318
+ ### Unit conversion
319
+ \`5 kg to lbs\` => 11.02 lbs
320
+ \`100 celsius to fahrenheit\` => 212 fahrenheit
321
+ \`1 mile to km\` => 1.60934 km
322
+
323
+ > **Note:** Unit conversions use IEEE 754 floating-point arithmetic. Results may include minor rounding artifacts (e.g., 211.99999999999997 instead of 212). Use the \`precision\` parameter to round.
324
+
325
+ ### Precision
326
+ Use the precision parameter (1\u201316 significant digits) for numeric results.
327
+
328
+ ### Operations
329
+ - **evaluate** (default): Compute a numeric result
330
+ - **simplify**: Reduce algebraic expressions symbolically (e.g., "2x + 3x" => "5 * x", "sin(x)^2 + cos(x)^2" => 1). Supports algebraic rules and common trigonometric identities (Pythagorean, double-angle, tan/sec/csc/cot relationships).
331
+ - **derivative**: Compute symbolic derivative (requires variable parameter, e.g., variable: "x")
332
+ `;
333
+ //# sourceMappingURL=math-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"math-service.js","sourceRoot":"","sources":["../../../src/services/math/math-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @fileoverview Types for the math service.
3
+ * @module services/math/types
4
+ */
5
+ /** Result of a math operation. */
6
+ export interface MathResult {
7
+ /** Formatted string representation of the result. */
8
+ result: string;
9
+ /** math.js type name: number, BigNumber, Complex, Matrix, Unit, string, boolean. */
10
+ resultType: string;
11
+ }
12
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/math/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,kCAAkC;AAClC,MAAM,WAAW,UAAU;IACzB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,UAAU,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @fileoverview Types for the math service.
3
+ * @module services/math/types
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/math/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@cyanheads/calculator-mcp-server",
3
+ "version": "0.1.6",
4
+ "description": "A calculator MCP server that lets any LLM verify mathematical computations. Evaluate, simplify, and differentiate expressions via a single tool. Powered by math.js v15.",
5
+ "mcpName": "io.github.cyanheads/calculator-mcp-server",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "bin": {
10
+ "calculator-mcp-server": "dist/index.js"
11
+ },
12
+ "files": ["dist/", "README.md", "LICENSE", "CLAUDE.md", "Dockerfile", "server.json"],
13
+ "scripts": {
14
+ "build": "bun scripts/build.ts",
15
+ "rebuild": "bun scripts/clean.ts && bun scripts/build.ts",
16
+ "clean": "bun scripts/clean.ts",
17
+ "devcheck": "bun scripts/devcheck.ts",
18
+ "tree": "bun scripts/tree.ts",
19
+ "format": "biome check --write --unsafe .",
20
+ "lint:mcp": "bun scripts/lint-mcp.ts",
21
+ "test": "vitest run",
22
+ "dev:stdio": "MCP_TRANSPORT_TYPE=stdio bun --watch src/index.ts",
23
+ "dev:http": "MCP_TRANSPORT_TYPE=http bun --watch src/index.ts",
24
+ "start:stdio": "MCP_TRANSPORT_TYPE=stdio node dist/index.js",
25
+ "start:http": "MCP_TRANSPORT_TYPE=http node dist/index.js"
26
+ },
27
+ "keywords": [
28
+ "mcp",
29
+ "mcp-server",
30
+ "model-context-protocol",
31
+ "calculator",
32
+ "math",
33
+ "mathjs",
34
+ "llm-tools",
35
+ "typescript"
36
+ ],
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/cyanheads/calculator-mcp-server.git"
40
+ },
41
+ "homepage": "https://github.com/cyanheads/calculator-mcp-server",
42
+ "bugs": {
43
+ "url": "https://github.com/cyanheads/calculator-mcp-server/issues"
44
+ },
45
+ "author": "cyanheads (https://github.com/cyanheads)",
46
+ "license": "Apache-2.0",
47
+ "engines": {
48
+ "node": ">=22.0.0",
49
+ "bun": ">=1.2.0"
50
+ },
51
+ "packageManager": "bun@1.3.11",
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "dependencies": {
56
+ "@cyanheads/mcp-ts-core": "^0.2.10",
57
+ "mathjs": "^15.1.1",
58
+ "pino-pretty": "^13.1.3"
59
+ },
60
+ "devDependencies": {
61
+ "@biomejs/biome": "^2.4.9",
62
+ "@types/node": "^25.5.0",
63
+ "depcheck": "^1.4.7",
64
+ "ignore": "^7.0.5",
65
+ "tsc-alias": "^1.8.16",
66
+ "tsx": "^4.21.0",
67
+ "typescript": "^6.0.2",
68
+ "vitest": "^4.1.2"
69
+ }
70
+ }
package/server.json ADDED
@@ -0,0 +1,141 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.cyanheads/calculator-mcp-server",
4
+ "description": "Calculator MCP server — evaluate, simplify, and differentiate math expressions. Powered by math.js v15.",
5
+ "repository": {
6
+ "url": "https://github.com/cyanheads/calculator-mcp-server",
7
+ "source": "github"
8
+ },
9
+ "version": "0.1.6",
10
+ "packages": [
11
+ {
12
+ "registryType": "npm",
13
+ "registryBaseUrl": "https://registry.npmjs.org",
14
+ "identifier": "@cyanheads/calculator-mcp-server",
15
+ "runtimeHint": "bun",
16
+ "version": "0.1.6",
17
+ "packageArguments": [
18
+ {
19
+ "type": "positional",
20
+ "value": "run"
21
+ },
22
+ {
23
+ "type": "positional",
24
+ "value": "start:stdio"
25
+ }
26
+ ],
27
+ "environmentVariables": [
28
+ {
29
+ "name": "CALC_MAX_EXPRESSION_LENGTH",
30
+ "description": "Maximum allowed expression string length.",
31
+ "format": "string",
32
+ "isRequired": false,
33
+ "default": "1000"
34
+ },
35
+ {
36
+ "name": "CALC_EVALUATION_TIMEOUT_MS",
37
+ "description": "Maximum evaluation time in milliseconds.",
38
+ "format": "string",
39
+ "isRequired": false,
40
+ "default": "5000"
41
+ },
42
+ {
43
+ "name": "CALC_MAX_RESULT_LENGTH",
44
+ "description": "Maximum result string length in characters.",
45
+ "format": "string",
46
+ "isRequired": false,
47
+ "default": "100000"
48
+ },
49
+ {
50
+ "name": "MCP_LOG_LEVEL",
51
+ "description": "Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').",
52
+ "format": "string",
53
+ "isRequired": false,
54
+ "default": "info"
55
+ }
56
+ ],
57
+ "transport": {
58
+ "type": "stdio"
59
+ }
60
+ },
61
+ {
62
+ "registryType": "npm",
63
+ "registryBaseUrl": "https://registry.npmjs.org",
64
+ "identifier": "@cyanheads/calculator-mcp-server",
65
+ "runtimeHint": "bun",
66
+ "version": "0.1.6",
67
+ "packageArguments": [
68
+ {
69
+ "type": "positional",
70
+ "value": "run"
71
+ },
72
+ {
73
+ "type": "positional",
74
+ "value": "start:http"
75
+ }
76
+ ],
77
+ "environmentVariables": [
78
+ {
79
+ "name": "CALC_MAX_EXPRESSION_LENGTH",
80
+ "description": "Maximum allowed expression string length.",
81
+ "format": "string",
82
+ "isRequired": false,
83
+ "default": "1000"
84
+ },
85
+ {
86
+ "name": "CALC_EVALUATION_TIMEOUT_MS",
87
+ "description": "Maximum evaluation time in milliseconds.",
88
+ "format": "string",
89
+ "isRequired": false,
90
+ "default": "5000"
91
+ },
92
+ {
93
+ "name": "CALC_MAX_RESULT_LENGTH",
94
+ "description": "Maximum result string length in characters.",
95
+ "format": "string",
96
+ "isRequired": false,
97
+ "default": "100000"
98
+ },
99
+ {
100
+ "name": "MCP_HTTP_HOST",
101
+ "description": "The hostname for the HTTP server.",
102
+ "format": "string",
103
+ "isRequired": false,
104
+ "default": "127.0.0.1"
105
+ },
106
+ {
107
+ "name": "MCP_HTTP_PORT",
108
+ "description": "The port to run the HTTP server on.",
109
+ "format": "string",
110
+ "isRequired": false,
111
+ "default": "3010"
112
+ },
113
+ {
114
+ "name": "MCP_HTTP_ENDPOINT_PATH",
115
+ "description": "The endpoint path for the MCP server.",
116
+ "format": "string",
117
+ "isRequired": false,
118
+ "default": "/mcp"
119
+ },
120
+ {
121
+ "name": "MCP_AUTH_MODE",
122
+ "description": "Authentication mode to use: 'none', 'jwt', or 'oauth'.",
123
+ "format": "string",
124
+ "isRequired": false,
125
+ "default": "none"
126
+ },
127
+ {
128
+ "name": "MCP_LOG_LEVEL",
129
+ "description": "Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').",
130
+ "format": "string",
131
+ "isRequired": false,
132
+ "default": "info"
133
+ }
134
+ ],
135
+ "transport": {
136
+ "type": "streamable-http",
137
+ "url": "http://localhost:3010/mcp"
138
+ }
139
+ }
140
+ ]
141
+ }