@ttoss/http-server-mcp-openapi 0.4.0 → 0.5.1
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/README.md +9 -2
- package/dist/index.cjs +153 -69
- package/dist/index.d.cts +42 -28
- package/dist/index.d.mts +42 -28
- package/dist/index.mjs +153 -70
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -157,8 +157,9 @@ registerOpenApiTools({
|
|
|
157
157
|
|
|
158
158
|
A value flagged with `serverManagedExtension` is never offered to the model:
|
|
159
159
|
|
|
160
|
-
- A **request-body property** is hidden from `inputSchema` and
|
|
161
|
-
API sets it itself). It still appears in
|
|
160
|
+
- A **request-body property** is hidden from `inputSchema` and, unless pinned,
|
|
161
|
+
never sent (the API sets it itself). It still appears in
|
|
162
|
+
`acceptedBodyFields`.
|
|
162
163
|
- A **path or query parameter** is hidden from `inputSchema` and listed in
|
|
163
164
|
`tool.serverManagedParameters`. `registerOpenApiTools` discards anything the
|
|
164
165
|
model sent for it and fills it from `serverParameters`, keyed by spec name:
|
|
@@ -183,6 +184,12 @@ A **string** extension value pins the parameter: `wait` declared with
|
|
|
183
184
|
`serverParameters`, and the entry in `serverManagedParameters` carries it as
|
|
184
185
|
`value`.
|
|
185
186
|
|
|
187
|
+
A pinned **request-body property** is sent by `tool.body`, over anything in the
|
|
188
|
+
args, as the JSON type its schema declares: `'true'` on a `boolean` is `true`,
|
|
189
|
+
`'3'` on an `integer` is `3`. A pin its type cannot hold (`'yes'` on a
|
|
190
|
+
`boolean`, any pin on an `object`, `array` or untyped property) throws while
|
|
191
|
+
the tools are generated, naming the operation and property.
|
|
192
|
+
|
|
186
193
|
### Reading custom extensions
|
|
187
194
|
|
|
188
195
|
Every `x-` prefixed extension on an operation is forwarded verbatim on
|
package/dist/index.cjs
CHANGED
|
@@ -368,6 +368,57 @@ var withPinned = args => {
|
|
|
368
368
|
});
|
|
369
369
|
};
|
|
370
370
|
};
|
|
371
|
+
var parseNumber = value => {
|
|
372
|
+
const parsed = Number(value);
|
|
373
|
+
return value.trim() !== "" && Number.isFinite(parsed) ? parsed : void 0;
|
|
374
|
+
};
|
|
375
|
+
/** Each JSON type a pin can hold, and how its text reads as that type. */
|
|
376
|
+
var PIN_PARSERS = {
|
|
377
|
+
string: value => {
|
|
378
|
+
return value;
|
|
379
|
+
},
|
|
380
|
+
boolean: value => {
|
|
381
|
+
if (value === "true") return true;
|
|
382
|
+
if (value === "false") return false;
|
|
383
|
+
},
|
|
384
|
+
number: parseNumber,
|
|
385
|
+
integer: value => {
|
|
386
|
+
const parsed = parseNumber(value);
|
|
387
|
+
return parsed !== void 0 && Number.isInteger(parsed) ? parsed : void 0;
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
/**
|
|
391
|
+
* Reads a pinned body value as the JSON type its schema declares. A body is
|
|
392
|
+
* JSON, so `'true'` on a boolean must reach the API as `true`; a query string
|
|
393
|
+
* carries the text either way. A pin the type cannot hold is a spec error.
|
|
394
|
+
*/
|
|
395
|
+
var typedPin = args => {
|
|
396
|
+
const {
|
|
397
|
+
value,
|
|
398
|
+
type,
|
|
399
|
+
where
|
|
400
|
+
} = args;
|
|
401
|
+
const typed = (typeof type === "string" ? PIN_PARSERS[type] : void 0)?.(value);
|
|
402
|
+
if (typed !== void 0) return typed;
|
|
403
|
+
throw new Error(`${where}: cannot pin '${value}' on a property of type ${JSON.stringify(type ?? "any")}; a pinned body property must be a boolean, integer, number or string that holds the value.`);
|
|
404
|
+
};
|
|
405
|
+
/**
|
|
406
|
+
* Wraps a `body` builder so pinned values replace whatever the args carry. An
|
|
407
|
+
* operation whose only body properties are pinned still gets a builder.
|
|
408
|
+
*/
|
|
409
|
+
var withPinnedBody = args => {
|
|
410
|
+
const {
|
|
411
|
+
build,
|
|
412
|
+
pinned
|
|
413
|
+
} = args;
|
|
414
|
+
if (Object.keys(pinned).length === 0) return build;
|
|
415
|
+
return values => {
|
|
416
|
+
return {
|
|
417
|
+
...(build ? build(values) : {}),
|
|
418
|
+
...pinned
|
|
419
|
+
};
|
|
420
|
+
};
|
|
421
|
+
};
|
|
371
422
|
var isPlainObject$1 = value => {
|
|
372
423
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
373
424
|
};
|
|
@@ -505,6 +556,94 @@ var collectServerManagedParameters = args => {
|
|
|
505
556
|
});
|
|
506
557
|
};
|
|
507
558
|
|
|
559
|
+
//#endregion
|
|
560
|
+
//#region src/body.ts
|
|
561
|
+
var resolveBodySchema = args => {
|
|
562
|
+
const rawBodySchema = args.requestBody?.content?.["application/json"]?.schema;
|
|
563
|
+
return resolveSchema(dereferenceSchema(rawBodySchema, args.spec, args.documents), args.spec, args.documents);
|
|
564
|
+
};
|
|
565
|
+
/**
|
|
566
|
+
* snake_case names of every top-level property an operation's request schema
|
|
567
|
+
* declares, including server-managed ones.
|
|
568
|
+
*/
|
|
569
|
+
var extractAcceptedBodyFields = args => {
|
|
570
|
+
const bodySchema = resolveBodySchema(args);
|
|
571
|
+
return Object.keys(bodySchema?.properties ?? {});
|
|
572
|
+
};
|
|
573
|
+
var isPlainObject = value => {
|
|
574
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
575
|
+
};
|
|
576
|
+
/**
|
|
577
|
+
* Folds a single-entry `allOf` into the property that wraps it. OpenAPI
|
|
578
|
+
* declares `allOf: [{ $ref }]` so a property can carry its own `description`
|
|
579
|
+
* next to a referenced schema; without folding, the referenced `type`,
|
|
580
|
+
* `nullable` and `items` would be lost. Keys on the wrapper win over the
|
|
581
|
+
* referenced schema's. Multi-entry `allOf` is left intact.
|
|
582
|
+
*/
|
|
583
|
+
var flattenSingleAllOf = schema => {
|
|
584
|
+
const {
|
|
585
|
+
allOf,
|
|
586
|
+
...rest
|
|
587
|
+
} = schema;
|
|
588
|
+
if (!Array.isArray(allOf) || allOf.length !== 1) return schema;
|
|
589
|
+
const [entry] = allOf;
|
|
590
|
+
if (!isPlainObject(entry)) return rest;
|
|
591
|
+
return {
|
|
592
|
+
...flattenSingleAllOf(entry),
|
|
593
|
+
...rest
|
|
594
|
+
};
|
|
595
|
+
};
|
|
596
|
+
var extractBodyProps = args => {
|
|
597
|
+
const toArgName = args.toArgName ?? snakeToCamel;
|
|
598
|
+
const bodySchema = resolveBodySchema(args);
|
|
599
|
+
if (!bodySchema?.properties) return [];
|
|
600
|
+
return Object.entries(bodySchema.properties).filter(([, value]) => {
|
|
601
|
+
return !readServerManaged({
|
|
602
|
+
node: value,
|
|
603
|
+
extension: args.serverManagedExtension
|
|
604
|
+
}).managed;
|
|
605
|
+
}).map(([key, value]) => {
|
|
606
|
+
const val = flattenSingleAllOf(value);
|
|
607
|
+
return {
|
|
608
|
+
snakeName: key,
|
|
609
|
+
argName: toArgName(key),
|
|
610
|
+
description: typeof val.description === "string" ? val.description : "",
|
|
611
|
+
required: (bodySchema.required || []).includes(key),
|
|
612
|
+
type: typeof val.type === "string" ? val.type : void 0,
|
|
613
|
+
items: val.items,
|
|
614
|
+
nullable: val.nullable === true,
|
|
615
|
+
oneOf: Array.isArray(val.oneOf) ? val.oneOf : void 0,
|
|
616
|
+
anyOf: Array.isArray(val.anyOf) ? val.anyOf : void 0,
|
|
617
|
+
allOf: Array.isArray(val.allOf) ? val.allOf : void 0
|
|
618
|
+
};
|
|
619
|
+
});
|
|
620
|
+
};
|
|
621
|
+
/**
|
|
622
|
+
* The value each server-managed body property pins, typed by its schema and
|
|
623
|
+
* keyed by its spec name. A property whose extension is not a string pins
|
|
624
|
+
* nothing: it is only hidden, for the consumer to fill.
|
|
625
|
+
*/
|
|
626
|
+
var extractPinnedBody = args => {
|
|
627
|
+
const bodySchema = resolveBodySchema(args);
|
|
628
|
+
const pinned = {};
|
|
629
|
+
for (const [key, value] of Object.entries(bodySchema?.properties ?? {})) {
|
|
630
|
+
const node = value;
|
|
631
|
+
const {
|
|
632
|
+
value: pin
|
|
633
|
+
} = readServerManaged({
|
|
634
|
+
node,
|
|
635
|
+
extension: args.serverManagedExtension
|
|
636
|
+
});
|
|
637
|
+
if (pin === void 0) continue;
|
|
638
|
+
pinned[key] = typedPin({
|
|
639
|
+
value: pin,
|
|
640
|
+
type: flattenSingleAllOf(node).type,
|
|
641
|
+
where: `${args.operationId} body property '${key}'`
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
return pinned;
|
|
645
|
+
};
|
|
646
|
+
|
|
508
647
|
//#endregion
|
|
509
648
|
//#region src/toolDefinitions.ts
|
|
510
649
|
/** Converts a camelCase `operationId` to a kebab-case tool name. */
|
|
@@ -605,66 +744,6 @@ var buildInputSchema = (pathParams, queryParams, bodyProps) => {
|
|
|
605
744
|
} : {})
|
|
606
745
|
};
|
|
607
746
|
};
|
|
608
|
-
var resolveBodySchema = args => {
|
|
609
|
-
const rawBodySchema = args.requestBody?.content?.["application/json"]?.schema;
|
|
610
|
-
return resolveSchema(dereferenceSchema(rawBodySchema, args.spec, args.documents), args.spec, args.documents);
|
|
611
|
-
};
|
|
612
|
-
/**
|
|
613
|
-
* snake_case names of every top-level property an operation's request schema
|
|
614
|
-
* declares, including server-managed ones.
|
|
615
|
-
*/
|
|
616
|
-
var extractAcceptedBodyFields = args => {
|
|
617
|
-
const bodySchema = resolveBodySchema(args);
|
|
618
|
-
return Object.keys(bodySchema?.properties ?? {});
|
|
619
|
-
};
|
|
620
|
-
var isPlainObject = value => {
|
|
621
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
622
|
-
};
|
|
623
|
-
/**
|
|
624
|
-
* Folds a single-entry `allOf` into the property that wraps it. OpenAPI
|
|
625
|
-
* declares `allOf: [{ $ref }]` so a property can carry its own `description`
|
|
626
|
-
* next to a referenced schema; without folding, the referenced `type`,
|
|
627
|
-
* `nullable` and `items` would be lost. Keys on the wrapper win over the
|
|
628
|
-
* referenced schema's. Multi-entry `allOf` is left intact.
|
|
629
|
-
*/
|
|
630
|
-
var flattenSingleAllOf = schema => {
|
|
631
|
-
const {
|
|
632
|
-
allOf,
|
|
633
|
-
...rest
|
|
634
|
-
} = schema;
|
|
635
|
-
if (!Array.isArray(allOf) || allOf.length !== 1) return schema;
|
|
636
|
-
const [entry] = allOf;
|
|
637
|
-
if (!isPlainObject(entry)) return rest;
|
|
638
|
-
return {
|
|
639
|
-
...flattenSingleAllOf(entry),
|
|
640
|
-
...rest
|
|
641
|
-
};
|
|
642
|
-
};
|
|
643
|
-
var extractBodyProps = args => {
|
|
644
|
-
const toArgName = args.toArgName ?? snakeToCamel;
|
|
645
|
-
const bodySchema = resolveBodySchema(args);
|
|
646
|
-
if (!bodySchema?.properties) return [];
|
|
647
|
-
return Object.entries(bodySchema.properties).filter(([, value]) => {
|
|
648
|
-
return !readServerManaged({
|
|
649
|
-
node: value,
|
|
650
|
-
extension: args.serverManagedExtension
|
|
651
|
-
}).managed;
|
|
652
|
-
}).map(([key, value]) => {
|
|
653
|
-
const val = flattenSingleAllOf(value);
|
|
654
|
-
return {
|
|
655
|
-
snakeName: key,
|
|
656
|
-
argName: toArgName(key),
|
|
657
|
-
description: typeof val.description === "string" ? val.description : "",
|
|
658
|
-
required: (bodySchema.required || []).includes(key),
|
|
659
|
-
type: typeof val.type === "string" ? val.type : void 0,
|
|
660
|
-
items: val.items,
|
|
661
|
-
nullable: val.nullable === true,
|
|
662
|
-
oneOf: Array.isArray(val.oneOf) ? val.oneOf : void 0,
|
|
663
|
-
anyOf: Array.isArray(val.anyOf) ? val.anyOf : void 0,
|
|
664
|
-
allOf: Array.isArray(val.allOf) ? val.allOf : void 0
|
|
665
|
-
};
|
|
666
|
-
});
|
|
667
|
-
};
|
|
668
747
|
/** Collects every `x-` prefixed extension declared on the operation. */
|
|
669
748
|
var extractExtensions = operation => {
|
|
670
749
|
const extensions = {};
|
|
@@ -697,11 +776,14 @@ var processOperation = args => {
|
|
|
697
776
|
toArgName,
|
|
698
777
|
serverManagedExtension
|
|
699
778
|
});
|
|
700
|
-
const
|
|
779
|
+
const bodyArgs = {
|
|
701
780
|
requestBody: args.operation.requestBody,
|
|
702
781
|
spec: args.spec,
|
|
703
782
|
serverManagedExtension,
|
|
704
|
-
documents
|
|
783
|
+
documents
|
|
784
|
+
};
|
|
785
|
+
const bodyProps = extractBodyProps({
|
|
786
|
+
...bodyArgs,
|
|
705
787
|
toArgName
|
|
706
788
|
});
|
|
707
789
|
const inputSchema = buildInputSchema(pathParams, queryParams, bodyProps);
|
|
@@ -710,11 +792,6 @@ var processOperation = args => {
|
|
|
710
792
|
queryParams
|
|
711
793
|
});
|
|
712
794
|
const pinned = pinnedArgs(serverManagedParameters);
|
|
713
|
-
const acceptedBodyFields = extractAcceptedBodyFields({
|
|
714
|
-
requestBody: args.operation.requestBody,
|
|
715
|
-
spec: args.spec,
|
|
716
|
-
documents
|
|
717
|
-
});
|
|
718
795
|
return {
|
|
719
796
|
name: toolName,
|
|
720
797
|
description: sanitizeDescription(args.operation.description),
|
|
@@ -730,8 +807,14 @@ var processOperation = args => {
|
|
|
730
807
|
build: buildQueryFn(queryParams),
|
|
731
808
|
pinned
|
|
732
809
|
}),
|
|
733
|
-
body:
|
|
734
|
-
|
|
810
|
+
body: withPinnedBody({
|
|
811
|
+
build: buildBodyFn(bodyProps),
|
|
812
|
+
pinned: extractPinnedBody({
|
|
813
|
+
...bodyArgs,
|
|
814
|
+
operationId: args.operation.operationId
|
|
815
|
+
})
|
|
816
|
+
}),
|
|
817
|
+
acceptedBodyFields: extractAcceptedBodyFields(bodyArgs),
|
|
735
818
|
extensions: extractExtensions(args.operation),
|
|
736
819
|
serverManagedParameters
|
|
737
820
|
};
|
|
@@ -893,6 +976,7 @@ exports.dereferenceSchema = dereferenceSchema;
|
|
|
893
976
|
exports.extractAcceptedBodyFields = extractAcceptedBodyFields;
|
|
894
977
|
exports.extractBodyProps = extractBodyProps;
|
|
895
978
|
exports.extractPathParams = extractPathParams;
|
|
979
|
+
exports.extractPinnedBody = extractPinnedBody;
|
|
896
980
|
exports.extractQueryParams = extractQueryParams;
|
|
897
981
|
exports.getJsonSchemaType = getJsonSchemaType;
|
|
898
982
|
exports.openApiToToolDefinitions = openApiToToolDefinitions;
|
package/dist/index.d.cts
CHANGED
|
@@ -232,6 +232,47 @@ declare const extractQueryParams: (args: ExtractParamsArgs) => Array<{
|
|
|
232
232
|
pinnedValue?: string;
|
|
233
233
|
}>;
|
|
234
234
|
//#endregion
|
|
235
|
+
//#region src/body.d.ts
|
|
236
|
+
/**
|
|
237
|
+
* snake_case names of every top-level property an operation's request schema
|
|
238
|
+
* declares, including server-managed ones.
|
|
239
|
+
*/
|
|
240
|
+
declare const extractAcceptedBodyFields: (args: {
|
|
241
|
+
requestBody?: RequestBodySpec;
|
|
242
|
+
spec: OpenApiSpec;
|
|
243
|
+
documents?: OpenApiDocuments;
|
|
244
|
+
}) => string[];
|
|
245
|
+
declare const extractBodyProps: (args: {
|
|
246
|
+
requestBody?: RequestBodySpec;
|
|
247
|
+
spec: OpenApiSpec;
|
|
248
|
+
serverManagedExtension: ServerManagedExtension;
|
|
249
|
+
documents?: OpenApiDocuments; /** Maps a spec name to its tool argument name. @default snakeToCamel */
|
|
250
|
+
toArgName?: ToArgName;
|
|
251
|
+
}) => Array<{
|
|
252
|
+
snakeName: string;
|
|
253
|
+
argName: string;
|
|
254
|
+
description: string;
|
|
255
|
+
required: boolean;
|
|
256
|
+
type?: string;
|
|
257
|
+
items?: unknown;
|
|
258
|
+
nullable: boolean;
|
|
259
|
+
oneOf?: unknown[];
|
|
260
|
+
anyOf?: unknown[];
|
|
261
|
+
allOf?: unknown[];
|
|
262
|
+
}>;
|
|
263
|
+
/**
|
|
264
|
+
* The value each server-managed body property pins, typed by its schema and
|
|
265
|
+
* keyed by its spec name. A property whose extension is not a string pins
|
|
266
|
+
* nothing: it is only hidden, for the consumer to fill.
|
|
267
|
+
*/
|
|
268
|
+
declare const extractPinnedBody: (args: {
|
|
269
|
+
requestBody?: RequestBodySpec;
|
|
270
|
+
spec: OpenApiSpec;
|
|
271
|
+
serverManagedExtension: ServerManagedExtension;
|
|
272
|
+
documents?: OpenApiDocuments;
|
|
273
|
+
operationId: string;
|
|
274
|
+
}) => Record<string, string | number | boolean>;
|
|
275
|
+
//#endregion
|
|
235
276
|
//#region src/registerOpenApiTools.d.ts
|
|
236
277
|
/** The resolved HTTP request a tool call maps to, before transport concerns. */
|
|
237
278
|
interface ResolvedRequest {
|
|
@@ -417,33 +458,6 @@ declare const buildInputSchema: (pathParams: Array<{
|
|
|
417
458
|
anyOf?: unknown[];
|
|
418
459
|
allOf?: unknown[];
|
|
419
460
|
}>) => JsonObjectSchema;
|
|
420
|
-
/**
|
|
421
|
-
* snake_case names of every top-level property an operation's request schema
|
|
422
|
-
* declares, including server-managed ones.
|
|
423
|
-
*/
|
|
424
|
-
declare const extractAcceptedBodyFields: (args: {
|
|
425
|
-
requestBody?: RequestBodySpec;
|
|
426
|
-
spec: OpenApiSpec;
|
|
427
|
-
documents?: OpenApiDocuments;
|
|
428
|
-
}) => string[];
|
|
429
|
-
declare const extractBodyProps: (args: {
|
|
430
|
-
requestBody?: RequestBodySpec;
|
|
431
|
-
spec: OpenApiSpec;
|
|
432
|
-
serverManagedExtension: ServerManagedExtension;
|
|
433
|
-
documents?: OpenApiDocuments; /** Maps a spec name to its tool argument name. @default snakeToCamel */
|
|
434
|
-
toArgName?: ToArgName;
|
|
435
|
-
}) => Array<{
|
|
436
|
-
snakeName: string;
|
|
437
|
-
argName: string;
|
|
438
|
-
description: string;
|
|
439
|
-
required: boolean;
|
|
440
|
-
type?: string;
|
|
441
|
-
items?: unknown;
|
|
442
|
-
nullable: boolean;
|
|
443
|
-
oneOf?: unknown[];
|
|
444
|
-
anyOf?: unknown[];
|
|
445
|
-
allOf?: unknown[];
|
|
446
|
-
}>;
|
|
447
461
|
declare const processOperation: (args: {
|
|
448
462
|
pathTemplate: string;
|
|
449
463
|
method: string;
|
|
@@ -484,4 +498,4 @@ declare const openApiToToolDefinitions: (args: {
|
|
|
484
498
|
options?: OpenApiToToolsOptions;
|
|
485
499
|
}) => ToolDefinition[];
|
|
486
500
|
//#endregion
|
|
487
|
-
export { DEFAULT_EXCLUDE_EXTENSION, DEFAULT_SERVER_MANAGED_EXTENSION, type JsonSchemaProperty, NO_CONTENT_TEXT, type OpenApiDocuments, type OpenApiSpec, type OpenApiToToolsOptions, type OperationSpec, type QueryParamSerialization, type RegisterOpenApiToolsArgs, type RequestBodySpec, type ResolvedParameter, type ResolvedRequest, type ResolvedToolOptions, type ServerManagedParameter, type ToolDefinition, buildBodyFn, buildInputSchema, buildPathFn, buildQueryFn, dereferenceSchema, extractAcceptedBodyFields, extractBodyProps, extractPathParams, extractQueryParams, getJsonSchemaType, openApiToToolDefinitions, operationIdToToolName, processOperation, processPath, registerOpenApiTools, resolveParameter, resolveSchema, snakeToCamel };
|
|
501
|
+
export { DEFAULT_EXCLUDE_EXTENSION, DEFAULT_SERVER_MANAGED_EXTENSION, type JsonSchemaProperty, NO_CONTENT_TEXT, type OpenApiDocuments, type OpenApiSpec, type OpenApiToToolsOptions, type OperationSpec, type QueryParamSerialization, type RegisterOpenApiToolsArgs, type RequestBodySpec, type ResolvedParameter, type ResolvedRequest, type ResolvedToolOptions, type ServerManagedParameter, type ToolDefinition, buildBodyFn, buildInputSchema, buildPathFn, buildQueryFn, dereferenceSchema, extractAcceptedBodyFields, extractBodyProps, extractPathParams, extractPinnedBody, extractQueryParams, getJsonSchemaType, openApiToToolDefinitions, operationIdToToolName, processOperation, processPath, registerOpenApiTools, resolveParameter, resolveSchema, snakeToCamel };
|
package/dist/index.d.mts
CHANGED
|
@@ -232,6 +232,47 @@ declare const extractQueryParams: (args: ExtractParamsArgs) => Array<{
|
|
|
232
232
|
pinnedValue?: string;
|
|
233
233
|
}>;
|
|
234
234
|
//#endregion
|
|
235
|
+
//#region src/body.d.ts
|
|
236
|
+
/**
|
|
237
|
+
* snake_case names of every top-level property an operation's request schema
|
|
238
|
+
* declares, including server-managed ones.
|
|
239
|
+
*/
|
|
240
|
+
declare const extractAcceptedBodyFields: (args: {
|
|
241
|
+
requestBody?: RequestBodySpec;
|
|
242
|
+
spec: OpenApiSpec;
|
|
243
|
+
documents?: OpenApiDocuments;
|
|
244
|
+
}) => string[];
|
|
245
|
+
declare const extractBodyProps: (args: {
|
|
246
|
+
requestBody?: RequestBodySpec;
|
|
247
|
+
spec: OpenApiSpec;
|
|
248
|
+
serverManagedExtension: ServerManagedExtension;
|
|
249
|
+
documents?: OpenApiDocuments; /** Maps a spec name to its tool argument name. @default snakeToCamel */
|
|
250
|
+
toArgName?: ToArgName;
|
|
251
|
+
}) => Array<{
|
|
252
|
+
snakeName: string;
|
|
253
|
+
argName: string;
|
|
254
|
+
description: string;
|
|
255
|
+
required: boolean;
|
|
256
|
+
type?: string;
|
|
257
|
+
items?: unknown;
|
|
258
|
+
nullable: boolean;
|
|
259
|
+
oneOf?: unknown[];
|
|
260
|
+
anyOf?: unknown[];
|
|
261
|
+
allOf?: unknown[];
|
|
262
|
+
}>;
|
|
263
|
+
/**
|
|
264
|
+
* The value each server-managed body property pins, typed by its schema and
|
|
265
|
+
* keyed by its spec name. A property whose extension is not a string pins
|
|
266
|
+
* nothing: it is only hidden, for the consumer to fill.
|
|
267
|
+
*/
|
|
268
|
+
declare const extractPinnedBody: (args: {
|
|
269
|
+
requestBody?: RequestBodySpec;
|
|
270
|
+
spec: OpenApiSpec;
|
|
271
|
+
serverManagedExtension: ServerManagedExtension;
|
|
272
|
+
documents?: OpenApiDocuments;
|
|
273
|
+
operationId: string;
|
|
274
|
+
}) => Record<string, string | number | boolean>;
|
|
275
|
+
//#endregion
|
|
235
276
|
//#region src/registerOpenApiTools.d.ts
|
|
236
277
|
/** The resolved HTTP request a tool call maps to, before transport concerns. */
|
|
237
278
|
interface ResolvedRequest {
|
|
@@ -417,33 +458,6 @@ declare const buildInputSchema: (pathParams: Array<{
|
|
|
417
458
|
anyOf?: unknown[];
|
|
418
459
|
allOf?: unknown[];
|
|
419
460
|
}>) => JsonObjectSchema;
|
|
420
|
-
/**
|
|
421
|
-
* snake_case names of every top-level property an operation's request schema
|
|
422
|
-
* declares, including server-managed ones.
|
|
423
|
-
*/
|
|
424
|
-
declare const extractAcceptedBodyFields: (args: {
|
|
425
|
-
requestBody?: RequestBodySpec;
|
|
426
|
-
spec: OpenApiSpec;
|
|
427
|
-
documents?: OpenApiDocuments;
|
|
428
|
-
}) => string[];
|
|
429
|
-
declare const extractBodyProps: (args: {
|
|
430
|
-
requestBody?: RequestBodySpec;
|
|
431
|
-
spec: OpenApiSpec;
|
|
432
|
-
serverManagedExtension: ServerManagedExtension;
|
|
433
|
-
documents?: OpenApiDocuments; /** Maps a spec name to its tool argument name. @default snakeToCamel */
|
|
434
|
-
toArgName?: ToArgName;
|
|
435
|
-
}) => Array<{
|
|
436
|
-
snakeName: string;
|
|
437
|
-
argName: string;
|
|
438
|
-
description: string;
|
|
439
|
-
required: boolean;
|
|
440
|
-
type?: string;
|
|
441
|
-
items?: unknown;
|
|
442
|
-
nullable: boolean;
|
|
443
|
-
oneOf?: unknown[];
|
|
444
|
-
anyOf?: unknown[];
|
|
445
|
-
allOf?: unknown[];
|
|
446
|
-
}>;
|
|
447
461
|
declare const processOperation: (args: {
|
|
448
462
|
pathTemplate: string;
|
|
449
463
|
method: string;
|
|
@@ -484,4 +498,4 @@ declare const openApiToToolDefinitions: (args: {
|
|
|
484
498
|
options?: OpenApiToToolsOptions;
|
|
485
499
|
}) => ToolDefinition[];
|
|
486
500
|
//#endregion
|
|
487
|
-
export { DEFAULT_EXCLUDE_EXTENSION, DEFAULT_SERVER_MANAGED_EXTENSION, type JsonSchemaProperty, NO_CONTENT_TEXT, type OpenApiDocuments, type OpenApiSpec, type OpenApiToToolsOptions, type OperationSpec, type QueryParamSerialization, type RegisterOpenApiToolsArgs, type RequestBodySpec, type ResolvedParameter, type ResolvedRequest, type ResolvedToolOptions, type ServerManagedParameter, type ToolDefinition, buildBodyFn, buildInputSchema, buildPathFn, buildQueryFn, dereferenceSchema, extractAcceptedBodyFields, extractBodyProps, extractPathParams, extractQueryParams, getJsonSchemaType, openApiToToolDefinitions, operationIdToToolName, processOperation, processPath, registerOpenApiTools, resolveParameter, resolveSchema, snakeToCamel };
|
|
501
|
+
export { DEFAULT_EXCLUDE_EXTENSION, DEFAULT_SERVER_MANAGED_EXTENSION, type JsonSchemaProperty, NO_CONTENT_TEXT, type OpenApiDocuments, type OpenApiSpec, type OpenApiToToolsOptions, type OperationSpec, type QueryParamSerialization, type RegisterOpenApiToolsArgs, type RequestBodySpec, type ResolvedParameter, type ResolvedRequest, type ResolvedToolOptions, type ServerManagedParameter, type ToolDefinition, buildBodyFn, buildInputSchema, buildPathFn, buildQueryFn, dereferenceSchema, extractAcceptedBodyFields, extractBodyProps, extractPathParams, extractPinnedBody, extractQueryParams, getJsonSchemaType, openApiToToolDefinitions, operationIdToToolName, processOperation, processPath, registerOpenApiTools, resolveParameter, resolveSchema, snakeToCamel };
|
package/dist/index.mjs
CHANGED
|
@@ -365,6 +365,57 @@ var withPinned = args => {
|
|
|
365
365
|
});
|
|
366
366
|
};
|
|
367
367
|
};
|
|
368
|
+
var parseNumber = value => {
|
|
369
|
+
const parsed = Number(value);
|
|
370
|
+
return value.trim() !== "" && Number.isFinite(parsed) ? parsed : void 0;
|
|
371
|
+
};
|
|
372
|
+
/** Each JSON type a pin can hold, and how its text reads as that type. */
|
|
373
|
+
var PIN_PARSERS = {
|
|
374
|
+
string: value => {
|
|
375
|
+
return value;
|
|
376
|
+
},
|
|
377
|
+
boolean: value => {
|
|
378
|
+
if (value === "true") return true;
|
|
379
|
+
if (value === "false") return false;
|
|
380
|
+
},
|
|
381
|
+
number: parseNumber,
|
|
382
|
+
integer: value => {
|
|
383
|
+
const parsed = parseNumber(value);
|
|
384
|
+
return parsed !== void 0 && Number.isInteger(parsed) ? parsed : void 0;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
/**
|
|
388
|
+
* Reads a pinned body value as the JSON type its schema declares. A body is
|
|
389
|
+
* JSON, so `'true'` on a boolean must reach the API as `true`; a query string
|
|
390
|
+
* carries the text either way. A pin the type cannot hold is a spec error.
|
|
391
|
+
*/
|
|
392
|
+
var typedPin = args => {
|
|
393
|
+
const {
|
|
394
|
+
value,
|
|
395
|
+
type,
|
|
396
|
+
where
|
|
397
|
+
} = args;
|
|
398
|
+
const typed = (typeof type === "string" ? PIN_PARSERS[type] : void 0)?.(value);
|
|
399
|
+
if (typed !== void 0) return typed;
|
|
400
|
+
throw new Error(`${where}: cannot pin '${value}' on a property of type ${JSON.stringify(type ?? "any")}; a pinned body property must be a boolean, integer, number or string that holds the value.`);
|
|
401
|
+
};
|
|
402
|
+
/**
|
|
403
|
+
* Wraps a `body` builder so pinned values replace whatever the args carry. An
|
|
404
|
+
* operation whose only body properties are pinned still gets a builder.
|
|
405
|
+
*/
|
|
406
|
+
var withPinnedBody = args => {
|
|
407
|
+
const {
|
|
408
|
+
build,
|
|
409
|
+
pinned
|
|
410
|
+
} = args;
|
|
411
|
+
if (Object.keys(pinned).length === 0) return build;
|
|
412
|
+
return values => {
|
|
413
|
+
return {
|
|
414
|
+
...(build ? build(values) : {}),
|
|
415
|
+
...pinned
|
|
416
|
+
};
|
|
417
|
+
};
|
|
418
|
+
};
|
|
368
419
|
var isPlainObject$1 = value => {
|
|
369
420
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
370
421
|
};
|
|
@@ -502,6 +553,94 @@ var collectServerManagedParameters = args => {
|
|
|
502
553
|
});
|
|
503
554
|
};
|
|
504
555
|
|
|
556
|
+
//#endregion
|
|
557
|
+
//#region src/body.ts
|
|
558
|
+
var resolveBodySchema = args => {
|
|
559
|
+
const rawBodySchema = args.requestBody?.content?.["application/json"]?.schema;
|
|
560
|
+
return resolveSchema(dereferenceSchema(rawBodySchema, args.spec, args.documents), args.spec, args.documents);
|
|
561
|
+
};
|
|
562
|
+
/**
|
|
563
|
+
* snake_case names of every top-level property an operation's request schema
|
|
564
|
+
* declares, including server-managed ones.
|
|
565
|
+
*/
|
|
566
|
+
var extractAcceptedBodyFields = args => {
|
|
567
|
+
const bodySchema = resolveBodySchema(args);
|
|
568
|
+
return Object.keys(bodySchema?.properties ?? {});
|
|
569
|
+
};
|
|
570
|
+
var isPlainObject = value => {
|
|
571
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
572
|
+
};
|
|
573
|
+
/**
|
|
574
|
+
* Folds a single-entry `allOf` into the property that wraps it. OpenAPI
|
|
575
|
+
* declares `allOf: [{ $ref }]` so a property can carry its own `description`
|
|
576
|
+
* next to a referenced schema; without folding, the referenced `type`,
|
|
577
|
+
* `nullable` and `items` would be lost. Keys on the wrapper win over the
|
|
578
|
+
* referenced schema's. Multi-entry `allOf` is left intact.
|
|
579
|
+
*/
|
|
580
|
+
var flattenSingleAllOf = schema => {
|
|
581
|
+
const {
|
|
582
|
+
allOf,
|
|
583
|
+
...rest
|
|
584
|
+
} = schema;
|
|
585
|
+
if (!Array.isArray(allOf) || allOf.length !== 1) return schema;
|
|
586
|
+
const [entry] = allOf;
|
|
587
|
+
if (!isPlainObject(entry)) return rest;
|
|
588
|
+
return {
|
|
589
|
+
...flattenSingleAllOf(entry),
|
|
590
|
+
...rest
|
|
591
|
+
};
|
|
592
|
+
};
|
|
593
|
+
var extractBodyProps = args => {
|
|
594
|
+
const toArgName = args.toArgName ?? snakeToCamel;
|
|
595
|
+
const bodySchema = resolveBodySchema(args);
|
|
596
|
+
if (!bodySchema?.properties) return [];
|
|
597
|
+
return Object.entries(bodySchema.properties).filter(([, value]) => {
|
|
598
|
+
return !readServerManaged({
|
|
599
|
+
node: value,
|
|
600
|
+
extension: args.serverManagedExtension
|
|
601
|
+
}).managed;
|
|
602
|
+
}).map(([key, value]) => {
|
|
603
|
+
const val = flattenSingleAllOf(value);
|
|
604
|
+
return {
|
|
605
|
+
snakeName: key,
|
|
606
|
+
argName: toArgName(key),
|
|
607
|
+
description: typeof val.description === "string" ? val.description : "",
|
|
608
|
+
required: (bodySchema.required || []).includes(key),
|
|
609
|
+
type: typeof val.type === "string" ? val.type : void 0,
|
|
610
|
+
items: val.items,
|
|
611
|
+
nullable: val.nullable === true,
|
|
612
|
+
oneOf: Array.isArray(val.oneOf) ? val.oneOf : void 0,
|
|
613
|
+
anyOf: Array.isArray(val.anyOf) ? val.anyOf : void 0,
|
|
614
|
+
allOf: Array.isArray(val.allOf) ? val.allOf : void 0
|
|
615
|
+
};
|
|
616
|
+
});
|
|
617
|
+
};
|
|
618
|
+
/**
|
|
619
|
+
* The value each server-managed body property pins, typed by its schema and
|
|
620
|
+
* keyed by its spec name. A property whose extension is not a string pins
|
|
621
|
+
* nothing: it is only hidden, for the consumer to fill.
|
|
622
|
+
*/
|
|
623
|
+
var extractPinnedBody = args => {
|
|
624
|
+
const bodySchema = resolveBodySchema(args);
|
|
625
|
+
const pinned = {};
|
|
626
|
+
for (const [key, value] of Object.entries(bodySchema?.properties ?? {})) {
|
|
627
|
+
const node = value;
|
|
628
|
+
const {
|
|
629
|
+
value: pin
|
|
630
|
+
} = readServerManaged({
|
|
631
|
+
node,
|
|
632
|
+
extension: args.serverManagedExtension
|
|
633
|
+
});
|
|
634
|
+
if (pin === void 0) continue;
|
|
635
|
+
pinned[key] = typedPin({
|
|
636
|
+
value: pin,
|
|
637
|
+
type: flattenSingleAllOf(node).type,
|
|
638
|
+
where: `${args.operationId} body property '${key}'`
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
return pinned;
|
|
642
|
+
};
|
|
643
|
+
|
|
505
644
|
//#endregion
|
|
506
645
|
//#region src/toolDefinitions.ts
|
|
507
646
|
/** Converts a camelCase `operationId` to a kebab-case tool name. */
|
|
@@ -602,66 +741,6 @@ var buildInputSchema = (pathParams, queryParams, bodyProps) => {
|
|
|
602
741
|
} : {})
|
|
603
742
|
};
|
|
604
743
|
};
|
|
605
|
-
var resolveBodySchema = args => {
|
|
606
|
-
const rawBodySchema = args.requestBody?.content?.["application/json"]?.schema;
|
|
607
|
-
return resolveSchema(dereferenceSchema(rawBodySchema, args.spec, args.documents), args.spec, args.documents);
|
|
608
|
-
};
|
|
609
|
-
/**
|
|
610
|
-
* snake_case names of every top-level property an operation's request schema
|
|
611
|
-
* declares, including server-managed ones.
|
|
612
|
-
*/
|
|
613
|
-
var extractAcceptedBodyFields = args => {
|
|
614
|
-
const bodySchema = resolveBodySchema(args);
|
|
615
|
-
return Object.keys(bodySchema?.properties ?? {});
|
|
616
|
-
};
|
|
617
|
-
var isPlainObject = value => {
|
|
618
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
619
|
-
};
|
|
620
|
-
/**
|
|
621
|
-
* Folds a single-entry `allOf` into the property that wraps it. OpenAPI
|
|
622
|
-
* declares `allOf: [{ $ref }]` so a property can carry its own `description`
|
|
623
|
-
* next to a referenced schema; without folding, the referenced `type`,
|
|
624
|
-
* `nullable` and `items` would be lost. Keys on the wrapper win over the
|
|
625
|
-
* referenced schema's. Multi-entry `allOf` is left intact.
|
|
626
|
-
*/
|
|
627
|
-
var flattenSingleAllOf = schema => {
|
|
628
|
-
const {
|
|
629
|
-
allOf,
|
|
630
|
-
...rest
|
|
631
|
-
} = schema;
|
|
632
|
-
if (!Array.isArray(allOf) || allOf.length !== 1) return schema;
|
|
633
|
-
const [entry] = allOf;
|
|
634
|
-
if (!isPlainObject(entry)) return rest;
|
|
635
|
-
return {
|
|
636
|
-
...flattenSingleAllOf(entry),
|
|
637
|
-
...rest
|
|
638
|
-
};
|
|
639
|
-
};
|
|
640
|
-
var extractBodyProps = args => {
|
|
641
|
-
const toArgName = args.toArgName ?? snakeToCamel;
|
|
642
|
-
const bodySchema = resolveBodySchema(args);
|
|
643
|
-
if (!bodySchema?.properties) return [];
|
|
644
|
-
return Object.entries(bodySchema.properties).filter(([, value]) => {
|
|
645
|
-
return !readServerManaged({
|
|
646
|
-
node: value,
|
|
647
|
-
extension: args.serverManagedExtension
|
|
648
|
-
}).managed;
|
|
649
|
-
}).map(([key, value]) => {
|
|
650
|
-
const val = flattenSingleAllOf(value);
|
|
651
|
-
return {
|
|
652
|
-
snakeName: key,
|
|
653
|
-
argName: toArgName(key),
|
|
654
|
-
description: typeof val.description === "string" ? val.description : "",
|
|
655
|
-
required: (bodySchema.required || []).includes(key),
|
|
656
|
-
type: typeof val.type === "string" ? val.type : void 0,
|
|
657
|
-
items: val.items,
|
|
658
|
-
nullable: val.nullable === true,
|
|
659
|
-
oneOf: Array.isArray(val.oneOf) ? val.oneOf : void 0,
|
|
660
|
-
anyOf: Array.isArray(val.anyOf) ? val.anyOf : void 0,
|
|
661
|
-
allOf: Array.isArray(val.allOf) ? val.allOf : void 0
|
|
662
|
-
};
|
|
663
|
-
});
|
|
664
|
-
};
|
|
665
744
|
/** Collects every `x-` prefixed extension declared on the operation. */
|
|
666
745
|
var extractExtensions = operation => {
|
|
667
746
|
const extensions = {};
|
|
@@ -694,11 +773,14 @@ var processOperation = args => {
|
|
|
694
773
|
toArgName,
|
|
695
774
|
serverManagedExtension
|
|
696
775
|
});
|
|
697
|
-
const
|
|
776
|
+
const bodyArgs = {
|
|
698
777
|
requestBody: args.operation.requestBody,
|
|
699
778
|
spec: args.spec,
|
|
700
779
|
serverManagedExtension,
|
|
701
|
-
documents
|
|
780
|
+
documents
|
|
781
|
+
};
|
|
782
|
+
const bodyProps = extractBodyProps({
|
|
783
|
+
...bodyArgs,
|
|
702
784
|
toArgName
|
|
703
785
|
});
|
|
704
786
|
const inputSchema = buildInputSchema(pathParams, queryParams, bodyProps);
|
|
@@ -707,11 +789,6 @@ var processOperation = args => {
|
|
|
707
789
|
queryParams
|
|
708
790
|
});
|
|
709
791
|
const pinned = pinnedArgs(serverManagedParameters);
|
|
710
|
-
const acceptedBodyFields = extractAcceptedBodyFields({
|
|
711
|
-
requestBody: args.operation.requestBody,
|
|
712
|
-
spec: args.spec,
|
|
713
|
-
documents
|
|
714
|
-
});
|
|
715
792
|
return {
|
|
716
793
|
name: toolName,
|
|
717
794
|
description: sanitizeDescription(args.operation.description),
|
|
@@ -727,8 +804,14 @@ var processOperation = args => {
|
|
|
727
804
|
build: buildQueryFn(queryParams),
|
|
728
805
|
pinned
|
|
729
806
|
}),
|
|
730
|
-
body:
|
|
731
|
-
|
|
807
|
+
body: withPinnedBody({
|
|
808
|
+
build: buildBodyFn(bodyProps),
|
|
809
|
+
pinned: extractPinnedBody({
|
|
810
|
+
...bodyArgs,
|
|
811
|
+
operationId: args.operation.operationId
|
|
812
|
+
})
|
|
813
|
+
}),
|
|
814
|
+
acceptedBodyFields: extractAcceptedBodyFields(bodyArgs),
|
|
732
815
|
extensions: extractExtensions(args.operation),
|
|
733
816
|
serverManagedParameters
|
|
734
817
|
};
|
|
@@ -879,4 +962,4 @@ var registerOpenApiTools = args => {
|
|
|
879
962
|
};
|
|
880
963
|
|
|
881
964
|
//#endregion
|
|
882
|
-
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 };
|
|
965
|
+
export { DEFAULT_EXCLUDE_EXTENSION, DEFAULT_SERVER_MANAGED_EXTENSION, NO_CONTENT_TEXT, buildBodyFn, buildInputSchema, buildPathFn, buildQueryFn, dereferenceSchema, extractAcceptedBodyFields, extractBodyProps, extractPathParams, extractPinnedBody, extractQueryParams, getJsonSchemaType, openApiToToolDefinitions, operationIdToToolName, processOperation, processPath, registerOpenApiTools, resolveParameter, resolveSchema, snakeToCamel };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttoss/http-server-mcp-openapi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Generate Model Context Protocol (MCP) tools from an OpenAPI specification for @ttoss/http-server-mcp",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -35,15 +35,15 @@
|
|
|
35
35
|
"dist"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@ttoss/http-server-mcp": "^0.29.
|
|
38
|
+
"@ttoss/http-server-mcp": "^0.29.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
42
42
|
"jest": "^30.4.2",
|
|
43
43
|
"supertest": "^7.2.2",
|
|
44
44
|
"tsdown": "^0.22.2",
|
|
45
|
-
"@ttoss/
|
|
46
|
-
"@ttoss/
|
|
45
|
+
"@ttoss/http-server": "^0.9.0",
|
|
46
|
+
"@ttoss/config": "^1.39.0"
|
|
47
47
|
},
|
|
48
48
|
"publishConfig": {
|
|
49
49
|
"access": "public",
|