@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 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 | Purpose | Use Case |
290
- |---------|---------|----------|
291
- | **Tools** | API endpoints for the AI to execute | Making API calls |
292
- | **Prompts** | Templated messages with argument substitution | Reusable workflow templates |
293
- | **Resources** | Read-only content for context | API documentation, schemas |
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 // Unique identifier
634
- title?: string // Human-readable display title
635
- description?: string // Description of the prompt
636
- arguments?: { // Template 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 // Template with {{argName}} placeholders
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 // Unique URI identifier
650
- name: string // Resource name
651
- title?: string // Human-readable display title
652
- description?: string // Description of the resource
653
- mimeType?: string // Content MIME type
654
- text?: string // Static text content
655
- blob?: string // Static binary content (base64)
656
- contentProvider?: () => Promise<string | { blob: string }> // Dynamic content
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
- this.apiClient = new ApiClient(
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 readFileSync3 } from "fs";
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(readFileSync(path2, "utf8"));
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 readFileSync2, statSync as statSync2, writeFile } from "fs";
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: readFileSync2,
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: readFileSync3,
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
- this.apiClient = new ApiClient(
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 readFileSync3 } from "fs";
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(readFileSync(path2, "utf8"));
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 readFileSync2, statSync as statSync2, writeFile } from "fs";
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: readFileSync2,
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: readFileSync3,
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ivotoby/openapi-mcp-server",
3
- "version": "1.12.5",
3
+ "version": "1.13.0",
4
4
  "description": "An MCP server that exposes OpenAPI endpoints as resources",
5
5
  "license": "MIT",
6
6
  "type": "module",