mcp-from-openapi 0.0.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 +666 -0
- package/package.json +51 -0
- package/src/errors.d.ts +38 -0
- package/src/errors.js +62 -0
- package/src/errors.js.map +1 -0
- package/src/generator.d.ts +65 -0
- package/src/generator.js +348 -0
- package/src/generator.js.map +1 -0
- package/src/index.d.ts +8 -0
- package/src/index.js +27 -0
- package/src/index.js.map +1 -0
- package/src/parameter-resolver.d.ts +32 -0
- package/src/parameter-resolver.js +211 -0
- package/src/parameter-resolver.js.map +1 -0
- package/src/response-builder.d.ts +30 -0
- package/src/response-builder.js +147 -0
- package/src/response-builder.js.map +1 -0
- package/src/schema-builder.d.ts +123 -0
- package/src/schema-builder.js +296 -0
- package/src/schema-builder.js.map +1 -0
- package/src/types.d.ts +371 -0
- package/src/types.js +38 -0
- package/src/types.js.map +1 -0
- package/src/validator.d.ts +26 -0
- package/src/validator.js +211 -0
- package/src/validator.js.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
# mcp-from-openapi
|
|
2
|
+
|
|
3
|
+
> Production-ready TypeScript library for converting OpenAPI specifications into MCP (Model Context Protocol) tool
|
|
4
|
+
> definitions
|
|
5
|
+
|
|
6
|
+
[](https://www.npmjs.com/package/mcp-from-openapi)
|
|
7
|
+
[](https://opensource.org/license/apache-2-0)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
|
|
10
|
+
## What This Solves
|
|
11
|
+
|
|
12
|
+
When converting OpenAPI specs to MCP tools, you encounter **parameter conflicts** - the same parameter name appears in
|
|
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.
|
|
15
|
+
|
|
16
|
+
**The Problem:**
|
|
17
|
+
|
|
18
|
+
```yaml
|
|
19
|
+
# OpenAPI spec
|
|
20
|
+
paths:
|
|
21
|
+
/users/{id}:
|
|
22
|
+
post:
|
|
23
|
+
parameters:
|
|
24
|
+
- name: id # id in PATH
|
|
25
|
+
in: path
|
|
26
|
+
requestBody:
|
|
27
|
+
content:
|
|
28
|
+
application/json:
|
|
29
|
+
schema:
|
|
30
|
+
properties:
|
|
31
|
+
id: # id in BODY - CONFLICT!
|
|
32
|
+
type: string
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**The Solution:**
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
{
|
|
39
|
+
inputSchema: {
|
|
40
|
+
properties: {
|
|
41
|
+
pathId: { type: "string" }, // Automatically renamed!
|
|
42
|
+
bodyId: { type: "string" } // Automatically renamed!
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
mapper: [
|
|
46
|
+
{ inputKey: "pathId", type: "path", key: "id" },
|
|
47
|
+
{ inputKey: "bodyId", type: "body", key: "id" }
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Now you know **exactly** how to build the HTTP request!
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
- 🎯 **Smart Parameter Handling** - Automatic conflict detection and resolution
|
|
57
|
+
- 📦 **Complete Schemas** - Input schema combines all parameters, output schema from responses
|
|
58
|
+
- 🔐 **Rich Metadata** - Authentication, servers, tags, deprecation status
|
|
59
|
+
- 🔧 **Multiple Input Sources** - Load from URL, file, YAML string, or JSON object
|
|
60
|
+
- ✅ **Production Ready** - Full TypeScript, validation, error handling, 80%+ test coverage
|
|
61
|
+
- 🧩 **Zod Compatible** - Schemas ready for json-schema-to-zod conversion
|
|
62
|
+
- 🚀 **MCP Native** - Designed specifically for Model Context Protocol integration
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install mcp-from-openapi
|
|
68
|
+
# or
|
|
69
|
+
yarn add mcp-from-openapi
|
|
70
|
+
# or
|
|
71
|
+
pnpm add mcp-from-openapi
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Quick Start
|
|
75
|
+
|
|
76
|
+
### Basic Usage
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { OpenAPIToolGenerator } from 'mcp-from-openapi';
|
|
80
|
+
|
|
81
|
+
// 1. Load OpenAPI spec
|
|
82
|
+
const generator = await OpenAPIToolGenerator.fromURL('https://api.example.com/openapi.json');
|
|
83
|
+
|
|
84
|
+
// 2. Generate tools
|
|
85
|
+
const tools = await generator.generateTools();
|
|
86
|
+
|
|
87
|
+
// 3. Each tool has everything you need:
|
|
88
|
+
tools.forEach((tool) => {
|
|
89
|
+
console.log(tool.name); // "createUser"
|
|
90
|
+
console.log(tool.inputSchema); // Combined schema for all params
|
|
91
|
+
console.log(tool.outputSchema); // Response schema
|
|
92
|
+
console.log(tool.mapper); // How to build the HTTP request
|
|
93
|
+
console.log(tool.metadata); // Auth, servers, tags, etc.
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Loading from Different Sources
|
|
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
|
|
142
|
+
|
|
143
|
+
The mapper tells you how to convert tool input into an HTTP request:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
function buildRequest(tool: McpOpenAPITool, input: any) {
|
|
147
|
+
let path = tool.metadata.path;
|
|
148
|
+
const query = new URLSearchParams();
|
|
149
|
+
const headers: Record<string, string> = {};
|
|
150
|
+
let body: any;
|
|
151
|
+
|
|
152
|
+
tool.mapper.forEach((m) => {
|
|
153
|
+
const value = input[m.inputKey];
|
|
154
|
+
if (!value) return;
|
|
155
|
+
|
|
156
|
+
switch (m.type) {
|
|
157
|
+
case 'path':
|
|
158
|
+
path = path.replace(`{${m.key}}`, encodeURIComponent(value));
|
|
159
|
+
break;
|
|
160
|
+
case 'query':
|
|
161
|
+
query.set(m.key, value);
|
|
162
|
+
break;
|
|
163
|
+
case 'header':
|
|
164
|
+
headers[m.key] = value;
|
|
165
|
+
break;
|
|
166
|
+
case 'body':
|
|
167
|
+
if (!body) body = {};
|
|
168
|
+
body[m.key] = value;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
url: `${tool.metadata.servers[0].url}${path}?${query}`,
|
|
175
|
+
method: tool.metadata.method,
|
|
176
|
+
headers,
|
|
177
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
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
|
+
```
|
|
584
|
+
|
|
585
|
+
### 3. Cache Generated Tools
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
class ToolCache {
|
|
589
|
+
private cache = new Map<string, McpOpenAPITool[]>();
|
|
590
|
+
|
|
591
|
+
async getTools(apiUrl: string): Promise<McpOpenAPITool[]> {
|
|
592
|
+
if (this.cache.has(apiUrl)) {
|
|
593
|
+
return this.cache.get(apiUrl)!;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const generator = await OpenAPIToolGenerator.fromURL(apiUrl);
|
|
597
|
+
const tools = await generator.generateTools();
|
|
598
|
+
this.cache.set(apiUrl, tools);
|
|
599
|
+
|
|
600
|
+
return tools;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
### 4. Use TypeScript
|
|
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
|
|
641
|
+
|
|
642
|
+
## Requirements
|
|
643
|
+
|
|
644
|
+
- Node.js >= 18.0.0
|
|
645
|
+
- 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
|
|
653
|
+
|
|
654
|
+
## Contributing
|
|
655
|
+
|
|
656
|
+
Contributions are welcome! Please see our contributing guidelines.
|
|
657
|
+
|
|
658
|
+
## Related Projects
|
|
659
|
+
|
|
660
|
+
- [Model Context Protocol](https://modelcontextprotocol.io/)
|
|
661
|
+
- [OpenAPI Specification](https://www.openapis.org/)
|
|
662
|
+
- [JSON Schema](https://json-schema.org/)
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
**Made with ❤️ for the MCP community**
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-from-openapi",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Production-ready library for converting OpenAPI specifications into MCP tool definitions",
|
|
5
|
+
"author": "AgentFront <info@agentfront.dev>",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"mcp",
|
|
9
|
+
"model-context-protocol",
|
|
10
|
+
"openapi",
|
|
11
|
+
"swagger",
|
|
12
|
+
"api",
|
|
13
|
+
"rest",
|
|
14
|
+
"schema",
|
|
15
|
+
"json-schema",
|
|
16
|
+
"typescript",
|
|
17
|
+
"tool-generator",
|
|
18
|
+
"anthropic",
|
|
19
|
+
"claude"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/agentfront/frontmcp.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/agentfront/frontmcp/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/agentfront/frontmcp/blob/main/libs/mcp-from-openapi/README.md",
|
|
29
|
+
"main": "./src/index.js",
|
|
30
|
+
"types": "./src/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
"./package.json": "./package.json",
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./src/index.d.ts",
|
|
35
|
+
"import": "./src/index.js",
|
|
36
|
+
"default": "./src/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@apidevtools/json-schema-ref-parser": "^11.5.4",
|
|
41
|
+
"json-schema": "^0.4.0",
|
|
42
|
+
"openapi-types": "^12.1.3",
|
|
43
|
+
"yaml": "^2.8.1"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^22.0.0",
|
|
47
|
+
"typescript": "^5.0.0",
|
|
48
|
+
"zod": "^3.23.8"
|
|
49
|
+
},
|
|
50
|
+
"type": "commonjs"
|
|
51
|
+
}
|