@frontmcp/adapters 0.3.0 → 0.4.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.
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSecurityContextFromAuth = createSecurityContextFromAuth;
4
+ exports.extractSecuritySchemes = extractSecuritySchemes;
5
+ exports.validateSecurityConfiguration = validateSecurityConfiguration;
6
+ exports.resolveToolSecurity = resolveToolSecurity;
7
+ const mcp_from_openapi_1 = require("mcp-from-openapi");
8
+ /**
9
+ * Resolve security context from FrontMCP auth info with support for multiple auth providers
10
+ *
11
+ * @param tool - OpenAPI tool to resolve security for
12
+ * @param authInfo - FrontMCP authentication info
13
+ * @param options - Adapter options with auth configuration
14
+ * @returns Security context for resolver
15
+ */
16
+ async function createSecurityContextFromAuth(tool, authInfo, options) {
17
+ // 1. Use custom security resolver if provided (highest priority)
18
+ if (options.securityResolver) {
19
+ return await options.securityResolver(tool, authInfo);
20
+ }
21
+ // 2. Use auth provider mapper if provided
22
+ if (options.authProviderMapper) {
23
+ const context = (0, mcp_from_openapi_1.createSecurityContext)({});
24
+ // Find all security schemes used by this tool
25
+ const securitySchemes = new Set();
26
+ for (const mapper of tool.mapper) {
27
+ if (mapper.security?.scheme) {
28
+ securitySchemes.add(mapper.security.scheme);
29
+ }
30
+ }
31
+ // Map each security scheme to its auth provider
32
+ for (const scheme of securitySchemes) {
33
+ const authExtractor = options.authProviderMapper[scheme];
34
+ if (authExtractor) {
35
+ const token = authExtractor(authInfo);
36
+ if (token) {
37
+ // Store in jwt field for http bearer auth
38
+ // For other auth types, you can extend this logic
39
+ context.jwt = token;
40
+ break; // Use first matching provider
41
+ }
42
+ }
43
+ }
44
+ // If no provider matched but we have a default token, use it
45
+ if (!context.jwt && authInfo.token) {
46
+ context.jwt = authInfo.token;
47
+ }
48
+ return context;
49
+ }
50
+ // 3. Use static auth if provided
51
+ if (options.staticAuth) {
52
+ return (0, mcp_from_openapi_1.createSecurityContext)(options.staticAuth);
53
+ }
54
+ // 4. Default: use main JWT token from auth context
55
+ return (0, mcp_from_openapi_1.createSecurityContext)({
56
+ jwt: authInfo?.token,
57
+ });
58
+ }
59
+ /**
60
+ * Extract all security schemes used by a set of tools
61
+ *
62
+ * @param tools - OpenAPI tools
63
+ * @returns Set of security scheme names
64
+ */
65
+ function extractSecuritySchemes(tools) {
66
+ const schemes = new Set();
67
+ for (const tool of tools) {
68
+ for (const mapper of tool.mapper) {
69
+ if (mapper.security?.scheme) {
70
+ schemes.add(mapper.security.scheme);
71
+ }
72
+ }
73
+ }
74
+ return schemes;
75
+ }
76
+ /**
77
+ * Validate security configuration against OpenAPI security requirements
78
+ *
79
+ * @param tools - OpenAPI tools
80
+ * @param options - Adapter options
81
+ * @returns Validation result with errors and warnings
82
+ */
83
+ function validateSecurityConfiguration(tools, options) {
84
+ const result = {
85
+ valid: true,
86
+ missingMappings: [],
87
+ warnings: [],
88
+ securityRiskScore: 'low',
89
+ };
90
+ // Extract all security schemes used
91
+ const securitySchemes = extractSecuritySchemes(tools);
92
+ // If includeSecurityInInput is true, auth is provided by user (high security risk)
93
+ const includeSecurityInInput = options.generateOptions?.includeSecurityInInput ?? false;
94
+ if (includeSecurityInInput) {
95
+ result.securityRiskScore = 'high';
96
+ result.warnings.push('SECURITY WARNING: includeSecurityInInput is enabled. Users will provide authentication directly in tool inputs. This increases security risk as credentials may be logged or exposed.');
97
+ // Don't validate mappings if security is in input
98
+ return result;
99
+ }
100
+ // Check if we have custom security resolver (most flexible, low risk)
101
+ if (options.securityResolver) {
102
+ result.securityRiskScore = 'low';
103
+ result.warnings.push('INFO: Using custom securityResolver. Ensure your resolver properly validates and secures credentials from context.');
104
+ return result;
105
+ }
106
+ // Check if we have static auth (medium risk - static credentials)
107
+ if (options.staticAuth && Object.keys(options.staticAuth).length > 0) {
108
+ result.securityRiskScore = 'medium';
109
+ result.warnings.push('SECURITY INFO: Using staticAuth with hardcoded credentials. Ensure credentials are stored securely (environment variables, secrets manager).');
110
+ // If static auth is provided, assume it covers all schemes
111
+ return result;
112
+ }
113
+ // Check authProviderMapper (low risk - context-based auth)
114
+ if (options.authProviderMapper) {
115
+ result.securityRiskScore = 'low';
116
+ // Validate that all schemes have mappings
117
+ for (const scheme of securitySchemes) {
118
+ if (!options.authProviderMapper[scheme]) {
119
+ result.valid = false;
120
+ result.missingMappings.push(scheme);
121
+ }
122
+ }
123
+ if (!result.valid) {
124
+ result.warnings.push(`ERROR: Missing auth provider mappings for security schemes: ${result.missingMappings.join(', ')}`);
125
+ }
126
+ return result;
127
+ }
128
+ // No auth configuration provided - will use default ctx.authInfo.token
129
+ // This only works if there's a single Bearer auth scheme
130
+ if (securitySchemes.size > 0) {
131
+ result.securityRiskScore = 'medium';
132
+ result.warnings.push(`INFO: No auth configuration provided. Using default ctx.authInfo.token for all security schemes: ${Array.from(securitySchemes).join(', ')}`);
133
+ result.warnings.push('RECOMMENDATION: For multiple auth providers, use authProviderMapper or securityResolver to map each security scheme to the correct auth provider.');
134
+ }
135
+ return result;
136
+ }
137
+ /**
138
+ * Resolve security for an OpenAPI tool with validation
139
+ *
140
+ * @param tool - OpenAPI tool with mapper
141
+ * @param authInfo - FrontMCP authentication info
142
+ * @param options - Adapter options with auth configuration
143
+ * @returns Resolved security (headers, query params, etc.)
144
+ * @throws Error if security cannot be resolved
145
+ */
146
+ async function resolveToolSecurity(tool, authInfo, options) {
147
+ const securityResolver = new mcp_from_openapi_1.SecurityResolver();
148
+ const securityContext = await createSecurityContextFromAuth(tool, authInfo, options);
149
+ // Validate that we have auth for this tool
150
+ const hasAuth = securityContext.jwt ||
151
+ securityContext.apiKey ||
152
+ securityContext.basic ||
153
+ securityContext.oauth2Token ||
154
+ (securityContext.apiKeys && Object.keys(securityContext.apiKeys).length > 0) ||
155
+ (securityContext.customHeaders && Object.keys(securityContext.customHeaders).length > 0);
156
+ // Check if this tool requires security
157
+ const requiresSecurity = tool.mapper.some((m) => m.security && m.required);
158
+ if (requiresSecurity && !hasAuth) {
159
+ // Extract security scheme names
160
+ const schemes = tool.mapper
161
+ .filter((m) => m.security && m.required)
162
+ .map((m) => m.security?.scheme ?? 'unknown')
163
+ .join(', ');
164
+ throw new Error(`Authentication required for tool '${tool.name}' but no auth configuration found.\n` +
165
+ `Required security schemes: ${schemes}\n` +
166
+ `Solutions:\n` +
167
+ ` 1. Add authProviderMapper: { '${schemes.split(',')[0].trim()}': (authInfo) => authInfo.user?.token }\n` +
168
+ ` 2. Add securityResolver: (tool, authInfo) => ({ jwt: authInfo.token })\n` +
169
+ ` 3. Add staticAuth: { jwt: process.env.API_TOKEN }\n` +
170
+ ` 4. Set generateOptions.includeSecurityInInput: true (not recommended for production)`);
171
+ }
172
+ return await securityResolver.resolve(tool.mapper, securityContext);
173
+ }
174
+ //# sourceMappingURL=openapi.security.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.security.js","sourceRoot":"","sources":["../../../src/openapi/openapi.security.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n SecurityResolver,\n createSecurityContext,\n type McpOpenAPITool,\n type SecurityContext,\n} from 'mcp-from-openapi';\nimport type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';\nimport type { OpenApiAdapterOptions } from './openapi.types';\n\n/**\n * Security scheme information extracted from OpenAPI spec\n */\nexport interface SecuritySchemeInfo {\n name: string;\n type: string;\n scheme?: string;\n in?: string;\n description?: string;\n}\n\n/**\n * Security validation result\n */\nexport interface SecurityValidationResult {\n valid: boolean;\n missingMappings: string[];\n warnings: string[];\n securityRiskScore: 'low' | 'medium' | 'high';\n}\n\n/**\n * Resolve security context from FrontMCP auth info with support for multiple auth providers\n *\n * @param tool - OpenAPI tool to resolve security for\n * @param authInfo - FrontMCP authentication info\n * @param options - Adapter options with auth configuration\n * @returns Security context for resolver\n */\nexport async function createSecurityContextFromAuth(\n tool: McpOpenAPITool,\n authInfo: AuthInfo,\n options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth'>\n): Promise<SecurityContext> {\n // 1. Use custom security resolver if provided (highest priority)\n if (options.securityResolver) {\n return await options.securityResolver(tool, authInfo);\n }\n\n // 2. Use auth provider mapper if provided\n if (options.authProviderMapper) {\n const context = createSecurityContext({});\n\n // Find all security schemes used by this tool\n const securitySchemes = new Set<string>();\n for (const mapper of tool.mapper) {\n if (mapper.security?.scheme) {\n securitySchemes.add(mapper.security.scheme);\n }\n }\n\n // Map each security scheme to its auth provider\n for (const scheme of securitySchemes) {\n const authExtractor = options.authProviderMapper[scheme];\n if (authExtractor) {\n const token = authExtractor(authInfo);\n if (token) {\n // Store in jwt field for http bearer auth\n // For other auth types, you can extend this logic\n context.jwt = token;\n break; // Use first matching provider\n }\n }\n }\n\n // If no provider matched but we have a default token, use it\n if (!context.jwt && authInfo.token) {\n context.jwt = authInfo.token;\n }\n\n return context;\n }\n\n // 3. Use static auth if provided\n if (options.staticAuth) {\n return createSecurityContext(options.staticAuth);\n }\n\n // 4. Default: use main JWT token from auth context\n return createSecurityContext({\n jwt: authInfo?.token,\n });\n}\n\n/**\n * Extract all security schemes used by a set of tools\n *\n * @param tools - OpenAPI tools\n * @returns Set of security scheme names\n */\nexport function extractSecuritySchemes(tools: McpOpenAPITool[]): Set<string> {\n const schemes = new Set<string>();\n\n for (const tool of tools) {\n for (const mapper of tool.mapper) {\n if (mapper.security?.scheme) {\n schemes.add(mapper.security.scheme);\n }\n }\n }\n\n return schemes;\n}\n\n/**\n * Validate security configuration against OpenAPI security requirements\n *\n * @param tools - OpenAPI tools\n * @param options - Adapter options\n * @returns Validation result with errors and warnings\n */\nexport function validateSecurityConfiguration(\n tools: McpOpenAPITool[],\n options: Pick<\n OpenApiAdapterOptions,\n 'securityResolver' | 'authProviderMapper' | 'staticAuth' | 'generateOptions'\n >\n): SecurityValidationResult {\n const result: SecurityValidationResult = {\n valid: true,\n missingMappings: [],\n warnings: [],\n securityRiskScore: 'low',\n };\n\n // Extract all security schemes used\n const securitySchemes = extractSecuritySchemes(tools);\n\n // If includeSecurityInInput is true, auth is provided by user (high security risk)\n const includeSecurityInInput = options.generateOptions?.includeSecurityInInput ?? false;\n\n if (includeSecurityInInput) {\n result.securityRiskScore = 'high';\n result.warnings.push(\n 'SECURITY WARNING: includeSecurityInInput is enabled. Users will provide authentication directly in tool inputs. This increases security risk as credentials may be logged or exposed.'\n );\n // Don't validate mappings if security is in input\n return result;\n }\n\n // Check if we have custom security resolver (most flexible, low risk)\n if (options.securityResolver) {\n result.securityRiskScore = 'low';\n result.warnings.push(\n 'INFO: Using custom securityResolver. Ensure your resolver properly validates and secures credentials from context.'\n );\n return result;\n }\n\n // Check if we have static auth (medium risk - static credentials)\n if (options.staticAuth && Object.keys(options.staticAuth).length > 0) {\n result.securityRiskScore = 'medium';\n result.warnings.push(\n 'SECURITY INFO: Using staticAuth with hardcoded credentials. Ensure credentials are stored securely (environment variables, secrets manager).'\n );\n // If static auth is provided, assume it covers all schemes\n return result;\n }\n\n // Check authProviderMapper (low risk - context-based auth)\n if (options.authProviderMapper) {\n result.securityRiskScore = 'low';\n\n // Validate that all schemes have mappings\n for (const scheme of securitySchemes) {\n if (!options.authProviderMapper[scheme]) {\n result.valid = false;\n result.missingMappings.push(scheme);\n }\n }\n\n if (!result.valid) {\n result.warnings.push(\n `ERROR: Missing auth provider mappings for security schemes: ${result.missingMappings.join(', ')}`\n );\n }\n\n return result;\n }\n\n // No auth configuration provided - will use default ctx.authInfo.token\n // This only works if there's a single Bearer auth scheme\n if (securitySchemes.size > 0) {\n result.securityRiskScore = 'medium';\n result.warnings.push(\n `INFO: No auth configuration provided. Using default ctx.authInfo.token for all security schemes: ${Array.from(securitySchemes).join(', ')}`\n );\n result.warnings.push(\n 'RECOMMENDATION: For multiple auth providers, use authProviderMapper or securityResolver to map each security scheme to the correct auth provider.'\n );\n }\n\n return result;\n}\n\n/**\n * Resolve security for an OpenAPI tool with validation\n *\n * @param tool - OpenAPI tool with mapper\n * @param authInfo - FrontMCP authentication info\n * @param options - Adapter options with auth configuration\n * @returns Resolved security (headers, query params, etc.)\n * @throws Error if security cannot be resolved\n */\nexport async function resolveToolSecurity(\n tool: McpOpenAPITool,\n authInfo: AuthInfo,\n options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth'>\n) {\n const securityResolver = new SecurityResolver();\n const securityContext = await createSecurityContextFromAuth(tool, authInfo, options);\n\n // Validate that we have auth for this tool\n const hasAuth =\n securityContext.jwt ||\n securityContext.apiKey ||\n securityContext.basic ||\n securityContext.oauth2Token ||\n (securityContext.apiKeys && Object.keys(securityContext.apiKeys).length > 0) ||\n (securityContext.customHeaders && Object.keys(securityContext.customHeaders).length > 0);\n\n // Check if this tool requires security\n const requiresSecurity = tool.mapper.some((m) => m.security && m.required);\n\n if (requiresSecurity && !hasAuth) {\n // Extract security scheme names\n const schemes = tool.mapper\n .filter((m) => m.security && m.required)\n .map((m) => m.security?.scheme ?? 'unknown')\n .join(', ');\n\n throw new Error(\n `Authentication required for tool '${tool.name}' but no auth configuration found.\\n` +\n `Required security schemes: ${schemes}\\n` +\n `Solutions:\\n` +\n ` 1. Add authProviderMapper: { '${schemes.split(',')[0].trim()}': (authInfo) => authInfo.user?.token }\\n` +\n ` 2. Add securityResolver: (tool, authInfo) => ({ jwt: authInfo.token })\\n` +\n ` 3. Add staticAuth: { jwt: process.env.API_TOKEN }\\n` +\n ` 4. Set generateOptions.includeSecurityInInput: true (not recommended for production)`\n );\n }\n\n return await securityResolver.resolve(tool.mapper, securityContext);\n}\n"]}
@@ -1,3 +1,10 @@
1
- import { McpToolDefinition } from "openapi-mcp-generator";
2
- import { OpenApiAdapterOptions } from "./openapi.types";
3
- export declare const createOpenApiTool: (oTool: McpToolDefinition, options: OpenApiAdapterOptions) => () => void;
1
+ import type { McpOpenAPITool } from 'mcp-from-openapi';
2
+ import type { OpenApiAdapterOptions } from './openapi.types';
3
+ /**
4
+ * Create a FrontMCP tool from an OpenAPI tool definition
5
+ *
6
+ * @param openapiTool - OpenAPI tool with mapper
7
+ * @param options - Adapter options
8
+ * @returns FrontMCP tool
9
+ */
10
+ export declare function createOpenApiTool(openapiTool: McpOpenAPITool, options: OpenApiAdapterOptions): () => void;
@@ -1,67 +1,65 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createOpenApiTool = void 0;
3
+ exports.createOpenApiTool = createOpenApiTool;
4
4
  const zod_1 = require("zod");
5
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;
6
+ const json_schema_to_zod_v3_1 = require("json-schema-to-zod-v3");
7
+ const openapi_utils_1 = require("./openapi.utils");
8
+ const openapi_security_1 = require("./openapi.security");
9
+ /**
10
+ * Create a FrontMCP tool from an OpenAPI tool definition
11
+ *
12
+ * @param openapiTool - OpenAPI tool with mapper
13
+ * @param options - Adapter options
14
+ * @returns FrontMCP tool
15
+ */
16
+ function createOpenApiTool(openapiTool, options) {
17
+ // Convert JSON Schema to Zod schema for input validation
18
+ const inputSchema = getZodSchemaFromJsonSchema(openapiTool.inputSchema, openapiTool.name);
10
19
  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
20
+ id: openapiTool.name,
21
+ name: openapiTool.name,
22
+ description: openapiTool.description,
23
+ inputSchema: inputSchema.shape || {},
24
+ rawInputSchema: openapiTool.inputSchema,
17
25
  })(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
- }
26
+ // 1. Resolve security from context
27
+ const security = await (0, openapi_security_1.resolveToolSecurity)(openapiTool, ctx.authInfo, options);
28
+ // 2. Build request from mapper
29
+ const { url, headers, body: requestBody } = (0, openapi_utils_1.buildRequest)(openapiTool, input, security, options.baseUrl);
30
+ // 3. Apply additional headers
31
+ (0, openapi_utils_1.applyAdditionalHeaders)(headers, options.additionalHeaders);
32
+ // 4. Apply custom headers mapper
33
+ if (options.headersMapper) {
34
+ const mappedHeaders = options.headersMapper(ctx.authInfo, headers);
35
+ mappedHeaders.forEach((value, key) => {
36
+ headers.set(key, value);
37
+ });
24
38
  }
25
- if (typeof headersMapper === 'function') {
26
- headers = headersMapper(ctx.authInfo, headers);
39
+ // 5. Apply custom body mapper
40
+ let finalBody = requestBody;
41
+ if (options.bodyMapper && requestBody) {
42
+ finalBody = options.bodyMapper(ctx.authInfo, requestBody);
27
43
  }
28
- if (!['HEAD', 'GET', 'OPTIONS'].includes(oTool.method)) {
29
- // prepare body
30
- if (oTool.requestBodyContentType && typeof input['requestBody'] !== 'undefined') {
31
- requestBodyData = input['requestBody'];
32
- if (oTool.requestBodyContentType?.includes('application/json')) {
33
- requestBodyData = JSON.stringify(requestBodyData);
34
- }
35
- headers.set('content-type', oTool.requestBodyContentType);
36
- }
44
+ // 6. Set content-type if we have a body
45
+ if (finalBody && !headers.has('content-type')) {
46
+ headers.set('content-type', 'application/json');
37
47
  }
38
- const query = queryParams.toString();
39
- const url = `${options.baseUrl}${urlPath}${query ? `?${query}` : ''}`;
40
- const res = await fetch(url, {
41
- method: oTool.method,
48
+ // 7. Execute request
49
+ const response = await fetch(url, {
50
+ method: openapiTool.metadata.method.toUpperCase(),
42
51
  headers,
43
- body: requestBodyData,
52
+ body: finalBody ? JSON.stringify(finalBody) : undefined,
44
53
  });
45
- const data = await res.text();
46
- let result = { data };
47
- if (res.headers.get('content-type')?.includes('application/json')) {
48
- try {
49
- result.data = JSON.parse(data);
50
- }
51
- catch (e) {
52
- console.error("failed to parse api response"); // migrate to logger
53
- result.data = data;
54
- }
55
- }
56
- return result;
54
+ // 8. Parse and return response
55
+ return await (0, openapi_utils_1.parseResponse)(response);
57
56
  });
58
- };
59
- exports.createOpenApiTool = createOpenApiTool;
57
+ }
60
58
  /**
61
59
  * Converts a JSON Schema to a Zod schema for runtime validation
62
60
  *
63
- * @param jsonSchema JSON Schema
64
- * @param toolName Tool name for error reporting
61
+ * @param jsonSchema - JSON Schema
62
+ * @param toolName - Tool name for error reporting
65
63
  * @returns Zod schema
66
64
  */
67
65
  function getZodSchemaFromJsonSchema(jsonSchema, toolName) {
@@ -69,41 +67,15 @@ function getZodSchemaFromJsonSchema(jsonSchema, toolName) {
69
67
  return zod_1.z.object({}).passthrough();
70
68
  }
71
69
  try {
72
- const zodSchema = (0, zod_from_json_schema_1.convertJsonSchemaToZod)(jsonSchema);
70
+ const zodSchema = (0, json_schema_to_zod_v3_1.convertJsonSchemaToZod)(jsonSchema);
73
71
  if (typeof zodSchema?.parse !== 'function') {
74
- throw new Error('Eval did not produce a valid Zod schema.');
72
+ throw new Error('Conversion did not produce a valid Zod schema.');
75
73
  }
76
74
  return zodSchema;
77
75
  }
78
76
  catch (err) {
79
- console.error(`Failed to generate/evaluate Zod schema for '${toolName}':`, err);
77
+ console.error(`Failed to generate Zod schema for '${toolName}':`, err);
80
78
  return zod_1.z.object({}).passthrough();
81
79
  }
82
80
  }
83
- const prepareUrl = (definition, validatedArgs) => {
84
- // Prepare URL, query parameters, headers, and request body
85
- let urlPath = definition.pathTemplate;
86
- const queryParams = new URLSearchParams({ "v": '1' });
87
- const headers = new Headers({ 'accept': 'application/json' });
88
- // Apply parameters to the URL path, query, or headers
89
- definition.executionParameters.forEach((param) => {
90
- const value = validatedArgs[param.name];
91
- if (typeof value !== 'undefined' && value !== null) {
92
- if (param.in === 'path') {
93
- urlPath = urlPath.replace(`{${param.name}}`, encodeURIComponent(String(value)));
94
- }
95
- else if (param.in === 'query') {
96
- queryParams.set(param.name, value);
97
- }
98
- else if (param.in === 'header') {
99
- headers.append(param.name.toLowerCase(), String(value));
100
- }
101
- }
102
- });
103
- // Ensure all path parameters are resolved
104
- if (urlPath.includes('{')) {
105
- throw new Error(`Failed to resolve path parameters: ${urlPath}`);
106
- }
107
- return { urlPath, headers, queryParams };
108
- };
109
81
  //# sourceMappingURL=openapi.tool.js.map
@@ -1 +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 if (oTool.requestBodyContentType?.includes('application/json')) {\n requestBodyData = JSON.stringify(requestBodyData);\n }\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
+ {"version":3,"file":"openapi.tool.js","sourceRoot":"","sources":["../../../src/openapi/openapi.tool.ts"],"names":[],"mappings":";;AAgBA,8CAyDC;AAzED,6BAAwB;AACxB,uCAAqC;AACrC,iEAA+D;AAI/D,mDAAsF;AACtF,yDAAyD;AAEzD;;;;;;GAMG;AACH,SAAgB,iBAAiB,CAC/B,WAA2B,EAC3B,OAA8B;IAE9B,yDAAyD;IACzD,MAAM,WAAW,GAAG,0BAA0B,CAAC,WAAW,CAAC,WAAW,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;IAE1F,OAAO,IAAA,UAAI,EAAC;QACV,EAAE,EAAE,WAAW,CAAC,IAAI;QACpB,IAAI,EAAE,WAAW,CAAC,IAAI;QACtB,WAAW,EAAE,WAAW,CAAC,WAAW;QACpC,WAAW,EAAE,WAAW,CAAC,KAAK,IAAI,EAAE;QACpC,cAAc,EAAE,WAAW,CAAC,WAAW;KACxC,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,mCAAmC;QACnC,MAAM,QAAQ,GAAG,MAAM,IAAA,sCAAmB,EAAC,WAAW,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAE/E,+BAA+B;QAC/B,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,IAAA,4BAAY,EACtD,WAAW,EACX,KAAK,EACL,QAAQ,EACR,OAAO,CAAC,OAAO,CAChB,CAAC;QAEF,8BAA8B;QAC9B,IAAA,sCAAsB,EAAC,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAE3D,iCAAiC;QACjC,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;YAC1B,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnE,aAAa,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;gBACnC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC1B,CAAC,CAAC,CAAC;QACL,CAAC;QAED,8BAA8B;QAC9B,IAAI,SAAS,GAAG,WAAW,CAAC;QAC5B,IAAI,OAAO,CAAC,UAAU,IAAI,WAAW,EAAE,CAAC;YACtC,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAC5D,CAAC;QAED,wCAAwC;QACxC,IAAI,SAAS,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;YAC9C,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;QAClD,CAAC;QAED,qBAAqB;QACrB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE;YACjD,OAAO;YACP,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;SACxD,CAAC,CAAC;QAEH,+BAA+B;QAC/B,OAAO,MAAM,IAAA,6BAAa,EAAC,QAAQ,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,SAAS,0BAA0B,CACjC,UAAuB,EACvB,QAAgB;IAEhB,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QAC1D,OAAO,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACpC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAA,8CAAsB,EAAC,UAAU,CAAC,CAAC;QACrD,IAAI,OAAO,SAAS,EAAE,KAAK,KAAK,UAAU,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,SAAuC,CAAC;IACjD,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,sCAAsC,QAAQ,IAAI,EAAE,GAAG,CAAC,CAAC;QACvE,OAAO,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACpC,CAAC;AACH,CAAC","sourcesContent":["import { z } from 'zod';\nimport { tool } from '@frontmcp/sdk';\nimport { convertJsonSchemaToZod } from 'json-schema-to-zod-v3';\nimport type { McpOpenAPITool } from 'mcp-from-openapi';\nimport type { OpenApiAdapterOptions } from './openapi.types';\nimport type { JSONSchema7 } from 'json-schema';\nimport { buildRequest, applyAdditionalHeaders, parseResponse } from './openapi.utils';\nimport { resolveToolSecurity } from './openapi.security';\n\n/**\n * Create a FrontMCP tool from an OpenAPI tool definition\n *\n * @param openapiTool - OpenAPI tool with mapper\n * @param options - Adapter options\n * @returns FrontMCP tool\n */\nexport function createOpenApiTool(\n openapiTool: McpOpenAPITool,\n options: OpenApiAdapterOptions\n) {\n // Convert JSON Schema to Zod schema for input validation\n const inputSchema = getZodSchemaFromJsonSchema(openapiTool.inputSchema, openapiTool.name);\n\n return tool({\n id: openapiTool.name,\n name: openapiTool.name,\n description: openapiTool.description,\n inputSchema: inputSchema.shape || {},\n rawInputSchema: openapiTool.inputSchema,\n })(async (input, ctx) => {\n // 1. Resolve security from context\n const security = await resolveToolSecurity(openapiTool, ctx.authInfo, options);\n\n // 2. Build request from mapper\n const { url, headers, body: requestBody } = buildRequest(\n openapiTool,\n input,\n security,\n options.baseUrl\n );\n\n // 3. Apply additional headers\n applyAdditionalHeaders(headers, options.additionalHeaders);\n\n // 4. Apply custom headers mapper\n if (options.headersMapper) {\n const mappedHeaders = options.headersMapper(ctx.authInfo, headers);\n mappedHeaders.forEach((value, key) => {\n headers.set(key, value);\n });\n }\n\n // 5. Apply custom body mapper\n let finalBody = requestBody;\n if (options.bodyMapper && requestBody) {\n finalBody = options.bodyMapper(ctx.authInfo, requestBody);\n }\n\n // 6. Set content-type if we have a body\n if (finalBody && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n\n // 7. Execute request\n const response = await fetch(url, {\n method: openapiTool.metadata.method.toUpperCase(),\n headers,\n body: finalBody ? JSON.stringify(finalBody) : undefined,\n });\n\n // 8. Parse and return response\n return await parseResponse(response);\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(\n jsonSchema: JSONSchema7,\n toolName: string\n): z.ZodObject<z.ZodRawShape> {\n if (typeof jsonSchema !== 'object' || jsonSchema === null) {\n return z.object({}).passthrough();\n }\n\n try {\n const zodSchema = convertJsonSchemaToZod(jsonSchema);\n if (typeof zodSchema?.parse !== 'function') {\n throw new Error('Conversion did not produce a valid Zod schema.');\n }\n return zodSchema as z.ZodObject<z.ZodRawShape>;\n } catch (err: unknown) {\n console.error(`Failed to generate Zod schema for '${toolName}':`, err);\n return z.object({}).passthrough();\n }\n}\n"]}
@@ -1,7 +1,7 @@
1
- import { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js";
2
- import { GetToolsOptions } from "openapi-mcp-generator/dist/api";
3
- import { OpenAPIV3 } from "openapi-types";
4
- interface BaseOptions extends Omit<GetToolsOptions, 'dereference'> {
1
+ import { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
2
+ import { OpenAPIV3 } from 'openapi-types';
3
+ import type { LoadOptions, GenerateOptions, McpOpenAPITool, SecurityContext } from 'mcp-from-openapi';
4
+ interface BaseOptions {
5
5
  /**
6
6
  * The name of the adapter.
7
7
  * This is used to identify the adapter in the MCP configuration.
@@ -13,6 +13,7 @@ interface BaseOptions extends Omit<GetToolsOptions, 'dereference'> {
13
13
  * This is used to construct the full URL for each request.
14
14
  * For example, if the API is hosted at https://api.example.com/v1,
15
15
  * the baseUrl should be set to https://api.example.com/v1.
16
+ * This overrides the baseUrl in LoadOptions.
16
17
  */
17
18
  baseUrl: string;
18
19
  /**
@@ -41,7 +42,76 @@ interface BaseOptions extends Omit<GetToolsOptions, 'dereference'> {
41
42
  * @param authInfo
42
43
  * @param body
43
44
  */
44
- bodyMapper?: (authInfo: AuthInfo, body: any) => any;
45
+ bodyMapper?: (authInfo: AuthInfo, body: Record<string, unknown>) => Record<string, unknown>;
46
+ /**
47
+ * Custom security resolver for resolving authentication from context.
48
+ * This allows you to map different auth providers to different tools/security schemes.
49
+ *
50
+ * Use this when:
51
+ * - You have multiple auth providers (e.g., GitHub OAuth, Google OAuth, API keys)
52
+ * - Different tools need different authentication
53
+ * - You need custom logic to select the right auth provider
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * securityResolver: (tool, authInfo) => {
58
+ * // Use GitHub token for GitHub API tools
59
+ * if (tool.name.startsWith('github_')) {
60
+ * return { jwt: authInfo.user?.githubToken };
61
+ * }
62
+ * // Use Google token for Google API tools
63
+ * if (tool.name.startsWith('google_')) {
64
+ * return { jwt: authInfo.user?.googleToken };
65
+ * }
66
+ * // Default to main JWT token
67
+ * return { jwt: authInfo.token };
68
+ * }
69
+ * ```
70
+ */
71
+ securityResolver?: (tool: McpOpenAPITool, authInfo: AuthInfo) => SecurityContext | Promise<SecurityContext>;
72
+ /**
73
+ * Map security scheme names to auth provider extractors.
74
+ * This allows different security schemes to use different auth providers.
75
+ *
76
+ * Use this when your OpenAPI spec has multiple security schemes
77
+ * and each should use a different auth provider from the context.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * authProviderMapper: {
82
+ * // GitHub OAuth security scheme
83
+ * 'GitHubAuth': (authInfo) => authInfo.user?.githubToken,
84
+ * // Google OAuth security scheme
85
+ * 'GoogleAuth': (authInfo) => authInfo.user?.googleToken,
86
+ * // API Key security scheme
87
+ * 'ApiKeyAuth': (authInfo) => authInfo.user?.apiKey,
88
+ * }
89
+ * ```
90
+ */
91
+ authProviderMapper?: Record<string, (authInfo: AuthInfo) => string | undefined>;
92
+ /**
93
+ * Static authentication configuration when not using dynamic auth from context.
94
+ * Useful for server-to-server APIs with static credentials.
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * staticAuth: {
99
+ * jwt: process.env.API_JWT_TOKEN,
100
+ * apiKey: process.env.API_KEY,
101
+ * }
102
+ * ```
103
+ */
104
+ staticAuth?: Partial<SecurityContext>;
105
+ /**
106
+ * Options for loading the OpenAPI specification
107
+ * @see LoadOptions from mcp-from-openapi
108
+ */
109
+ loadOptions?: Omit<LoadOptions, 'baseUrl'>;
110
+ /**
111
+ * Options for generating tools from the OpenAPI specification
112
+ * @see GenerateOptions from mcp-from-openapi
113
+ */
114
+ generateOptions?: GenerateOptions;
45
115
  }
46
116
  interface SpecOptions extends BaseOptions {
47
117
  /**
@@ -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\";\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"]}
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 { OpenAPIV3 } from 'openapi-types';\nimport type { LoadOptions, GenerateOptions, McpOpenAPITool, SecurityContext } from 'mcp-from-openapi';\n\ninterface BaseOptions {\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 * This overrides the baseUrl in LoadOptions.\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 /**\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 /**\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: Record<string, unknown>) => Record<string, unknown>;\n\n /**\n * Custom security resolver for resolving authentication from context.\n * This allows you to map different auth providers to different tools/security schemes.\n *\n * Use this when:\n * - You have multiple auth providers (e.g., GitHub OAuth, Google OAuth, API keys)\n * - Different tools need different authentication\n * - You need custom logic to select the right auth provider\n *\n * @example\n * ```typescript\n * securityResolver: (tool, authInfo) => {\n * // Use GitHub token for GitHub API tools\n * if (tool.name.startsWith('github_')) {\n * return { jwt: authInfo.user?.githubToken };\n * }\n * // Use Google token for Google API tools\n * if (tool.name.startsWith('google_')) {\n * return { jwt: authInfo.user?.googleToken };\n * }\n * // Default to main JWT token\n * return { jwt: authInfo.token };\n * }\n * ```\n */\n securityResolver?: (\n tool: McpOpenAPITool,\n authInfo: AuthInfo\n ) => SecurityContext | Promise<SecurityContext>;\n\n /**\n * Map security scheme names to auth provider extractors.\n * This allows different security schemes to use different auth providers.\n *\n * Use this when your OpenAPI spec has multiple security schemes\n * and each should use a different auth provider from the context.\n *\n * @example\n * ```typescript\n * authProviderMapper: {\n * // GitHub OAuth security scheme\n * 'GitHubAuth': (authInfo) => authInfo.user?.githubToken,\n * // Google OAuth security scheme\n * 'GoogleAuth': (authInfo) => authInfo.user?.googleToken,\n * // API Key security scheme\n * 'ApiKeyAuth': (authInfo) => authInfo.user?.apiKey,\n * }\n * ```\n */\n authProviderMapper?: Record<string, (authInfo: AuthInfo) => string | undefined>;\n\n /**\n * Static authentication configuration when not using dynamic auth from context.\n * Useful for server-to-server APIs with static credentials.\n *\n * @example\n * ```typescript\n * staticAuth: {\n * jwt: process.env.API_JWT_TOKEN,\n * apiKey: process.env.API_KEY,\n * }\n * ```\n */\n staticAuth?: Partial<SecurityContext>;\n\n /**\n * Options for loading the OpenAPI specification\n * @see LoadOptions from mcp-from-openapi\n */\n loadOptions?: Omit<LoadOptions, 'baseUrl'>; // baseUrl is in BaseOptions\n\n /**\n * Options for generating tools from the OpenAPI specification\n * @see GenerateOptions from mcp-from-openapi\n */\n generateOptions?: GenerateOptions;\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"]}
@@ -0,0 +1,35 @@
1
+ import type { McpOpenAPITool, SecurityResolver } from 'mcp-from-openapi';
2
+ /**
3
+ * Request configuration for building HTTP requests
4
+ */
5
+ export interface RequestConfig {
6
+ url: string;
7
+ headers: Headers;
8
+ body?: Record<string, unknown>;
9
+ }
10
+ /**
11
+ * Build HTTP request from OpenAPI tool and input parameters
12
+ *
13
+ * @param tool - OpenAPI tool definition with mapper
14
+ * @param input - User input parameters
15
+ * @param security - Resolved security (headers, query params, etc.)
16
+ * @param baseUrl - API base URL
17
+ * @returns Request configuration ready for fetch
18
+ */
19
+ export declare function buildRequest(tool: McpOpenAPITool, input: Record<string, unknown>, security: Awaited<ReturnType<SecurityResolver['resolve']>>, baseUrl: string): RequestConfig;
20
+ /**
21
+ * Apply custom headers to request
22
+ *
23
+ * @param headers - Current headers
24
+ * @param additionalHeaders - Additional static headers to add
25
+ */
26
+ export declare function applyAdditionalHeaders(headers: Headers, additionalHeaders?: Record<string, string>): void;
27
+ /**
28
+ * Parse API response based on content type
29
+ *
30
+ * @param response - Fetch response
31
+ * @returns Parsed response data
32
+ */
33
+ export declare function parseResponse(response: Response): Promise<{
34
+ data: unknown;
35
+ }>;