mcp-from-openapi 2.2.0 → 2.3.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/README.md +61 -554
- package/esm/index.mjs +132 -10
- package/esm/package.json +4 -3
- package/format-resolver.d.ts +12 -0
- package/generator.d.ts +1 -1
- package/index.d.ts +2 -1
- package/index.js +135 -11
- package/package.json +4 -3
- package/types.d.ts +22 -2
- package/CHANGELOG.md +0 -83
package/README.md
CHANGED
|
@@ -1,34 +1,31 @@
|
|
|
1
1
|
# mcp-from-openapi
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
> definitions
|
|
3
|
+
> Convert OpenAPI specifications into MCP tool definitions with automatic parameter conflict resolution
|
|
5
4
|
|
|
6
5
|
[](https://www.npmjs.com/package/mcp-from-openapi)
|
|
7
6
|
[](https://opensource.org/license/apache-2-0)
|
|
8
|
-
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://nodejs.org/)
|
|
9
9
|
|
|
10
10
|
## What This Solves
|
|
11
11
|
|
|
12
|
-
When converting OpenAPI specs to MCP tools, you
|
|
13
|
-
different locations (path, query, body). This library automatically resolves these conflicts and provides an **explicit
|
|
14
|
-
mapper** that tells you exactly how to construct HTTP requests.
|
|
12
|
+
When converting OpenAPI specs to MCP tools, you hit **parameter conflicts** -- the same name appears in different locations (path, query, body). This library resolves them automatically and gives you an **explicit mapper** for building HTTP requests.
|
|
15
13
|
|
|
16
14
|
**The Problem:**
|
|
17
15
|
|
|
18
16
|
```yaml
|
|
19
|
-
# OpenAPI spec
|
|
20
17
|
paths:
|
|
21
18
|
/users/{id}:
|
|
22
19
|
post:
|
|
23
20
|
parameters:
|
|
24
|
-
- name: id
|
|
21
|
+
- name: id # path
|
|
25
22
|
in: path
|
|
26
23
|
requestBody:
|
|
27
24
|
content:
|
|
28
25
|
application/json:
|
|
29
26
|
schema:
|
|
30
27
|
properties:
|
|
31
|
-
id:
|
|
28
|
+
id: # body -- CONFLICT!
|
|
32
29
|
type: string
|
|
33
30
|
```
|
|
34
31
|
|
|
@@ -38,8 +35,8 @@ paths:
|
|
|
38
35
|
{
|
|
39
36
|
inputSchema: {
|
|
40
37
|
properties: {
|
|
41
|
-
pathId: { type: "string" }, // Automatically renamed
|
|
42
|
-
bodyId: { type: "string" } // Automatically renamed
|
|
38
|
+
pathId: { type: "string" }, // Automatically renamed
|
|
39
|
+
bodyId: { type: "string" } // Automatically renamed
|
|
43
40
|
}
|
|
44
41
|
},
|
|
45
42
|
mapper: [
|
|
@@ -49,17 +46,18 @@ paths:
|
|
|
49
46
|
}
|
|
50
47
|
```
|
|
51
48
|
|
|
52
|
-
Now you know
|
|
49
|
+
Now you know exactly how to build the HTTP request.
|
|
53
50
|
|
|
54
51
|
## Features
|
|
55
52
|
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
53
|
+
- **Smart Parameter Handling** -- Automatic conflict detection and resolution across path, query, header, cookie, and body
|
|
54
|
+
- **Complete Schemas** -- Input schema combines all parameters; output schema from responses (with oneOf unions)
|
|
55
|
+
- **Security Resolution** -- Framework-agnostic auth for Bearer, Basic, Digest, API Key, OAuth2, OpenID, mTLS, HMAC, AWS Sig V4
|
|
56
|
+
- **SSRF Prevention** -- Blocks internal IPs, localhost, and cloud metadata endpoints by default during `$ref` resolution
|
|
57
|
+
- **Multiple Input Sources** -- Load from URL, file, YAML string, or JSON object
|
|
58
|
+
- **Rich Metadata** -- Authentication, servers, tags, deprecation, external docs, `x-frontmcp` extension
|
|
59
|
+
- **Production Ready** -- Full TypeScript support, validation, structured errors, 80%+ test coverage
|
|
60
|
+
- **MCP Native** -- Designed specifically for Model Context Protocol integration
|
|
63
61
|
|
|
64
62
|
## Installation
|
|
65
63
|
|
|
@@ -73,594 +71,103 @@ pnpm add mcp-from-openapi
|
|
|
73
71
|
|
|
74
72
|
## Quick Start
|
|
75
73
|
|
|
76
|
-
### Basic Usage
|
|
77
|
-
|
|
78
74
|
```typescript
|
|
79
75
|
import { OpenAPIToolGenerator } from 'mcp-from-openapi';
|
|
80
76
|
|
|
81
|
-
//
|
|
77
|
+
// Load an OpenAPI spec
|
|
82
78
|
const generator = await OpenAPIToolGenerator.fromURL('https://api.example.com/openapi.json');
|
|
83
79
|
|
|
84
|
-
//
|
|
80
|
+
// Generate MCP tools
|
|
85
81
|
const tools = await generator.generateTools();
|
|
86
82
|
|
|
87
|
-
//
|
|
83
|
+
// Each tool has everything you need
|
|
88
84
|
tools.forEach((tool) => {
|
|
89
|
-
console.log(tool.name);
|
|
90
|
-
console.log(tool.inputSchema);
|
|
91
|
-
console.log(tool.outputSchema);
|
|
92
|
-
console.log(tool.mapper);
|
|
93
|
-
console.log(tool.metadata);
|
|
85
|
+
console.log(tool.name); // "createUser"
|
|
86
|
+
console.log(tool.inputSchema); // Combined schema for all params
|
|
87
|
+
console.log(tool.outputSchema); // Response schema
|
|
88
|
+
console.log(tool.mapper); // How to build the HTTP request
|
|
89
|
+
console.log(tool.metadata); // Auth, servers, tags, etc.
|
|
94
90
|
});
|
|
95
91
|
```
|
|
96
92
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
// From URL
|
|
101
|
-
const generator = await OpenAPIToolGenerator.fromURL('https://api.example.com/openapi.json');
|
|
102
|
-
|
|
103
|
-
// From file
|
|
104
|
-
const generator = await OpenAPIToolGenerator.fromFile('./openapi.yaml');
|
|
105
|
-
|
|
106
|
-
// From YAML string
|
|
107
|
-
const yamlString = `
|
|
108
|
-
openapi: 3.0.0
|
|
109
|
-
info:
|
|
110
|
-
title: My API
|
|
111
|
-
version: 1.0.0
|
|
112
|
-
paths:
|
|
113
|
-
/users:
|
|
114
|
-
get:
|
|
115
|
-
responses:
|
|
116
|
-
'200':
|
|
117
|
-
description: Success
|
|
118
|
-
`;
|
|
119
|
-
const generator = await OpenAPIToolGenerator.fromYAML(yamlString);
|
|
120
|
-
|
|
121
|
-
// From JSON object
|
|
122
|
-
const openApiSpec = { openapi: '3.0.0' /* ... */ };
|
|
123
|
-
const generator = await OpenAPIToolGenerator.fromJSON(openApiSpec);
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### Understanding the Output
|
|
127
|
-
|
|
128
|
-
Each generated tool includes:
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
interface McpOpenAPITool {
|
|
132
|
-
name: string; // Operation ID or generated name
|
|
133
|
-
description: string; // From operation summary/description
|
|
134
|
-
inputSchema: JSONSchema7; // Combined input schema (all params)
|
|
135
|
-
outputSchema?: JSONSchema7; // Response schema (can be union)
|
|
136
|
-
mapper: ParameterMapper[]; // Input → Request mapping
|
|
137
|
-
metadata: ToolMetadata; // Auth, servers, etc.
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Using the Mapper
|
|
93
|
+
## Using the Mapper
|
|
142
94
|
|
|
143
|
-
The mapper tells you how to convert tool
|
|
95
|
+
The mapper tells you how to convert tool inputs into an HTTP request:
|
|
144
96
|
|
|
145
97
|
```typescript
|
|
146
|
-
function buildRequest(tool: McpOpenAPITool, input: any) {
|
|
98
|
+
function buildRequest(tool: McpOpenAPITool, input: Record<string, any>) {
|
|
147
99
|
let path = tool.metadata.path;
|
|
148
100
|
const query = new URLSearchParams();
|
|
149
101
|
const headers: Record<string, string> = {};
|
|
150
|
-
let body: any;
|
|
102
|
+
let body: Record<string, any> | undefined;
|
|
151
103
|
|
|
152
|
-
tool.mapper
|
|
104
|
+
for (const m of tool.mapper) {
|
|
153
105
|
const value = input[m.inputKey];
|
|
154
|
-
if (
|
|
106
|
+
if (value === undefined) continue;
|
|
155
107
|
|
|
156
108
|
switch (m.type) {
|
|
157
109
|
case 'path':
|
|
158
110
|
path = path.replace(`{${m.key}}`, encodeURIComponent(value));
|
|
159
111
|
break;
|
|
160
112
|
case 'query':
|
|
161
|
-
query.set(m.key, value);
|
|
113
|
+
query.set(m.key, String(value));
|
|
162
114
|
break;
|
|
163
115
|
case 'header':
|
|
164
|
-
headers[m.key] = value;
|
|
116
|
+
headers[m.key] = String(value);
|
|
165
117
|
break;
|
|
166
118
|
case 'body':
|
|
167
119
|
if (!body) body = {};
|
|
168
120
|
body[m.key] = value;
|
|
169
121
|
break;
|
|
170
122
|
}
|
|
171
|
-
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const baseUrl = tool.metadata.servers?.[0]?.url ?? '';
|
|
126
|
+
const qs = query.toString();
|
|
172
127
|
|
|
173
128
|
return {
|
|
174
|
-
url: `${
|
|
175
|
-
method: tool.metadata.method,
|
|
129
|
+
url: `${baseUrl}${path}${qs ? '?' + qs : ''}`,
|
|
130
|
+
method: tool.metadata.method.toUpperCase(),
|
|
176
131
|
headers,
|
|
177
132
|
body: body ? JSON.stringify(body) : undefined,
|
|
178
133
|
};
|
|
179
134
|
}
|
|
180
|
-
|
|
181
|
-
// Example usage
|
|
182
|
-
const request = buildRequest(tool, {
|
|
183
|
-
pathId: 'user-123',
|
|
184
|
-
bodyName: 'John Doe',
|
|
185
|
-
bodyEmail: 'john@example.com',
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
const response = await fetch(request.url, {
|
|
189
|
-
method: request.method,
|
|
190
|
-
headers: request.headers,
|
|
191
|
-
body: request.body,
|
|
192
|
-
});
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Handling Parameter Conflicts
|
|
196
|
-
|
|
197
|
-
When the same parameter name appears in different locations, the library automatically renames them:
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
// OpenAPI with conflicts:
|
|
201
|
-
// - id in path
|
|
202
|
-
// - id in query
|
|
203
|
-
// - id in body
|
|
204
|
-
|
|
205
|
-
const tool = await generator.generateTool('/users/{id}', 'post');
|
|
206
|
-
|
|
207
|
-
// Generated input schema:
|
|
208
|
-
{
|
|
209
|
-
properties: {
|
|
210
|
-
pathId: { type: "string" }, // Renamed!
|
|
211
|
-
queryId: { type: "string" }, // Renamed!
|
|
212
|
-
bodyId: { type: "string" } // Renamed!
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Your input should use the renamed keys:
|
|
217
|
-
const input = {
|
|
218
|
-
pathId: "user-123", // Not "id"
|
|
219
|
-
queryId: "track-456",
|
|
220
|
-
bodyId: "internal-789"
|
|
221
|
-
};
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
## Common Use Cases
|
|
225
|
-
|
|
226
|
-
### 1. Build an MCP Server
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
230
|
-
import { OpenAPIToolGenerator } from 'mcp-from-openapi';
|
|
231
|
-
|
|
232
|
-
const server = new Server(/* ... */);
|
|
233
|
-
|
|
234
|
-
// Load tools from OpenAPI
|
|
235
|
-
const generator = await OpenAPIToolGenerator.fromURL('https://api.example.com/openapi.json');
|
|
236
|
-
const tools = await generator.generateTools();
|
|
237
|
-
|
|
238
|
-
// Register each tool
|
|
239
|
-
tools.forEach((tool) => {
|
|
240
|
-
server.setRequestHandler(tool.name, async (request) => {
|
|
241
|
-
const httpRequest = buildRequest(tool, request.params);
|
|
242
|
-
const response = await fetch(httpRequest.url, httpRequest);
|
|
243
|
-
return response.json();
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### 2. Filter Operations
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
// Only GET operations
|
|
252
|
-
const tools = await generator.generateTools({
|
|
253
|
-
filterFn: (op) => op.method === 'get', // op has path and method properties
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// Specific operations by ID
|
|
257
|
-
const tools = await generator.generateTools({
|
|
258
|
-
includeOperations: ['getUser', 'createUser'],
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
// Exclude deprecated
|
|
262
|
-
const tools = await generator.generateTools({
|
|
263
|
-
includeDeprecated: false,
|
|
264
|
-
});
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
### 3. Handle Multiple Response Codes
|
|
268
|
-
|
|
269
|
-
```typescript
|
|
270
|
-
// Include all response status codes
|
|
271
|
-
const tools = await generator.generateTools({
|
|
272
|
-
includeAllResponses: true, // Creates oneOf union
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
// Or prefer specific codes only
|
|
276
|
-
const tools = await generator.generateTools({
|
|
277
|
-
preferredStatusCodes: [200, 201],
|
|
278
|
-
includeAllResponses: false,
|
|
279
|
-
});
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
### 4. Custom Base URL
|
|
283
|
-
|
|
284
|
-
```typescript
|
|
285
|
-
const generator = await OpenAPIToolGenerator.fromURL(url, {
|
|
286
|
-
baseUrl: 'https://staging.api.example.com',
|
|
287
|
-
});
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### 5. Validate Before Generating
|
|
291
|
-
|
|
292
|
-
```typescript
|
|
293
|
-
const generator = await OpenAPIToolGenerator.fromFile('./openapi.yaml');
|
|
294
|
-
|
|
295
|
-
const validation = await generator.validate();
|
|
296
|
-
if (!validation.valid) {
|
|
297
|
-
console.error('Validation errors:', validation.errors);
|
|
298
|
-
throw new Error('Invalid OpenAPI spec');
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const tools = await generator.generateTools();
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### 6. Custom Naming Strategy
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
const tool = await generator.generateTool('/users/{id}', 'post', {
|
|
308
|
-
namingStrategy: {
|
|
309
|
-
conflictResolver: (paramName, location, index) => {
|
|
310
|
-
// Custom naming logic
|
|
311
|
-
return `${location.toUpperCase()}_${paramName}`;
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
});
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### 7. Integration with Zod
|
|
318
|
-
|
|
319
|
-
```typescript
|
|
320
|
-
import { zodSchema } from 'json-schema-to-zod';
|
|
321
|
-
|
|
322
|
-
const tools = await generator.generateTools();
|
|
323
|
-
|
|
324
|
-
const validatedTools = tools.map((tool) => ({
|
|
325
|
-
...tool,
|
|
326
|
-
validateInput: zodSchema(tool.inputSchema),
|
|
327
|
-
validateOutput: tool.outputSchema ? zodSchema(tool.outputSchema) : null,
|
|
328
|
-
}));
|
|
329
|
-
|
|
330
|
-
// Use validators
|
|
331
|
-
const validatedInput = validatedTools[0].validateInput.parse(userInput);
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
## API Reference
|
|
335
|
-
|
|
336
|
-
### OpenAPIToolGenerator
|
|
337
|
-
|
|
338
|
-
#### Static Factory Methods
|
|
339
|
-
|
|
340
|
-
```typescript
|
|
341
|
-
// Load from URL
|
|
342
|
-
static async fromURL(url: string, options?: LoadOptions): Promise<OpenAPIToolGenerator>
|
|
343
|
-
|
|
344
|
-
// Load from file path
|
|
345
|
-
static async fromFile(filePath: string, options?: LoadOptions): Promise<OpenAPIToolGenerator>
|
|
346
|
-
|
|
347
|
-
// Load from YAML string
|
|
348
|
-
static async fromYAML(yaml: string, options?: LoadOptions): Promise<OpenAPIToolGenerator>
|
|
349
|
-
|
|
350
|
-
// Load from JSON object
|
|
351
|
-
static async fromJSON(json: object, options?: LoadOptions): Promise<OpenAPIToolGenerator>
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
#### Instance Methods
|
|
355
|
-
|
|
356
|
-
```typescript
|
|
357
|
-
// Generate all tools
|
|
358
|
-
async generateTools(options?: GenerateOptions): Promise<McpOpenAPITool[]>
|
|
359
|
-
|
|
360
|
-
// Generate a specific tool
|
|
361
|
-
async generateTool(path: string, method: string, options?: GenerateOptions): Promise<McpOpenAPITool>
|
|
362
|
-
|
|
363
|
-
// Get OpenAPI document
|
|
364
|
-
getDocument(): OpenAPIDocument
|
|
365
|
-
|
|
366
|
-
// Validate OpenAPI document
|
|
367
|
-
async validate(): Promise<ValidationResult>
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
### Configuration Options
|
|
371
|
-
|
|
372
|
-
#### LoadOptions
|
|
373
|
-
|
|
374
|
-
```typescript
|
|
375
|
-
interface LoadOptions {
|
|
376
|
-
dereference?: boolean; // Resolve $refs (default: true)
|
|
377
|
-
baseUrl?: string; // Override base URL
|
|
378
|
-
headers?: Record<string, string>; // Custom headers for URL loading
|
|
379
|
-
timeout?: number; // Request timeout (default: 30000ms)
|
|
380
|
-
validate?: boolean; // Validate document (default: true)
|
|
381
|
-
followRedirects?: boolean; // Follow redirects (default: true)
|
|
382
|
-
}
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
#### GenerateOptions
|
|
386
|
-
|
|
387
|
-
```typescript
|
|
388
|
-
interface GenerateOptions {
|
|
389
|
-
includeOperations?: string[]; // Include only these operation IDs
|
|
390
|
-
excludeOperations?: string[]; // Exclude these operation IDs
|
|
391
|
-
filterFn?: (op: OperationWithContext) => boolean; // Custom filter (op has path and method)
|
|
392
|
-
namingStrategy?: NamingStrategy; // Custom naming for conflicts
|
|
393
|
-
preferredStatusCodes?: number[]; // Preferred response codes
|
|
394
|
-
includeDeprecated?: boolean; // Include deprecated ops (default: false)
|
|
395
|
-
includeAllResponses?: boolean; // Include all status codes (default: true)
|
|
396
|
-
maxSchemaDepth?: number; // Max depth for schemas (default: 10)
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// OperationWithContext extends OperationObject with:
|
|
400
|
-
interface OperationWithContext extends OperationObject {
|
|
401
|
-
path: string; // The API path
|
|
402
|
-
method: string; // The HTTP method (get, post, etc.)
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
#### NamingStrategy
|
|
408
|
-
|
|
409
|
-
```typescript
|
|
410
|
-
interface NamingStrategy {
|
|
411
|
-
conflictResolver: (paramName: string, location: ParameterLocation, index: number) => string;
|
|
412
|
-
|
|
413
|
-
toolNameGenerator?: (path: string, method: HTTPMethod, operationId?: string) => string;
|
|
414
|
-
}
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
### Types
|
|
418
|
-
|
|
419
|
-
#### McpOpenAPITool
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
interface McpOpenAPITool {
|
|
423
|
-
name: string;
|
|
424
|
-
description: string;
|
|
425
|
-
inputSchema: JSONSchema7;
|
|
426
|
-
outputSchema?: JSONSchema7;
|
|
427
|
-
mapper: ParameterMapper[];
|
|
428
|
-
metadata: ToolMetadata;
|
|
429
|
-
}
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
#### ParameterMapper
|
|
433
|
-
|
|
434
|
-
```typescript
|
|
435
|
-
interface ParameterMapper {
|
|
436
|
-
inputKey: string; // Property name in input schema
|
|
437
|
-
type: ParameterLocation; // 'path' | 'query' | 'header' | 'cookie' | 'body'
|
|
438
|
-
key: string; // Original parameter name
|
|
439
|
-
required?: boolean;
|
|
440
|
-
style?: string;
|
|
441
|
-
explode?: boolean;
|
|
442
|
-
serialization?: SerializationInfo;
|
|
443
|
-
}
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
#### ToolMetadata
|
|
447
|
-
|
|
448
|
-
```typescript
|
|
449
|
-
interface ToolMetadata {
|
|
450
|
-
path: string;
|
|
451
|
-
method: HTTPMethod;
|
|
452
|
-
operationId?: string;
|
|
453
|
-
tags?: string[];
|
|
454
|
-
deprecated?: boolean;
|
|
455
|
-
security?: SecurityRequirement[];
|
|
456
|
-
servers?: ServerInfo[];
|
|
457
|
-
responseStatusCodes?: number[];
|
|
458
|
-
externalDocs?: ExternalDocumentation;
|
|
459
|
-
}
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
## Error Handling
|
|
463
|
-
|
|
464
|
-
```typescript
|
|
465
|
-
import { LoadError, ParseError, ValidationError, GenerationError } from 'mcp-from-openapi';
|
|
466
|
-
|
|
467
|
-
try {
|
|
468
|
-
const generator = await OpenAPIToolGenerator.fromURL(url);
|
|
469
|
-
const tools = await generator.generateTools();
|
|
470
|
-
} catch (error) {
|
|
471
|
-
if (error instanceof LoadError) {
|
|
472
|
-
console.error('Failed to load:', error.message);
|
|
473
|
-
} else if (error instanceof ParseError) {
|
|
474
|
-
console.error('Failed to parse:', error.message);
|
|
475
|
-
} else if (error instanceof ValidationError) {
|
|
476
|
-
console.error('Invalid spec:', error.errors);
|
|
477
|
-
} else if (error instanceof GenerationError) {
|
|
478
|
-
console.error('Generation failed:', error.message);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
```
|
|
482
|
-
|
|
483
|
-
## Architecture
|
|
484
|
-
|
|
485
|
-
### System Overview
|
|
486
|
-
|
|
487
|
-
The library follows a modular architecture with clear separation of concerns:
|
|
488
|
-
|
|
489
|
-
```
|
|
490
|
-
OpenAPIToolGenerator (Main Entry Point)
|
|
491
|
-
├── Validator → OpenAPI document validation
|
|
492
|
-
├── ParameterResolver → Parameter conflict resolution & mapping
|
|
493
|
-
├── ResponseBuilder → Output schema generation
|
|
494
|
-
└── SchemaBuilder → Schema manipulation utilities
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
### Data Flow
|
|
498
|
-
|
|
499
|
-
```
|
|
500
|
-
User Input (URL/File/String/Object)
|
|
501
|
-
↓
|
|
502
|
-
Load & Parse
|
|
503
|
-
↓
|
|
504
|
-
OpenAPI Document
|
|
505
|
-
↓
|
|
506
|
-
Validate (optional)
|
|
507
|
-
↓
|
|
508
|
-
Dereference $refs (optional)
|
|
509
|
-
↓
|
|
510
|
-
For Each Operation:
|
|
511
|
-
├── ParameterResolver → inputSchema + mapper
|
|
512
|
-
├── ResponseBuilder → outputSchema
|
|
513
|
-
└── Metadata Extractor → metadata
|
|
514
|
-
↓
|
|
515
|
-
McpOpenAPITool[]
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
### Core Components
|
|
519
|
-
|
|
520
|
-
#### 1. OpenAPIToolGenerator
|
|
521
|
-
|
|
522
|
-
- **Responsibility**: Entry point, orchestration, document management
|
|
523
|
-
- **Key Methods**: Factory methods, generateTools(), validate()
|
|
524
|
-
|
|
525
|
-
#### 2. ParameterResolver
|
|
526
|
-
|
|
527
|
-
- **Responsibility**: Collect parameters, detect conflicts, generate mapper
|
|
528
|
-
- **Algorithm**:
|
|
529
|
-
1. Collect all parameters by name from all sources
|
|
530
|
-
2. Detect naming conflicts
|
|
531
|
-
3. Apply naming strategy to resolve conflicts
|
|
532
|
-
4. Build combined input schema
|
|
533
|
-
5. Create mapper entries
|
|
534
|
-
|
|
535
|
-
#### 3. ResponseBuilder
|
|
536
|
-
|
|
537
|
-
- **Responsibility**: Extract response schemas, handle multiple status codes
|
|
538
|
-
- **Features**:
|
|
539
|
-
- Prefer specific status codes
|
|
540
|
-
- Generate union types (oneOf) for multiple responses
|
|
541
|
-
- Add metadata (status code, content type)
|
|
542
|
-
|
|
543
|
-
#### 4. Validator
|
|
544
|
-
|
|
545
|
-
- **Responsibility**: Validate OpenAPI document structure
|
|
546
|
-
- **Checks**:
|
|
547
|
-
- OpenAPI version (3.0.x or 3.1.x)
|
|
548
|
-
- Required fields (info, paths, etc.)
|
|
549
|
-
- Path parameters defined
|
|
550
|
-
- Operation structure
|
|
551
|
-
|
|
552
|
-
### Design Patterns
|
|
553
|
-
|
|
554
|
-
1. **Factory Pattern** - For creating generator instances
|
|
555
|
-
2. **Strategy Pattern** - For parameter naming customization
|
|
556
|
-
3. **Builder Pattern** - For schema construction
|
|
557
|
-
4. **Template Method** - For tool generation workflow
|
|
558
|
-
|
|
559
|
-
### Extension Points
|
|
560
|
-
|
|
561
|
-
1. **Custom Naming Strategies** - Implement `NamingStrategy` interface
|
|
562
|
-
2. **Custom Filters** - Use `filterFn` in `GenerateOptions`
|
|
563
|
-
3. **Custom Validators** - Extend `Validator` class
|
|
564
|
-
4. **Schema Transformations** - Use `SchemaBuilder` utilities
|
|
565
|
-
|
|
566
|
-
## Best Practices
|
|
567
|
-
|
|
568
|
-
### 1. Always Dereference in Production
|
|
569
|
-
|
|
570
|
-
```typescript
|
|
571
|
-
const generator = await OpenAPIToolGenerator.fromURL(url, {
|
|
572
|
-
dereference: true, // Resolve all $refs for easier consumption
|
|
573
|
-
});
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
### 2. Validate Before Generating
|
|
577
|
-
|
|
578
|
-
```typescript
|
|
579
|
-
const validation = await generator.validate();
|
|
580
|
-
if (!validation.valid) {
|
|
581
|
-
throw new Error('Invalid OpenAPI spec');
|
|
582
|
-
}
|
|
583
135
|
```
|
|
584
136
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
```typescript
|
|
588
|
-
class ToolCache {
|
|
589
|
-
private cache = new Map<string, McpOpenAPITool[]>();
|
|
137
|
+
## Documentation
|
|
590
138
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
```typescript
|
|
608
|
-
import type { McpOpenAPITool, LoadOptions } from 'mcp-from-openapi';
|
|
609
|
-
|
|
610
|
-
const options: LoadOptions = {
|
|
611
|
-
dereference: true,
|
|
612
|
-
validate: true,
|
|
613
|
-
};
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
### 5. Handle Errors Properly
|
|
617
|
-
|
|
618
|
-
```typescript
|
|
619
|
-
try {
|
|
620
|
-
const tools = await generator.generateTools();
|
|
621
|
-
} catch (error) {
|
|
622
|
-
if (error instanceof ValidationError) {
|
|
623
|
-
console.error('Validation errors:', error.errors);
|
|
624
|
-
}
|
|
625
|
-
// Handle appropriately
|
|
626
|
-
}
|
|
627
|
-
```
|
|
628
|
-
|
|
629
|
-
## Examples
|
|
630
|
-
|
|
631
|
-
Check the `examples/` directory for comprehensive examples including:
|
|
632
|
-
|
|
633
|
-
1. Basic usage
|
|
634
|
-
2. Parameter conflict resolution
|
|
635
|
-
3. Custom naming strategies
|
|
636
|
-
4. Multiple response handling
|
|
637
|
-
5. Authentication handling
|
|
638
|
-
6. Operation filtering
|
|
639
|
-
7. Zod integration
|
|
640
|
-
8. Request mapping
|
|
139
|
+
| Document | Description |
|
|
140
|
+
|----------|-------------|
|
|
141
|
+
| [Getting Started](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/getting-started.md) | Loading specs, generating tools, building requests |
|
|
142
|
+
| [Configuration](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/configuration.md) | LoadOptions, GenerateOptions, RefResolutionOptions |
|
|
143
|
+
| [Parameter Conflicts](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/parameter-conflicts.md) | How conflict detection and resolution works |
|
|
144
|
+
| [Response Schemas](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/response-schemas.md) | Output schemas, status codes, oneOf unions |
|
|
145
|
+
| [Security](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/security.md) | SecurityResolver, all auth types, custom resolvers |
|
|
146
|
+
| [SSRF Prevention](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/ssrf-prevention.md) | Ref resolution security, blocked IPs and hosts |
|
|
147
|
+
| [Format Resolution](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/FORMAT_RESOLUTION.md) | Format-to-schema enrichment (uuid, date-time, email, int32, etc.) |
|
|
148
|
+
| [Naming Strategies](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/naming-strategies.md) | Custom tool naming and conflict resolvers |
|
|
149
|
+
| [SchemaBuilder](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/schema-builder.md) | JSON Schema utility methods |
|
|
150
|
+
| [Error Handling](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/error-handling.md) | Error classes, context, and patterns |
|
|
151
|
+
| [x-frontmcp Extension](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/x-frontmcp.md) | Custom OpenAPI extension for MCP annotations |
|
|
152
|
+
| [API Reference](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/api-reference.md) | Complete types, interfaces, and exports |
|
|
153
|
+
| [Examples](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/examples.md) | MCP server, Zod, filtering, security, and more |
|
|
154
|
+
| [Architecture](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/architecture.md) | System overview, data flow, design patterns |
|
|
641
155
|
|
|
642
156
|
## Requirements
|
|
643
157
|
|
|
644
|
-
- Node.js >=
|
|
158
|
+
- Node.js >= 20.0.0
|
|
645
159
|
- TypeScript >= 5.0 (for TypeScript users)
|
|
646
|
-
|
|
647
|
-
## Dependencies
|
|
648
|
-
|
|
649
|
-
- `@apidevtools/json-schema-ref-parser` - $ref dereferencing
|
|
650
|
-
- `yaml` - YAML parsing
|
|
651
|
-
- `undici` - Modern HTTP client
|
|
652
|
-
- `json-schema` - Type definitions
|
|
160
|
+
- Peer dependency: `zod@^4.0.0`
|
|
653
161
|
|
|
654
162
|
## Contributing
|
|
655
163
|
|
|
656
|
-
Contributions are welcome! Please see our
|
|
164
|
+
Contributions are welcome! Please see our [issues page](https://github.com/agentfront/mcp-from-openapi/issues).
|
|
657
165
|
|
|
658
166
|
## Related Projects
|
|
659
167
|
|
|
660
168
|
- [Model Context Protocol](https://modelcontextprotocol.io/)
|
|
661
169
|
- [OpenAPI Specification](https://www.openapis.org/)
|
|
662
|
-
- [JSON Schema](https://json-schema.org/)
|
|
663
170
|
|
|
664
|
-
|
|
171
|
+
## License
|
|
665
172
|
|
|
666
|
-
|
|
173
|
+
[Apache 2.0](https://github.com/agentfront/mcp-from-openapi/blob/main/LICENSE)
|