@ivotoby/openapi-mcp-server 1.4.1 → 1.5.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/README.md +105 -2
- package/dist/bundle.js +128 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# OpenAPI MCP Server
|
|
2
2
|
|
|
3
|
+
[](https://smithery.ai/server/@ivo-toby/mcp-openapi-server)
|
|
4
|
+
|
|
3
5
|
A Model Context Protocol (MCP) server that exposes OpenAPI endpoints as MCP resources. This server allows Large Language Models to discover and interact with REST APIs defined by OpenAPI specifications through the MCP protocol.
|
|
4
6
|
|
|
5
7
|
## Overview
|
|
@@ -63,7 +65,7 @@ npx @ivotoby/openapi-mcp-server \
|
|
|
63
65
|
# Initialize a session (first request)
|
|
64
66
|
curl -X POST http://localhost:3000/mcp \
|
|
65
67
|
-H "Content-Type: application/json" \
|
|
66
|
-
-d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"
|
|
68
|
+
-d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl-client","version":"1.0.0"}}}'
|
|
67
69
|
|
|
68
70
|
# The response includes a Mcp-Session-Id header that you must use for subsequent requests
|
|
69
71
|
# and the InitializeResult directly in the POST response body.
|
|
@@ -118,6 +120,8 @@ The server can be configured through environment variables or command line argum
|
|
|
118
120
|
|
|
119
121
|
- `API_BASE_URL` - Base URL for the API endpoints
|
|
120
122
|
- `OPENAPI_SPEC_PATH` - Path or URL to OpenAPI specification
|
|
123
|
+
- `OPENAPI_SPEC_FROM_STDIN` - Set to "true" to read OpenAPI spec from standard input
|
|
124
|
+
- `OPENAPI_SPEC_INLINE` - Provide OpenAPI spec content directly as a string
|
|
121
125
|
- `API_HEADERS` - Comma-separated key:value pairs for API headers
|
|
122
126
|
- `SERVER_NAME` - Name for the MCP server (default: "mcp-openapi-server")
|
|
123
127
|
- `SERVER_VERSION` - Version of the server (default: "1.0.0")
|
|
@@ -136,7 +140,7 @@ npx @ivotoby/openapi-mcp-server \
|
|
|
136
140
|
--openapi-spec https://api.example.com/openapi.json \
|
|
137
141
|
--headers "Authorization:Bearer token123,X-API-Key:your-api-key" \
|
|
138
142
|
--name "my-mcp-server" \
|
|
139
|
-
--version "1.0.0" \
|
|
143
|
+
--server-version "1.0.0" \
|
|
140
144
|
--transport http \
|
|
141
145
|
--port 3000 \
|
|
142
146
|
--host 127.0.0.1 \
|
|
@@ -144,6 +148,105 @@ npx @ivotoby/openapi-mcp-server \
|
|
|
144
148
|
--disable-abbreviation true
|
|
145
149
|
```
|
|
146
150
|
|
|
151
|
+
## OpenAPI Specification Loading
|
|
152
|
+
|
|
153
|
+
The MCP server supports multiple methods for loading OpenAPI specifications, providing flexibility for different deployment scenarios:
|
|
154
|
+
|
|
155
|
+
### 1. URL Loading (Default)
|
|
156
|
+
|
|
157
|
+
Load the OpenAPI spec from a remote URL:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npx @ivotoby/openapi-mcp-server \
|
|
161
|
+
--api-base-url https://api.example.com \
|
|
162
|
+
--openapi-spec https://api.example.com/openapi.json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 2. Local File Loading
|
|
166
|
+
|
|
167
|
+
Load the OpenAPI spec from a local file:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
npx @ivotoby/openapi-mcp-server \
|
|
171
|
+
--api-base-url https://api.example.com \
|
|
172
|
+
--openapi-spec ./path/to/openapi.yaml
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 3. Standard Input Loading
|
|
176
|
+
|
|
177
|
+
Read the OpenAPI spec from standard input (useful for piping or containerized environments):
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# Pipe from file
|
|
181
|
+
cat openapi.json | npx @ivotoby/openapi-mcp-server \
|
|
182
|
+
--api-base-url https://api.example.com \
|
|
183
|
+
--spec-from-stdin
|
|
184
|
+
|
|
185
|
+
# Pipe from curl
|
|
186
|
+
curl -s https://api.example.com/openapi.json | npx @ivotoby/openapi-mcp-server \
|
|
187
|
+
--api-base-url https://api.example.com \
|
|
188
|
+
--spec-from-stdin
|
|
189
|
+
|
|
190
|
+
# Using environment variable
|
|
191
|
+
export OPENAPI_SPEC_FROM_STDIN=true
|
|
192
|
+
echo '{"openapi": "3.0.0", ...}' | npx @ivotoby/openapi-mcp-server \
|
|
193
|
+
--api-base-url https://api.example.com
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 4. Inline Specification
|
|
197
|
+
|
|
198
|
+
Provide the OpenAPI spec content directly as a command line argument:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
npx @ivotoby/openapi-mcp-server \
|
|
202
|
+
--api-base-url https://api.example.com \
|
|
203
|
+
--spec-inline '{"openapi": "3.0.0", "info": {"title": "My API", "version": "1.0.0"}, "paths": {}}'
|
|
204
|
+
|
|
205
|
+
# Using environment variable
|
|
206
|
+
export OPENAPI_SPEC_INLINE='{"openapi": "3.0.0", ...}'
|
|
207
|
+
npx @ivotoby/openapi-mcp-server --api-base-url https://api.example.com
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Supported Formats
|
|
211
|
+
|
|
212
|
+
All loading methods support both JSON and YAML formats. The server automatically detects the format and parses accordingly.
|
|
213
|
+
|
|
214
|
+
### Docker and Container Usage
|
|
215
|
+
|
|
216
|
+
For containerized deployments, you can mount OpenAPI specs or use stdin:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Mount local file
|
|
220
|
+
docker run -v /path/to/spec:/app/spec.json your-mcp-server \
|
|
221
|
+
--api-base-url https://api.example.com \
|
|
222
|
+
--openapi-spec /app/spec.json
|
|
223
|
+
|
|
224
|
+
# Use stdin with docker
|
|
225
|
+
cat openapi.json | docker run -i your-mcp-server \
|
|
226
|
+
--api-base-url https://api.example.com \
|
|
227
|
+
--spec-from-stdin
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Error Handling
|
|
231
|
+
|
|
232
|
+
The server provides detailed error messages for spec loading failures:
|
|
233
|
+
|
|
234
|
+
- **URL loading**: HTTP status codes and network errors
|
|
235
|
+
- **File loading**: File system errors (not found, permissions, etc.)
|
|
236
|
+
- **Stdin loading**: Empty input or read errors
|
|
237
|
+
- **Inline loading**: Missing content errors
|
|
238
|
+
- **Parsing errors**: Detailed JSON/YAML syntax error messages
|
|
239
|
+
|
|
240
|
+
### Validation
|
|
241
|
+
|
|
242
|
+
Only one specification source can be used at a time. The server will validate that exactly one of the following is provided:
|
|
243
|
+
|
|
244
|
+
- `--openapi-spec` (URL or file path)
|
|
245
|
+
- `--spec-from-stdin`
|
|
246
|
+
- `--spec-inline`
|
|
247
|
+
|
|
248
|
+
If multiple sources are specified, the server will exit with an error message.
|
|
249
|
+
|
|
147
250
|
### OpenAPI Schema Processing
|
|
148
251
|
|
|
149
252
|
#### Reference Resolution
|
package/dist/bundle.js
CHANGED
|
@@ -14381,27 +14381,96 @@ var OpenAPISpecLoader = class {
|
|
|
14381
14381
|
this.disableAbbreviation = config?.disableAbbreviation ?? false;
|
|
14382
14382
|
}
|
|
14383
14383
|
/**
|
|
14384
|
-
* Load an OpenAPI specification from
|
|
14384
|
+
* Load an OpenAPI specification from various sources
|
|
14385
14385
|
*/
|
|
14386
|
-
async loadOpenAPISpec(specPathOrUrl) {
|
|
14386
|
+
async loadOpenAPISpec(specPathOrUrl, inputMethod = "url", inlineContent) {
|
|
14387
14387
|
let specContent;
|
|
14388
|
-
|
|
14389
|
-
|
|
14390
|
-
|
|
14391
|
-
|
|
14388
|
+
try {
|
|
14389
|
+
switch (inputMethod) {
|
|
14390
|
+
case "url":
|
|
14391
|
+
specContent = await this.loadFromUrl(specPathOrUrl);
|
|
14392
|
+
break;
|
|
14393
|
+
case "file":
|
|
14394
|
+
specContent = await this.loadFromFile(specPathOrUrl);
|
|
14395
|
+
break;
|
|
14396
|
+
case "stdin":
|
|
14397
|
+
specContent = await this.loadFromStdin();
|
|
14398
|
+
break;
|
|
14399
|
+
case "inline":
|
|
14400
|
+
if (!inlineContent) {
|
|
14401
|
+
throw new Error("Inline content is required when using 'inline' input method");
|
|
14402
|
+
}
|
|
14403
|
+
specContent = inlineContent;
|
|
14404
|
+
break;
|
|
14405
|
+
default:
|
|
14406
|
+
throw new Error(`Unsupported input method: ${inputMethod}`);
|
|
14392
14407
|
}
|
|
14393
|
-
|
|
14394
|
-
|
|
14395
|
-
|
|
14408
|
+
} catch (error) {
|
|
14409
|
+
if (error instanceof Error) {
|
|
14410
|
+
throw new Error(`Failed to load OpenAPI spec from ${inputMethod}: ${error.message}`);
|
|
14411
|
+
}
|
|
14412
|
+
throw error;
|
|
14413
|
+
}
|
|
14414
|
+
return this.parseSpecContent(specContent, inputMethod);
|
|
14415
|
+
}
|
|
14416
|
+
/**
|
|
14417
|
+
* Load spec content from URL
|
|
14418
|
+
*/
|
|
14419
|
+
async loadFromUrl(url2) {
|
|
14420
|
+
const response = await fetch(url2);
|
|
14421
|
+
if (!response.ok) {
|
|
14422
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
14423
|
+
}
|
|
14424
|
+
return await response.text();
|
|
14425
|
+
}
|
|
14426
|
+
/**
|
|
14427
|
+
* Load spec content from local file
|
|
14428
|
+
*/
|
|
14429
|
+
async loadFromFile(filePath) {
|
|
14430
|
+
return await readFile(filePath, "utf-8");
|
|
14431
|
+
}
|
|
14432
|
+
/**
|
|
14433
|
+
* Load spec content from standard input
|
|
14434
|
+
*/
|
|
14435
|
+
async loadFromStdin() {
|
|
14436
|
+
return new Promise((resolve5, reject) => {
|
|
14437
|
+
let data = "";
|
|
14438
|
+
process.stdin.setEncoding("utf8");
|
|
14439
|
+
process.stdin.on("data", (chunk) => {
|
|
14440
|
+
data += chunk;
|
|
14441
|
+
});
|
|
14442
|
+
process.stdin.on("end", () => {
|
|
14443
|
+
if (data.trim().length === 0) {
|
|
14444
|
+
reject(new Error("No data received from stdin"));
|
|
14445
|
+
} else {
|
|
14446
|
+
resolve5(data);
|
|
14447
|
+
}
|
|
14448
|
+
});
|
|
14449
|
+
process.stdin.on("error", (error) => {
|
|
14450
|
+
reject(new Error(`Error reading from stdin: ${error.message}`));
|
|
14451
|
+
});
|
|
14452
|
+
process.stdin.resume();
|
|
14453
|
+
});
|
|
14454
|
+
}
|
|
14455
|
+
/**
|
|
14456
|
+
* Parse spec content as JSON or YAML
|
|
14457
|
+
*/
|
|
14458
|
+
parseSpecContent(specContent, source) {
|
|
14459
|
+
if (!specContent || specContent.trim().length === 0) {
|
|
14460
|
+
throw new Error(`Empty or invalid spec content from ${source}`);
|
|
14396
14461
|
}
|
|
14397
14462
|
try {
|
|
14398
14463
|
return JSON.parse(specContent);
|
|
14399
14464
|
} catch (jsonError) {
|
|
14400
14465
|
try {
|
|
14401
|
-
|
|
14466
|
+
const yamlResult = js_yaml_default.load(specContent);
|
|
14467
|
+
if (!yamlResult || typeof yamlResult !== "object") {
|
|
14468
|
+
throw new Error("YAML parsing resulted in invalid object");
|
|
14469
|
+
}
|
|
14470
|
+
return yamlResult;
|
|
14402
14471
|
} catch (yamlError) {
|
|
14403
14472
|
throw new Error(
|
|
14404
|
-
`Failed to parse
|
|
14473
|
+
`Failed to parse as JSON or YAML. JSON error: ${jsonError.message}. YAML error: ${yamlError.message}`
|
|
14405
14474
|
);
|
|
14406
14475
|
}
|
|
14407
14476
|
}
|
|
@@ -14782,7 +14851,11 @@ var ToolsManager = class {
|
|
|
14782
14851
|
* Initialize tools from the OpenAPI specification
|
|
14783
14852
|
*/
|
|
14784
14853
|
async initialize() {
|
|
14785
|
-
const spec = await this.specLoader.loadOpenAPISpec(
|
|
14854
|
+
const spec = await this.specLoader.loadOpenAPISpec(
|
|
14855
|
+
this.config.openApiSpec,
|
|
14856
|
+
this.config.specInputMethod,
|
|
14857
|
+
this.config.inlineSpecContent
|
|
14858
|
+
);
|
|
14786
14859
|
if (this.config.toolsMode === "dynamic") {
|
|
14787
14860
|
this.tools = this.createDynamicTools();
|
|
14788
14861
|
return;
|
|
@@ -23293,6 +23366,12 @@ function loadConfig() {
|
|
|
23293
23366
|
alias: "s",
|
|
23294
23367
|
type: "string",
|
|
23295
23368
|
description: "Path or URL to OpenAPI specification"
|
|
23369
|
+
}).option("spec-from-stdin", {
|
|
23370
|
+
type: "boolean",
|
|
23371
|
+
description: "Read OpenAPI spec from standard input"
|
|
23372
|
+
}).option("spec-inline", {
|
|
23373
|
+
type: "string",
|
|
23374
|
+
description: "Provide OpenAPI spec content directly as a string"
|
|
23296
23375
|
}).option("headers", {
|
|
23297
23376
|
alias: "H",
|
|
23298
23377
|
type: "string",
|
|
@@ -23301,7 +23380,7 @@ function loadConfig() {
|
|
|
23301
23380
|
alias: "n",
|
|
23302
23381
|
type: "string",
|
|
23303
23382
|
description: "Server name"
|
|
23304
|
-
}).option("version", {
|
|
23383
|
+
}).option("server-version", {
|
|
23305
23384
|
alias: "v",
|
|
23306
23385
|
type: "string",
|
|
23307
23386
|
description: "Server version"
|
|
@@ -23338,21 +23417,51 @@ function loadConfig() {
|
|
|
23338
23417
|
const httpPort = argv.port ?? (process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT, 10) : 3e3);
|
|
23339
23418
|
const httpHost = argv.host || process.env.HTTP_HOST || "127.0.0.1";
|
|
23340
23419
|
const endpointPath = argv.path || process.env.ENDPOINT_PATH || "/mcp";
|
|
23341
|
-
const
|
|
23420
|
+
const specFromStdin = argv["spec-from-stdin"] || process.env.OPENAPI_SPEC_FROM_STDIN === "true";
|
|
23421
|
+
const specInline = argv["spec-inline"] || process.env.OPENAPI_SPEC_INLINE;
|
|
23342
23422
|
const openApiSpec = argv["openapi-spec"] || process.env.OPENAPI_SPEC_PATH;
|
|
23423
|
+
const specInputCount = [specFromStdin, !!specInline, !!openApiSpec].filter(Boolean).length;
|
|
23424
|
+
if (specInputCount === 0) {
|
|
23425
|
+
throw new Error(
|
|
23426
|
+
"OpenAPI spec is required. Use one of: --openapi-spec, --spec-from-stdin, or --spec-inline"
|
|
23427
|
+
);
|
|
23428
|
+
}
|
|
23429
|
+
if (specInputCount > 1) {
|
|
23430
|
+
throw new Error("Only one OpenAPI spec input method can be specified at a time");
|
|
23431
|
+
}
|
|
23432
|
+
let specInputMethod;
|
|
23433
|
+
let specPath;
|
|
23434
|
+
let inlineSpecContent;
|
|
23435
|
+
if (specFromStdin) {
|
|
23436
|
+
specInputMethod = "stdin";
|
|
23437
|
+
specPath = "stdin";
|
|
23438
|
+
} else if (specInline) {
|
|
23439
|
+
specInputMethod = "inline";
|
|
23440
|
+
specPath = "inline";
|
|
23441
|
+
inlineSpecContent = specInline;
|
|
23442
|
+
} else if (openApiSpec) {
|
|
23443
|
+
if (openApiSpec.startsWith("http://") || openApiSpec.startsWith("https://")) {
|
|
23444
|
+
specInputMethod = "url";
|
|
23445
|
+
} else {
|
|
23446
|
+
specInputMethod = "file";
|
|
23447
|
+
}
|
|
23448
|
+
specPath = openApiSpec;
|
|
23449
|
+
} else {
|
|
23450
|
+
throw new Error("OpenAPI spec is required");
|
|
23451
|
+
}
|
|
23452
|
+
const apiBaseUrl = argv["api-base-url"] || process.env.API_BASE_URL;
|
|
23343
23453
|
const disableAbbreviation = argv["disable-abbreviation"] || (process.env.DISABLE_ABBREVIATION ? process.env.DISABLE_ABBREVIATION === "true" : false);
|
|
23344
23454
|
if (!apiBaseUrl) {
|
|
23345
23455
|
throw new Error("API base URL is required (--api-base-url or API_BASE_URL)");
|
|
23346
23456
|
}
|
|
23347
|
-
if (!openApiSpec) {
|
|
23348
|
-
throw new Error("OpenAPI spec is required (--openapi-spec or OPENAPI_SPEC_PATH)");
|
|
23349
|
-
}
|
|
23350
23457
|
const headers = parseHeaders(argv.headers || process.env.API_HEADERS);
|
|
23351
23458
|
return {
|
|
23352
23459
|
name: argv.name || process.env.SERVER_NAME || "mcp-openapi-server",
|
|
23353
|
-
version: argv
|
|
23460
|
+
version: argv["server-version"] || process.env.SERVER_VERSION || "1.0.0",
|
|
23354
23461
|
apiBaseUrl,
|
|
23355
|
-
openApiSpec,
|
|
23462
|
+
openApiSpec: specPath,
|
|
23463
|
+
specInputMethod,
|
|
23464
|
+
inlineSpecContent,
|
|
23356
23465
|
headers,
|
|
23357
23466
|
transportType,
|
|
23358
23467
|
httpPort,
|