@ttoss/http-server-mcp-openapi 0.2.14 → 0.3.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/dist/index.mjs CHANGED
@@ -1,7 +1,10 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- import { registerToolFromSchema } from "@ttoss/http-server-mcp";
2
+ import { getApiHeaders, registerToolFromSchema } from "@ttoss/http-server-mcp";
3
3
 
4
4
  //#region src/schema.ts
5
+ var isRecord = value => {
6
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7
+ };
5
8
  var getAlternativeSchemas = schema => {
6
9
  if (Array.isArray(schema.oneOf) && schema.oneOf.length > 0) return schema.oneOf;
7
10
  if (Array.isArray(schema.anyOf) && schema.anyOf.length > 0) return schema.anyOf;
@@ -23,36 +26,96 @@ var mergeResolvedSchemas = resolvedAlternatives => {
23
26
  required: requiredIntersection && requiredIntersection.length > 0 ? requiredIntersection : void 0
24
27
  };
25
28
  };
29
+ var stripDotSlash = file => {
30
+ return file.replace(/^\.\//, "");
31
+ };
32
+ var findDocument = args => {
33
+ if (!args.documents) return void 0;
34
+ const wanted = stripDotSlash(args.file);
35
+ for (const [key, document] of Object.entries(args.documents)) if (stripDotSlash(key) === wanted) return document;
36
+ };
37
+ /** Follows an RFC 6901 JSON pointer (`/components/schemas/Tag`) into a value. */
38
+ var followPointer = args => {
39
+ const tokens = args.pointer.split("/").slice(1);
40
+ let current = args.root;
41
+ for (const rawToken of tokens) {
42
+ if (!isRecord(current) && !Array.isArray(current)) return void 0;
43
+ const token = decodeURIComponent(rawToken).replace(/~1/g, "/").replace(/~0/g, "~");
44
+ current = current[token];
45
+ }
46
+ return current;
47
+ };
48
+ /** Stable identity for each document, so cycle keys never collide across files. */
49
+ var documentIds = /* @__PURE__ */new WeakMap();
50
+ var nextDocumentId = 0;
51
+ var documentId = spec => {
52
+ const known = documentIds.get(spec);
53
+ if (known !== void 0) return known;
54
+ nextDocumentId += 1;
55
+ documentIds.set(spec, nextDocumentId);
56
+ return nextDocumentId;
57
+ };
58
+ /**
59
+ * Resolves a `$ref` to its target and the document the target lives in (the
60
+ * scope its own nested refs resolve against). A ref without a file part
61
+ * (`#/components/schemas/X`) points into the current document; one with a
62
+ * file part (`./tags.yaml#/components/schemas/Tag`) points into the matching
63
+ * entry of `documents`. Returns `undefined` when the target does not exist.
64
+ */
65
+ var resolveRef = args => {
66
+ const hashIndex = args.ref.indexOf("#");
67
+ const file = hashIndex === -1 ? args.ref : args.ref.slice(0, hashIndex);
68
+ const pointer = hashIndex === -1 ? "" : args.ref.slice(hashIndex + 1);
69
+ const spec = file === "" ? args.scope.spec : findDocument({
70
+ file,
71
+ documents: args.scope.documents
72
+ });
73
+ if (!spec) return void 0;
74
+ const value = followPointer({
75
+ root: spec,
76
+ pointer
77
+ });
78
+ if (value === void 0) return void 0;
79
+ return {
80
+ value,
81
+ scope: {
82
+ spec,
83
+ documents: args.scope.documents
84
+ },
85
+ key: `${documentId(spec)}#${pointer}`
86
+ };
87
+ };
26
88
  var dereferenceValue = args => {
27
89
  const {
28
90
  value,
29
- spec,
91
+ scope,
30
92
  seenRefs
31
93
  } = args;
32
94
  if (Array.isArray(value)) return value.map(item => {
33
95
  return dereferenceValue({
34
96
  value: item,
35
- spec,
97
+ scope,
36
98
  seenRefs
37
99
  });
38
100
  });
39
- if (value && typeof value === "object") {
40
- const obj = value;
41
- if (typeof obj.$ref === "string") {
42
- const refName = obj.$ref.replace("#/components/schemas/", "");
43
- if (seenRefs.has(refName)) return {};
44
- const resolved = spec.components?.schemas?.[refName];
45
- if (resolved === void 0) return {};
101
+ if (isRecord(value)) {
102
+ if (typeof value.$ref === "string") {
103
+ const target = resolveRef({
104
+ ref: value.$ref,
105
+ scope
106
+ });
107
+ if (!target) return {};
108
+ if (seenRefs.has(target.key)) return {};
46
109
  return dereferenceValue({
47
- value: resolved,
48
- spec,
49
- seenRefs: new Set(seenRefs).add(refName)
110
+ value: target.value,
111
+ scope: target.scope,
112
+ seenRefs: new Set(seenRefs).add(target.key)
50
113
  });
51
114
  }
52
115
  const result = {};
53
- for (const [key, entryValue] of Object.entries(obj)) result[key] = dereferenceValue({
116
+ for (const [key, entryValue] of Object.entries(value)) result[key] = dereferenceValue({
54
117
  value: entryValue,
55
- spec,
118
+ scope,
56
119
  seenRefs
57
120
  });
58
121
  return result;
@@ -64,12 +127,17 @@ var dereferenceValue = args => {
64
127
  * `properties`, `items`, `oneOf`, `anyOf`, etc.), producing a self-contained
65
128
  * schema safe to hand to an MCP client or LLM provider as a tool definition —
66
129
  * provider tool schemas have no `components` section to resolve refs against.
130
+ *
131
+ * Refs into other files resolve against `documents`; see {@link OpenApiDocuments}.
67
132
  */
68
- var dereferenceSchema = (schema, spec) => {
133
+ var dereferenceSchema = (schema, spec, documents) => {
69
134
  if (!schema) return schema;
70
135
  return dereferenceValue({
71
136
  value: schema,
72
- spec,
137
+ scope: {
138
+ spec,
139
+ documents
140
+ },
73
141
  seenRefs: /* @__PURE__ */new Set()
74
142
  });
75
143
  };
@@ -78,25 +146,39 @@ var dereferenceSchema = (schema, spec) => {
78
146
  * and merges `oneOf` / `anyOf` alternatives (union of properties, intersection
79
147
  * of `required`) so the caller sees one flat property set.
80
148
  */
81
- var resolveSchema = (schema, spec) => {
149
+ var resolveSchema = (schema, spec, documents) => {
82
150
  if (!schema) return {};
83
151
  if (typeof schema.$ref === "string") {
84
- const refName = schema.$ref.replace("#/components/schemas/", "");
85
- const resolved = spec.components?.schemas?.[refName];
86
- return resolveSchema(resolved, spec);
152
+ const target = resolveRef({
153
+ ref: schema.$ref,
154
+ scope: {
155
+ spec,
156
+ documents
157
+ }
158
+ });
159
+ return resolveSchema(target?.value, target?.scope.spec ?? spec, documents);
87
160
  }
88
161
  const alternatives = getAlternativeSchemas(schema);
89
162
  if (alternatives) return mergeResolvedSchemas(alternatives.map(candidate => {
90
- return resolveSchema(candidate, spec);
163
+ return resolveSchema(candidate, spec, documents);
91
164
  }));
92
165
  return schema;
93
166
  };
94
- /** Follows a parameter `$ref` into `components.parameters`, if present. */
95
- var resolveParameter = (param, spec) => {
167
+ /**
168
+ * Follows a parameter `$ref` — into `components.parameters`, or into another
169
+ * file through `documents` — if present.
170
+ */
171
+ var resolveParameter = (param, spec, documents) => {
96
172
  if (!param) return {};
97
173
  if (typeof param.$ref === "string") {
98
- const refName = param.$ref.replace("#/components/parameters/", "");
99
- return spec.components?.parameters?.[refName] || {};
174
+ const target = resolveRef({
175
+ ref: param.$ref,
176
+ scope: {
177
+ spec,
178
+ documents
179
+ }
180
+ });
181
+ return isRecord(target?.value) ? target.value : {};
100
182
  }
101
183
  return param;
102
184
  };
@@ -106,9 +188,9 @@ var buildPathFn = (pathTemplate, pathParams) => {
106
188
  let result = pathTemplate;
107
189
  for (const {
108
190
  name,
109
- camelName
191
+ argName
110
192
  } of pathParams) {
111
- const value = args[camelName];
193
+ const value = args[argName];
112
194
  if (value !== void 0) result = result.replace(`{${name}}`, encodeURIComponent(String(value)));
113
195
  }
114
196
  return result;
@@ -119,9 +201,6 @@ var NON_EXPLODED_DELIMITERS = {
119
201
  spaceDelimited: " ",
120
202
  pipeDelimited: "|"
121
203
  };
122
- var isRecord = value => {
123
- return typeof value === "object" && value !== null && !Array.isArray(value);
124
- };
125
204
  /**
126
205
  * Appends a `deepObject` value as bracketed keys — `filters[documentId][$eq]`.
127
206
  * OpenAPI only defines one level, but the APIs that ask for `deepObject`
@@ -209,7 +288,7 @@ var buildQueryFn = queryParams => {
209
288
  return args => {
210
289
  const search = new URLSearchParams();
211
290
  for (const param of queryParams) {
212
- const value = args[param.camelName];
291
+ const value = args[param.argName];
213
292
  if (value === void 0 || value === null) continue;
214
293
  appendQueryValue({
215
294
  search,
@@ -222,8 +301,8 @@ var buildQueryFn = queryParams => {
222
301
  };
223
302
  };
224
303
  /**
225
- * Builds a function that maps camelCase args back to a snake_case request
226
- * body, skipping `undefined` args. Returns `undefined` when the op has no body.
304
+ * Builds a function that maps tool args back to a request body keyed by the
305
+ * spec's property names, skipping `undefined` args. Returns `undefined` when the op has no body.
227
306
  */
228
307
  var buildBodyFn = bodyProps => {
229
308
  if (bodyProps.length === 0) return void 0;
@@ -231,8 +310,8 @@ var buildBodyFn = bodyProps => {
231
310
  const body = {};
232
311
  for (const {
233
312
  snakeName,
234
- camelName
235
- } of bodyProps) if (args[camelName] !== void 0) body[snakeName] = args[camelName];
313
+ argName
314
+ } of bodyProps) if (args[argName] !== void 0) body[snakeName] = args[argName];
236
315
  return body;
237
316
  };
238
317
  };
@@ -243,17 +322,93 @@ var DEFAULT_EXCLUDE_EXTENSION = "x-mcp-exclude";
243
322
  var DEFAULT_SERVER_MANAGED_EXTENSION = "x-mcp-server-managed";
244
323
 
245
324
  //#endregion
246
- //#region src/toolDefinitions.ts
325
+ //#region src/parameters.ts
247
326
  /**
248
327
  * Folds `_` and `-` separators into camelCase. OpenAPI operation and parameter
249
- * names may be snake_case (`agent_id`) or kebab-case (`list-tools`), and MCP
250
- * tool inputs are camelCase by convention, so both are folded here.
328
+ * names may be snake_case (`agent_id`) or kebab-case (`list-tools`); with the
329
+ * default `argumentNames: 'camelCase'` both are folded into tool arguments.
251
330
  */
252
331
  var snakeToCamel = str => {
253
332
  return str.replace(/[_-]([a-z])/g, (_, letter) => {
254
333
  return letter.toUpperCase();
255
334
  });
256
335
  };
336
+ var verbatim = name => {
337
+ return name;
338
+ };
339
+ var argNameMapper = argumentNames => {
340
+ return argumentNames === "verbatim" ? verbatim : snakeToCamel;
341
+ };
342
+ /**
343
+ * Deduplicates parameter entries by `name`, keeping the last occurrence. When
344
+ * path-item-level and operation-level parameters are concatenated (operation
345
+ * last), this makes the operation-level entry win — as the OpenAPI spec requires.
346
+ */
347
+ var dedupeByName = items => {
348
+ const byName = /* @__PURE__ */new Map();
349
+ for (const item of items) byName.set(item.name, item);
350
+ return [...byName.values()];
351
+ };
352
+ var extractPathParams = args => {
353
+ const toArgName = args.toArgName ?? snakeToCamel;
354
+ const flag = args.serverManagedExtension ?? "x-mcp-server-managed";
355
+ return dedupeByName((args.parameters || []).map(p => {
356
+ return resolveParameter(p, args.spec, args.documents);
357
+ }).filter(p => {
358
+ return p.in === "path";
359
+ }).map(p => {
360
+ return {
361
+ name: p.name || "",
362
+ argName: toArgName(p.name || ""),
363
+ serverManaged: Boolean(p[flag])
364
+ };
365
+ }));
366
+ };
367
+ var extractQueryParams = args => {
368
+ const toArgName = args.toArgName ?? snakeToCamel;
369
+ const flag = args.serverManagedExtension ?? "x-mcp-server-managed";
370
+ return dedupeByName((args.parameters || []).map(p => {
371
+ return resolveParameter(p, args.spec, args.documents);
372
+ }).filter(p => {
373
+ return p.in === "query";
374
+ }).map(p => {
375
+ return {
376
+ name: p.name || "",
377
+ argName: toArgName(p.name || ""),
378
+ description: p.description || "",
379
+ required: p.required || false,
380
+ type: p.schema?.type || "string",
381
+ style: p.style,
382
+ explode: p.explode,
383
+ serverManaged: Boolean(p[flag])
384
+ };
385
+ }));
386
+ };
387
+ /** Lists the path and query params flagged as server-managed. */
388
+ var collectServerManagedParameters = args => {
389
+ return [...args.pathParams.map(p => {
390
+ return {
391
+ ...p,
392
+ in: "path"
393
+ };
394
+ }), ...args.queryParams.map(p => {
395
+ return {
396
+ ...p,
397
+ in: "query"
398
+ };
399
+ })].filter(p => {
400
+ return p.serverManaged;
401
+ }).map(p => {
402
+ return {
403
+ name: p.name,
404
+ in: p.in,
405
+ argName: p.argName
406
+ };
407
+ });
408
+ };
409
+
410
+ //#endregion
411
+ //#region src/toolDefinitions.ts
257
412
  /** Converts a camelCase `operationId` to a kebab-case tool name. */
258
413
  var operationIdToToolName = operationId => {
259
414
  return operationId.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
@@ -269,11 +424,13 @@ var sanitizeDescription = description => {
269
424
  return (description || "").replace(/'/g, "\\'").replace(/\n/g, " ").trim();
270
425
  };
271
426
  /**
272
- * Builds a single body/query property's `JsonSchemaProperty`. Split out of
273
- * {@link buildInputSchema} to keep that function's size and branching down.
427
+ * Forwards a property's `oneOf` / `anyOf` / multi-entry `allOf` verbatim, or
428
+ * returns `undefined` when it declares none.
274
429
  */
275
- var buildTypedProperty = param => {
276
- const description = sanitizeDescription(param.description);
430
+ var buildComposedProperty = param => {
431
+ const {
432
+ description
433
+ } = param;
277
434
  if (param.oneOf && param.oneOf.length > 0) return {
278
435
  oneOf: param.oneOf,
279
436
  description
@@ -282,6 +439,25 @@ var buildTypedProperty = param => {
282
439
  anyOf: param.anyOf,
283
440
  description
284
441
  };
442
+ if (param.allOf && param.allOf.length > 0) return {
443
+ allOf: param.allOf,
444
+ description
445
+ };
446
+ };
447
+ /**
448
+ * Builds a single body/query property's `JsonSchemaProperty`. Split out of
449
+ * {@link buildInputSchema} to keep that function's size and branching down.
450
+ */
451
+ var buildTypedProperty = param => {
452
+ const description = sanitizeDescription(param.description);
453
+ const composed = buildComposedProperty({
454
+ ...param,
455
+ description
456
+ });
457
+ if (composed) return composed;
458
+ if (param.type === void 0) return {
459
+ description
460
+ };
285
461
  const jsonType = getJsonSchemaType(param.type);
286
462
  const finalType = param.nullable === true ? [jsonType, "null"] : jsonType;
287
463
  if (param.type === "array") return {
@@ -297,23 +473,29 @@ var buildTypedProperty = param => {
297
473
  };
298
474
  };
299
475
  var buildInputSchema = (pathParams, queryParams, bodyProps) => {
300
- const allParams = [...pathParams, ...queryParams, ...bodyProps];
476
+ const modelPathParams = pathParams.filter(p => {
477
+ return !p.serverManaged;
478
+ });
479
+ const modelQueryParams = queryParams.filter(p => {
480
+ return !p.serverManaged;
481
+ });
482
+ const allParams = [...modelPathParams, ...modelQueryParams, ...bodyProps];
301
483
  if (allParams.length === 0) return {
302
484
  type: "object"
303
485
  };
304
- const requiredFields = [...pathParams.map(p => {
305
- return p.camelName;
306
- }), ...queryParams.filter(p => {
486
+ const requiredFields = [...modelPathParams.map(p => {
487
+ return p.argName;
488
+ }), ...modelQueryParams.filter(p => {
307
489
  return p.required;
308
490
  }).map(p => {
309
- return p.camelName;
491
+ return p.argName;
310
492
  }), ...bodyProps.filter(p => {
311
493
  return p.required;
312
494
  }).map(p => {
313
- return p.camelName;
495
+ return p.argName;
314
496
  })];
315
497
  const properties = {};
316
- for (const param of allParams) properties[param.camelName] = "type" in param ? buildTypedProperty(param) : {
498
+ for (const param of allParams) properties[param.argName] = "description" in param ? buildTypedProperty(param) : {
317
499
  type: "string",
318
500
  description: ""
319
501
  };
@@ -325,48 +507,9 @@ var buildInputSchema = (pathParams, queryParams, bodyProps) => {
325
507
  } : {})
326
508
  };
327
509
  };
328
- /**
329
- * Deduplicates parameter entries by `name`, keeping the last occurrence. When
330
- * path-item-level and operation-level parameters are concatenated (operation
331
- * last), this makes the operation-level entry win — as the OpenAPI spec requires.
332
- */
333
- var dedupeByName = items => {
334
- const byName = /* @__PURE__ */new Map();
335
- for (const item of items) byName.set(item.name, item);
336
- return [...byName.values()];
337
- };
338
- var extractPathParams = args => {
339
- return dedupeByName((args.parameters || []).map(p => {
340
- return resolveParameter(p, args.spec);
341
- }).filter(p => {
342
- return p.in === "path";
343
- }).map(p => {
344
- return {
345
- name: p.name || "",
346
- camelName: snakeToCamel(p.name || "")
347
- };
348
- }));
349
- };
350
- var extractQueryParams = args => {
351
- return dedupeByName((args.parameters || []).map(p => {
352
- return resolveParameter(p, args.spec);
353
- }).filter(p => {
354
- return p.in === "query";
355
- }).map(p => {
356
- return {
357
- name: p.name || "",
358
- camelName: snakeToCamel(p.name || ""),
359
- description: p.description || "",
360
- required: p.required || false,
361
- type: p.schema?.type || "string",
362
- style: p.style,
363
- explode: p.explode
364
- };
365
- }));
366
- };
367
510
  var resolveBodySchema = args => {
368
511
  const rawBodySchema = args.requestBody?.content?.["application/json"]?.schema;
369
- return resolveSchema(dereferenceSchema(rawBodySchema, args.spec), args.spec);
512
+ return resolveSchema(dereferenceSchema(rawBodySchema, args.spec, args.documents), args.spec, args.documents);
370
513
  };
371
514
  /**
372
515
  * snake_case names of every top-level property an operation's request schema
@@ -376,23 +519,48 @@ var extractAcceptedBodyFields = args => {
376
519
  const bodySchema = resolveBodySchema(args);
377
520
  return Object.keys(bodySchema?.properties ?? {});
378
521
  };
522
+ var isPlainObject = value => {
523
+ return typeof value === "object" && value !== null && !Array.isArray(value);
524
+ };
525
+ /**
526
+ * Folds a single-entry `allOf` into the property that wraps it. OpenAPI
527
+ * declares `allOf: [{ $ref }]` so a property can carry its own `description`
528
+ * next to a referenced schema; without folding, the referenced `type`,
529
+ * `nullable` and `items` would be lost. Keys on the wrapper win over the
530
+ * referenced schema's. Multi-entry `allOf` is left intact.
531
+ */
532
+ var flattenSingleAllOf = schema => {
533
+ const {
534
+ allOf,
535
+ ...rest
536
+ } = schema;
537
+ if (!Array.isArray(allOf) || allOf.length !== 1) return schema;
538
+ const [entry] = allOf;
539
+ if (!isPlainObject(entry)) return rest;
540
+ return {
541
+ ...flattenSingleAllOf(entry),
542
+ ...rest
543
+ };
544
+ };
379
545
  var extractBodyProps = args => {
546
+ const toArgName = args.toArgName ?? snakeToCamel;
380
547
  const bodySchema = resolveBodySchema(args);
381
548
  if (!bodySchema?.properties) return [];
382
549
  return Object.entries(bodySchema.properties).filter(([, value]) => {
383
550
  return !value[args.serverManagedExtension];
384
551
  }).map(([key, value]) => {
385
- const val = value;
552
+ const val = flattenSingleAllOf(value);
386
553
  return {
387
554
  snakeName: key,
388
- camelName: snakeToCamel(key),
555
+ argName: toArgName(key),
389
556
  description: typeof val.description === "string" ? val.description : "",
390
557
  required: (bodySchema.required || []).includes(key),
391
- type: typeof val.type === "string" ? val.type : "string",
558
+ type: typeof val.type === "string" ? val.type : void 0,
392
559
  items: val.items,
393
560
  nullable: val.nullable === true,
394
561
  oneOf: Array.isArray(val.oneOf) ? val.oneOf : void 0,
395
- anyOf: Array.isArray(val.anyOf) ? val.anyOf : void 0
562
+ anyOf: Array.isArray(val.anyOf) ? val.anyOf : void 0,
563
+ allOf: Array.isArray(val.allOf) ? val.allOf : void 0
396
564
  };
397
565
  });
398
566
  };
@@ -409,23 +577,37 @@ var processOperation = args => {
409
577
  if (args.operation[args.options.excludeExtension]) return null;
410
578
  const toolName = operationIdToToolName(args.operation.operationId);
411
579
  const parameters = [...(args.pathItemParameters ?? []), ...(args.operation.parameters ?? [])];
580
+ const {
581
+ documents,
582
+ serverManagedExtension
583
+ } = args.options;
584
+ const toArgName = argNameMapper(args.options.argumentNames);
412
585
  const pathParams = extractPathParams({
413
586
  parameters,
414
- spec: args.spec
587
+ spec: args.spec,
588
+ documents,
589
+ toArgName,
590
+ serverManagedExtension
415
591
  });
416
592
  const queryParams = extractQueryParams({
417
593
  parameters,
418
- spec: args.spec
594
+ spec: args.spec,
595
+ documents,
596
+ toArgName,
597
+ serverManagedExtension
419
598
  });
420
599
  const bodyProps = extractBodyProps({
421
600
  requestBody: args.operation.requestBody,
422
601
  spec: args.spec,
423
- serverManagedExtension: args.options.serverManagedExtension
602
+ serverManagedExtension,
603
+ documents,
604
+ toArgName
424
605
  });
425
606
  const inputSchema = buildInputSchema(pathParams, queryParams, bodyProps);
426
607
  const acceptedBodyFields = extractAcceptedBodyFields({
427
608
  requestBody: args.operation.requestBody,
428
- spec: args.spec
609
+ spec: args.spec,
610
+ documents
429
611
  });
430
612
  return {
431
613
  name: toolName,
@@ -438,7 +620,11 @@ var processOperation = args => {
438
620
  query: buildQueryFn(queryParams),
439
621
  body: buildBodyFn(bodyProps),
440
622
  acceptedBodyFields,
441
- extensions: extractExtensions(args.operation)
623
+ extensions: extractExtensions(args.operation),
624
+ serverManagedParameters: collectServerManagedParameters({
625
+ pathParams,
626
+ queryParams
627
+ })
442
628
  };
443
629
  };
444
630
  var processPath = args => {
@@ -459,6 +645,15 @@ var processPath = args => {
459
645
  }
460
646
  return tools;
461
647
  };
648
+ /** Applies the defaults to {@link OpenApiToToolsOptions}. */
649
+ var resolveOptions = (options = {}) => {
650
+ return {
651
+ excludeExtension: options.excludeExtension ?? "x-mcp-exclude",
652
+ serverManagedExtension: options.serverManagedExtension ?? "x-mcp-server-managed",
653
+ argumentNames: options.argumentNames ?? "camelCase",
654
+ documents: options.documents
655
+ };
656
+ };
462
657
  /**
463
658
  * Translates one or more OpenAPI documents into REST-backed MCP tool
464
659
  * definitions. Each translatable operation (has an `operationId`, a supported
@@ -472,10 +667,7 @@ var processPath = args => {
472
667
  * ```
473
668
  */
474
669
  var openApiToToolDefinitions = args => {
475
- const options = {
476
- excludeExtension: args.options?.excludeExtension ?? "x-mcp-exclude",
477
- serverManagedExtension: args.options?.serverManagedExtension ?? "x-mcp-server-managed"
478
- };
670
+ const options = resolveOptions(args.options);
479
671
  const specs = Array.isArray(args.spec) ? args.spec : [args.spec];
480
672
  const tools = [];
481
673
  for (const spec of specs) {
@@ -492,13 +684,34 @@ var openApiToToolDefinitions = args => {
492
684
 
493
685
  //#endregion
494
686
  //#region src/registerOpenApiTools.ts
687
+ /** The text the default `toText` answers when the API returned no body. */
688
+ var NO_CONTENT_TEXT = "Succeeded. The operation returned no content.";
495
689
  var defaultToText = data => {
690
+ if (data === void 0 || data === "") return NO_CONTENT_TEXT;
496
691
  return typeof data === "string" ? data : JSON.stringify(data, null, 2);
497
692
  };
498
693
  /**
694
+ * Replaces whatever the model sent for server-managed parameters with the
695
+ * values `serverParameters` supplies, so the model can never set them.
696
+ */
697
+ var applyServerParameters = async args => {
698
+ const managed = args.tool.serverManagedParameters;
699
+ if (managed.length === 0) return args.handlerArgs;
700
+ const result = {
701
+ ...args.handlerArgs
702
+ };
703
+ for (const param of managed) delete result[param.argName];
704
+ const values = args.serverParameters ? await args.serverParameters({
705
+ tool: args.tool,
706
+ headers: args.headers
707
+ }) : {};
708
+ for (const param of managed) if (values[param.name] !== void 0) result[param.argName] = values[param.name];
709
+ return result;
710
+ };
711
+ /**
499
712
  * Derives MCP tools from OpenAPI document(s) and registers each on the given
500
- * MCP server. Every tool's handler resolves the incoming camelCase args into a
501
- * concrete HTTP request and delegates execution to `callApi`.
713
+ * MCP server. Every tool's handler resolves the incoming args into a concrete
714
+ * HTTP request and delegates execution to `callApi`.
502
715
  *
503
716
  * @returns The list of {@link ToolDefinition} that were registered.
504
717
  *
@@ -512,10 +725,10 @@ var defaultToText = data => {
512
725
  * registerOpenApiTools({
513
726
  * server,
514
727
  * spec: myOpenApiDocument,
515
- * callApi: async ({ method, url, body }) => {
728
+ * callApi: async ({ method, url, body, headers }) => {
516
729
  * const res = await fetch(`https://api.example.com${url}`, {
517
730
  * method,
518
- * headers: { 'Content-Type': 'application/json' },
731
+ * headers: { ...headers, 'Content-Type': 'application/json' },
519
732
  * body: body ? JSON.stringify(body) : undefined,
520
733
  * });
521
734
  * return res.json();
@@ -533,7 +746,14 @@ var registerOpenApiTools = args => {
533
746
  name: tool.name,
534
747
  description: tool.description,
535
748
  inputSchema: tool.inputSchema,
536
- handler: async handlerArgs => {
749
+ handler: async rawArgs => {
750
+ const headers = getApiHeaders();
751
+ const handlerArgs = await applyServerParameters({
752
+ tool,
753
+ handlerArgs: rawArgs,
754
+ headers,
755
+ serverParameters: args.serverParameters
756
+ });
537
757
  const url = tool.path(handlerArgs) + (tool.query ? tool.query(handlerArgs) : "");
538
758
  return {
539
759
  content: [{
@@ -542,7 +762,8 @@ var registerOpenApiTools = args => {
542
762
  method: tool.method,
543
763
  url,
544
764
  body: tool.body ? tool.body(handlerArgs) : void 0,
545
- tool
765
+ tool,
766
+ headers
546
767
  }))
547
768
  }]
548
769
  };
@@ -552,4 +773,4 @@ var registerOpenApiTools = args => {
552
773
  };
553
774
 
554
775
  //#endregion
555
- export { DEFAULT_EXCLUDE_EXTENSION, DEFAULT_SERVER_MANAGED_EXTENSION, buildBodyFn, buildInputSchema, buildPathFn, buildQueryFn, dereferenceSchema, extractAcceptedBodyFields, extractBodyProps, extractPathParams, extractQueryParams, getJsonSchemaType, openApiToToolDefinitions, operationIdToToolName, processOperation, processPath, registerOpenApiTools, resolveParameter, resolveSchema, snakeToCamel };
776
+ export { DEFAULT_EXCLUDE_EXTENSION, DEFAULT_SERVER_MANAGED_EXTENSION, NO_CONTENT_TEXT, buildBodyFn, buildInputSchema, buildPathFn, buildQueryFn, dereferenceSchema, extractAcceptedBodyFields, extractBodyProps, extractPathParams, extractQueryParams, getJsonSchemaType, openApiToToolDefinitions, operationIdToToolName, processOperation, processPath, registerOpenApiTools, resolveParameter, resolveSchema, snakeToCamel };