@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.
- package/LICENSE +201 -0
- package/README.md +4 -1
- package/package.json +8 -5
- package/src/openapi/README.md +753 -0
- package/src/openapi/__tests__/fixtures.d.ts +58 -0
- package/src/openapi/__tests__/fixtures.js +286 -0
- package/src/openapi/__tests__/fixtures.js.map +1 -0
- package/src/openapi/openapi.adapter.d.ts +6 -1
- package/src/openapi/openapi.adapter.js +73 -22
- package/src/openapi/openapi.adapter.js.map +1 -1
- package/src/openapi/openapi.security.d.ts +56 -0
- package/src/openapi/openapi.security.js +174 -0
- package/src/openapi/openapi.security.js.map +1 -0
- package/src/openapi/openapi.tool.d.ts +10 -3
- package/src/openapi/openapi.tool.js +50 -78
- package/src/openapi/openapi.tool.js.map +1 -1
- package/src/openapi/openapi.types.d.ts +75 -5
- package/src/openapi/openapi.types.js.map +1 -1
- package/src/openapi/openapi.utils.d.ts +35 -0
- package/src/openapi/openapi.utils.js +126 -0
- package/src/openapi/openapi.utils.js.map +1 -0
|
@@ -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 {
|
|
2
|
-
import { OpenApiAdapterOptions } from
|
|
3
|
-
|
|
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 =
|
|
3
|
+
exports.createOpenApiTool = createOpenApiTool;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const sdk_1 = require("@frontmcp/sdk");
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
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:
|
|
12
|
-
name:
|
|
13
|
-
description:
|
|
14
|
-
inputSchema: inputSchema,
|
|
15
|
-
rawInputSchema:
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
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:
|
|
52
|
+
body: finalBody ? JSON.stringify(finalBody) : undefined,
|
|
44
53
|
});
|
|
45
|
-
|
|
46
|
-
|
|
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,
|
|
70
|
+
const zodSchema = (0, json_schema_to_zod_v3_1.convertJsonSchemaToZod)(jsonSchema);
|
|
73
71
|
if (typeof zodSchema?.parse !== 'function') {
|
|
74
|
-
throw new Error('
|
|
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
|
|
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":"
|
|
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 { 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
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
interface BaseOptions
|
|
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:
|
|
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
|
|
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
|
+
}>;
|