@ivotoby/openapi-mcp-server 1.3.0 → 1.4.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/bundle.js +139 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -215,7 +215,7 @@ To see debug logs:
215
215
 
216
216
  2. When using HTTP transport:
217
217
  ```bash
218
- npx @ivotoby/openapi-mcp-server --transport http 2>debug.log
218
+ npx @ivotoby/openapi-mcp-server --transport http &2>debug.log
219
219
  ```
220
220
 
221
221
  ## For Developers
package/dist/bundle.js CHANGED
@@ -14450,6 +14450,33 @@ var OpenAPISpecLoader = class {
14450
14450
  }
14451
14451
  return schemaObj;
14452
14452
  }
14453
+ /**
14454
+ * Determine the appropriate JSON Schema type for a parameter
14455
+ * @param paramSchema The OpenAPI schema object after inlining
14456
+ * @param paramName The name of the parameter (for logging purposes)
14457
+ * @returns The determined type string, or undefined if the type should be omitted
14458
+ */
14459
+ determineParameterType(paramSchema, paramName) {
14460
+ if (Object.keys(paramSchema).length === 0 && typeof paramSchema !== "boolean") {
14461
+ console.warn(
14462
+ `Parameter '${paramName}' schema was empty after inlining (potential cycle or unresolvable ref), defaulting to string.`
14463
+ );
14464
+ return "string";
14465
+ }
14466
+ if (typeof paramSchema === "boolean") {
14467
+ return "boolean";
14468
+ }
14469
+ if (paramSchema.type) {
14470
+ return paramSchema.type;
14471
+ }
14472
+ const hasProperties = "properties" in paramSchema && paramSchema.properties && Object.keys(paramSchema.properties).length > 0;
14473
+ const hasItems = paramSchema.type === "array" && !!paramSchema.items;
14474
+ const hasComposition = "allOf" in paramSchema && paramSchema.allOf && paramSchema.allOf.length > 0 || "anyOf" in paramSchema && paramSchema.anyOf && paramSchema.anyOf.length > 0 || "oneOf" in paramSchema && paramSchema.oneOf && paramSchema.oneOf.length > 0;
14475
+ if (!hasProperties && !hasItems && !hasComposition) {
14476
+ return "string";
14477
+ }
14478
+ return void 0;
14479
+ }
14453
14480
  /**
14454
14481
  * Parse an OpenAPI specification into a map of tools
14455
14482
  */
@@ -14459,7 +14486,10 @@ var OpenAPISpecLoader = class {
14459
14486
  if (!pathItem) continue;
14460
14487
  for (const [method, operation] of Object.entries(pathItem)) {
14461
14488
  if (method === "parameters" || !operation) continue;
14462
- if (!["get", "put", "post", "delete", "options", "head", "patch", "trace"].includes(method)) {
14489
+ if (!["get", "post", "put", "patch", "delete", "options", "head"].includes(
14490
+ method.toLowerCase()
14491
+ )) {
14492
+ console.warn(`Skipping non-HTTP method "${method}" for path ${path}`);
14463
14493
  continue;
14464
14494
  }
14465
14495
  const op = operation;
@@ -14479,22 +14509,35 @@ var OpenAPISpecLoader = class {
14479
14509
  if (op.parameters) {
14480
14510
  for (const param of op.parameters) {
14481
14511
  let paramObj;
14482
- if (!("name" in param)) {
14483
- if ("$ref" in param && typeof param.$ref === "string") {
14484
- const refMatch = param.$ref.match(/^#\/components\/parameters\/(.+)$/);
14485
- if (refMatch && spec.components?.parameters) {
14486
- const paramName = refMatch[1];
14487
- const resolvedParam = spec.components.parameters[paramName];
14488
- if (!resolvedParam || !("name" in resolvedParam)) continue;
14512
+ if ("$ref" in param && typeof param.$ref === "string") {
14513
+ const refMatch = param.$ref.match(/^#\/components\/parameters\/(.+)$/);
14514
+ if (refMatch && spec.components?.parameters) {
14515
+ const paramNameFromRef = refMatch[1];
14516
+ const resolvedParam = spec.components.parameters[paramNameFromRef];
14517
+ if (resolvedParam && "name" in resolvedParam && "in" in resolvedParam) {
14489
14518
  paramObj = resolvedParam;
14490
14519
  } else {
14520
+ console.warn(
14521
+ `Could not resolve parameter reference or invalid structure: ${param.$ref}`
14522
+ );
14491
14523
  continue;
14492
14524
  }
14493
14525
  } else {
14526
+ console.warn(`Could not parse parameter reference: ${param.$ref}`);
14494
14527
  continue;
14495
14528
  }
14496
- } else {
14529
+ } else if ("name" in param && "in" in param) {
14497
14530
  paramObj = param;
14531
+ } else {
14532
+ console.warn(
14533
+ "Skipping parameter due to missing 'name' or 'in' properties and not being a valid $ref:",
14534
+ param
14535
+ );
14536
+ continue;
14537
+ }
14538
+ if (!paramObj) {
14539
+ console.warn("Failed to process a parameter (paramObj is undefined):", param);
14540
+ continue;
14498
14541
  }
14499
14542
  if (paramObj.schema) {
14500
14543
  const paramSchema = this.inlineSchema(
@@ -14503,16 +14546,20 @@ var OpenAPISpecLoader = class {
14503
14546
  /* @__PURE__ */ new Set()
14504
14547
  );
14505
14548
  const paramDef = {
14506
- description: paramObj.description || `${paramObj.name} parameter`
14549
+ description: paramObj.description || `${paramObj.name} parameter`,
14550
+ "x-parameter-location": paramObj.in
14551
+ // Store parameter location (path, query, etc.)
14507
14552
  };
14508
- if (paramSchema.type) {
14509
- paramDef.type = paramSchema.type;
14510
- } else {
14511
- paramDef.type = "string";
14553
+ const paramType = this.determineParameterType(paramSchema, paramObj.name);
14554
+ if (paramType !== void 0) {
14555
+ paramDef.type = paramType;
14512
14556
  }
14513
- for (const [key, value] of Object.entries(paramSchema)) {
14514
- if (key === "type" || key === "description") continue;
14515
- paramDef[key] = value;
14557
+ if (typeof paramSchema === "object" && paramSchema !== null) {
14558
+ for (const [key, value] of Object.entries(paramSchema)) {
14559
+ if (key === "description" && paramDef.description) continue;
14560
+ if (key === "type" && paramDef.type) continue;
14561
+ paramDef[key] = value;
14562
+ }
14516
14563
  }
14517
14564
  tool.inputSchema.properties[paramObj.name] = paramDef;
14518
14565
  if (paramObj.required === true) {
@@ -14794,6 +14841,13 @@ var ToolsManager = class {
14794
14841
  getAllTools() {
14795
14842
  return Array.from(this.tools.values());
14796
14843
  }
14844
+ /**
14845
+ * Get all available tools with their IDs
14846
+ * Returns array of [toolId, tool] pairs
14847
+ */
14848
+ getToolsWithIds() {
14849
+ return Array.from(this.tools.entries());
14850
+ }
14797
14851
  /**
14798
14852
  * Find a tool by ID or name
14799
14853
  */
@@ -18145,6 +18199,24 @@ var ApiClient = class {
18145
18199
  });
18146
18200
  }
18147
18201
  axiosInstance;
18202
+ toolsMap = /* @__PURE__ */ new Map();
18203
+ /**
18204
+ * Set the available tools for the client
18205
+ *
18206
+ * @param tools - Map of tool ID to tool definition
18207
+ */
18208
+ setTools(tools) {
18209
+ this.toolsMap = tools;
18210
+ }
18211
+ /**
18212
+ * Get a tool definition by ID
18213
+ *
18214
+ * @param toolId - The tool ID
18215
+ * @returns The tool definition if found
18216
+ */
18217
+ getToolDefinition(toolId) {
18218
+ return this.toolsMap.get(toolId);
18219
+ }
18148
18220
  /**
18149
18221
  * Execute an API call based on the tool ID and parameters
18150
18222
  *
@@ -18155,15 +18227,57 @@ var ApiClient = class {
18155
18227
  async executeApiCall(toolId, params) {
18156
18228
  try {
18157
18229
  const { method, path } = this.parseToolId(toolId);
18230
+ const toolDef = this.getToolDefinition(toolId);
18231
+ const paramsCopy = { ...params };
18232
+ let resolvedPath = path;
18233
+ const escapeRegExp = (str2) => {
18234
+ return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18235
+ };
18236
+ if (toolDef?.inputSchema?.properties) {
18237
+ for (const [key, value] of Object.entries(paramsCopy)) {
18238
+ const paramDef = toolDef.inputSchema.properties[key];
18239
+ const paramDef_any = paramDef;
18240
+ const paramLocation = paramDef_any?.["x-parameter-location"];
18241
+ if (paramLocation === "path") {
18242
+ const escapedKey = escapeRegExp(key);
18243
+ const paramRegex = new RegExp(`\\{${escapedKey}\\}|:${escapedKey}(?:\\/|$)`, "g");
18244
+ if (paramRegex.test(resolvedPath)) {
18245
+ resolvedPath = resolvedPath.replace(
18246
+ paramRegex,
18247
+ (match) => encodeURIComponent(value) + (match.endsWith("/") ? "/" : "")
18248
+ );
18249
+ } else {
18250
+ resolvedPath = resolvedPath.replace(`/${key}`, `/${encodeURIComponent(value)}`);
18251
+ }
18252
+ delete paramsCopy[key];
18253
+ }
18254
+ }
18255
+ } else {
18256
+ for (const key of Object.keys(paramsCopy)) {
18257
+ const value = paramsCopy[key];
18258
+ const escapedKey = escapeRegExp(key);
18259
+ const paramRegex = new RegExp(`\\{${escapedKey}\\}|:${escapedKey}(?:\\/|$)`, "g");
18260
+ if (paramRegex.test(resolvedPath)) {
18261
+ resolvedPath = resolvedPath.replace(
18262
+ paramRegex,
18263
+ (match) => encodeURIComponent(value) + (match.endsWith("/") ? "/" : "")
18264
+ );
18265
+ delete paramsCopy[key];
18266
+ } else if (resolvedPath.includes(`/${key}`)) {
18267
+ resolvedPath = resolvedPath.replace(`/${key}`, `/${encodeURIComponent(value)}`);
18268
+ delete paramsCopy[key];
18269
+ }
18270
+ }
18271
+ }
18158
18272
  const config = {
18159
18273
  method: method.toLowerCase(),
18160
- url: path,
18274
+ url: resolvedPath,
18161
18275
  headers: this.headers
18162
18276
  };
18163
18277
  if (["get", "delete", "head", "options"].includes(method.toLowerCase())) {
18164
- config.params = this.processQueryParams(params);
18278
+ config.params = this.processQueryParams(paramsCopy);
18165
18279
  } else {
18166
- config.data = params;
18280
+ config.data = paramsCopy;
18167
18281
  }
18168
18282
  const response = await this.axiosInstance(config);
18169
18283
  return response.data;
@@ -18286,6 +18400,11 @@ var OpenAPIServer = class {
18286
18400
  */
18287
18401
  async start(transport) {
18288
18402
  await this.toolsManager.initialize();
18403
+ const toolsMap = /* @__PURE__ */ new Map();
18404
+ for (const [toolId, tool] of this.toolsManager.getToolsWithIds()) {
18405
+ toolsMap.set(toolId, tool);
18406
+ }
18407
+ this.apiClient.setTools(toolsMap);
18289
18408
  await this.server.connect(transport);
18290
18409
  }
18291
18410
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ivotoby/openapi-mcp-server",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "An MCP server that exposes OpenAPI endpoints as resources",
5
5
  "license": "MIT",
6
6
  "type": "module",