@ivotoby/openapi-mcp-server 1.12.5 → 1.14.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 +69 -21
- package/dist/bundle.js +184 -59
- package/dist/cli.js +183 -59
- package/package.json +1 -1
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,
|
|
@@ -14406,14 +14408,39 @@ function generateToolId(method, path2) {
|
|
|
14406
14408
|
return `${method.toUpperCase()}::${sanitizedPath}`;
|
|
14407
14409
|
}
|
|
14408
14410
|
|
|
14411
|
+
// src/utils/logger.ts
|
|
14412
|
+
var Logger = class {
|
|
14413
|
+
constructor(verbose = true) {
|
|
14414
|
+
this.verbose = verbose;
|
|
14415
|
+
}
|
|
14416
|
+
setVerbose(verbose) {
|
|
14417
|
+
this.verbose = verbose ?? true;
|
|
14418
|
+
}
|
|
14419
|
+
warn(message, ...optionalParams) {
|
|
14420
|
+
if (this.verbose) {
|
|
14421
|
+
console.warn(message, ...optionalParams);
|
|
14422
|
+
}
|
|
14423
|
+
}
|
|
14424
|
+
error(message, ...optionalParams) {
|
|
14425
|
+
if (this.verbose) {
|
|
14426
|
+
console.error(message, ...optionalParams);
|
|
14427
|
+
}
|
|
14428
|
+
}
|
|
14429
|
+
fatal(message, ...optionalParams) {
|
|
14430
|
+
console.error(message, ...optionalParams);
|
|
14431
|
+
}
|
|
14432
|
+
};
|
|
14433
|
+
|
|
14409
14434
|
// src/openapi-loader.ts
|
|
14410
14435
|
var OpenAPISpecLoader = class {
|
|
14411
14436
|
/**
|
|
14412
14437
|
* Disable name optimization
|
|
14413
14438
|
*/
|
|
14414
14439
|
disableAbbreviation;
|
|
14440
|
+
logger;
|
|
14415
14441
|
constructor(config) {
|
|
14416
14442
|
this.disableAbbreviation = config?.disableAbbreviation ?? false;
|
|
14443
|
+
this.logger = new Logger(config?.verbose ?? true);
|
|
14417
14444
|
}
|
|
14418
14445
|
/**
|
|
14419
14446
|
* Load an OpenAPI specification from various sources
|
|
@@ -14633,7 +14660,7 @@ var OpenAPISpecLoader = class {
|
|
|
14633
14660
|
*/
|
|
14634
14661
|
determineParameterType(paramSchema, paramName) {
|
|
14635
14662
|
if (Object.keys(paramSchema).length === 0 && typeof paramSchema !== "boolean") {
|
|
14636
|
-
|
|
14663
|
+
this.logger.warn(
|
|
14637
14664
|
`Parameter '${paramName}' schema was empty after inlining (potential cycle or unresolvable ref), defaulting to string.`
|
|
14638
14665
|
);
|
|
14639
14666
|
return "string";
|
|
@@ -14689,19 +14716,19 @@ var OpenAPISpecLoader = class {
|
|
|
14689
14716
|
if (resolvedParam && "name" in resolvedParam && "in" in resolvedParam) {
|
|
14690
14717
|
paramObj = resolvedParam;
|
|
14691
14718
|
} else {
|
|
14692
|
-
|
|
14719
|
+
this.logger.warn(
|
|
14693
14720
|
`Could not resolve path-level parameter reference or invalid structure: ${param.$ref}`
|
|
14694
14721
|
);
|
|
14695
14722
|
continue;
|
|
14696
14723
|
}
|
|
14697
14724
|
} else {
|
|
14698
|
-
|
|
14725
|
+
this.logger.warn(`Could not parse path-level parameter reference: ${param.$ref}`);
|
|
14699
14726
|
continue;
|
|
14700
14727
|
}
|
|
14701
14728
|
} else if ("name" in param && "in" in param) {
|
|
14702
14729
|
paramObj = param;
|
|
14703
14730
|
} else {
|
|
14704
|
-
|
|
14731
|
+
this.logger.warn(
|
|
14705
14732
|
"Skipping path-level parameter due to missing 'name' or 'in' properties and not being a valid $ref:",
|
|
14706
14733
|
param
|
|
14707
14734
|
);
|
|
@@ -14717,7 +14744,7 @@ var OpenAPISpecLoader = class {
|
|
|
14717
14744
|
if (!["get", "post", "put", "patch", "delete", "options", "head"].includes(
|
|
14718
14745
|
method.toLowerCase()
|
|
14719
14746
|
)) {
|
|
14720
|
-
|
|
14747
|
+
this.logger.warn(`Skipping non-HTTP method "${method}" for path ${path2}`);
|
|
14721
14748
|
continue;
|
|
14722
14749
|
}
|
|
14723
14750
|
const op = operation;
|
|
@@ -14755,19 +14782,19 @@ var OpenAPISpecLoader = class {
|
|
|
14755
14782
|
if (resolvedParam && "name" in resolvedParam && "in" in resolvedParam) {
|
|
14756
14783
|
paramObj = resolvedParam;
|
|
14757
14784
|
} else {
|
|
14758
|
-
|
|
14785
|
+
this.logger.warn(
|
|
14759
14786
|
`Could not resolve parameter reference or invalid structure: ${param.$ref}`
|
|
14760
14787
|
);
|
|
14761
14788
|
continue;
|
|
14762
14789
|
}
|
|
14763
14790
|
} else {
|
|
14764
|
-
|
|
14791
|
+
this.logger.warn(`Could not parse parameter reference: ${param.$ref}`);
|
|
14765
14792
|
continue;
|
|
14766
14793
|
}
|
|
14767
14794
|
} else if ("name" in param && "in" in param) {
|
|
14768
14795
|
paramObj = param;
|
|
14769
14796
|
} else {
|
|
14770
|
-
|
|
14797
|
+
this.logger.warn(
|
|
14771
14798
|
"Skipping parameter due to missing 'name' or 'in' properties and not being a valid $ref:",
|
|
14772
14799
|
param
|
|
14773
14800
|
);
|
|
@@ -14818,19 +14845,19 @@ var OpenAPISpecLoader = class {
|
|
|
14818
14845
|
if (resolvedRequestBody && "content" in resolvedRequestBody) {
|
|
14819
14846
|
requestBodyObj = resolvedRequestBody;
|
|
14820
14847
|
} else {
|
|
14821
|
-
|
|
14848
|
+
this.logger.warn(
|
|
14822
14849
|
`Could not resolve requestBody reference or invalid structure: ${op.requestBody.$ref}`
|
|
14823
14850
|
);
|
|
14824
14851
|
continue;
|
|
14825
14852
|
}
|
|
14826
14853
|
} else {
|
|
14827
|
-
|
|
14854
|
+
this.logger.warn(`Could not parse requestBody reference: ${op.requestBody.$ref}`);
|
|
14828
14855
|
continue;
|
|
14829
14856
|
}
|
|
14830
14857
|
} else if ("content" in op.requestBody) {
|
|
14831
14858
|
requestBodyObj = op.requestBody;
|
|
14832
14859
|
} else {
|
|
14833
|
-
|
|
14860
|
+
this.logger.warn("Skipping requestBody due to invalid structure:", op.requestBody);
|
|
14834
14861
|
continue;
|
|
14835
14862
|
}
|
|
14836
14863
|
let mediaTypeObj;
|
|
@@ -15033,12 +15060,15 @@ var ToolsManager = class {
|
|
|
15033
15060
|
this.config = config;
|
|
15034
15061
|
this.config.toolsMode = this.config.toolsMode || "all";
|
|
15035
15062
|
this.specLoader = new OpenAPISpecLoader({
|
|
15036
|
-
disableAbbreviation: this.config.disableAbbreviation
|
|
15063
|
+
disableAbbreviation: this.config.disableAbbreviation,
|
|
15064
|
+
verbose: this.config.verbose
|
|
15037
15065
|
});
|
|
15066
|
+
this.logger = new Logger(this.config.verbose);
|
|
15038
15067
|
}
|
|
15039
15068
|
tools = /* @__PURE__ */ new Map();
|
|
15040
15069
|
specLoader;
|
|
15041
15070
|
loadedSpec;
|
|
15071
|
+
logger;
|
|
15042
15072
|
/**
|
|
15043
15073
|
* Get the OpenAPI spec loader instance
|
|
15044
15074
|
*/
|
|
@@ -15124,7 +15154,7 @@ var ToolsManager = class {
|
|
|
15124
15154
|
}
|
|
15125
15155
|
this.tools = filtered2;
|
|
15126
15156
|
for (const [toolId, tool] of this.tools.entries()) {
|
|
15127
|
-
|
|
15157
|
+
this.logger.error(`Registered tool: ${toolId} (${tool.name})`);
|
|
15128
15158
|
}
|
|
15129
15159
|
return;
|
|
15130
15160
|
}
|
|
@@ -15170,7 +15200,7 @@ var ToolsManager = class {
|
|
|
15170
15200
|
}
|
|
15171
15201
|
this.tools = filtered;
|
|
15172
15202
|
for (const [toolId, tool] of this.tools.entries()) {
|
|
15173
|
-
|
|
15203
|
+
this.logger.error(`Registered tool: ${toolId} (${tool.name})`);
|
|
15174
15204
|
}
|
|
15175
15205
|
}
|
|
15176
15206
|
/**
|
|
@@ -19061,8 +19091,9 @@ var ApiClient = class {
|
|
|
19061
19091
|
* @param baseUrl - Base URL for the API
|
|
19062
19092
|
* @param authProviderOrHeaders - AuthProvider instance or static headers for backward compatibility
|
|
19063
19093
|
* @param specLoader - Optional OpenAPI spec loader for dynamic meta-tools
|
|
19094
|
+
* @param options - Optional HTTP client configuration
|
|
19064
19095
|
*/
|
|
19065
|
-
constructor(baseUrl, authProviderOrHeaders, specLoader) {
|
|
19096
|
+
constructor(baseUrl, authProviderOrHeaders, specLoader, options) {
|
|
19066
19097
|
this.axiosInstance = axios_default.create({
|
|
19067
19098
|
baseURL: baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`,
|
|
19068
19099
|
timeout: 3e4,
|
|
@@ -19071,8 +19102,9 @@ var ApiClient = class {
|
|
|
19071
19102
|
// 50MB response body limit
|
|
19072
19103
|
maxBodyLength: 50 * 1024 * 1024,
|
|
19073
19104
|
// 50MB request body limit
|
|
19074
|
-
maxRedirects: 5
|
|
19105
|
+
maxRedirects: 5,
|
|
19075
19106
|
// Limit redirect chains to prevent abuse
|
|
19107
|
+
httpsAgent: options?.httpsAgent
|
|
19076
19108
|
});
|
|
19077
19109
|
if (!authProviderOrHeaders) {
|
|
19078
19110
|
this.authProvider = new StaticAuthProvider();
|
|
@@ -19767,8 +19799,10 @@ var OpenAPIServer = class {
|
|
|
19767
19799
|
promptsManager;
|
|
19768
19800
|
resourcesManager;
|
|
19769
19801
|
config;
|
|
19802
|
+
logger;
|
|
19770
19803
|
constructor(config) {
|
|
19771
19804
|
this.config = config;
|
|
19805
|
+
this.logger = new Logger(config.verbose);
|
|
19772
19806
|
if (config.prompts?.length) {
|
|
19773
19807
|
this.promptsManager = new PromptsManager({ prompts: config.prompts });
|
|
19774
19808
|
}
|
|
@@ -19787,19 +19821,60 @@ var OpenAPIServer = class {
|
|
|
19787
19821
|
if (this.resourcesManager) {
|
|
19788
19822
|
capabilities.resources = {};
|
|
19789
19823
|
}
|
|
19790
|
-
this.server = new Server(
|
|
19791
|
-
{ name: config.name, version: config.version },
|
|
19792
|
-
{ capabilities }
|
|
19793
|
-
);
|
|
19824
|
+
this.server = new Server({ name: config.name, version: config.version }, { capabilities });
|
|
19794
19825
|
this.toolsManager = new ToolsManager(config);
|
|
19795
19826
|
const authProviderOrHeaders = config.authProvider || new StaticAuthProvider(config.headers);
|
|
19796
|
-
|
|
19827
|
+
const apiClientOptions = this.createApiClientOptions();
|
|
19828
|
+
this.apiClient = apiClientOptions ? new ApiClient(
|
|
19797
19829
|
config.apiBaseUrl,
|
|
19798
19830
|
authProviderOrHeaders,
|
|
19799
|
-
this.toolsManager.getSpecLoader()
|
|
19800
|
-
|
|
19831
|
+
this.toolsManager.getSpecLoader(),
|
|
19832
|
+
apiClientOptions
|
|
19833
|
+
) : new ApiClient(config.apiBaseUrl, authProviderOrHeaders, this.toolsManager.getSpecLoader());
|
|
19801
19834
|
this.initializeHandlers();
|
|
19802
19835
|
}
|
|
19836
|
+
createApiClientOptions() {
|
|
19837
|
+
const hasClientCert = !!this.config.clientCertPath;
|
|
19838
|
+
const hasClientKey = !!this.config.clientKeyPath;
|
|
19839
|
+
if (hasClientCert !== hasClientKey) {
|
|
19840
|
+
throw new Error("clientCertPath and clientKeyPath must be provided together");
|
|
19841
|
+
}
|
|
19842
|
+
if (this.config.clientKeyPassphrase && !hasClientKey) {
|
|
19843
|
+
throw new Error("clientKeyPassphrase requires clientKeyPath and clientCertPath");
|
|
19844
|
+
}
|
|
19845
|
+
const rejectUnauthorized = this.config.rejectUnauthorized ?? true;
|
|
19846
|
+
const shouldConfigureHttpsAgent = hasClientCert || hasClientKey || !!this.config.caCertPath || rejectUnauthorized === false;
|
|
19847
|
+
if (!shouldConfigureHttpsAgent) {
|
|
19848
|
+
return void 0;
|
|
19849
|
+
}
|
|
19850
|
+
let apiUrl;
|
|
19851
|
+
try {
|
|
19852
|
+
apiUrl = new URL(this.config.apiBaseUrl.trim());
|
|
19853
|
+
} catch {
|
|
19854
|
+
throw new Error("TLS options require apiBaseUrl to be a valid https:// URL");
|
|
19855
|
+
}
|
|
19856
|
+
if (apiUrl.protocol !== "https:") {
|
|
19857
|
+
throw new Error("TLS options require apiBaseUrl to use https://");
|
|
19858
|
+
}
|
|
19859
|
+
const httpsAgentOptions = {
|
|
19860
|
+
rejectUnauthorized
|
|
19861
|
+
};
|
|
19862
|
+
if (this.config.clientCertPath) {
|
|
19863
|
+
httpsAgentOptions.cert = readFileSync(this.config.clientCertPath, "utf8");
|
|
19864
|
+
}
|
|
19865
|
+
if (this.config.clientKeyPath) {
|
|
19866
|
+
httpsAgentOptions.key = readFileSync(this.config.clientKeyPath, "utf8");
|
|
19867
|
+
}
|
|
19868
|
+
if (this.config.caCertPath) {
|
|
19869
|
+
httpsAgentOptions.ca = readFileSync(this.config.caCertPath, "utf8");
|
|
19870
|
+
}
|
|
19871
|
+
if (this.config.clientKeyPassphrase) {
|
|
19872
|
+
httpsAgentOptions.passphrase = this.config.clientKeyPassphrase;
|
|
19873
|
+
}
|
|
19874
|
+
return {
|
|
19875
|
+
httpsAgent: new HttpsAgent(httpsAgentOptions)
|
|
19876
|
+
};
|
|
19877
|
+
}
|
|
19803
19878
|
/**
|
|
19804
19879
|
* Initialize request handlers
|
|
19805
19880
|
*/
|
|
@@ -19811,21 +19886,21 @@ var OpenAPIServer = class {
|
|
|
19811
19886
|
});
|
|
19812
19887
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
19813
19888
|
const { id, name, arguments: params } = request.params;
|
|
19814
|
-
|
|
19815
|
-
|
|
19889
|
+
this.logger.error("Received request:", request.params);
|
|
19890
|
+
this.logger.error("Using parameters from arguments:", params);
|
|
19816
19891
|
const idOrName = typeof id === "string" ? id : typeof name === "string" ? name : "";
|
|
19817
19892
|
if (!idOrName) {
|
|
19818
19893
|
throw new Error("Tool ID or name is required");
|
|
19819
19894
|
}
|
|
19820
19895
|
const toolInfo = this.toolsManager.findTool(idOrName);
|
|
19821
19896
|
if (!toolInfo) {
|
|
19822
|
-
|
|
19897
|
+
this.logger.error(
|
|
19823
19898
|
`Available tools: ${Array.from(this.toolsManager.getAllTools()).map((t) => t.name).join(", ")}`
|
|
19824
19899
|
);
|
|
19825
19900
|
throw new Error(`Tool not found: ${idOrName}`);
|
|
19826
19901
|
}
|
|
19827
19902
|
const { toolId, tool } = toolInfo;
|
|
19828
|
-
|
|
19903
|
+
this.logger.error(`Executing tool: ${toolId} (${tool.name})`);
|
|
19829
19904
|
try {
|
|
19830
19905
|
const result = await this.apiClient.executeApiCall(toolId, params || {});
|
|
19831
19906
|
return {
|
|
@@ -19856,10 +19931,7 @@ var OpenAPIServer = class {
|
|
|
19856
19931
|
prompts: this.promptsManager.getAllPrompts()
|
|
19857
19932
|
}));
|
|
19858
19933
|
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
19859
|
-
return this.promptsManager.getPrompt(
|
|
19860
|
-
request.params.name,
|
|
19861
|
-
request.params.arguments
|
|
19862
|
-
);
|
|
19934
|
+
return this.promptsManager.getPrompt(request.params.name, request.params.arguments);
|
|
19863
19935
|
});
|
|
19864
19936
|
}
|
|
19865
19937
|
if (this.resourcesManager) {
|
|
@@ -20211,7 +20283,7 @@ function sync_default(start, callback) {
|
|
|
20211
20283
|
|
|
20212
20284
|
// node_modules/yargs/lib/platform-shims/esm.mjs
|
|
20213
20285
|
import { inspect } from "util";
|
|
20214
|
-
import { readFileSync as
|
|
20286
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
20215
20287
|
import { fileURLToPath } from "url";
|
|
20216
20288
|
|
|
20217
20289
|
// node_modules/yargs-parser/build/lib/index.js
|
|
@@ -21154,7 +21226,7 @@ function stripQuotes(val) {
|
|
|
21154
21226
|
}
|
|
21155
21227
|
|
|
21156
21228
|
// node_modules/yargs-parser/build/lib/index.js
|
|
21157
|
-
import { readFileSync } from "fs";
|
|
21229
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
21158
21230
|
var _a;
|
|
21159
21231
|
var _b;
|
|
21160
21232
|
var _c;
|
|
@@ -21181,7 +21253,7 @@ var parser = new YargsParser({
|
|
|
21181
21253
|
if (typeof __require !== "undefined") {
|
|
21182
21254
|
return __require(path2);
|
|
21183
21255
|
} else if (path2.match(/\.json$/)) {
|
|
21184
|
-
return JSON.parse(
|
|
21256
|
+
return JSON.parse(readFileSync2(path2, "utf8"));
|
|
21185
21257
|
} else {
|
|
21186
21258
|
throw Error("only .json config files are supported in ESM");
|
|
21187
21259
|
}
|
|
@@ -21233,12 +21305,12 @@ var YError = class _YError extends Error {
|
|
|
21233
21305
|
};
|
|
21234
21306
|
|
|
21235
21307
|
// node_modules/y18n/build/lib/platform-shims/node.js
|
|
21236
|
-
import { readFileSync as
|
|
21308
|
+
import { readFileSync as readFileSync3, statSync as statSync2, writeFile } from "fs";
|
|
21237
21309
|
import { format as format2 } from "util";
|
|
21238
21310
|
import { resolve as resolve3 } from "path";
|
|
21239
21311
|
var node_default2 = {
|
|
21240
21312
|
fs: {
|
|
21241
|
-
readFileSync:
|
|
21313
|
+
readFileSync: readFileSync3,
|
|
21242
21314
|
writeFile
|
|
21243
21315
|
},
|
|
21244
21316
|
format: format2,
|
|
@@ -21462,7 +21534,7 @@ var esm_default = {
|
|
|
21462
21534
|
nextTick: process.nextTick,
|
|
21463
21535
|
stdColumns: typeof process.stdout.columns !== "undefined" ? process.stdout.columns : null
|
|
21464
21536
|
},
|
|
21465
|
-
readFileSync:
|
|
21537
|
+
readFileSync: readFileSync4,
|
|
21466
21538
|
require: () => {
|
|
21467
21539
|
throw new YError(REQUIRE_ERROR);
|
|
21468
21540
|
},
|
|
@@ -24765,6 +24837,25 @@ function parseHeaders(headerStr) {
|
|
|
24765
24837
|
}
|
|
24766
24838
|
return headers;
|
|
24767
24839
|
}
|
|
24840
|
+
function parseOptionalBoolean(value, optionName) {
|
|
24841
|
+
if (value === void 0 || value === null || value === "") {
|
|
24842
|
+
return void 0;
|
|
24843
|
+
}
|
|
24844
|
+
if (typeof value === "boolean") {
|
|
24845
|
+
return value;
|
|
24846
|
+
}
|
|
24847
|
+
if (typeof value !== "string") {
|
|
24848
|
+
throw new Error(`${optionName} must be a boolean value`);
|
|
24849
|
+
}
|
|
24850
|
+
const normalized = value.trim().toLowerCase();
|
|
24851
|
+
if (["true", "1", "yes", "on"].includes(normalized)) {
|
|
24852
|
+
return true;
|
|
24853
|
+
}
|
|
24854
|
+
if (["false", "0", "no", "off"].includes(normalized)) {
|
|
24855
|
+
return false;
|
|
24856
|
+
}
|
|
24857
|
+
throw new Error(`${optionName} must be one of: true, false, 1, 0, yes, no, on, off`);
|
|
24858
|
+
}
|
|
24768
24859
|
function loadConfig() {
|
|
24769
24860
|
const argv = yargs_default(hideBin(process.argv)).option("transport", {
|
|
24770
24861
|
alias: "t",
|
|
@@ -24799,6 +24890,21 @@ function loadConfig() {
|
|
|
24799
24890
|
alias: "H",
|
|
24800
24891
|
type: "string",
|
|
24801
24892
|
description: "API headers in format 'key1:value1,key2:value2'"
|
|
24893
|
+
}).option("client-cert", {
|
|
24894
|
+
type: "string",
|
|
24895
|
+
description: "Path to client certificate PEM file for mutual TLS"
|
|
24896
|
+
}).option("client-key", {
|
|
24897
|
+
type: "string",
|
|
24898
|
+
description: "Path to client private key PEM file for mutual TLS"
|
|
24899
|
+
}).option("ca-cert", {
|
|
24900
|
+
type: "string",
|
|
24901
|
+
description: "Path to custom CA certificate PEM file"
|
|
24902
|
+
}).option("client-key-passphrase", {
|
|
24903
|
+
type: "string",
|
|
24904
|
+
description: "Passphrase for encrypted client private key"
|
|
24905
|
+
}).option("reject-unauthorized", {
|
|
24906
|
+
type: "string",
|
|
24907
|
+
description: "Whether to reject untrusted server certificates"
|
|
24802
24908
|
}).option("name", {
|
|
24803
24909
|
alias: "n",
|
|
24804
24910
|
type: "string",
|
|
@@ -24842,6 +24948,9 @@ function loadConfig() {
|
|
|
24842
24948
|
}).option("resources-inline", {
|
|
24843
24949
|
type: "string",
|
|
24844
24950
|
description: "Provide resources directly as JSON string"
|
|
24951
|
+
}).option("verbose", {
|
|
24952
|
+
type: "string",
|
|
24953
|
+
description: "Enable verbose logging"
|
|
24845
24954
|
}).help().parseSync();
|
|
24846
24955
|
let transportType;
|
|
24847
24956
|
if (argv.transport === "http" || process.env.TRANSPORT_TYPE === "http") {
|
|
@@ -24893,15 +25002,18 @@ function loadConfig() {
|
|
|
24893
25002
|
if (normalized === "all" || normalized === "dynamic" || normalized === "explicit") {
|
|
24894
25003
|
toolsMode = normalized;
|
|
24895
25004
|
} else {
|
|
24896
|
-
throw new Error(
|
|
24897
|
-
"Invalid tools mode. Expected one of: all, dynamic, explicit"
|
|
24898
|
-
);
|
|
25005
|
+
throw new Error("Invalid tools mode. Expected one of: all, dynamic, explicit");
|
|
24899
25006
|
}
|
|
24900
25007
|
}
|
|
24901
25008
|
if (!apiBaseUrl) {
|
|
24902
25009
|
throw new Error("API base URL is required (--api-base-url or API_BASE_URL)");
|
|
24903
25010
|
}
|
|
24904
25011
|
const headers = parseHeaders(argv.headers || process.env.API_HEADERS);
|
|
25012
|
+
const rejectUnauthorizedInput = parseOptionalBoolean(
|
|
25013
|
+
argv["reject-unauthorized"] ?? process.env.REJECT_UNAUTHORIZED,
|
|
25014
|
+
"--reject-unauthorized/REJECT_UNAUTHORIZED"
|
|
25015
|
+
);
|
|
25016
|
+
const verbose = parseOptionalBoolean(argv.verbose ?? process.env.VERBOSE, "--verbose/VERBOSE") ?? true;
|
|
24905
25017
|
return {
|
|
24906
25018
|
name: argv.name || process.env.SERVER_NAME || "mcp-openapi-server",
|
|
24907
25019
|
version: argv["server-version"] || process.env.SERVER_VERSION || "1.0.0",
|
|
@@ -24910,6 +25022,11 @@ function loadConfig() {
|
|
|
24910
25022
|
specInputMethod,
|
|
24911
25023
|
inlineSpecContent,
|
|
24912
25024
|
headers,
|
|
25025
|
+
clientCertPath: argv["client-cert"] || process.env.CLIENT_CERT_PATH,
|
|
25026
|
+
clientKeyPath: argv["client-key"] || process.env.CLIENT_KEY_PATH,
|
|
25027
|
+
caCertPath: argv["ca-cert"] || process.env.CA_CERT_PATH,
|
|
25028
|
+
clientKeyPassphrase: argv["client-key-passphrase"] || process.env.CLIENT_KEY_PASSPHRASE,
|
|
25029
|
+
rejectUnauthorized: rejectUnauthorizedInput ?? true,
|
|
24913
25030
|
transportType,
|
|
24914
25031
|
httpPort,
|
|
24915
25032
|
httpHost,
|
|
@@ -24923,7 +25040,8 @@ function loadConfig() {
|
|
|
24923
25040
|
promptsPath: argv.prompts || process.env.PROMPTS_PATH,
|
|
24924
25041
|
promptsInline: argv["prompts-inline"] || process.env.PROMPTS_INLINE,
|
|
24925
25042
|
resourcesPath: argv.resources || process.env.RESOURCES_PATH,
|
|
24926
|
-
resourcesInline: argv["resources-inline"] || process.env.RESOURCES_INLINE
|
|
25043
|
+
resourcesInline: argv["resources-inline"] || process.env.RESOURCES_INLINE,
|
|
25044
|
+
verbose
|
|
24927
25045
|
};
|
|
24928
25046
|
}
|
|
24929
25047
|
|
|
@@ -24936,7 +25054,6 @@ import {
|
|
|
24936
25054
|
import * as http3 from "http";
|
|
24937
25055
|
import { randomUUID } from "crypto";
|
|
24938
25056
|
var StreamableHttpServerTransport = class {
|
|
24939
|
-
// Track if using an external server
|
|
24940
25057
|
/**
|
|
24941
25058
|
* Initialize a new StreamableHttpServerTransport
|
|
24942
25059
|
*
|
|
@@ -24951,11 +25068,12 @@ var StreamableHttpServerTransport = class {
|
|
|
24951
25068
|
* further handlers from running for those requests. The MCP handler should be added after
|
|
24952
25069
|
* custom handlers and will return early for non-MCP paths.
|
|
24953
25070
|
*/
|
|
24954
|
-
constructor(port, host = "127.0.0.1", endpointPath = "/mcp", server) {
|
|
25071
|
+
constructor(port, host = "127.0.0.1", endpointPath = "/mcp", server, verbose = true) {
|
|
24955
25072
|
this.port = port;
|
|
24956
25073
|
this.host = host;
|
|
24957
25074
|
this.endpointPath = endpointPath;
|
|
24958
25075
|
this.isExternalServer = !!server;
|
|
25076
|
+
this.logger = new Logger(verbose);
|
|
24959
25077
|
if (server) {
|
|
24960
25078
|
this.server = server;
|
|
24961
25079
|
this.server.on("request", this.handleRequest.bind(this));
|
|
@@ -24973,6 +25091,8 @@ var StreamableHttpServerTransport = class {
|
|
|
24973
25091
|
// Maps request IDs to session IDs
|
|
24974
25092
|
healthCheckPath = "/health";
|
|
24975
25093
|
isExternalServer;
|
|
25094
|
+
// Track if using an external server
|
|
25095
|
+
logger;
|
|
24976
25096
|
/**
|
|
24977
25097
|
* Callback when message is received
|
|
24978
25098
|
*/
|
|
@@ -24995,7 +25115,7 @@ var StreamableHttpServerTransport = class {
|
|
|
24995
25115
|
return new Promise((resolve6, reject) => {
|
|
24996
25116
|
this.server.listen(this.port, this.host, () => {
|
|
24997
25117
|
this.started = true;
|
|
24998
|
-
|
|
25118
|
+
this.logger.error(
|
|
24999
25119
|
`Streamable HTTP transport listening on http://${this.host}:${this.port}${this.endpointPath}`
|
|
25000
25120
|
);
|
|
25001
25121
|
resolve6();
|
|
@@ -25048,34 +25168,34 @@ var StreamableHttpServerTransport = class {
|
|
|
25048
25168
|
* @param message JSON-RPC message
|
|
25049
25169
|
*/
|
|
25050
25170
|
async send(message) {
|
|
25051
|
-
|
|
25171
|
+
this.logger.error(`StreamableHttpServerTransport: Sending message: ${JSON.stringify(message)}`);
|
|
25052
25172
|
let targetSessionId;
|
|
25053
25173
|
let messageIdForThisResponse = null;
|
|
25054
25174
|
if (isJSONRPCResponse(message) && message.id !== null) {
|
|
25055
25175
|
messageIdForThisResponse = message.id;
|
|
25056
25176
|
targetSessionId = this.requestSessionMap.get(messageIdForThisResponse);
|
|
25057
|
-
|
|
25177
|
+
this.logger.error(
|
|
25058
25178
|
`StreamableHttpServerTransport: Potential target session for response ID ${messageIdForThisResponse}: ${targetSessionId}`
|
|
25059
25179
|
);
|
|
25060
25180
|
if (targetSessionId && this.initResponseHandlers.has(targetSessionId)) {
|
|
25061
|
-
|
|
25181
|
+
this.logger.error(
|
|
25062
25182
|
`StreamableHttpServerTransport: Session ${targetSessionId} has initResponseHandlers. Invoking them for message ID ${messageIdForThisResponse}.`
|
|
25063
25183
|
);
|
|
25064
25184
|
const handlers = this.initResponseHandlers.get(targetSessionId);
|
|
25065
25185
|
[...handlers].forEach((handler) => handler(message));
|
|
25066
25186
|
if (!this.requestSessionMap.has(messageIdForThisResponse)) {
|
|
25067
|
-
|
|
25187
|
+
this.logger.error(
|
|
25068
25188
|
`StreamableHttpServerTransport: Response for ID ${messageIdForThisResponse} was handled by an initResponseHandler (e.g., synchronous POST response for initialize or tools/list).`
|
|
25069
25189
|
);
|
|
25070
25190
|
return;
|
|
25071
25191
|
} else {
|
|
25072
|
-
|
|
25192
|
+
this.logger.error(
|
|
25073
25193
|
`StreamableHttpServerTransport: Response for ID ${messageIdForThisResponse} was NOT exclusively handled by an initResponseHandler or handler did not remove from requestSessionMap. Proceeding to GET stream / broadcast if applicable.`
|
|
25074
25194
|
);
|
|
25075
25195
|
}
|
|
25076
25196
|
}
|
|
25077
25197
|
if (this.requestSessionMap.has(messageIdForThisResponse)) {
|
|
25078
|
-
|
|
25198
|
+
this.logger.error(
|
|
25079
25199
|
`StreamableHttpServerTransport: Deleting request ID ${messageIdForThisResponse} from requestSessionMap as it's being processed for GET stream or broadcast.`
|
|
25080
25200
|
);
|
|
25081
25201
|
this.requestSessionMap.delete(messageIdForThisResponse);
|
|
@@ -25083,7 +25203,7 @@ var StreamableHttpServerTransport = class {
|
|
|
25083
25203
|
}
|
|
25084
25204
|
if (!targetSessionId) {
|
|
25085
25205
|
const idForLog = messageIdForThisResponse !== null ? messageIdForThisResponse : isJSONRPCRequest(message) ? message.id : "N/A";
|
|
25086
|
-
|
|
25206
|
+
this.logger.warn(
|
|
25087
25207
|
`StreamableHttpServerTransport: No specific target session for message (ID: ${idForLog}). Broadcasting to all applicable sessions.`
|
|
25088
25208
|
);
|
|
25089
25209
|
for (const [sid, session2] of this.sessions.entries()) {
|
|
@@ -25095,12 +25215,12 @@ var StreamableHttpServerTransport = class {
|
|
|
25095
25215
|
}
|
|
25096
25216
|
const session = this.sessions.get(targetSessionId);
|
|
25097
25217
|
if (session && session.activeResponses.size > 0) {
|
|
25098
|
-
|
|
25218
|
+
this.logger.error(
|
|
25099
25219
|
`StreamableHttpServerTransport: Sending message (ID: ${messageIdForThisResponse}) to GET stream for session ${targetSessionId} (${session.activeResponses.size} active connections).`
|
|
25100
25220
|
);
|
|
25101
25221
|
this.sendMessageToSession(targetSessionId, session, message);
|
|
25102
25222
|
} else if (targetSessionId) {
|
|
25103
|
-
|
|
25223
|
+
this.logger.error(
|
|
25104
25224
|
`StreamableHttpServerTransport: No active GET connections for session ${targetSessionId} to send message (ID: ${messageIdForThisResponse}). Message might not be delivered if not handled by POST.`
|
|
25105
25225
|
);
|
|
25106
25226
|
}
|
|
@@ -25579,20 +25699,22 @@ async function loadResources(pathOrUrl, inline) {
|
|
|
25579
25699
|
|
|
25580
25700
|
// src/index.ts
|
|
25581
25701
|
async function main() {
|
|
25702
|
+
const logger = new Logger(true);
|
|
25582
25703
|
try {
|
|
25583
25704
|
const config = loadConfig();
|
|
25705
|
+
logger.setVerbose(config.verbose);
|
|
25584
25706
|
if (config.promptsPath || config.promptsInline) {
|
|
25585
25707
|
const prompts = await loadPrompts(config.promptsPath, config.promptsInline);
|
|
25586
25708
|
if (prompts) {
|
|
25587
25709
|
config.prompts = prompts;
|
|
25588
|
-
|
|
25710
|
+
logger.error(`Loaded ${prompts.length} prompt(s)`);
|
|
25589
25711
|
}
|
|
25590
25712
|
}
|
|
25591
25713
|
if (config.resourcesPath || config.resourcesInline) {
|
|
25592
25714
|
const resources = await loadResources(config.resourcesPath, config.resourcesInline);
|
|
25593
25715
|
if (resources) {
|
|
25594
25716
|
config.resources = resources;
|
|
25595
|
-
|
|
25717
|
+
logger.error(`Loaded ${resources.length} resource(s)`);
|
|
25596
25718
|
}
|
|
25597
25719
|
}
|
|
25598
25720
|
const server = new OpenAPIServer(config);
|
|
@@ -25601,24 +25723,27 @@ async function main() {
|
|
|
25601
25723
|
transport = new StreamableHttpServerTransport(
|
|
25602
25724
|
config.httpPort,
|
|
25603
25725
|
config.httpHost,
|
|
25604
|
-
config.endpointPath
|
|
25726
|
+
config.endpointPath,
|
|
25727
|
+
void 0,
|
|
25728
|
+
config.verbose
|
|
25605
25729
|
);
|
|
25606
25730
|
await server.start(transport);
|
|
25607
|
-
|
|
25731
|
+
logger.error(
|
|
25608
25732
|
`OpenAPI MCP Server running on http://${config.httpHost}:${config.httpPort}${config.endpointPath}`
|
|
25609
25733
|
);
|
|
25610
25734
|
} else {
|
|
25611
25735
|
transport = new StdioServerTransport();
|
|
25612
25736
|
await server.start(transport);
|
|
25613
|
-
|
|
25737
|
+
logger.error("OpenAPI MCP Server running on stdio");
|
|
25614
25738
|
}
|
|
25615
25739
|
} catch (error) {
|
|
25616
|
-
|
|
25740
|
+
logger.fatal("Failed to start server:", error);
|
|
25617
25741
|
process.exit(1);
|
|
25618
25742
|
}
|
|
25619
25743
|
}
|
|
25620
25744
|
export {
|
|
25621
25745
|
ApiClient,
|
|
25746
|
+
Logger,
|
|
25622
25747
|
OpenAPIServer,
|
|
25623
25748
|
OpenAPISpecLoader,
|
|
25624
25749
|
PromptsManager,
|