@frontmcp/adapters 0.2.0 → 0.2.2

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 CHANGED
@@ -1,3 +1,27 @@
1
- # FrontMcp Adapters
1
+ # FrontMCP Adapters
2
2
 
3
- TBD
3
+ This package contains adapters that extend FrontMCP servers with external capabilities by auto-generating, transforming, or delegating MCP tools.
4
+
5
+ ## Available adapters
6
+
7
+ ### OpenAPI Adapter
8
+ Generate MCP tools from an OpenAPI spec. Each operation becomes an MCP tool with strong input validation and automatic request/response handling.
9
+
10
+ - Code: `libs/adapters/src/openapi`
11
+ - README: `libs/adapters/src/openapi/README.md`
12
+ - Demo usage: `apps/demo/src/apps/expenses/index.ts`
13
+ - Example spec (used by the demo): https://frontmcp-test.proxy.beeceptor.com/openapi.json
14
+ - Generator library: https://www.npmjs.com/package/openapi-mcp-generator
15
+
16
+ Quick example:
17
+ ```ts
18
+ import { OpenapiAdapter } from '@frontmcp/adapters';
19
+
20
+ OpenapiAdapter.init({
21
+ name: 'backend:api',
22
+ url: 'https://frontmcp-test.proxy.beeceptor.com/openapi.json',
23
+ baseUrl: 'https://frontmcp-test.proxy.beeceptor.com',
24
+ });
25
+ ```
26
+
27
+ For detailed options and advanced usage, see the adapter README and the docs page `docs/adapters/openapi-adapter.mdx`.
package/package.json CHANGED
@@ -1,20 +1,21 @@
1
1
  {
2
2
  "name": "@frontmcp/adapters",
3
- "version": "0.2.0",
4
- "type": "commonjs",
3
+ "version": "0.2.2",
5
4
  "main": "./src/index.js",
6
5
  "types": "./src/index.d.ts",
7
6
  "exports": {
8
7
  "./package.json": "./package.json",
9
- "./openapi": {
10
- "types": "./src/openapi/index.d.ts",
11
- "import": "./src/openapi/index.js",
12
- "default": "./src/openapi/index.js"
8
+ ".": {
9
+ "types": "./src/index.d.ts",
10
+ "import": "./src/index.js",
11
+ "default": "./src/index.js"
13
12
  }
14
13
  },
15
14
  "dependencies": {
16
15
  "tslib": "^2.3.0",
16
+ "@frontmcp/sdk": "^0.2.2",
17
17
  "openapi-mcp-generator": "^3.2.0",
18
18
  "json-schema-to-zod": "^2.6.1"
19
- }
19
+ },
20
+ "type": "commonjs"
20
21
  }
package/src/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { default as OpenapiAdapter } from './openapi';
2
+ export * from './openapi';
package/src/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenapiAdapter = void 0;
4
+ const tslib_1 = require("tslib");
5
+ var openapi_1 = require("./openapi");
6
+ Object.defineProperty(exports, "OpenapiAdapter", { enumerable: true, get: function () { return tslib_1.__importDefault(openapi_1).default; } });
7
+ tslib_1.__exportStar(require("./openapi"), exports);
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;AAAA,qCAAmD;AAA3C,kIAAA,OAAO,OAAkB;AACjC,oDAAyB","sourcesContent":["export {default as OpenapiAdapter} from './openapi'\nexport * from './openapi'"]}
@@ -5,12 +5,4 @@ export default class OpenapiAdapter extends DynamicAdapter<OpenApiAdapterOptions
5
5
  constructor(options: OpenApiAdapterOptions);
6
6
  fetch(): Promise<FrontMcpAdapterResponse>;
7
7
  private parseTools;
8
- /**
9
- * Converts a JSON Schema to a Zod schema for runtime validation
10
- *
11
- * @param jsonSchema JSON Schema
12
- * @param toolName Tool name for error reporting
13
- * @returns Zod schema
14
- */
15
- private getZodSchemaFromJsonSchema;
16
8
  }
@@ -2,67 +2,41 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const sdk_1 = require("@frontmcp/sdk");
5
- const zod_1 = require("zod");
6
5
  const openapi_mcp_generator_1 = require("openapi-mcp-generator");
7
- const zod_from_json_schema_1 = require("zod-from-json-schema");
6
+ const openapi_tool_1 = require("./openapi.tool");
8
7
  let OpenapiAdapter = class OpenapiAdapter extends sdk_1.DynamicAdapter {
9
8
  constructor(options) {
10
9
  super();
11
10
  this.options = options;
12
11
  }
13
12
  async fetch() {
14
- const openapiLink = this.options.url;
15
- const openApiTools = await withSilencedConsole((0, openapi_mcp_generator_1.getToolsFromOpenApi)(openapiLink, {
13
+ let urlOrSpec = '';
14
+ if ('url' in this.options) {
15
+ urlOrSpec = this.options.url;
16
+ }
17
+ else if ('spec' in this.options) {
18
+ urlOrSpec = this.options.spec;
19
+ }
20
+ else {
21
+ throw new Error('Either url or spec must be provided');
22
+ }
23
+ const { baseUrl, filterFn, defaultInclude, excludeOperationIds } = this.options;
24
+ const openApiTools = await (0, openapi_mcp_generator_1.getToolsFromOpenApi)(urlOrSpec, {
25
+ baseUrl,
26
+ filterFn,
27
+ defaultInclude,
28
+ excludeOperationIds,
16
29
  dereference: false,
17
- }));
30
+ });
18
31
  return {
19
32
  tools: this.parseTools(openApiTools),
20
33
  };
21
34
  }
22
35
  parseTools(openApiTools) {
23
- return openApiTools.map(oTool => {
24
- const inputSchema = this.getZodSchemaFromJsonSchema(oTool.inputSchema, oTool.name);
25
- // const outputSchema = this.getZodSchemaFromJsonSchema(oTool.outputSchema, oTool.name);
26
- return (0, sdk_1.tool)({
27
- id: oTool.name,
28
- name: oTool.name,
29
- description: oTool.description,
30
- inputSchema: inputSchema.shape,
31
- rawInputSchema: oTool.inputSchema,
32
- // outputSchema: outputSchema.shape
33
- })((input, ctx) => {
34
- return {
35
- data: {
36
- id: '1',
37
- name: 'test',
38
- },
39
- };
40
- });
36
+ return openApiTools.map(tool => {
37
+ return (0, openapi_tool_1.createOpenApiTool)(tool, this.options);
41
38
  });
42
39
  }
43
- /**
44
- * Converts a JSON Schema to a Zod schema for runtime validation
45
- *
46
- * @param jsonSchema JSON Schema
47
- * @param toolName Tool name for error reporting
48
- * @returns Zod schema
49
- */
50
- getZodSchemaFromJsonSchema(jsonSchema, toolName) {
51
- if (typeof jsonSchema !== 'object' || jsonSchema === null) {
52
- return zod_1.z.object({}).passthrough();
53
- }
54
- try {
55
- const zodSchema = (0, zod_from_json_schema_1.convertJsonSchemaToZod)(jsonSchema);
56
- if (typeof zodSchema?.parse !== 'function') {
57
- throw new Error('Eval did not produce a valid Zod schema.');
58
- }
59
- return zodSchema;
60
- }
61
- catch (err) {
62
- console.error(`Failed to generate/evaluate Zod schema for '${toolName}':`, err);
63
- return zod_1.z.object({}).passthrough();
64
- }
65
- }
66
40
  };
67
41
  OpenapiAdapter = tslib_1.__decorate([
68
42
  (0, sdk_1.Adapter)({
@@ -72,20 +46,4 @@ OpenapiAdapter = tslib_1.__decorate([
72
46
  tslib_1.__metadata("design:paramtypes", [Object])
73
47
  ], OpenapiAdapter);
74
48
  exports.default = OpenapiAdapter;
75
- async function withSilencedConsole(fn) {
76
- const methods = ['log', 'info', 'debug', 'warn', 'error', 'table', 'group', 'groupCollapsed', 'groupEnd', 'dir'];
77
- const originals = {};
78
- try {
79
- for (const m of methods) {
80
- originals[m] = console[m];
81
- console[m] = () => {
82
- };
83
- }
84
- return await fn;
85
- }
86
- finally {
87
- for (const m of Object.keys(originals))
88
- console[m] = originals[m];
89
- }
90
- }
91
49
  //# sourceMappingURL=openapi.adapter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.adapter.js","sourceRoot":"","sources":["../../../src/openapi/openapi.adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Adapter,\n DynamicAdapter,\n FrontMcpAdapterResponse,\n tool,\n} from '@frontmcp/sdk';\nimport {OpenApiAdapterOptions} from './openapi.types';\nimport {z} from 'zod';\nimport {getToolsFromOpenApi, McpToolDefinition} from 'openapi-mcp-generator';\nimport {convertJsonSchemaToZod} from 'zod-from-json-schema';\n\n\n@Adapter({\n name: 'openapi',\n description: 'OpenAPI adapter that plugin for expense-mcp',\n})\nexport default class OpenapiAdapter extends DynamicAdapter<OpenApiAdapterOptions> {\n options: OpenApiAdapterOptions;\n\n constructor(options: OpenApiAdapterOptions) {\n super();\n this.options = options;\n }\n\n\n async fetch(): Promise<FrontMcpAdapterResponse> {\n const openapiLink = this.options.url\n const openApiTools = await withSilencedConsole(getToolsFromOpenApi(openapiLink, {\n dereference: false,\n }));\n\n return {\n tools: this.parseTools(openApiTools),\n };\n }\n\n private parseTools(openApiTools: McpToolDefinition[]) {\n return openApiTools.map(oTool => {\n const inputSchema = this.getZodSchemaFromJsonSchema(oTool.inputSchema, oTool.name);\n // const outputSchema = this.getZodSchemaFromJsonSchema(oTool.outputSchema, oTool.name);\n\n return tool({\n id: oTool.name,\n name: oTool.name,\n description: oTool.description,\n inputSchema: inputSchema.shape,\n rawInputSchema: oTool.inputSchema as any,\n // outputSchema: outputSchema.shape\n })((input, ctx) => {\n return {\n data: {\n id: '1',\n name: 'test',\n },\n };\n });\n });\n }\n\n\n /**\n * Converts a JSON Schema to a Zod schema for runtime validation\n *\n * @param jsonSchema JSON Schema\n * @param toolName Tool name for error reporting\n * @returns Zod schema\n */\n private getZodSchemaFromJsonSchema(jsonSchema: any, toolName: string): z.ZodObject<any> {\n if (typeof jsonSchema !== 'object' || jsonSchema === null) {\n return z.object({}).passthrough();\n }\n try {\n const zodSchema = convertJsonSchemaToZod(jsonSchema);\n if (typeof zodSchema?.parse !== 'function') {\n throw new Error('Eval did not produce a valid Zod schema.');\n }\n return zodSchema as any;\n } catch (err: any) {\n console.error(`Failed to generate/evaluate Zod schema for '${toolName}':`, err);\n return z.object({}).passthrough();\n }\n }\n}\n\nasync function withSilencedConsole(fn: Promise<any>) {\n const methods = ['log', 'info', 'debug', 'warn', 'error', 'table', 'group', 'groupCollapsed', 'groupEnd', 'dir'];\n const originals = {};\n try {\n for (const m of methods) {\n originals[m] = console[m];\n console[m] = () => {\n };\n }\n return await fn;\n } finally {\n for (const m of Object.keys(originals)) console[m] = originals[m];\n }\n}\n"]}
1
+ {"version":3,"file":"openapi.adapter.js","sourceRoot":"","sources":["../../../src/openapi/openapi.adapter.ts"],"names":[],"mappings":";;;AAAA,uCAIuB;AAEvB,iEAA6E;AAC7E,iDAAiD;AAOlC,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,oBAAqC;IAG/E,YAAY,OAA8B;QACxC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAGD,KAAK,CAAC,KAAK;QACT,IAAI,SAAS,GAAgC,EAAE,CAAA;QAC/C,IAAI,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC1B,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;QAC/B,CAAC;aAAM,IAAI,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,EAAC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,mBAAmB,EAAC,GAAG,IAAI,CAAC,OAAO,CAAC;QAC9E,MAAM,YAAY,GAAG,MAAM,IAAA,2CAAmB,EAAC,SAAS,EAAE;YACxD,OAAO;YACP,QAAQ;YACR,cAAc;YACd,mBAAmB;YACnB,WAAW,EAAE,KAAK;SACnB,CAAC,CAAC;QAEH,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;SACrC,CAAC;IACJ,CAAC;IAEO,UAAU,CAAC,YAAiC;QAClD,OAAO,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YAC7B,OAAO,IAAA,gCAAiB,EAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AArCoB,cAAc;IAJlC,IAAA,aAAO,EAAC;QACP,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,8CAA8C;KAC5D,CAAC;;GACmB,cAAc,CAqClC;kBArCoB,cAAc","sourcesContent":["import {\n Adapter,\n DynamicAdapter,\n FrontMcpAdapterResponse,\n} from '@frontmcp/sdk';\nimport {OpenApiAdapterOptions} from './openapi.types';\nimport {getToolsFromOpenApi, McpToolDefinition} from 'openapi-mcp-generator';\nimport {createOpenApiTool} from \"./openapi.tool\";\nimport {OpenAPIV3} from \"openapi-types\";\n\n@Adapter({\n name: 'openapi',\n description: 'OpenAPI adapter that plugin for expense-mcp',\n})\nexport default class OpenapiAdapter extends DynamicAdapter<OpenApiAdapterOptions> {\n options: OpenApiAdapterOptions;\n\n constructor(options: OpenApiAdapterOptions) {\n super();\n this.options = options;\n }\n\n\n async fetch(): Promise<FrontMcpAdapterResponse> {\n let urlOrSpec: string | OpenAPIV3.Document = ''\n if ('url' in this.options) {\n urlOrSpec = this.options.url;\n } else if ('spec' in this.options) {\n urlOrSpec = this.options.spec;\n } else {\n throw new Error('Either url or spec must be provided');\n }\n const {baseUrl, filterFn, defaultInclude, excludeOperationIds} = this.options;\n const openApiTools = await getToolsFromOpenApi(urlOrSpec, {\n baseUrl,\n filterFn,\n defaultInclude,\n excludeOperationIds,\n dereference: false,\n });\n\n return {\n tools: this.parseTools(openApiTools),\n };\n }\n\n private parseTools(openApiTools: McpToolDefinition[]) {\n return openApiTools.map(tool => {\n return createOpenApiTool(tool, this.options);\n });\n }\n}"]}
@@ -0,0 +1,3 @@
1
+ import { McpToolDefinition } from "openapi-mcp-generator";
2
+ import { OpenApiAdapterOptions } from "./openapi.types";
3
+ export declare const createOpenApiTool: (oTool: McpToolDefinition, options: OpenApiAdapterOptions) => () => void;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createOpenApiTool = void 0;
4
+ const zod_1 = require("zod");
5
+ const sdk_1 = require("@frontmcp/sdk");
6
+ const zod_from_json_schema_1 = require("zod-from-json-schema");
7
+ const createOpenApiTool = (oTool, options) => {
8
+ const inputSchema = getZodSchemaFromJsonSchema(oTool.inputSchema, oTool.name);
9
+ const { additionalHeaders, headersMapper } = options;
10
+ return (0, sdk_1.tool)({
11
+ id: oTool.name,
12
+ name: oTool.name,
13
+ description: oTool.description,
14
+ inputSchema: inputSchema,
15
+ rawInputSchema: oTool.inputSchema,
16
+ // outputSchema: outputSchema.shape
17
+ })(async (input, ctx) => {
18
+ let { urlPath, headers, queryParams } = prepareUrl(oTool, input);
19
+ let requestBodyData = undefined;
20
+ if (additionalHeaders) {
21
+ for (const [key, value] of Object.entries(additionalHeaders)) {
22
+ headers.append(key, value);
23
+ }
24
+ }
25
+ if (typeof headersMapper === 'function') {
26
+ headers = headersMapper(ctx.authInfo, headers);
27
+ }
28
+ if (!['HEAD', 'GET', 'OPTIONS'].includes(oTool.method)) {
29
+ // prepare body
30
+ if (oTool.requestBodyContentType && typeof input['requestBody'] !== 'undefined') {
31
+ requestBodyData = input['requestBody'];
32
+ headers.set('content-type', oTool.requestBodyContentType);
33
+ }
34
+ }
35
+ const query = queryParams.toString();
36
+ const url = `${options.baseUrl}${urlPath}${query ? `?${query}` : ''}`;
37
+ const res = await fetch(url, {
38
+ method: oTool.method,
39
+ headers,
40
+ body: requestBodyData,
41
+ });
42
+ const data = await res.text();
43
+ let result = { data };
44
+ if (res.headers.get('content-type')?.includes('application/json')) {
45
+ try {
46
+ result.data = JSON.parse(data);
47
+ }
48
+ catch (e) {
49
+ console.error("failed to parse api response"); // migrate to logger
50
+ result.data = data;
51
+ }
52
+ }
53
+ return result;
54
+ });
55
+ };
56
+ exports.createOpenApiTool = createOpenApiTool;
57
+ /**
58
+ * Converts a JSON Schema to a Zod schema for runtime validation
59
+ *
60
+ * @param jsonSchema JSON Schema
61
+ * @param toolName Tool name for error reporting
62
+ * @returns Zod schema
63
+ */
64
+ function getZodSchemaFromJsonSchema(jsonSchema, toolName) {
65
+ if (typeof jsonSchema !== 'object' || jsonSchema === null) {
66
+ return zod_1.z.object({}).passthrough();
67
+ }
68
+ try {
69
+ const zodSchema = (0, zod_from_json_schema_1.convertJsonSchemaToZod)(jsonSchema);
70
+ if (typeof zodSchema?.parse !== 'function') {
71
+ throw new Error('Eval did not produce a valid Zod schema.');
72
+ }
73
+ return zodSchema;
74
+ }
75
+ catch (err) {
76
+ console.error(`Failed to generate/evaluate Zod schema for '${toolName}':`, err);
77
+ return zod_1.z.object({}).passthrough();
78
+ }
79
+ }
80
+ const prepareUrl = (definition, validatedArgs) => {
81
+ // Prepare URL, query parameters, headers, and request body
82
+ let urlPath = definition.pathTemplate;
83
+ const queryParams = new URLSearchParams({ "v": '1' });
84
+ const headers = new Headers({ 'accept': 'application/json' });
85
+ // Apply parameters to the URL path, query, or headers
86
+ definition.executionParameters.forEach((param) => {
87
+ const value = validatedArgs[param.name];
88
+ if (typeof value !== 'undefined' && value !== null) {
89
+ if (param.in === 'path') {
90
+ urlPath = urlPath.replace(`{${param.name}}`, encodeURIComponent(String(value)));
91
+ }
92
+ else if (param.in === 'query') {
93
+ queryParams.set(param.name, value);
94
+ }
95
+ else if (param.in === 'header') {
96
+ headers.append(param.name.toLowerCase(), String(value));
97
+ }
98
+ }
99
+ });
100
+ // Ensure all path parameters are resolved
101
+ if (urlPath.includes('{')) {
102
+ throw new Error(`Failed to resolve path parameters: ${urlPath}`);
103
+ }
104
+ return { urlPath, headers, queryParams };
105
+ };
106
+ //# sourceMappingURL=openapi.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.tool.js","sourceRoot":"","sources":["../../../src/openapi/openapi.tool.ts"],"names":[],"mappings":"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","sourcesContent":["import {z} from \"zod\";\nimport {McpToolDefinition} from \"openapi-mcp-generator\";\nimport {tool} from \"@frontmcp/sdk\";\nimport {convertJsonSchemaToZod} from \"zod-from-json-schema\";\nimport {OpenApiAdapterOptions} from \"./openapi.types\";\n\n\nexport const createOpenApiTool = (oTool: McpToolDefinition, options: OpenApiAdapterOptions) => {\n const inputSchema = getZodSchemaFromJsonSchema(oTool.inputSchema, oTool.name);\n\n const {additionalHeaders, headersMapper} = options;\n return tool({\n id: oTool.name,\n name: oTool.name,\n description: oTool.description,\n inputSchema: inputSchema as any,\n rawInputSchema: oTool.inputSchema as any,\n // outputSchema: outputSchema.shape\n })(async (input, ctx) => {\n\n let {urlPath, headers, queryParams} = prepareUrl(oTool, input);\n let requestBodyData: any = undefined;\n\n if (additionalHeaders) {\n for (const [key, value] of Object.entries(additionalHeaders)) {\n headers.append(key, value);\n }\n }\n if (typeof headersMapper === 'function') {\n headers = headersMapper(ctx.authInfo, headers)\n }\n\n if (!['HEAD', 'GET', 'OPTIONS'].includes(oTool.method)) {\n // prepare body\n if (oTool.requestBodyContentType && typeof input['requestBody'] !== 'undefined') {\n requestBodyData = input['requestBody'];\n headers.set('content-type', oTool.requestBodyContentType);\n }\n }\n\n const query = queryParams.toString()\n const url = `${options.baseUrl}${urlPath}${query ? `?${query}` : ''}`;\n const res = await fetch(url, {\n method: oTool.method,\n headers,\n body: requestBodyData,\n });\n const data = await res.text()\n let result = {data}\n if (res.headers.get('content-type')?.includes('application/json')) {\n try {\n result.data = JSON.parse(data)\n } catch (e) {\n console.error(\"failed to parse api response\")// migrate to logger\n result.data = data\n }\n }\n return result\n });\n};\n\n\n/**\n * Converts a JSON Schema to a Zod schema for runtime validation\n *\n * @param jsonSchema JSON Schema\n * @param toolName Tool name for error reporting\n * @returns Zod schema\n */\nfunction getZodSchemaFromJsonSchema(jsonSchema: any, toolName: string): z.ZodObject<any> {\n if (typeof jsonSchema !== 'object' || jsonSchema === null) {\n return z.object({}).passthrough();\n }\n try {\n const zodSchema = convertJsonSchemaToZod(jsonSchema);\n if (typeof zodSchema?.parse !== 'function') {\n throw new Error('Eval did not produce a valid Zod schema.');\n }\n return zodSchema as any;\n } catch (err: any) {\n console.error(`Failed to generate/evaluate Zod schema for '${toolName}':`, err);\n return z.object({}).passthrough();\n }\n}\n\nconst prepareUrl = (definition: McpToolDefinition, validatedArgs: any) => {\n // Prepare URL, query parameters, headers, and request body\n let urlPath = definition.pathTemplate;\n const queryParams = new URLSearchParams({\"v\": '1'})\n const headers = new Headers({'accept': 'application/json'})\n\n\n // Apply parameters to the URL path, query, or headers\n definition.executionParameters.forEach((param) => {\n const value = validatedArgs[param.name];\n if (typeof value !== 'undefined' && value !== null) {\n if (param.in === 'path') {\n urlPath = urlPath.replace(`{${param.name}}`, encodeURIComponent(String(value)));\n } else if (param.in === 'query') {\n queryParams.set(param.name, value)\n } else if (param.in === 'header') {\n headers.append(param.name.toLowerCase(), String(value));\n }\n }\n });\n\n // Ensure all path parameters are resolved\n if (urlPath.includes('{')) {\n throw new Error(`Failed to resolve path parameters: ${urlPath}`);\n }\n\n return {urlPath, headers, queryParams};\n}"]}
@@ -1,18 +1,60 @@
1
1
  import { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js";
2
- export interface OpenApiAdapterOptions {
2
+ import { GetToolsOptions } from "openapi-mcp-generator/dist/api";
3
+ import { OpenAPIV3 } from "openapi-types";
4
+ interface BaseOptions extends Omit<GetToolsOptions, 'dereference'> {
5
+ /**
6
+ * The name of the adapter.
7
+ * This is used to identify the adapter in the MCP configuration.
8
+ * Also used to prefix tools if conflicted with other adapters in the same app.
9
+ */
3
10
  name: string;
4
- url: string;
11
+ /**
12
+ * The base URL of the API.
13
+ * This is used to construct the full URL for each request.
14
+ * For example, if the API is hosted at https://api.example.com/v1,
15
+ * the baseUrl should be set to https://api.example.com/v1.
16
+ */
17
+ baseUrl: string;
18
+ /**
19
+ * Additional headers to be sent with each request.
20
+ * This can be used to set authentication headers,
21
+ * such as Authorization or API Key.
22
+ */
5
23
  additionalHeaders?: Record<string, string>;
6
- headersMapper?: (authInfo: AuthInfo, headers: Record<string, string>) => Record<string, string>;
7
- bodyMapper?: (authInfo: AuthInfo, body: any) => any;
8
24
  /**
9
25
  * This can be used to map request information to specific
10
- * input schema values as required by the API.
26
+ * headers as required by the API.
27
+ * For example, mapping tenantId from authenticated session payload to
28
+ * a specific header, this key will be hidden to mcp clients
29
+ * and filled by the adapter before sending the request to the API.
30
+ * @param authInfo
31
+ * @param headers
32
+ */
33
+ headersMapper?: (authInfo: AuthInfo, headers: Headers) => Headers;
34
+ /**
35
+ * This can be used to map request information to specific
36
+ * body values as required by the API.
11
37
  * For example, mapping tenantId from authenticated session payload to
12
- * a specific proprty in the input schema, this key will be hidden to mcp clients
38
+ * a specific property in the body, this key will be hidden to mcp clients
13
39
  * and filled by the adapter before sending the request to the API.
40
+ *
14
41
  * @param authInfo
15
- * @param request
42
+ * @param body
16
43
  */
17
- inputSchemaMapper?: (inputSchema: any) => any;
44
+ bodyMapper?: (authInfo: AuthInfo, body: any) => any;
45
+ }
46
+ interface SpecOptions extends BaseOptions {
47
+ /**
48
+ * The OpenAPI specification the OpenAPI specification.
49
+ */
50
+ spec: OpenAPIV3.Document;
51
+ }
52
+ interface UrlOptions extends BaseOptions {
53
+ /**
54
+ * The URL of the OpenAPI specification.
55
+ * Can be a local file path or a remote URL.
56
+ */
57
+ url: string;
18
58
  }
59
+ export type OpenApiAdapterOptions = SpecOptions | UrlOptions;
60
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.types.js","sourceRoot":"","sources":["../../../src/openapi/openapi.types.ts"],"names":[],"mappings":"","sourcesContent":["import {AuthInfo} from \"@modelcontextprotocol/sdk/server/auth/types.js\";\n\nexport interface OpenApiAdapterOptions {\n name: string;\n url: string;\n\n additionalHeaders?: Record<string, string>;\n headersMapper?: (authInfo: AuthInfo, headers: Record<string, string>) => Record<string, string>;\n bodyMapper?: (authInfo: AuthInfo, body: any) => any;\n\n /**\n * This can be used to map request information to specific\n * input schema values as required by the API.\n * For example, mapping tenantId from authenticated session payload to\n * a specific proprty in the input schema, this key will be hidden to mcp clients\n * and filled by the adapter before sending the request to the API.\n * @param authInfo\n * @param request\n */\n inputSchemaMapper?: (inputSchema: any) => any;\n}\n"]}
1
+ {"version":3,"file":"openapi.types.js","sourceRoot":"","sources":["../../../src/openapi/openapi.types.ts"],"names":[],"mappings":"","sourcesContent":["import {AuthInfo} from \"@modelcontextprotocol/sdk/server/auth/types.js\";\nimport {GetToolsOptions} from \"openapi-mcp-generator/dist/api\";\nimport {OpenAPIV3} from \"openapi-types\";\n\ninterface BaseOptions extends Omit<GetToolsOptions, 'dereference'> {\n /**\n * The name of the adapter.\n * This is used to identify the adapter in the MCP configuration.\n * Also used to prefix tools if conflicted with other adapters in the same app.\n */\n name: string;\n\n /**\n * The base URL of the API.\n * This is used to construct the full URL for each request.\n * For example, if the API is hosted at https://api.example.com/v1,\n * the baseUrl should be set to https://api.example.com/v1.\n */\n baseUrl: string;\n\n /**\n * Additional headers to be sent with each request.\n * This can be used to set authentication headers,\n * such as Authorization or API Key.\n */\n additionalHeaders?: Record<string, string>;\n /**\n * This can be used to map request information to specific\n * headers as required by the API.\n * For example, mapping tenantId from authenticated session payload to\n * a specific header, this key will be hidden to mcp clients\n * and filled by the adapter before sending the request to the API.\n * @param authInfo\n * @param headers\n */\n headersMapper?: (authInfo: AuthInfo, headers: Headers) => Headers;\n /**\n * This can be used to map request information to specific\n * body values as required by the API.\n * For example, mapping tenantId from authenticated session payload to\n * a specific property in the body, this key will be hidden to mcp clients\n * and filled by the adapter before sending the request to the API.\n *\n * @param authInfo\n * @param body\n */\n bodyMapper?: (authInfo: AuthInfo, body: any) => any;\n}\n\ninterface SpecOptions extends BaseOptions {\n /**\n * The OpenAPI specification the OpenAPI specification.\n */\n spec: OpenAPIV3.Document;\n}\n\ninterface UrlOptions extends BaseOptions {\n /**\n * The URL of the OpenAPI specification.\n * Can be a local file path or a remote URL.\n */\n url: string;\n}\n\nexport type OpenApiAdapterOptions = SpecOptions | UrlOptions;\n"]}