@ivotoby/openapi-mcp-server 1.12.5 → 1.13.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 +64 -20
- package/dist/bundle.js +105 -22
- package/dist/cli.js +105 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,7 +36,6 @@ The server supports two transport methods:
|
|
|
36
36
|
No need to clone this repository. Simply configure Claude Desktop to use this MCP server:
|
|
37
37
|
|
|
38
38
|
1. Locate or create your Claude Desktop configuration file:
|
|
39
|
-
|
|
40
39
|
- On macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
41
40
|
|
|
42
41
|
2. Add the following configuration:
|
|
@@ -120,6 +119,11 @@ The server can be configured through environment variables or command line argum
|
|
|
120
119
|
- `OPENAPI_SPEC_FROM_STDIN` - Set to "true" to read OpenAPI spec from standard input
|
|
121
120
|
- `OPENAPI_SPEC_INLINE` - Provide OpenAPI spec content directly as a string
|
|
122
121
|
- `API_HEADERS` - Comma-separated key:value pairs for API headers
|
|
122
|
+
- `CLIENT_CERT_PATH` - Path to client certificate PEM file for mutual TLS
|
|
123
|
+
- `CLIENT_KEY_PATH` - Path to client private key PEM file for mutual TLS
|
|
124
|
+
- `CA_CERT_PATH` - Path to custom CA certificate PEM file for private/internal CAs
|
|
125
|
+
- `CLIENT_KEY_PASSPHRASE` - Passphrase for an encrypted client private key
|
|
126
|
+
- `REJECT_UNAUTHORIZED` - Whether to reject untrusted server certificates (default: `true`)
|
|
123
127
|
- `SERVER_NAME` - Name for the MCP server (default: "mcp-openapi-server")
|
|
124
128
|
- `SERVER_VERSION` - Version of the server (default: "1.0.0")
|
|
125
129
|
- `TRANSPORT_TYPE` - Transport type to use: "stdio" or "http" (default: "stdio")
|
|
@@ -140,6 +144,8 @@ npx @ivotoby/openapi-mcp-server \
|
|
|
140
144
|
--api-base-url https://api.example.com \
|
|
141
145
|
--openapi-spec https://api.example.com/openapi.json \
|
|
142
146
|
--headers "Authorization:Bearer token123,X-API-Key:your-api-key" \
|
|
147
|
+
--client-cert ./certs/client.pem \
|
|
148
|
+
--client-key ./certs/client-key.pem \
|
|
143
149
|
--name "my-mcp-server" \
|
|
144
150
|
--server-version "1.0.0" \
|
|
145
151
|
--transport http \
|
|
@@ -149,6 +155,42 @@ npx @ivotoby/openapi-mcp-server \
|
|
|
149
155
|
--disable-abbreviation true
|
|
150
156
|
```
|
|
151
157
|
|
|
158
|
+
## Mutual TLS (mTLS)
|
|
159
|
+
|
|
160
|
+
If your upstream API requires client certificate authentication, you can attach TLS credentials directly to outbound requests.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npx @ivotoby/openapi-mcp-server \
|
|
164
|
+
--api-base-url https://secure-api.example.com \
|
|
165
|
+
--openapi-spec https://secure-api.example.com/openapi.json \
|
|
166
|
+
--client-cert ./certs/client.pem \
|
|
167
|
+
--client-key ./certs/client-key.pem \
|
|
168
|
+
--headers "Authorization:Bearer token123"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
This is orthogonal to HTTP-level auth, so mTLS can be combined with static headers or an `AuthProvider`.
|
|
172
|
+
|
|
173
|
+
TLS-related options only apply when `--api-base-url` uses `https://`.
|
|
174
|
+
|
|
175
|
+
For private CAs or encrypted keys:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
npx @ivotoby/openapi-mcp-server \
|
|
179
|
+
--api-base-url https://internal-api.example.com \
|
|
180
|
+
--openapi-spec ./openapi.yaml \
|
|
181
|
+
--client-cert ./certs/client.pem \
|
|
182
|
+
--client-key ./certs/client-key.pem \
|
|
183
|
+
--client-key-passphrase "$CLIENT_KEY_PASSPHRASE" \
|
|
184
|
+
--ca-cert ./certs/internal-ca.pem \
|
|
185
|
+
--reject-unauthorized false
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
- `--client-cert` / `CLIENT_CERT_PATH`: client certificate PEM file
|
|
189
|
+
- `--client-key` / `CLIENT_KEY_PATH`: client private key PEM file
|
|
190
|
+
- `--client-key-passphrase` / `CLIENT_KEY_PASSPHRASE`: passphrase for encrypted private keys
|
|
191
|
+
- `--ca-cert` / `CA_CERT_PATH`: custom CA bundle for private/internal certificate authorities
|
|
192
|
+
- `--reject-unauthorized` / `REJECT_UNAUTHORIZED`: set to `false` only when you intentionally want to allow self-signed or otherwise untrusted server certificates
|
|
193
|
+
|
|
152
194
|
## OpenAPI Specification Loading
|
|
153
195
|
|
|
154
196
|
The MCP server supports multiple methods for loading OpenAPI specifications, providing flexibility for different deployment scenarios:
|
|
@@ -286,11 +328,11 @@ In addition to exposing OpenAPI endpoints as **tools**, this server can expose *
|
|
|
286
328
|
|
|
287
329
|
### What Are Prompts and Resources?
|
|
288
330
|
|
|
289
|
-
| Feature
|
|
290
|
-
|
|
291
|
-
| **Tools**
|
|
292
|
-
| **Prompts**
|
|
293
|
-
| **Resources** | Read-only content for context
|
|
331
|
+
| Feature | Purpose | Use Case |
|
|
332
|
+
| ------------- | --------------------------------------------- | --------------------------- |
|
|
333
|
+
| **Tools** | API endpoints for the AI to execute | Making API calls |
|
|
334
|
+
| **Prompts** | Templated messages with argument substitution | Reusable workflow templates |
|
|
335
|
+
| **Resources** | Read-only content for context | API documentation, schemas |
|
|
294
336
|
|
|
295
337
|
### Loading Prompts
|
|
296
338
|
|
|
@@ -434,11 +476,13 @@ curl http://localhost:3000/health
|
|
|
434
476
|
```
|
|
435
477
|
|
|
436
478
|
**Health Response Fields:**
|
|
479
|
+
|
|
437
480
|
- `status`: Always returns "healthy" when server is running
|
|
438
481
|
- `activeSessions`: Number of active MCP sessions
|
|
439
482
|
- `uptime`: Server uptime in seconds
|
|
440
483
|
|
|
441
484
|
**Key Features:**
|
|
485
|
+
|
|
442
486
|
- No authentication required
|
|
443
487
|
- Works with any HTTP method (GET, POST, etc.)
|
|
444
488
|
- Ideal for load balancers, Kubernetes probes, and monitoring systems
|
|
@@ -470,7 +514,6 @@ HEALTHCHECK --interval=30s --timeout=3s \
|
|
|
470
514
|
To see debug logs:
|
|
471
515
|
|
|
472
516
|
1. When using stdio transport with Claude Desktop:
|
|
473
|
-
|
|
474
517
|
- Logs appear in the Claude Desktop logs
|
|
475
518
|
|
|
476
519
|
2. When using HTTP transport:
|
|
@@ -630,15 +673,16 @@ if (resourcesManager) {
|
|
|
630
673
|
|
|
631
674
|
```typescript
|
|
632
675
|
interface PromptDefinition {
|
|
633
|
-
name: string
|
|
634
|
-
title?: string
|
|
635
|
-
description?: string
|
|
636
|
-
arguments?: {
|
|
676
|
+
name: string // Unique identifier
|
|
677
|
+
title?: string // Human-readable display title
|
|
678
|
+
description?: string // Description of the prompt
|
|
679
|
+
arguments?: {
|
|
680
|
+
// Template arguments
|
|
637
681
|
name: string
|
|
638
682
|
description?: string
|
|
639
683
|
required?: boolean
|
|
640
684
|
}[]
|
|
641
|
-
template: string
|
|
685
|
+
template: string // Template with {{argName}} placeholders
|
|
642
686
|
}
|
|
643
687
|
```
|
|
644
688
|
|
|
@@ -646,14 +690,14 @@ interface PromptDefinition {
|
|
|
646
690
|
|
|
647
691
|
```typescript
|
|
648
692
|
interface ResourceDefinition {
|
|
649
|
-
uri: string
|
|
650
|
-
name: string
|
|
651
|
-
title?: string
|
|
652
|
-
description?: string
|
|
653
|
-
mimeType?: string
|
|
654
|
-
text?: string
|
|
655
|
-
blob?: string
|
|
656
|
-
contentProvider?: () => Promise<string | { blob: string }>
|
|
693
|
+
uri: string // Unique URI identifier
|
|
694
|
+
name: string // Resource name
|
|
695
|
+
title?: string // Human-readable display title
|
|
696
|
+
description?: string // Description of the resource
|
|
697
|
+
mimeType?: string // Content MIME type
|
|
698
|
+
text?: string // Static text content
|
|
699
|
+
blob?: string // Static binary content (base64)
|
|
700
|
+
contentProvider?: () => Promise<string | { blob: string }> // Dynamic content
|
|
657
701
|
}
|
|
658
702
|
```
|
|
659
703
|
|
package/dist/bundle.js
CHANGED
|
@@ -11659,6 +11659,8 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
11659
11659
|
|
|
11660
11660
|
// src/server.ts
|
|
11661
11661
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11662
|
+
import { readFileSync } from "node:fs";
|
|
11663
|
+
import { Agent as HttpsAgent } from "node:https";
|
|
11662
11664
|
import {
|
|
11663
11665
|
ListToolsRequestSchema,
|
|
11664
11666
|
CallToolRequestSchema,
|
|
@@ -19061,8 +19063,9 @@ var ApiClient = class {
|
|
|
19061
19063
|
* @param baseUrl - Base URL for the API
|
|
19062
19064
|
* @param authProviderOrHeaders - AuthProvider instance or static headers for backward compatibility
|
|
19063
19065
|
* @param specLoader - Optional OpenAPI spec loader for dynamic meta-tools
|
|
19066
|
+
* @param options - Optional HTTP client configuration
|
|
19064
19067
|
*/
|
|
19065
|
-
constructor(baseUrl, authProviderOrHeaders, specLoader) {
|
|
19068
|
+
constructor(baseUrl, authProviderOrHeaders, specLoader, options) {
|
|
19066
19069
|
this.axiosInstance = axios_default.create({
|
|
19067
19070
|
baseURL: baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`,
|
|
19068
19071
|
timeout: 3e4,
|
|
@@ -19071,8 +19074,9 @@ var ApiClient = class {
|
|
|
19071
19074
|
// 50MB response body limit
|
|
19072
19075
|
maxBodyLength: 50 * 1024 * 1024,
|
|
19073
19076
|
// 50MB request body limit
|
|
19074
|
-
maxRedirects: 5
|
|
19077
|
+
maxRedirects: 5,
|
|
19075
19078
|
// Limit redirect chains to prevent abuse
|
|
19079
|
+
httpsAgent: options?.httpsAgent
|
|
19076
19080
|
});
|
|
19077
19081
|
if (!authProviderOrHeaders) {
|
|
19078
19082
|
this.authProvider = new StaticAuthProvider();
|
|
@@ -19787,19 +19791,60 @@ var OpenAPIServer = class {
|
|
|
19787
19791
|
if (this.resourcesManager) {
|
|
19788
19792
|
capabilities.resources = {};
|
|
19789
19793
|
}
|
|
19790
|
-
this.server = new Server(
|
|
19791
|
-
{ name: config.name, version: config.version },
|
|
19792
|
-
{ capabilities }
|
|
19793
|
-
);
|
|
19794
|
+
this.server = new Server({ name: config.name, version: config.version }, { capabilities });
|
|
19794
19795
|
this.toolsManager = new ToolsManager(config);
|
|
19795
19796
|
const authProviderOrHeaders = config.authProvider || new StaticAuthProvider(config.headers);
|
|
19796
|
-
|
|
19797
|
+
const apiClientOptions = this.createApiClientOptions();
|
|
19798
|
+
this.apiClient = apiClientOptions ? new ApiClient(
|
|
19797
19799
|
config.apiBaseUrl,
|
|
19798
19800
|
authProviderOrHeaders,
|
|
19799
|
-
this.toolsManager.getSpecLoader()
|
|
19800
|
-
|
|
19801
|
+
this.toolsManager.getSpecLoader(),
|
|
19802
|
+
apiClientOptions
|
|
19803
|
+
) : new ApiClient(config.apiBaseUrl, authProviderOrHeaders, this.toolsManager.getSpecLoader());
|
|
19801
19804
|
this.initializeHandlers();
|
|
19802
19805
|
}
|
|
19806
|
+
createApiClientOptions() {
|
|
19807
|
+
const hasClientCert = !!this.config.clientCertPath;
|
|
19808
|
+
const hasClientKey = !!this.config.clientKeyPath;
|
|
19809
|
+
if (hasClientCert !== hasClientKey) {
|
|
19810
|
+
throw new Error("clientCertPath and clientKeyPath must be provided together");
|
|
19811
|
+
}
|
|
19812
|
+
if (this.config.clientKeyPassphrase && !hasClientKey) {
|
|
19813
|
+
throw new Error("clientKeyPassphrase requires clientKeyPath and clientCertPath");
|
|
19814
|
+
}
|
|
19815
|
+
const rejectUnauthorized = this.config.rejectUnauthorized ?? true;
|
|
19816
|
+
const shouldConfigureHttpsAgent = hasClientCert || hasClientKey || !!this.config.caCertPath || rejectUnauthorized === false;
|
|
19817
|
+
if (!shouldConfigureHttpsAgent) {
|
|
19818
|
+
return void 0;
|
|
19819
|
+
}
|
|
19820
|
+
let apiUrl;
|
|
19821
|
+
try {
|
|
19822
|
+
apiUrl = new URL(this.config.apiBaseUrl.trim());
|
|
19823
|
+
} catch {
|
|
19824
|
+
throw new Error("TLS options require apiBaseUrl to be a valid https:// URL");
|
|
19825
|
+
}
|
|
19826
|
+
if (apiUrl.protocol !== "https:") {
|
|
19827
|
+
throw new Error("TLS options require apiBaseUrl to use https://");
|
|
19828
|
+
}
|
|
19829
|
+
const httpsAgentOptions = {
|
|
19830
|
+
rejectUnauthorized
|
|
19831
|
+
};
|
|
19832
|
+
if (this.config.clientCertPath) {
|
|
19833
|
+
httpsAgentOptions.cert = readFileSync(this.config.clientCertPath, "utf8");
|
|
19834
|
+
}
|
|
19835
|
+
if (this.config.clientKeyPath) {
|
|
19836
|
+
httpsAgentOptions.key = readFileSync(this.config.clientKeyPath, "utf8");
|
|
19837
|
+
}
|
|
19838
|
+
if (this.config.caCertPath) {
|
|
19839
|
+
httpsAgentOptions.ca = readFileSync(this.config.caCertPath, "utf8");
|
|
19840
|
+
}
|
|
19841
|
+
if (this.config.clientKeyPassphrase) {
|
|
19842
|
+
httpsAgentOptions.passphrase = this.config.clientKeyPassphrase;
|
|
19843
|
+
}
|
|
19844
|
+
return {
|
|
19845
|
+
httpsAgent: new HttpsAgent(httpsAgentOptions)
|
|
19846
|
+
};
|
|
19847
|
+
}
|
|
19803
19848
|
/**
|
|
19804
19849
|
* Initialize request handlers
|
|
19805
19850
|
*/
|
|
@@ -19856,10 +19901,7 @@ var OpenAPIServer = class {
|
|
|
19856
19901
|
prompts: this.promptsManager.getAllPrompts()
|
|
19857
19902
|
}));
|
|
19858
19903
|
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
19859
|
-
return this.promptsManager.getPrompt(
|
|
19860
|
-
request.params.name,
|
|
19861
|
-
request.params.arguments
|
|
19862
|
-
);
|
|
19904
|
+
return this.promptsManager.getPrompt(request.params.name, request.params.arguments);
|
|
19863
19905
|
});
|
|
19864
19906
|
}
|
|
19865
19907
|
if (this.resourcesManager) {
|
|
@@ -20211,7 +20253,7 @@ function sync_default(start, callback) {
|
|
|
20211
20253
|
|
|
20212
20254
|
// node_modules/yargs/lib/platform-shims/esm.mjs
|
|
20213
20255
|
import { inspect } from "util";
|
|
20214
|
-
import { readFileSync as
|
|
20256
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
20215
20257
|
import { fileURLToPath } from "url";
|
|
20216
20258
|
|
|
20217
20259
|
// node_modules/yargs-parser/build/lib/index.js
|
|
@@ -21154,7 +21196,7 @@ function stripQuotes(val) {
|
|
|
21154
21196
|
}
|
|
21155
21197
|
|
|
21156
21198
|
// node_modules/yargs-parser/build/lib/index.js
|
|
21157
|
-
import { readFileSync } from "fs";
|
|
21199
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
21158
21200
|
var _a;
|
|
21159
21201
|
var _b;
|
|
21160
21202
|
var _c;
|
|
@@ -21181,7 +21223,7 @@ var parser = new YargsParser({
|
|
|
21181
21223
|
if (typeof __require !== "undefined") {
|
|
21182
21224
|
return __require(path2);
|
|
21183
21225
|
} else if (path2.match(/\.json$/)) {
|
|
21184
|
-
return JSON.parse(
|
|
21226
|
+
return JSON.parse(readFileSync2(path2, "utf8"));
|
|
21185
21227
|
} else {
|
|
21186
21228
|
throw Error("only .json config files are supported in ESM");
|
|
21187
21229
|
}
|
|
@@ -21233,12 +21275,12 @@ var YError = class _YError extends Error {
|
|
|
21233
21275
|
};
|
|
21234
21276
|
|
|
21235
21277
|
// node_modules/y18n/build/lib/platform-shims/node.js
|
|
21236
|
-
import { readFileSync as
|
|
21278
|
+
import { readFileSync as readFileSync3, statSync as statSync2, writeFile } from "fs";
|
|
21237
21279
|
import { format as format2 } from "util";
|
|
21238
21280
|
import { resolve as resolve3 } from "path";
|
|
21239
21281
|
var node_default2 = {
|
|
21240
21282
|
fs: {
|
|
21241
|
-
readFileSync:
|
|
21283
|
+
readFileSync: readFileSync3,
|
|
21242
21284
|
writeFile
|
|
21243
21285
|
},
|
|
21244
21286
|
format: format2,
|
|
@@ -21462,7 +21504,7 @@ var esm_default = {
|
|
|
21462
21504
|
nextTick: process.nextTick,
|
|
21463
21505
|
stdColumns: typeof process.stdout.columns !== "undefined" ? process.stdout.columns : null
|
|
21464
21506
|
},
|
|
21465
|
-
readFileSync:
|
|
21507
|
+
readFileSync: readFileSync4,
|
|
21466
21508
|
require: () => {
|
|
21467
21509
|
throw new YError(REQUIRE_ERROR);
|
|
21468
21510
|
},
|
|
@@ -24765,6 +24807,25 @@ function parseHeaders(headerStr) {
|
|
|
24765
24807
|
}
|
|
24766
24808
|
return headers;
|
|
24767
24809
|
}
|
|
24810
|
+
function parseOptionalBoolean(value, optionName) {
|
|
24811
|
+
if (value === void 0 || value === null || value === "") {
|
|
24812
|
+
return void 0;
|
|
24813
|
+
}
|
|
24814
|
+
if (typeof value === "boolean") {
|
|
24815
|
+
return value;
|
|
24816
|
+
}
|
|
24817
|
+
if (typeof value !== "string") {
|
|
24818
|
+
throw new Error(`${optionName} must be a boolean value`);
|
|
24819
|
+
}
|
|
24820
|
+
const normalized = value.trim().toLowerCase();
|
|
24821
|
+
if (["true", "1", "yes", "on"].includes(normalized)) {
|
|
24822
|
+
return true;
|
|
24823
|
+
}
|
|
24824
|
+
if (["false", "0", "no", "off"].includes(normalized)) {
|
|
24825
|
+
return false;
|
|
24826
|
+
}
|
|
24827
|
+
throw new Error(`${optionName} must be one of: true, false, 1, 0, yes, no, on, off`);
|
|
24828
|
+
}
|
|
24768
24829
|
function loadConfig() {
|
|
24769
24830
|
const argv = yargs_default(hideBin(process.argv)).option("transport", {
|
|
24770
24831
|
alias: "t",
|
|
@@ -24799,6 +24860,21 @@ function loadConfig() {
|
|
|
24799
24860
|
alias: "H",
|
|
24800
24861
|
type: "string",
|
|
24801
24862
|
description: "API headers in format 'key1:value1,key2:value2'"
|
|
24863
|
+
}).option("client-cert", {
|
|
24864
|
+
type: "string",
|
|
24865
|
+
description: "Path to client certificate PEM file for mutual TLS"
|
|
24866
|
+
}).option("client-key", {
|
|
24867
|
+
type: "string",
|
|
24868
|
+
description: "Path to client private key PEM file for mutual TLS"
|
|
24869
|
+
}).option("ca-cert", {
|
|
24870
|
+
type: "string",
|
|
24871
|
+
description: "Path to custom CA certificate PEM file"
|
|
24872
|
+
}).option("client-key-passphrase", {
|
|
24873
|
+
type: "string",
|
|
24874
|
+
description: "Passphrase for encrypted client private key"
|
|
24875
|
+
}).option("reject-unauthorized", {
|
|
24876
|
+
type: "string",
|
|
24877
|
+
description: "Whether to reject untrusted server certificates"
|
|
24802
24878
|
}).option("name", {
|
|
24803
24879
|
alias: "n",
|
|
24804
24880
|
type: "string",
|
|
@@ -24893,15 +24969,17 @@ function loadConfig() {
|
|
|
24893
24969
|
if (normalized === "all" || normalized === "dynamic" || normalized === "explicit") {
|
|
24894
24970
|
toolsMode = normalized;
|
|
24895
24971
|
} else {
|
|
24896
|
-
throw new Error(
|
|
24897
|
-
"Invalid tools mode. Expected one of: all, dynamic, explicit"
|
|
24898
|
-
);
|
|
24972
|
+
throw new Error("Invalid tools mode. Expected one of: all, dynamic, explicit");
|
|
24899
24973
|
}
|
|
24900
24974
|
}
|
|
24901
24975
|
if (!apiBaseUrl) {
|
|
24902
24976
|
throw new Error("API base URL is required (--api-base-url or API_BASE_URL)");
|
|
24903
24977
|
}
|
|
24904
24978
|
const headers = parseHeaders(argv.headers || process.env.API_HEADERS);
|
|
24979
|
+
const rejectUnauthorizedInput = parseOptionalBoolean(
|
|
24980
|
+
argv["reject-unauthorized"] ?? process.env.REJECT_UNAUTHORIZED,
|
|
24981
|
+
"--reject-unauthorized/REJECT_UNAUTHORIZED"
|
|
24982
|
+
);
|
|
24905
24983
|
return {
|
|
24906
24984
|
name: argv.name || process.env.SERVER_NAME || "mcp-openapi-server",
|
|
24907
24985
|
version: argv["server-version"] || process.env.SERVER_VERSION || "1.0.0",
|
|
@@ -24910,6 +24988,11 @@ function loadConfig() {
|
|
|
24910
24988
|
specInputMethod,
|
|
24911
24989
|
inlineSpecContent,
|
|
24912
24990
|
headers,
|
|
24991
|
+
clientCertPath: argv["client-cert"] || process.env.CLIENT_CERT_PATH,
|
|
24992
|
+
clientKeyPath: argv["client-key"] || process.env.CLIENT_KEY_PATH,
|
|
24993
|
+
caCertPath: argv["ca-cert"] || process.env.CA_CERT_PATH,
|
|
24994
|
+
clientKeyPassphrase: argv["client-key-passphrase"] || process.env.CLIENT_KEY_PASSPHRASE,
|
|
24995
|
+
rejectUnauthorized: rejectUnauthorizedInput ?? true,
|
|
24913
24996
|
transportType,
|
|
24914
24997
|
httpPort,
|
|
24915
24998
|
httpHost,
|
package/dist/cli.js
CHANGED
|
@@ -11659,6 +11659,8 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
11659
11659
|
|
|
11660
11660
|
// src/server.ts
|
|
11661
11661
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11662
|
+
import { readFileSync } from "node:fs";
|
|
11663
|
+
import { Agent as HttpsAgent } from "node:https";
|
|
11662
11664
|
import {
|
|
11663
11665
|
ListToolsRequestSchema,
|
|
11664
11666
|
CallToolRequestSchema,
|
|
@@ -19061,8 +19063,9 @@ var ApiClient = class {
|
|
|
19061
19063
|
* @param baseUrl - Base URL for the API
|
|
19062
19064
|
* @param authProviderOrHeaders - AuthProvider instance or static headers for backward compatibility
|
|
19063
19065
|
* @param specLoader - Optional OpenAPI spec loader for dynamic meta-tools
|
|
19066
|
+
* @param options - Optional HTTP client configuration
|
|
19064
19067
|
*/
|
|
19065
|
-
constructor(baseUrl, authProviderOrHeaders, specLoader) {
|
|
19068
|
+
constructor(baseUrl, authProviderOrHeaders, specLoader, options) {
|
|
19066
19069
|
this.axiosInstance = axios_default.create({
|
|
19067
19070
|
baseURL: baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`,
|
|
19068
19071
|
timeout: 3e4,
|
|
@@ -19071,8 +19074,9 @@ var ApiClient = class {
|
|
|
19071
19074
|
// 50MB response body limit
|
|
19072
19075
|
maxBodyLength: 50 * 1024 * 1024,
|
|
19073
19076
|
// 50MB request body limit
|
|
19074
|
-
maxRedirects: 5
|
|
19077
|
+
maxRedirects: 5,
|
|
19075
19078
|
// Limit redirect chains to prevent abuse
|
|
19079
|
+
httpsAgent: options?.httpsAgent
|
|
19076
19080
|
});
|
|
19077
19081
|
if (!authProviderOrHeaders) {
|
|
19078
19082
|
this.authProvider = new StaticAuthProvider();
|
|
@@ -19787,19 +19791,60 @@ var OpenAPIServer = class {
|
|
|
19787
19791
|
if (this.resourcesManager) {
|
|
19788
19792
|
capabilities.resources = {};
|
|
19789
19793
|
}
|
|
19790
|
-
this.server = new Server(
|
|
19791
|
-
{ name: config.name, version: config.version },
|
|
19792
|
-
{ capabilities }
|
|
19793
|
-
);
|
|
19794
|
+
this.server = new Server({ name: config.name, version: config.version }, { capabilities });
|
|
19794
19795
|
this.toolsManager = new ToolsManager(config);
|
|
19795
19796
|
const authProviderOrHeaders = config.authProvider || new StaticAuthProvider(config.headers);
|
|
19796
|
-
|
|
19797
|
+
const apiClientOptions = this.createApiClientOptions();
|
|
19798
|
+
this.apiClient = apiClientOptions ? new ApiClient(
|
|
19797
19799
|
config.apiBaseUrl,
|
|
19798
19800
|
authProviderOrHeaders,
|
|
19799
|
-
this.toolsManager.getSpecLoader()
|
|
19800
|
-
|
|
19801
|
+
this.toolsManager.getSpecLoader(),
|
|
19802
|
+
apiClientOptions
|
|
19803
|
+
) : new ApiClient(config.apiBaseUrl, authProviderOrHeaders, this.toolsManager.getSpecLoader());
|
|
19801
19804
|
this.initializeHandlers();
|
|
19802
19805
|
}
|
|
19806
|
+
createApiClientOptions() {
|
|
19807
|
+
const hasClientCert = !!this.config.clientCertPath;
|
|
19808
|
+
const hasClientKey = !!this.config.clientKeyPath;
|
|
19809
|
+
if (hasClientCert !== hasClientKey) {
|
|
19810
|
+
throw new Error("clientCertPath and clientKeyPath must be provided together");
|
|
19811
|
+
}
|
|
19812
|
+
if (this.config.clientKeyPassphrase && !hasClientKey) {
|
|
19813
|
+
throw new Error("clientKeyPassphrase requires clientKeyPath and clientCertPath");
|
|
19814
|
+
}
|
|
19815
|
+
const rejectUnauthorized = this.config.rejectUnauthorized ?? true;
|
|
19816
|
+
const shouldConfigureHttpsAgent = hasClientCert || hasClientKey || !!this.config.caCertPath || rejectUnauthorized === false;
|
|
19817
|
+
if (!shouldConfigureHttpsAgent) {
|
|
19818
|
+
return void 0;
|
|
19819
|
+
}
|
|
19820
|
+
let apiUrl;
|
|
19821
|
+
try {
|
|
19822
|
+
apiUrl = new URL(this.config.apiBaseUrl.trim());
|
|
19823
|
+
} catch {
|
|
19824
|
+
throw new Error("TLS options require apiBaseUrl to be a valid https:// URL");
|
|
19825
|
+
}
|
|
19826
|
+
if (apiUrl.protocol !== "https:") {
|
|
19827
|
+
throw new Error("TLS options require apiBaseUrl to use https://");
|
|
19828
|
+
}
|
|
19829
|
+
const httpsAgentOptions = {
|
|
19830
|
+
rejectUnauthorized
|
|
19831
|
+
};
|
|
19832
|
+
if (this.config.clientCertPath) {
|
|
19833
|
+
httpsAgentOptions.cert = readFileSync(this.config.clientCertPath, "utf8");
|
|
19834
|
+
}
|
|
19835
|
+
if (this.config.clientKeyPath) {
|
|
19836
|
+
httpsAgentOptions.key = readFileSync(this.config.clientKeyPath, "utf8");
|
|
19837
|
+
}
|
|
19838
|
+
if (this.config.caCertPath) {
|
|
19839
|
+
httpsAgentOptions.ca = readFileSync(this.config.caCertPath, "utf8");
|
|
19840
|
+
}
|
|
19841
|
+
if (this.config.clientKeyPassphrase) {
|
|
19842
|
+
httpsAgentOptions.passphrase = this.config.clientKeyPassphrase;
|
|
19843
|
+
}
|
|
19844
|
+
return {
|
|
19845
|
+
httpsAgent: new HttpsAgent(httpsAgentOptions)
|
|
19846
|
+
};
|
|
19847
|
+
}
|
|
19803
19848
|
/**
|
|
19804
19849
|
* Initialize request handlers
|
|
19805
19850
|
*/
|
|
@@ -19856,10 +19901,7 @@ var OpenAPIServer = class {
|
|
|
19856
19901
|
prompts: this.promptsManager.getAllPrompts()
|
|
19857
19902
|
}));
|
|
19858
19903
|
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
19859
|
-
return this.promptsManager.getPrompt(
|
|
19860
|
-
request.params.name,
|
|
19861
|
-
request.params.arguments
|
|
19862
|
-
);
|
|
19904
|
+
return this.promptsManager.getPrompt(request.params.name, request.params.arguments);
|
|
19863
19905
|
});
|
|
19864
19906
|
}
|
|
19865
19907
|
if (this.resourcesManager) {
|
|
@@ -20211,7 +20253,7 @@ function sync_default(start, callback) {
|
|
|
20211
20253
|
|
|
20212
20254
|
// node_modules/yargs/lib/platform-shims/esm.mjs
|
|
20213
20255
|
import { inspect } from "util";
|
|
20214
|
-
import { readFileSync as
|
|
20256
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
20215
20257
|
import { fileURLToPath } from "url";
|
|
20216
20258
|
|
|
20217
20259
|
// node_modules/yargs-parser/build/lib/index.js
|
|
@@ -21154,7 +21196,7 @@ function stripQuotes(val) {
|
|
|
21154
21196
|
}
|
|
21155
21197
|
|
|
21156
21198
|
// node_modules/yargs-parser/build/lib/index.js
|
|
21157
|
-
import { readFileSync } from "fs";
|
|
21199
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
21158
21200
|
var _a;
|
|
21159
21201
|
var _b;
|
|
21160
21202
|
var _c;
|
|
@@ -21181,7 +21223,7 @@ var parser = new YargsParser({
|
|
|
21181
21223
|
if (typeof __require !== "undefined") {
|
|
21182
21224
|
return __require(path2);
|
|
21183
21225
|
} else if (path2.match(/\.json$/)) {
|
|
21184
|
-
return JSON.parse(
|
|
21226
|
+
return JSON.parse(readFileSync2(path2, "utf8"));
|
|
21185
21227
|
} else {
|
|
21186
21228
|
throw Error("only .json config files are supported in ESM");
|
|
21187
21229
|
}
|
|
@@ -21233,12 +21275,12 @@ var YError = class _YError extends Error {
|
|
|
21233
21275
|
};
|
|
21234
21276
|
|
|
21235
21277
|
// node_modules/y18n/build/lib/platform-shims/node.js
|
|
21236
|
-
import { readFileSync as
|
|
21278
|
+
import { readFileSync as readFileSync3, statSync as statSync2, writeFile } from "fs";
|
|
21237
21279
|
import { format as format2 } from "util";
|
|
21238
21280
|
import { resolve as resolve3 } from "path";
|
|
21239
21281
|
var node_default2 = {
|
|
21240
21282
|
fs: {
|
|
21241
|
-
readFileSync:
|
|
21283
|
+
readFileSync: readFileSync3,
|
|
21242
21284
|
writeFile
|
|
21243
21285
|
},
|
|
21244
21286
|
format: format2,
|
|
@@ -21462,7 +21504,7 @@ var esm_default = {
|
|
|
21462
21504
|
nextTick: process.nextTick,
|
|
21463
21505
|
stdColumns: typeof process.stdout.columns !== "undefined" ? process.stdout.columns : null
|
|
21464
21506
|
},
|
|
21465
|
-
readFileSync:
|
|
21507
|
+
readFileSync: readFileSync4,
|
|
21466
21508
|
require: () => {
|
|
21467
21509
|
throw new YError(REQUIRE_ERROR);
|
|
21468
21510
|
},
|
|
@@ -24765,6 +24807,25 @@ function parseHeaders(headerStr) {
|
|
|
24765
24807
|
}
|
|
24766
24808
|
return headers;
|
|
24767
24809
|
}
|
|
24810
|
+
function parseOptionalBoolean(value, optionName) {
|
|
24811
|
+
if (value === void 0 || value === null || value === "") {
|
|
24812
|
+
return void 0;
|
|
24813
|
+
}
|
|
24814
|
+
if (typeof value === "boolean") {
|
|
24815
|
+
return value;
|
|
24816
|
+
}
|
|
24817
|
+
if (typeof value !== "string") {
|
|
24818
|
+
throw new Error(`${optionName} must be a boolean value`);
|
|
24819
|
+
}
|
|
24820
|
+
const normalized = value.trim().toLowerCase();
|
|
24821
|
+
if (["true", "1", "yes", "on"].includes(normalized)) {
|
|
24822
|
+
return true;
|
|
24823
|
+
}
|
|
24824
|
+
if (["false", "0", "no", "off"].includes(normalized)) {
|
|
24825
|
+
return false;
|
|
24826
|
+
}
|
|
24827
|
+
throw new Error(`${optionName} must be one of: true, false, 1, 0, yes, no, on, off`);
|
|
24828
|
+
}
|
|
24768
24829
|
function loadConfig() {
|
|
24769
24830
|
const argv = yargs_default(hideBin(process.argv)).option("transport", {
|
|
24770
24831
|
alias: "t",
|
|
@@ -24799,6 +24860,21 @@ function loadConfig() {
|
|
|
24799
24860
|
alias: "H",
|
|
24800
24861
|
type: "string",
|
|
24801
24862
|
description: "API headers in format 'key1:value1,key2:value2'"
|
|
24863
|
+
}).option("client-cert", {
|
|
24864
|
+
type: "string",
|
|
24865
|
+
description: "Path to client certificate PEM file for mutual TLS"
|
|
24866
|
+
}).option("client-key", {
|
|
24867
|
+
type: "string",
|
|
24868
|
+
description: "Path to client private key PEM file for mutual TLS"
|
|
24869
|
+
}).option("ca-cert", {
|
|
24870
|
+
type: "string",
|
|
24871
|
+
description: "Path to custom CA certificate PEM file"
|
|
24872
|
+
}).option("client-key-passphrase", {
|
|
24873
|
+
type: "string",
|
|
24874
|
+
description: "Passphrase for encrypted client private key"
|
|
24875
|
+
}).option("reject-unauthorized", {
|
|
24876
|
+
type: "string",
|
|
24877
|
+
description: "Whether to reject untrusted server certificates"
|
|
24802
24878
|
}).option("name", {
|
|
24803
24879
|
alias: "n",
|
|
24804
24880
|
type: "string",
|
|
@@ -24893,15 +24969,17 @@ function loadConfig() {
|
|
|
24893
24969
|
if (normalized === "all" || normalized === "dynamic" || normalized === "explicit") {
|
|
24894
24970
|
toolsMode = normalized;
|
|
24895
24971
|
} else {
|
|
24896
|
-
throw new Error(
|
|
24897
|
-
"Invalid tools mode. Expected one of: all, dynamic, explicit"
|
|
24898
|
-
);
|
|
24972
|
+
throw new Error("Invalid tools mode. Expected one of: all, dynamic, explicit");
|
|
24899
24973
|
}
|
|
24900
24974
|
}
|
|
24901
24975
|
if (!apiBaseUrl) {
|
|
24902
24976
|
throw new Error("API base URL is required (--api-base-url or API_BASE_URL)");
|
|
24903
24977
|
}
|
|
24904
24978
|
const headers = parseHeaders(argv.headers || process.env.API_HEADERS);
|
|
24979
|
+
const rejectUnauthorizedInput = parseOptionalBoolean(
|
|
24980
|
+
argv["reject-unauthorized"] ?? process.env.REJECT_UNAUTHORIZED,
|
|
24981
|
+
"--reject-unauthorized/REJECT_UNAUTHORIZED"
|
|
24982
|
+
);
|
|
24905
24983
|
return {
|
|
24906
24984
|
name: argv.name || process.env.SERVER_NAME || "mcp-openapi-server",
|
|
24907
24985
|
version: argv["server-version"] || process.env.SERVER_VERSION || "1.0.0",
|
|
@@ -24910,6 +24988,11 @@ function loadConfig() {
|
|
|
24910
24988
|
specInputMethod,
|
|
24911
24989
|
inlineSpecContent,
|
|
24912
24990
|
headers,
|
|
24991
|
+
clientCertPath: argv["client-cert"] || process.env.CLIENT_CERT_PATH,
|
|
24992
|
+
clientKeyPath: argv["client-key"] || process.env.CLIENT_KEY_PATH,
|
|
24993
|
+
caCertPath: argv["ca-cert"] || process.env.CA_CERT_PATH,
|
|
24994
|
+
clientKeyPassphrase: argv["client-key-passphrase"] || process.env.CLIENT_KEY_PASSPHRASE,
|
|
24995
|
+
rejectUnauthorized: rejectUnauthorizedInput ?? true,
|
|
24913
24996
|
transportType,
|
|
24914
24997
|
httpPort,
|
|
24915
24998
|
httpHost,
|