@frontmcp/adapters 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +4 -1
- package/package.json +7 -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,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test fixtures and mocks for OpenAPI adapter tests
|
|
3
|
+
*/
|
|
4
|
+
import type { OpenAPIV3 } from 'openapi-types';
|
|
5
|
+
/**
|
|
6
|
+
* Basic OpenAPI spec without security
|
|
7
|
+
*/
|
|
8
|
+
export declare const basicOpenApiSpec: OpenAPIV3.Document;
|
|
9
|
+
/**
|
|
10
|
+
* OpenAPI spec with Bearer authentication
|
|
11
|
+
*/
|
|
12
|
+
export declare const bearerAuthSpec: OpenAPIV3.Document;
|
|
13
|
+
/**
|
|
14
|
+
* OpenAPI spec with multiple security schemes
|
|
15
|
+
*/
|
|
16
|
+
export declare const multiAuthSpec: OpenAPIV3.Document;
|
|
17
|
+
/**
|
|
18
|
+
* Mock AuthInfo for testing
|
|
19
|
+
*/
|
|
20
|
+
export declare const mockAuthInfo: {
|
|
21
|
+
token: string;
|
|
22
|
+
user: {
|
|
23
|
+
id: string;
|
|
24
|
+
email: string;
|
|
25
|
+
githubToken: string;
|
|
26
|
+
slackToken: string;
|
|
27
|
+
apiKey: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Mock context for tool execution
|
|
32
|
+
*/
|
|
33
|
+
export declare const mockContext: {
|
|
34
|
+
authInfo: {
|
|
35
|
+
token: string;
|
|
36
|
+
user: {
|
|
37
|
+
id: string;
|
|
38
|
+
email: string;
|
|
39
|
+
githubToken: string;
|
|
40
|
+
slackToken: string;
|
|
41
|
+
apiKey: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Mock fetch responses
|
|
47
|
+
*/
|
|
48
|
+
export declare const mockFetchSuccess: (data: unknown) => Promise<Response>;
|
|
49
|
+
export declare const mockFetchError: (status: number, message: string) => Promise<Response>;
|
|
50
|
+
/**
|
|
51
|
+
* Spy on console methods for testing logs
|
|
52
|
+
*/
|
|
53
|
+
export declare const spyOnConsole: () => {
|
|
54
|
+
restore: () => void;
|
|
55
|
+
log: jest.SpyInstance<void, [message?: any, ...optionalParams: any[]], any>;
|
|
56
|
+
error: jest.SpyInstance<void, [message?: any, ...optionalParams: any[]], any>;
|
|
57
|
+
warn: jest.SpyInstance<void, [message?: any, ...optionalParams: any[]], any>;
|
|
58
|
+
};
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference types="jest" />
|
|
3
|
+
/**
|
|
4
|
+
* Test fixtures and mocks for OpenAPI adapter tests
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.spyOnConsole = exports.mockFetchError = exports.mockFetchSuccess = exports.mockContext = exports.mockAuthInfo = exports.multiAuthSpec = exports.bearerAuthSpec = exports.basicOpenApiSpec = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Basic OpenAPI spec without security
|
|
10
|
+
*/
|
|
11
|
+
exports.basicOpenApiSpec = {
|
|
12
|
+
openapi: '3.0.0',
|
|
13
|
+
info: {
|
|
14
|
+
title: 'Test API',
|
|
15
|
+
version: '1.0.0',
|
|
16
|
+
},
|
|
17
|
+
paths: {
|
|
18
|
+
'/users/{id}': {
|
|
19
|
+
get: {
|
|
20
|
+
operationId: 'getUser',
|
|
21
|
+
summary: 'Get user by ID',
|
|
22
|
+
parameters: [
|
|
23
|
+
{
|
|
24
|
+
name: 'id',
|
|
25
|
+
in: 'path',
|
|
26
|
+
required: true,
|
|
27
|
+
schema: { type: 'string' },
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
responses: {
|
|
31
|
+
'200': {
|
|
32
|
+
description: 'Success',
|
|
33
|
+
content: {
|
|
34
|
+
'application/json': {
|
|
35
|
+
schema: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
id: { type: 'string' },
|
|
39
|
+
name: { type: 'string' },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
post: {
|
|
48
|
+
operationId: 'createUser',
|
|
49
|
+
summary: 'Create a new user',
|
|
50
|
+
requestBody: {
|
|
51
|
+
required: true,
|
|
52
|
+
content: {
|
|
53
|
+
'application/json': {
|
|
54
|
+
schema: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
name: { type: 'string' },
|
|
58
|
+
email: { type: 'string' },
|
|
59
|
+
},
|
|
60
|
+
required: ['name', 'email'],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
responses: {
|
|
66
|
+
'201': {
|
|
67
|
+
description: 'Created',
|
|
68
|
+
content: {
|
|
69
|
+
'application/json': {
|
|
70
|
+
schema: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
id: { type: 'string' },
|
|
74
|
+
name: { type: 'string' },
|
|
75
|
+
email: { type: 'string' },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* OpenAPI spec with Bearer authentication
|
|
88
|
+
*/
|
|
89
|
+
exports.bearerAuthSpec = {
|
|
90
|
+
openapi: '3.0.0',
|
|
91
|
+
info: {
|
|
92
|
+
title: 'Authenticated API',
|
|
93
|
+
version: '1.0.0',
|
|
94
|
+
},
|
|
95
|
+
components: {
|
|
96
|
+
securitySchemes: {
|
|
97
|
+
BearerAuth: {
|
|
98
|
+
type: 'http',
|
|
99
|
+
scheme: 'bearer',
|
|
100
|
+
bearerFormat: 'JWT',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
security: [{ BearerAuth: [] }],
|
|
105
|
+
paths: {
|
|
106
|
+
'/protected': {
|
|
107
|
+
get: {
|
|
108
|
+
operationId: 'getProtected',
|
|
109
|
+
summary: 'Get protected resource',
|
|
110
|
+
responses: {
|
|
111
|
+
'200': {
|
|
112
|
+
description: 'Success',
|
|
113
|
+
content: {
|
|
114
|
+
'application/json': {
|
|
115
|
+
schema: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
message: { type: 'string' },
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* OpenAPI spec with multiple security schemes
|
|
131
|
+
*/
|
|
132
|
+
exports.multiAuthSpec = {
|
|
133
|
+
openapi: '3.0.0',
|
|
134
|
+
info: {
|
|
135
|
+
title: 'Multi-Auth API',
|
|
136
|
+
version: '1.0.0',
|
|
137
|
+
},
|
|
138
|
+
components: {
|
|
139
|
+
securitySchemes: {
|
|
140
|
+
GitHubAuth: {
|
|
141
|
+
type: 'http',
|
|
142
|
+
scheme: 'bearer',
|
|
143
|
+
description: 'GitHub OAuth token',
|
|
144
|
+
},
|
|
145
|
+
SlackAuth: {
|
|
146
|
+
type: 'http',
|
|
147
|
+
scheme: 'bearer',
|
|
148
|
+
description: 'Slack OAuth token',
|
|
149
|
+
},
|
|
150
|
+
ApiKeyAuth: {
|
|
151
|
+
type: 'apiKey',
|
|
152
|
+
in: 'header',
|
|
153
|
+
name: 'X-API-Key',
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
paths: {
|
|
158
|
+
'/github/repos': {
|
|
159
|
+
get: {
|
|
160
|
+
operationId: 'github_getRepos',
|
|
161
|
+
summary: 'Get GitHub repos',
|
|
162
|
+
security: [{ GitHubAuth: [] }],
|
|
163
|
+
responses: {
|
|
164
|
+
'200': {
|
|
165
|
+
description: 'Success',
|
|
166
|
+
content: {
|
|
167
|
+
'application/json': {
|
|
168
|
+
schema: {
|
|
169
|
+
type: 'array',
|
|
170
|
+
items: {
|
|
171
|
+
type: 'object',
|
|
172
|
+
properties: {
|
|
173
|
+
name: { type: 'string' },
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
'/slack/messages': {
|
|
184
|
+
post: {
|
|
185
|
+
operationId: 'slack_postMessage',
|
|
186
|
+
summary: 'Post Slack message',
|
|
187
|
+
security: [{ SlackAuth: [] }],
|
|
188
|
+
requestBody: {
|
|
189
|
+
required: true,
|
|
190
|
+
content: {
|
|
191
|
+
'application/json': {
|
|
192
|
+
schema: {
|
|
193
|
+
type: 'object',
|
|
194
|
+
properties: {
|
|
195
|
+
channel: { type: 'string' },
|
|
196
|
+
text: { type: 'string' },
|
|
197
|
+
},
|
|
198
|
+
required: ['channel', 'text'],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
responses: {
|
|
204
|
+
'200': {
|
|
205
|
+
description: 'Success',
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
'/admin/settings': {
|
|
211
|
+
get: {
|
|
212
|
+
operationId: 'admin_getSettings',
|
|
213
|
+
summary: 'Get admin settings',
|
|
214
|
+
security: [{ ApiKeyAuth: [] }],
|
|
215
|
+
responses: {
|
|
216
|
+
'200': {
|
|
217
|
+
description: 'Success',
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* Mock AuthInfo for testing
|
|
226
|
+
*/
|
|
227
|
+
exports.mockAuthInfo = {
|
|
228
|
+
token: 'mock-jwt-token',
|
|
229
|
+
user: {
|
|
230
|
+
id: 'user-123',
|
|
231
|
+
email: 'test@example.com',
|
|
232
|
+
githubToken: 'github-token-123',
|
|
233
|
+
slackToken: 'slack-token-456',
|
|
234
|
+
apiKey: 'api-key-789',
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Mock context for tool execution
|
|
239
|
+
*/
|
|
240
|
+
exports.mockContext = {
|
|
241
|
+
authInfo: exports.mockAuthInfo,
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* Mock fetch responses
|
|
245
|
+
*/
|
|
246
|
+
const mockFetchSuccess = (data) => {
|
|
247
|
+
return Promise.resolve({
|
|
248
|
+
ok: true,
|
|
249
|
+
status: 200,
|
|
250
|
+
statusText: 'OK',
|
|
251
|
+
headers: new Headers({ 'content-type': 'application/json' }),
|
|
252
|
+
text: () => Promise.resolve(JSON.stringify(data)),
|
|
253
|
+
json: () => Promise.resolve(data),
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
exports.mockFetchSuccess = mockFetchSuccess;
|
|
257
|
+
const mockFetchError = (status, message) => {
|
|
258
|
+
return Promise.resolve({
|
|
259
|
+
ok: false,
|
|
260
|
+
status,
|
|
261
|
+
statusText: message,
|
|
262
|
+
headers: new Headers({ 'content-type': 'text/plain' }),
|
|
263
|
+
text: () => Promise.resolve(message),
|
|
264
|
+
});
|
|
265
|
+
};
|
|
266
|
+
exports.mockFetchError = mockFetchError;
|
|
267
|
+
/**
|
|
268
|
+
* Spy on console methods for testing logs
|
|
269
|
+
*/
|
|
270
|
+
const spyOnConsole = () => {
|
|
271
|
+
const consoleSpy = {
|
|
272
|
+
log: jest.spyOn(console, 'log').mockImplementation(),
|
|
273
|
+
error: jest.spyOn(console, 'error').mockImplementation(),
|
|
274
|
+
warn: jest.spyOn(console, 'warn').mockImplementation(),
|
|
275
|
+
};
|
|
276
|
+
return {
|
|
277
|
+
...consoleSpy,
|
|
278
|
+
restore: () => {
|
|
279
|
+
consoleSpy.log.mockRestore();
|
|
280
|
+
consoleSpy.error.mockRestore();
|
|
281
|
+
consoleSpy.warn.mockRestore();
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
};
|
|
285
|
+
exports.spyOnConsole = spyOnConsole;
|
|
286
|
+
//# sourceMappingURL=fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../../../../src/openapi/__tests__/fixtures.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"jest\" />\n/**\n * Test fixtures and mocks for OpenAPI adapter tests\n */\n\nimport type { OpenAPIV3 } from 'openapi-types';\n/**\n * Basic OpenAPI spec without security\n */\nexport const basicOpenApiSpec: OpenAPIV3.Document = {\n openapi: '3.0.0',\n info: {\n title: 'Test API',\n version: '1.0.0',\n },\n paths: {\n '/users/{id}': {\n get: {\n operationId: 'getUser',\n summary: 'Get user by ID',\n parameters: [\n {\n name: 'id',\n in: 'path',\n required: true,\n schema: { type: 'string' },\n },\n ],\n responses: {\n '200': {\n description: 'Success',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n name: { type: 'string' },\n },\n },\n },\n },\n },\n },\n },\n post: {\n operationId: 'createUser',\n summary: 'Create a new user',\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n email: { type: 'string' },\n },\n required: ['name', 'email'],\n },\n },\n },\n },\n responses: {\n '201': {\n description: 'Created',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n name: { type: 'string' },\n email: { type: 'string' },\n },\n },\n },\n },\n },\n },\n },\n },\n },\n};\n\n/**\n * OpenAPI spec with Bearer authentication\n */\nexport const bearerAuthSpec: OpenAPIV3.Document = {\n openapi: '3.0.0',\n info: {\n title: 'Authenticated API',\n version: '1.0.0',\n },\n components: {\n securitySchemes: {\n BearerAuth: {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n },\n },\n },\n security: [{ BearerAuth: [] }],\n paths: {\n '/protected': {\n get: {\n operationId: 'getProtected',\n summary: 'Get protected resource',\n responses: {\n '200': {\n description: 'Success',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n message: { type: 'string' },\n },\n },\n },\n },\n },\n },\n },\n },\n },\n};\n\n/**\n * OpenAPI spec with multiple security schemes\n */\nexport const multiAuthSpec: OpenAPIV3.Document = {\n openapi: '3.0.0',\n info: {\n title: 'Multi-Auth API',\n version: '1.0.0',\n },\n components: {\n securitySchemes: {\n GitHubAuth: {\n type: 'http',\n scheme: 'bearer',\n description: 'GitHub OAuth token',\n },\n SlackAuth: {\n type: 'http',\n scheme: 'bearer',\n description: 'Slack OAuth token',\n },\n ApiKeyAuth: {\n type: 'apiKey',\n in: 'header',\n name: 'X-API-Key',\n },\n },\n },\n paths: {\n '/github/repos': {\n get: {\n operationId: 'github_getRepos',\n summary: 'Get GitHub repos',\n security: [{ GitHubAuth: [] }],\n responses: {\n '200': {\n description: 'Success',\n content: {\n 'application/json': {\n schema: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n },\n },\n },\n },\n },\n },\n },\n },\n },\n '/slack/messages': {\n post: {\n operationId: 'slack_postMessage',\n summary: 'Post Slack message',\n security: [{ SlackAuth: [] }],\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n channel: { type: 'string' },\n text: { type: 'string' },\n },\n required: ['channel', 'text'],\n },\n },\n },\n },\n responses: {\n '200': {\n description: 'Success',\n },\n },\n },\n },\n '/admin/settings': {\n get: {\n operationId: 'admin_getSettings',\n summary: 'Get admin settings',\n security: [{ ApiKeyAuth: [] }],\n responses: {\n '200': {\n description: 'Success',\n },\n },\n },\n },\n },\n};\n\n/**\n * Mock AuthInfo for testing\n */\nexport const mockAuthInfo = {\n token: 'mock-jwt-token',\n user: {\n id: 'user-123',\n email: 'test@example.com',\n githubToken: 'github-token-123',\n slackToken: 'slack-token-456',\n apiKey: 'api-key-789',\n },\n};\n\n/**\n * Mock context for tool execution\n */\nexport const mockContext = {\n authInfo: mockAuthInfo,\n};\n\n/**\n * Mock fetch responses\n */\nexport const mockFetchSuccess = (data: unknown) => {\n return Promise.resolve({\n ok: true,\n status: 200,\n statusText: 'OK',\n headers: new Headers({ 'content-type': 'application/json' }),\n text: () => Promise.resolve(JSON.stringify(data)),\n json: () => Promise.resolve(data),\n } as Response);\n};\n\nexport const mockFetchError = (status: number, message: string) => {\n return Promise.resolve({\n ok: false,\n status,\n statusText: message,\n headers: new Headers({ 'content-type': 'text/plain' }),\n text: () => Promise.resolve(message),\n } as Response);\n};\n\n/**\n * Spy on console methods for testing logs\n */\nexport const spyOnConsole = () => {\n const consoleSpy = {\n log: jest.spyOn(console, 'log').mockImplementation(),\n error: jest.spyOn(console, 'error').mockImplementation(),\n warn: jest.spyOn(console, 'warn').mockImplementation(),\n };\n\n return {\n ...consoleSpy,\n restore: () => {\n consoleSpy.log.mockRestore();\n consoleSpy.error.mockRestore();\n consoleSpy.warn.mockRestore();\n },\n };\n};\n"]}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { DynamicAdapter, FrontMcpAdapterResponse } from '@frontmcp/sdk';
|
|
2
2
|
import { OpenApiAdapterOptions } from './openapi.types';
|
|
3
3
|
export default class OpenapiAdapter extends DynamicAdapter<OpenApiAdapterOptions> {
|
|
4
|
+
private generator?;
|
|
4
5
|
options: OpenApiAdapterOptions;
|
|
5
6
|
constructor(options: OpenApiAdapterOptions);
|
|
6
7
|
fetch(): Promise<FrontMcpAdapterResponse>;
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Initialize the OpenAPI tool generator from URL or spec
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
private initializeGenerator;
|
|
8
13
|
}
|
|
@@ -2,46 +2,97 @@
|
|
|
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
|
|
5
|
+
const mcp_from_openapi_1 = require("mcp-from-openapi");
|
|
6
6
|
const openapi_tool_1 = require("./openapi.tool");
|
|
7
|
+
const openapi_security_1 = require("./openapi.security");
|
|
7
8
|
let OpenapiAdapter = class OpenapiAdapter extends sdk_1.DynamicAdapter {
|
|
8
9
|
constructor(options) {
|
|
9
10
|
super();
|
|
10
11
|
this.options = options;
|
|
11
12
|
}
|
|
12
13
|
async fetch() {
|
|
13
|
-
|
|
14
|
+
// Lazy load: Initialize generator on first fetch if not already initialized
|
|
15
|
+
if (!this.generator) {
|
|
16
|
+
this.generator = await this.initializeGenerator();
|
|
17
|
+
}
|
|
18
|
+
// Generate tools from OpenAPI spec
|
|
19
|
+
const openapiTools = await this.generator.generateTools({
|
|
20
|
+
includeOperations: this.options.generateOptions?.includeOperations,
|
|
21
|
+
excludeOperations: this.options.generateOptions?.excludeOperations,
|
|
22
|
+
filterFn: this.options.generateOptions?.filterFn,
|
|
23
|
+
namingStrategy: this.options.generateOptions?.namingStrategy,
|
|
24
|
+
preferredStatusCodes: this.options.generateOptions?.preferredStatusCodes ?? [200, 201, 202, 204],
|
|
25
|
+
includeDeprecated: this.options.generateOptions?.includeDeprecated ?? false,
|
|
26
|
+
includeAllResponses: this.options.generateOptions?.includeAllResponses ?? true,
|
|
27
|
+
includeSecurityInInput: this.options.generateOptions?.includeSecurityInInput ?? false,
|
|
28
|
+
maxSchemaDepth: this.options.generateOptions?.maxSchemaDepth,
|
|
29
|
+
includeExamples: this.options.generateOptions?.includeExamples,
|
|
30
|
+
});
|
|
31
|
+
// Validate security configuration
|
|
32
|
+
const validation = (0, openapi_security_1.validateSecurityConfiguration)(openapiTools, this.options);
|
|
33
|
+
// Log security information
|
|
34
|
+
console.log(`\n[OpenAPI Adapter: ${this.options.name}] Security Analysis:`);
|
|
35
|
+
console.log(` Security Risk Score: ${validation.securityRiskScore.toUpperCase()}`);
|
|
36
|
+
console.log(` Valid Configuration: ${validation.valid ? 'YES' : 'NO'}`);
|
|
37
|
+
if (validation.warnings.length > 0) {
|
|
38
|
+
console.log('\n Messages:');
|
|
39
|
+
validation.warnings.forEach((warning) => {
|
|
40
|
+
console.log(` - ${warning}`);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// Fail if configuration is invalid and security is required
|
|
44
|
+
if (!validation.valid) {
|
|
45
|
+
throw new Error(`[OpenAPI Adapter: ${this.options.name}] Invalid security configuration.\n` +
|
|
46
|
+
`Missing auth provider mappings for security schemes: ${validation.missingMappings.join(', ')}\n\n` +
|
|
47
|
+
`Your OpenAPI spec requires these security schemes, but no auth configuration was provided.\n\n` +
|
|
48
|
+
`Add one of the following to your adapter configuration:\n\n` +
|
|
49
|
+
`1. authProviderMapper (recommended):\n` +
|
|
50
|
+
` authProviderMapper: {\n` +
|
|
51
|
+
validation.missingMappings.map((s) => ` '${s}': (authInfo) => authInfo.user?.${s.toLowerCase()}Token,`).join('\n') +
|
|
52
|
+
`\n }\n\n` +
|
|
53
|
+
`2. securityResolver:\n` +
|
|
54
|
+
` securityResolver: (tool, authInfo) => ({ jwt: authInfo.token })\n\n` +
|
|
55
|
+
`3. staticAuth:\n` +
|
|
56
|
+
` staticAuth: { jwt: process.env.API_TOKEN }\n\n` +
|
|
57
|
+
`4. Include security in input (NOT recommended for production):\n` +
|
|
58
|
+
` generateOptions: { includeSecurityInInput: true }`);
|
|
59
|
+
}
|
|
60
|
+
console.log(''); // Empty line for readability
|
|
61
|
+
// Convert OpenAPI tools to FrontMCP tools
|
|
62
|
+
const tools = openapiTools.map((openapiTool) => (0, openapi_tool_1.createOpenApiTool)(openapiTool, this.options));
|
|
63
|
+
return { tools };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Initialize the OpenAPI tool generator from URL or spec
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
69
|
+
async initializeGenerator() {
|
|
14
70
|
if ('url' in this.options) {
|
|
15
|
-
|
|
71
|
+
return await mcp_from_openapi_1.OpenAPIToolGenerator.fromURL(this.options.url, {
|
|
72
|
+
baseUrl: this.options.baseUrl,
|
|
73
|
+
validate: this.options.loadOptions?.validate ?? true,
|
|
74
|
+
dereference: this.options.loadOptions?.dereference ?? true,
|
|
75
|
+
headers: this.options.loadOptions?.headers,
|
|
76
|
+
timeout: this.options.loadOptions?.timeout,
|
|
77
|
+
followRedirects: this.options.loadOptions?.followRedirects,
|
|
78
|
+
});
|
|
16
79
|
}
|
|
17
80
|
else if ('spec' in this.options) {
|
|
18
|
-
|
|
81
|
+
return await mcp_from_openapi_1.OpenAPIToolGenerator.fromJSON(this.options.spec, {
|
|
82
|
+
baseUrl: this.options.baseUrl,
|
|
83
|
+
validate: this.options.loadOptions?.validate ?? true,
|
|
84
|
+
dereference: this.options.loadOptions?.dereference ?? true,
|
|
85
|
+
});
|
|
19
86
|
}
|
|
20
87
|
else {
|
|
21
|
-
throw new Error('Either url or spec must be provided');
|
|
88
|
+
throw new Error('Either url or spec must be provided in OpenApiAdapterOptions');
|
|
22
89
|
}
|
|
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,
|
|
29
|
-
dereference: false,
|
|
30
|
-
});
|
|
31
|
-
return {
|
|
32
|
-
tools: this.parseTools(openApiTools),
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
parseTools(openApiTools) {
|
|
36
|
-
return openApiTools.map(tool => {
|
|
37
|
-
return (0, openapi_tool_1.createOpenApiTool)(tool, this.options);
|
|
38
|
-
});
|
|
39
90
|
}
|
|
40
91
|
};
|
|
41
92
|
OpenapiAdapter = tslib_1.__decorate([
|
|
42
93
|
(0, sdk_1.Adapter)({
|
|
43
94
|
name: 'openapi',
|
|
44
|
-
description: 'OpenAPI adapter
|
|
95
|
+
description: 'OpenAPI adapter for FrontMCP - Automatically generates MCP tools from OpenAPI specifications',
|
|
45
96
|
}),
|
|
46
97
|
tslib_1.__metadata("design:paramtypes", [Object])
|
|
47
98
|
], OpenapiAdapter);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.adapter.js","sourceRoot":"","sources":["../../../src/openapi/openapi.adapter.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"openapi.adapter.js","sourceRoot":"","sources":["../../../src/openapi/openapi.adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import { Adapter, DynamicAdapter, FrontMcpAdapterResponse } from '@frontmcp/sdk';\nimport { OpenApiAdapterOptions } from './openapi.types';\nimport { OpenAPIToolGenerator } from 'mcp-from-openapi';\nimport { createOpenApiTool } from './openapi.tool';\nimport { validateSecurityConfiguration } from './openapi.security';\n\n@Adapter({\n name: 'openapi',\n description: 'OpenAPI adapter for FrontMCP - Automatically generates MCP tools from OpenAPI specifications',\n})\nexport default class OpenapiAdapter extends DynamicAdapter<OpenApiAdapterOptions> {\n private generator?: OpenAPIToolGenerator;\n public options: OpenApiAdapterOptions;\n\n constructor(options: OpenApiAdapterOptions) {\n super();\n this.options = options;\n }\n\n async fetch(): Promise<FrontMcpAdapterResponse> {\n // Lazy load: Initialize generator on first fetch if not already initialized\n if (!this.generator) {\n this.generator = await this.initializeGenerator();\n }\n\n // Generate tools from OpenAPI spec\n const openapiTools = await this.generator.generateTools({\n includeOperations: this.options.generateOptions?.includeOperations,\n excludeOperations: this.options.generateOptions?.excludeOperations,\n filterFn: this.options.generateOptions?.filterFn,\n namingStrategy: this.options.generateOptions?.namingStrategy,\n preferredStatusCodes: this.options.generateOptions?.preferredStatusCodes ?? [200, 201, 202, 204],\n includeDeprecated: this.options.generateOptions?.includeDeprecated ?? false,\n includeAllResponses: this.options.generateOptions?.includeAllResponses ?? true,\n includeSecurityInInput: this.options.generateOptions?.includeSecurityInInput ?? false,\n maxSchemaDepth: this.options.generateOptions?.maxSchemaDepth,\n includeExamples: this.options.generateOptions?.includeExamples,\n });\n\n // Validate security configuration\n const validation = validateSecurityConfiguration(openapiTools, this.options);\n\n // Log security information\n console.log(`\\n[OpenAPI Adapter: ${this.options.name}] Security Analysis:`);\n console.log(` Security Risk Score: ${validation.securityRiskScore.toUpperCase()}`);\n console.log(` Valid Configuration: ${validation.valid ? 'YES' : 'NO'}`);\n\n if (validation.warnings.length > 0) {\n console.log('\\n Messages:');\n validation.warnings.forEach((warning) => {\n console.log(` - ${warning}`);\n });\n }\n\n // Fail if configuration is invalid and security is required\n if (!validation.valid) {\n throw new Error(\n `[OpenAPI Adapter: ${this.options.name}] Invalid security configuration.\\n` +\n `Missing auth provider mappings for security schemes: ${validation.missingMappings.join(', ')}\\n\\n` +\n `Your OpenAPI spec requires these security schemes, but no auth configuration was provided.\\n\\n` +\n `Add one of the following to your adapter configuration:\\n\\n` +\n `1. authProviderMapper (recommended):\\n` +\n ` authProviderMapper: {\\n` +\n validation.missingMappings.map((s) => ` '${s}': (authInfo) => authInfo.user?.${s.toLowerCase()}Token,`).join('\\n') +\n `\\n }\\n\\n` +\n `2. securityResolver:\\n` +\n ` securityResolver: (tool, authInfo) => ({ jwt: authInfo.token })\\n\\n` +\n `3. staticAuth:\\n` +\n ` staticAuth: { jwt: process.env.API_TOKEN }\\n\\n` +\n `4. Include security in input (NOT recommended for production):\\n` +\n ` generateOptions: { includeSecurityInInput: true }`\n );\n }\n\n console.log(''); // Empty line for readability\n\n // Convert OpenAPI tools to FrontMCP tools\n const tools = openapiTools.map((openapiTool) =>\n createOpenApiTool(openapiTool, this.options)\n );\n\n return { tools };\n }\n\n /**\n * Initialize the OpenAPI tool generator from URL or spec\n * @private\n */\n private async initializeGenerator(): Promise<OpenAPIToolGenerator> {\n if ('url' in this.options) {\n return await OpenAPIToolGenerator.fromURL(this.options.url, {\n baseUrl: this.options.baseUrl,\n validate: this.options.loadOptions?.validate ?? true,\n dereference: this.options.loadOptions?.dereference ?? true,\n headers: this.options.loadOptions?.headers,\n timeout: this.options.loadOptions?.timeout,\n followRedirects: this.options.loadOptions?.followRedirects,\n });\n } else if ('spec' in this.options) {\n return await OpenAPIToolGenerator.fromJSON(this.options.spec, {\n baseUrl: this.options.baseUrl,\n validate: this.options.loadOptions?.validate ?? true,\n dereference: this.options.loadOptions?.dereference ?? true,\n });\n } else {\n throw new Error('Either url or spec must be provided in OpenApiAdapterOptions');\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type McpOpenAPITool, type SecurityContext } from 'mcp-from-openapi';
|
|
2
|
+
import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
|
|
3
|
+
import type { OpenApiAdapterOptions } from './openapi.types';
|
|
4
|
+
/**
|
|
5
|
+
* Security scheme information extracted from OpenAPI spec
|
|
6
|
+
*/
|
|
7
|
+
export interface SecuritySchemeInfo {
|
|
8
|
+
name: string;
|
|
9
|
+
type: string;
|
|
10
|
+
scheme?: string;
|
|
11
|
+
in?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Security validation result
|
|
16
|
+
*/
|
|
17
|
+
export interface SecurityValidationResult {
|
|
18
|
+
valid: boolean;
|
|
19
|
+
missingMappings: string[];
|
|
20
|
+
warnings: string[];
|
|
21
|
+
securityRiskScore: 'low' | 'medium' | 'high';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve security context from FrontMCP auth info with support for multiple auth providers
|
|
25
|
+
*
|
|
26
|
+
* @param tool - OpenAPI tool to resolve security for
|
|
27
|
+
* @param authInfo - FrontMCP authentication info
|
|
28
|
+
* @param options - Adapter options with auth configuration
|
|
29
|
+
* @returns Security context for resolver
|
|
30
|
+
*/
|
|
31
|
+
export declare function createSecurityContextFromAuth(tool: McpOpenAPITool, authInfo: AuthInfo, options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth'>): Promise<SecurityContext>;
|
|
32
|
+
/**
|
|
33
|
+
* Extract all security schemes used by a set of tools
|
|
34
|
+
*
|
|
35
|
+
* @param tools - OpenAPI tools
|
|
36
|
+
* @returns Set of security scheme names
|
|
37
|
+
*/
|
|
38
|
+
export declare function extractSecuritySchemes(tools: McpOpenAPITool[]): Set<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Validate security configuration against OpenAPI security requirements
|
|
41
|
+
*
|
|
42
|
+
* @param tools - OpenAPI tools
|
|
43
|
+
* @param options - Adapter options
|
|
44
|
+
* @returns Validation result with errors and warnings
|
|
45
|
+
*/
|
|
46
|
+
export declare function validateSecurityConfiguration(tools: McpOpenAPITool[], options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth' | 'generateOptions'>): SecurityValidationResult;
|
|
47
|
+
/**
|
|
48
|
+
* Resolve security for an OpenAPI tool with validation
|
|
49
|
+
*
|
|
50
|
+
* @param tool - OpenAPI tool with mapper
|
|
51
|
+
* @param authInfo - FrontMCP authentication info
|
|
52
|
+
* @param options - Adapter options with auth configuration
|
|
53
|
+
* @returns Resolved security (headers, query params, etc.)
|
|
54
|
+
* @throws Error if security cannot be resolved
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveToolSecurity(tool: McpOpenAPITool, authInfo: AuthInfo, options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth'>): Promise<import("mcp-from-openapi").ResolvedSecurity>;
|