@mcp-layer/gateway 0.1.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arnout Kazemier
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,468 @@
1
+ # @mcp-layer/gateway
2
+
3
+ `@mcp-layer/gateway` provides shared runtime primitives for MCP adapter packages. It centralizes catalog bootstrap, request-scoped session resolution, schema validation, breaker-backed execution, telemetry helpers, and deterministic item mapping so adapter packages (for example REST and GraphQL) stay thin.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [When to use this package](#when-to-use-this-package)
9
+ - [Usage](#usage)
10
+ - [API Reference](#api-reference)
11
+ - [Runtime Error Reference](#runtime-error-reference)
12
+
13
+ ## Installation
14
+
15
+ ```sh
16
+ pnpm add @mcp-layer/gateway
17
+ # or
18
+ npm install @mcp-layer/gateway
19
+ # or
20
+ yarn add @mcp-layer/gateway
21
+ ```
22
+
23
+ ## When to use this package
24
+
25
+ Use this package when you are building an interface layer on top of MCP that needs to:
26
+
27
+ - discover and normalize MCP catalog data once,
28
+ - resolve sessions per request (direct session or manager mode),
29
+ - validate tool/prompt inputs with trust-aware schema rules,
30
+ - execute MCP calls with optional circuit breakers,
31
+ - instrument request lifecycles with OpenTelemetry API,
32
+ - map MCP item names deterministically for generated operation surfaces.
33
+
34
+ ## Usage
35
+
36
+ This example demonstrates how adapter packages bootstrap runtime state once and reuse it per request. This matters because repeated catalog extraction and ad-hoc breaker creation quickly cause drift between adapters.
37
+
38
+ Expected behavior: one runtime context is created per bootstrap session, with a stable prefix/version and reusable resolver/validator/executor primitives.
39
+
40
+ ```js
41
+ import { createRuntime, createMap } from '@mcp-layer/gateway';
42
+
43
+ const runtime = await createRuntime({
44
+ session,
45
+ validation: {
46
+ trustSchemas: 'auto'
47
+ }
48
+ }, {
49
+ name: 'my-adapter',
50
+ serviceName: 'my-adapter-runtime'
51
+ });
52
+
53
+ const context = runtime.contexts[0];
54
+ const mapping = createMap(context.catalog);
55
+
56
+ const resolved = await context.resolve(request);
57
+ const check = context.validator.validate('tool', 'echo', { text: 'hello', loud: false });
58
+ if (!check.valid) {
59
+ throw new Error('Validation failed.');
60
+ }
61
+
62
+ const result = await context.execute(request, 'tools/call', {
63
+ name: 'echo',
64
+ arguments: { text: 'hello', loud: false }
65
+ });
66
+
67
+ console.log(mapping.findField('tool', 'echo'));
68
+ console.log(result);
69
+
70
+ await runtime.close();
71
+ ```
72
+
73
+ ## API Reference
74
+
75
+ ### `createRuntime(options, meta?)`
76
+
77
+ Creates shared runtime contexts for adapter plugins.
78
+
79
+ Signature:
80
+
81
+ ```ts
82
+ createRuntime(
83
+ options: {
84
+ session: Session | Session[];
85
+ manager?: { get(request): Promise<Session>; close?(): Promise<void> };
86
+ prefix?: string | ((version, info, sessionName) => string);
87
+ validation?: {
88
+ trustSchemas?: 'auto' | true | false;
89
+ maxSchemaDepth?: number;
90
+ maxSchemaSize?: number;
91
+ maxPatternLength?: number;
92
+ maxToolNameLength?: number;
93
+ maxTemplateParamLength?: number;
94
+ };
95
+ resilience?: {
96
+ enabled?: boolean;
97
+ timeout?: number;
98
+ errorThresholdPercentage?: number;
99
+ resetTimeout?: number;
100
+ volumeThreshold?: number;
101
+ };
102
+ telemetry?: {
103
+ enabled?: boolean;
104
+ serviceName?: string;
105
+ metricPrefix?: string;
106
+ api?: OpenTelemetryApi;
107
+ };
108
+ errors?: {
109
+ exposeDetails?: boolean;
110
+ };
111
+ normalizeError?: (error, instance, requestId, options) => unknown;
112
+ },
113
+ meta?: {
114
+ name?: string;
115
+ serviceName?: string;
116
+ }
117
+ ): Promise<{
118
+ config;
119
+ contexts: Array<{
120
+ session;
121
+ catalog;
122
+ info;
123
+ version;
124
+ prefix;
125
+ validator;
126
+ telemetry;
127
+ resolve(request): Promise<{ session, breaker }>;
128
+ execute(request, method, params): Promise<Record<string, unknown>>;
129
+ normalize(error, instance, requestId?): unknown;
130
+ }>;
131
+ breakers: Map<string, CircuitBreaker>;
132
+ normalize(error, instance, requestId?): unknown;
133
+ close(): Promise<void>;
134
+ }>;
135
+ ```
136
+
137
+ Behavior notes:
138
+
139
+ - `manager` requires a bootstrap `session` (catalog extraction source).
140
+ - manager mode does not support `session` arrays.
141
+ - `close()` shuts down breaker instances and calls `manager.close()` when available.
142
+ - validation registration is preloaded from catalog tool/prompt input schemas.
143
+
144
+ ### `createMap(catalog)`
145
+
146
+ Builds a deterministic lookup/mapping model for MCP items.
147
+
148
+ Returns:
149
+
150
+ - `tools`, `prompts`, `resources`, `templates`: sorted item lists.
151
+ - `entries`: generated metadata entries `{ type, root, name, field, item }`.
152
+ - `find(type, name)`: lookup item by type/name.
153
+ - `findField(type, name)`: lookup generated field name.
154
+ - `byType(type)`: return filtered list for a type.
155
+
156
+ ### `deriveApiVersion(info)`
157
+
158
+ Derives adapter version prefix strings (`v0`, `v1`, etc.) from server version info.
159
+
160
+ ### `resolvePrefix(prefixOption, version, info, sessionName)`
161
+
162
+ Computes adapter mount prefix from static or callback prefix config.
163
+
164
+ ### `createValidator(config, session)` and `SchemaValidator`
165
+
166
+ Creates trust-aware JSON-schema validator helpers for tool/prompt inputs.
167
+
168
+ ### `createCircuitBreaker(session, config)` and `executeWithBreaker(...)`
169
+
170
+ Shared circuit-breaker primitives for MCP methods.
171
+
172
+ ### `createTelemetry(config)` and `createCallContext(config)`
173
+
174
+ OpenTelemetry API integration and request-span/metric helper utilities.
175
+
176
+ ### `validateRuntimeOptions(opts, meta?)` and `defaults(serviceName)`
177
+
178
+ Shared adapter option normalization and defaults.
179
+
180
+ ## Runtime Error Reference
181
+
182
+ This section is written for high-pressure debugging moments. Each entry maps to concrete `createRuntime(...)` option-validation branches in `src/config/validate.js`.
183
+
184
+ <a id="error-688cda"></a>
185
+ ### `"{option}" must be a positive number.`
186
+
187
+ Thrown from: `requirePositiveNumber`
188
+
189
+ This happens when numeric limits are `<= 0`, `NaN`, or non-finite.
190
+
191
+ Step-by-step resolution:
192
+ 1. Read `{option}` in the error message and locate where that config value is built.
193
+ 2. Coerce external/env values to numbers before calling `createRuntime`.
194
+ 3. Reject invalid values early (`> 0` only) in your adapter bootstrap.
195
+ 4. Add tests covering both invalid and valid values for that exact option.
196
+
197
+ This example shows a preflight guard that fails early before runtime creation. This matters because it prevents startup with invalid resilience values. Expected behavior: invalid numbers throw before adapter registration.
198
+
199
+ <details>
200
+ <summary>Fix Example: sanitize numeric runtime options before createRuntime</summary>
201
+
202
+ ```js
203
+ const timeout = Number(process.env.MCP_TIMEOUT_MS ?? 30000);
204
+ if (!Number.isFinite(timeout) || timeout <= 0)
205
+ throw new Error('MCP_TIMEOUT_MS must be a positive number');
206
+
207
+ const runtime = await createRuntime({
208
+ session,
209
+ resilience: { timeout }
210
+ });
211
+ ```
212
+
213
+ </details>
214
+
215
+ <a id="error-a103aa"></a>
216
+ ### `validation.trustSchemas must be "auto", true, or false.`
217
+
218
+ Thrown from: `trustMode`
219
+
220
+ This happens when `validation.trustSchemas` is set to an unsupported value.
221
+
222
+ Step-by-step resolution:
223
+ 1. Inspect `validation.trustSchemas` at runtime (not only static config).
224
+ 2. Replace stringified booleans like `"true"` with real booleans.
225
+ 3. Use `"auto"` unless you have a strict reason to force trust/untrust.
226
+ 4. Add config tests for all three supported values.
227
+
228
+ This example demonstrates the allowed trust mode values. This matters because schema trust mode controls whether validation executes for remote catalogs. Expected behavior: runtime initializes without trust-mode validation failures.
229
+
230
+ <details>
231
+ <summary>Fix Example: set trustSchemas to an allowed value</summary>
232
+
233
+ ```js
234
+ await createRuntime({
235
+ session,
236
+ validation: {
237
+ trustSchemas: 'auto'
238
+ }
239
+ });
240
+ ```
241
+
242
+ </details>
243
+
244
+ <a id="error-a74b20"></a>
245
+ ### `prefix must be a string or function.`
246
+
247
+ Thrown from: `validateRuntimeOptions`
248
+
249
+ This happens when `prefix` is neither a string nor a callback.
250
+
251
+ Step-by-step resolution:
252
+ 1. Use a static string prefix for fixed routes.
253
+ 2. Use a callback for dynamic per-session prefixes.
254
+ 3. Remove non-supported types from env/config mapping.
255
+ 4. Add tests for both allowed prefix forms.
256
+
257
+ This example shows dynamic per-session prefixing. This matters for multi-session adapters that need stable route isolation. Expected behavior: each session resolves to a deterministic mount path.
258
+
259
+ <details>
260
+ <summary>Fix Example: use a valid prefix callback</summary>
261
+
262
+ ```js
263
+ await createRuntime({
264
+ session,
265
+ prefix: function prefix(version, info, name) {
266
+ return `/${version}/${name}`;
267
+ }
268
+ });
269
+ ```
270
+
271
+ </details>
272
+
273
+ <a id="error-712f62"></a>
274
+ ### `manager does not support multiple sessions. Register multiple plugins instead.`
275
+
276
+ Thrown from: `validateRuntimeOptions`
277
+
278
+ This happens when `manager` is provided with `session` as an array.
279
+
280
+ Step-by-step resolution:
281
+ 1. In manager mode, provide exactly one bootstrap session.
282
+ 2. If you need multiple static sessions, remove manager mode.
283
+ 3. Register separate adapter instances for each static session surface.
284
+ 4. Add explicit startup tests for manager mode vs multi-session mode.
285
+
286
+ This example demonstrates valid manager-mode bootstrapping. This matters because manager mode resolves sessions per request and needs one catalog source at startup. Expected behavior: runtime initializes and delegates per-request resolution through manager.
287
+
288
+ <details>
289
+ <summary>Fix Example: manager mode with one bootstrap session</summary>
290
+
291
+ ```js
292
+ await createRuntime({
293
+ session: bootstrapSession,
294
+ manager
295
+ });
296
+ ```
297
+
298
+ </details>
299
+
300
+ <a id="error-c773d9"></a>
301
+ ### `session is required when manager is provided (used for catalog bootstrap).`
302
+
303
+ Thrown from: `validateRuntimeOptions`
304
+
305
+ This happens when `manager` is configured without a bootstrap `session`.
306
+
307
+ Step-by-step resolution:
308
+ 1. Provide a bootstrap session alongside manager.
309
+ 2. Ensure bootstrap session connects before runtime creation.
310
+ 3. Keep bootstrap session aligned with manager-provided session capabilities.
311
+ 4. Add a startup test that asserts this requirement.
312
+
313
+ This example shows manager mode with explicit bootstrap catalog source. This matters because the runtime must extract catalog metadata at initialization time. Expected behavior: runtime can build validators and route metadata before serving requests.
314
+
315
+ <details>
316
+ <summary>Fix Example: include bootstrap session when manager is enabled</summary>
317
+
318
+ ```js
319
+ await createRuntime({
320
+ session: bootstrapSession,
321
+ manager
322
+ });
323
+ ```
324
+
325
+ </details>
326
+
327
+ <a id="error-6e22f2"></a>
328
+ ### `session or manager option is required.`
329
+
330
+ Thrown from: `validateRuntimeOptions`
331
+
332
+ This happens when runtime options include neither `session` nor `manager`.
333
+
334
+ Step-by-step resolution:
335
+ 1. Pass a connected `session` for static mode.
336
+ 2. Or pass `manager` plus a bootstrap `session` for dynamic mode.
337
+ 3. Add adapter-level assertions before calling `createRuntime`.
338
+ 4. Add tests that verify startup failure without session sources.
339
+
340
+ This example demonstrates the minimum static-mode runtime configuration. This matters because all adapter behavior depends on a concrete session source. Expected behavior: runtime boots with one context.
341
+
342
+ <details>
343
+ <summary>Fix Example: provide session source</summary>
344
+
345
+ ```js
346
+ await createRuntime({ session });
347
+ ```
348
+
349
+ </details>
350
+
351
+ <a id="error-b78e6d"></a>
352
+ ### `manager must be an object with a get(request) function.`
353
+
354
+ Thrown from: `validateRuntimeOptions`
355
+
356
+ This happens when `manager` is missing or does not implement `get(request)`.
357
+
358
+ Step-by-step resolution:
359
+ 1. Ensure manager is an object, not a primitive.
360
+ 2. Implement `async get(request)` returning a `Session`.
361
+ 3. Optionally add `close()` for lifecycle cleanup.
362
+ 4. Add contract tests for malformed manager values.
363
+
364
+ This example shows the minimum manager contract expected by gateway runtime. This matters because request-scoped session resolution depends on `get(request)`. Expected behavior: each request can resolve a valid session.
365
+
366
+ <details>
367
+ <summary>Fix Example: implement required manager interface</summary>
368
+
369
+ ```js
370
+ const manager = {
371
+ async get(request) {
372
+ return resolveSession(request);
373
+ }
374
+ };
375
+ ```
376
+
377
+ </details>
378
+
379
+ <a id="error-6fc055"></a>
380
+ ### `errors must be an object.`
381
+
382
+ Thrown from: `validateRuntimeOptions`
383
+
384
+ This happens when `errors` is provided as a primitive instead of an object.
385
+
386
+ Step-by-step resolution:
387
+ 1. Ensure `errors` is an object literal.
388
+ 2. Keep supported keys under `errors` (for example `exposeDetails`).
389
+ 3. Remove string/boolean shortcuts from config loaders.
390
+ 4. Add shape tests for valid and invalid `errors` values.
391
+
392
+ This example demonstrates valid error option shape. This matters because adapters pass error options into normalization paths. Expected behavior: runtime accepts configuration and exposes normalized error behavior.
393
+
394
+ <details>
395
+ <summary>Fix Example: pass errors as an object</summary>
396
+
397
+ ```js
398
+ await createRuntime({
399
+ session,
400
+ errors: { exposeDetails: false }
401
+ });
402
+ ```
403
+
404
+ </details>
405
+
406
+ <a id="error-349758"></a>
407
+ ### `validation must be an object.`
408
+
409
+ Thrown from: `validateRuntimeOptions`
410
+
411
+ This happens when `validation` is not an object.
412
+
413
+ Step-by-step resolution:
414
+ 1. Move validation keys under `validation`.
415
+ 2. Ensure `validation` is a plain object.
416
+ 3. Keep limit values typed correctly.
417
+ 4. Add tests for malformed and valid validation objects.
418
+
419
+ This example shows valid validation option structure. This matters because the runtime merges these limits before validator construction. Expected behavior: schema limits are applied and runtime starts cleanly.
420
+
421
+ <details>
422
+ <summary>Fix Example: provide validation settings as object</summary>
423
+
424
+ ```js
425
+ await createRuntime({
426
+ session,
427
+ validation: {
428
+ maxSchemaDepth: 10,
429
+ maxSchemaSize: 102400,
430
+ maxPatternLength: 1000
431
+ }
432
+ });
433
+ ```
434
+
435
+ </details>
436
+
437
+ <a id="error-b72182"></a>
438
+ ### `normalizeError must be a function.`
439
+
440
+ Thrown from: `validateRuntimeOptions`
441
+
442
+ This happens when `normalizeError` exists but is not callable.
443
+
444
+ Step-by-step resolution:
445
+ 1. Ensure `normalizeError` is a function reference.
446
+ 2. Keep signature aligned with runtime usage (`error`, `instance`, `requestId`, `options`).
447
+ 3. Remove object/string placeholders from config files.
448
+ 4. Add tests validating custom normalizer wiring.
449
+
450
+ This example shows a valid runtime normalizer hook. This matters because adapters rely on this hook to shape transport-specific error payloads. Expected behavior: thrown upstream errors are converted to your adapter format.
451
+
452
+ <details>
453
+ <summary>Fix Example: pass callable normalizeError handler</summary>
454
+
455
+ ```js
456
+ await createRuntime({
457
+ session,
458
+ normalizeError: function normalizeError(error, instance, requestId, options) {
459
+ return { error, instance, requestId, options };
460
+ }
461
+ });
462
+ ```
463
+
464
+ </details>
465
+
466
+ ## License
467
+
468
+ MIT
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@mcp-layer/gateway",
3
+ "version": "0.1.0",
4
+ "description": "Shared runtime primitives for MCP HTTP/GraphQL adapters with validation, resilience, telemetry, and catalog mapping.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/3rd-Eden/mcp-layer"
8
+ },
9
+ "type": "module",
10
+ "keywords": [
11
+ "mcp",
12
+ "mcp-layer",
13
+ "gateway",
14
+ "runtime",
15
+ "adapter",
16
+ "validation",
17
+ "resilience",
18
+ "telemetry"
19
+ ],
20
+ "main": "src/index.js",
21
+ "exports": {
22
+ ".": {
23
+ "import": "./src/index.js",
24
+ "default": "./src/index.js"
25
+ },
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "src",
30
+ "LICENSE"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "dependencies": {
36
+ "ajv": "^8.17.0",
37
+ "ajv-formats": "^3.0.0",
38
+ "json-schema-traverse": "^1.0.0",
39
+ "opossum": "^8.1.0",
40
+ "safe-regex2": "^2.0.0",
41
+ "@mcp-layer/schema": "1.0.2",
42
+ "@mcp-layer/error": "0.2.0"
43
+ },
44
+ "peerDependencies": {
45
+ "@opentelemetry/api": "^1.9.0"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "@opentelemetry/api": {
49
+ "optional": true
50
+ }
51
+ },
52
+ "devDependencies": {
53
+ "fastify": "^5.7.4",
54
+ "@mcp-layer/attach": "1.1.1",
55
+ "@mcp-layer/test-server": "1.0.1"
56
+ },
57
+ "scripts": {
58
+ "test": "node --test --experimental-test-coverage ./test/**/*.test.js"
59
+ }
60
+ }
package/src/call.js ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Create telemetry helpers for a request lifecycle.
3
+ * @param {{ telemetry: ReturnType<import('./telemetry/index.js').createTelemetry> | null, spanName: string, attributes: Record<string, unknown>, labels: Record<string, string>, validationLabels?: Record<string, string> }} config - Telemetry context.
4
+ * @returns {{ recordValidation: () => void, recordSuccess: () => void, recordError: (error: Error & { code?: string | number }) => void, recordStatus: (status: string, errorType?: string | number) => void, finish: () => void }}
5
+ */
6
+ export function createCallContext(config) {
7
+ const start = Date.now();
8
+ const span = config.telemetry
9
+ ? config.telemetry.tracer.startSpan(config.spanName, { attributes: config.attributes })
10
+ : null;
11
+
12
+ /**
13
+ * Record a validation error in telemetry.
14
+ * @returns {void}
15
+ */
16
+ function recordValidation() {
17
+ if (config.telemetry) {
18
+ config.telemetry.metrics.validationErrors.add(1, config.validationLabels ?? {});
19
+ }
20
+ if (span) {
21
+ span.setStatus({ code: 2, message: 'Validation failed' });
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Record a successful call in telemetry.
27
+ * @returns {void}
28
+ */
29
+ function recordSuccess() {
30
+ if (config.telemetry) {
31
+ config.telemetry.metrics.callDuration.record(Date.now() - start, {
32
+ ...config.labels,
33
+ status: 'success'
34
+ });
35
+ }
36
+ if (span) {
37
+ span.setStatus({ code: 1 });
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Record an error call in telemetry.
43
+ * @param {Error & { code?: string | number }} error - Error that occurred.
44
+ * @returns {void}
45
+ */
46
+ function recordError(error) {
47
+ if (config.telemetry) {
48
+ config.telemetry.metrics.callErrors.add(1, {
49
+ ...config.labels,
50
+ error_type: error.code || 'unknown'
51
+ });
52
+ config.telemetry.metrics.callDuration.record(Date.now() - start, {
53
+ ...config.labels,
54
+ status: 'error'
55
+ });
56
+ }
57
+ if (span) {
58
+ span.recordException(error);
59
+ span.setStatus({ code: 2, message: error.message });
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Record a custom status in telemetry.
65
+ * @param {string} status - Status label to record.
66
+ * @param {string | number} [errorType] - Optional error type label.
67
+ * @returns {void}
68
+ */
69
+ function recordStatus(status, errorType) {
70
+ if (config.telemetry) {
71
+ if (errorType !== undefined) {
72
+ config.telemetry.metrics.callErrors.add(1, {
73
+ ...config.labels,
74
+ error_type: errorType
75
+ });
76
+ }
77
+ config.telemetry.metrics.callDuration.record(Date.now() - start, {
78
+ ...config.labels,
79
+ status
80
+ });
81
+ }
82
+ if (span && status !== 'success') {
83
+ span.setStatus({ code: 2, message: String(status) });
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Close the span, if any.
89
+ * @returns {void}
90
+ */
91
+ function finish() {
92
+ if (span) span.end();
93
+ }
94
+
95
+ return { recordValidation, recordSuccess, recordError, recordStatus, finish };
96
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Build runtime defaults for an adapter.
3
+ * @param {string} serviceName - Default telemetry service name.
4
+ * @returns {{ prefix: string | undefined, validation: { trustSchemas: 'auto' | true | false, maxSchemaDepth: number, maxSchemaSize: number, maxPatternLength: number, maxToolNameLength: number, maxTemplateParamLength: number }, resilience: { enabled: boolean, timeout: number, errorThresholdPercentage: number, resetTimeout: number, volumeThreshold: number }, telemetry: { enabled: boolean, serviceName: string, metricPrefix: string, api?: import('@opentelemetry/api') }, errors: { exposeDetails: boolean } }}
5
+ */
6
+ export function defaults(serviceName) {
7
+ return {
8
+ // Prefix remains optional so adapters can mount at root without forcing an
9
+ // extra segment and still support reverse-proxy path composition.
10
+ prefix: undefined,
11
+ validation: {
12
+ // Auto trust keeps local/stdio schemas fast while distrusting remote
13
+ // schemas unless users explicitly opt in.
14
+ trustSchemas: 'auto',
15
+ maxSchemaDepth: 10,
16
+ maxSchemaSize: 100 * 1024,
17
+ maxPatternLength: 1000,
18
+ maxToolNameLength: 64,
19
+ maxTemplateParamLength: 200,
20
+ },
21
+ resilience: {
22
+ enabled: true,
23
+ timeout: 30000,
24
+ errorThresholdPercentage: 50,
25
+ resetTimeout: 30000,
26
+ volumeThreshold: 5,
27
+ },
28
+ telemetry: {
29
+ enabled: false,
30
+ serviceName,
31
+ metricPrefix: 'adapter',
32
+ api: undefined,
33
+ },
34
+ errors: {
35
+ // Avoid leaking upstream details in default adapter responses.
36
+ exposeDetails: false,
37
+ }
38
+ };
39
+ }