@ivotoby/openapi-mcp-server 1.8.1 → 1.8.3

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/dist/bundle.js +273 -8
  2. package/dist/cli.js +273 -8
  3. package/package.json +1 -1
package/dist/bundle.js CHANGED
@@ -14381,10 +14381,15 @@ function parseToolId(toolId) {
14381
14381
  return { method, path: "/" + path };
14382
14382
  }
14383
14383
  function sanitizeForToolId(input) {
14384
- return input.replace(/[^A-Za-z0-9_-]/g, "").replace(/_{3,}/g, "__").replace(/^[_-]+|[_-]+$/g, "");
14384
+ let result = input.replace(/[^A-Za-z0-9_-]/g, "").replace(/_{3,}/g, "__");
14385
+ result = collapseExcessiveHyphens(result);
14386
+ return result.replace(/^[_-]+|[_-]+$/g, "");
14387
+ }
14388
+ function collapseExcessiveHyphens(input) {
14389
+ return input.replace(/-{4,}/g, "---");
14385
14390
  }
14386
14391
  function generateToolId(method, path) {
14387
- const cleanPath = path.replace(/^\//, "").replace(/\/+/g, "/").replace(/\{([^}]+)\}/g, "$1").replace(/\//g, "__");
14392
+ const cleanPath = path.replace(/^\//, "").replace(/\/+/g, "/").replace(/\{([^}]+)\}/g, "---$1").replace(/\//g, "__");
14388
14393
  const sanitizedPath = sanitizeForToolId(cleanPath);
14389
14394
  return `${method.toUpperCase()}::${sanitizedPath}`;
14390
14395
  }
@@ -14989,6 +14994,19 @@ var ToolsManager = class {
14989
14994
  }
14990
14995
  tools = /* @__PURE__ */ new Map();
14991
14996
  specLoader;
14997
+ loadedSpec;
14998
+ /**
14999
+ * Get the OpenAPI spec loader instance
15000
+ */
15001
+ getSpecLoader() {
15002
+ return this.specLoader;
15003
+ }
15004
+ /**
15005
+ * Get the loaded OpenAPI specification
15006
+ */
15007
+ getOpenApiSpec() {
15008
+ return this.loadedSpec;
15009
+ }
14992
15010
  /**
14993
15011
  * Create dynamic discovery meta-tools
14994
15012
  */
@@ -15016,7 +15034,12 @@ var ToolsManager = class {
15016
15034
  inputSchema: {
15017
15035
  type: "object",
15018
15036
  properties: {
15019
- endpoint: { type: "string", description: "Endpoint path to invoke" },
15037
+ endpoint: { type: "string", description: "Endpoint path to invoke (e.g. /users/{id})" },
15038
+ method: {
15039
+ type: "string",
15040
+ description: "HTTP method to use (optional, e.g. GET, POST, PUT, DELETE)",
15041
+ enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]
15042
+ },
15020
15043
  params: {
15021
15044
  type: "object",
15022
15045
  description: "Parameters for the API call",
@@ -15037,6 +15060,7 @@ var ToolsManager = class {
15037
15060
  this.config.specInputMethod,
15038
15061
  this.config.inlineSpecContent
15039
15062
  );
15063
+ this.loadedSpec = spec;
15040
15064
  if (this.config.toolsMode === "dynamic") {
15041
15065
  this.tools = this.createDynamicTools();
15042
15066
  return;
@@ -18472,18 +18496,39 @@ var StaticAuthProvider = class {
18472
18496
  }
18473
18497
  };
18474
18498
 
18499
+ // src/utils/http-methods.ts
18500
+ var VALID_HTTP_METHODS = [
18501
+ "get",
18502
+ "post",
18503
+ "put",
18504
+ "patch",
18505
+ "delete",
18506
+ "options",
18507
+ "head"
18508
+ ];
18509
+ var GET_LIKE_METHODS = ["get", "delete", "head", "options"];
18510
+ function isValidHttpMethod(method) {
18511
+ return VALID_HTTP_METHODS.includes(method.toLowerCase());
18512
+ }
18513
+ function isGetLikeMethod(method) {
18514
+ return GET_LIKE_METHODS.includes(method.toLowerCase());
18515
+ }
18516
+
18475
18517
  // src/api-client.ts
18476
18518
  var ApiClient = class {
18477
18519
  axiosInstance;
18478
18520
  toolsMap = /* @__PURE__ */ new Map();
18479
18521
  authProvider;
18522
+ specLoader;
18523
+ openApiSpec;
18480
18524
  /**
18481
18525
  * Create a new API client
18482
18526
  *
18483
18527
  * @param baseUrl - Base URL for the API
18484
18528
  * @param authProviderOrHeaders - AuthProvider instance or static headers for backward compatibility
18529
+ * @param specLoader - Optional OpenAPI spec loader for dynamic meta-tools
18485
18530
  */
18486
- constructor(baseUrl, authProviderOrHeaders) {
18531
+ constructor(baseUrl, authProviderOrHeaders, specLoader) {
18487
18532
  this.axiosInstance = axios_default.create({
18488
18533
  baseURL: baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`
18489
18534
  });
@@ -18494,6 +18539,7 @@ var ApiClient = class {
18494
18539
  } else {
18495
18540
  this.authProvider = authProviderOrHeaders;
18496
18541
  }
18542
+ this.specLoader = specLoader;
18497
18543
  }
18498
18544
  /**
18499
18545
  * Set the available tools for the client
@@ -18503,6 +18549,14 @@ var ApiClient = class {
18503
18549
  setTools(tools) {
18504
18550
  this.toolsMap = tools;
18505
18551
  }
18552
+ /**
18553
+ * Set the OpenAPI specification for dynamic meta-tools
18554
+ *
18555
+ * @param spec - The OpenAPI specification document
18556
+ */
18557
+ setOpenApiSpec(spec) {
18558
+ this.openApiSpec = spec;
18559
+ }
18506
18560
  /**
18507
18561
  * Get a tool definition by ID
18508
18562
  *
@@ -18532,6 +18586,15 @@ var ApiClient = class {
18532
18586
  */
18533
18587
  async executeApiCallWithRetry(toolId, params, isRetry) {
18534
18588
  try {
18589
+ if (toolId === "LIST-API-ENDPOINTS") {
18590
+ return await this.handleListApiEndpoints();
18591
+ }
18592
+ if (toolId === "GET-API-ENDPOINT-SCHEMA") {
18593
+ return this.handleGetApiEndpointSchema(toolId, params);
18594
+ }
18595
+ if (toolId === "INVOKE-API-ENDPOINT") {
18596
+ return this.handleInvokeApiEndpoint(toolId, params);
18597
+ }
18535
18598
  const { method, path } = this.parseToolId(toolId);
18536
18599
  const toolDef = this.getToolDefinition(toolId);
18537
18600
  const paramsCopy = { ...params };
@@ -18546,7 +18609,10 @@ var ApiClient = class {
18546
18609
  const paramLocation = paramDef_any?.["x-parameter-location"];
18547
18610
  if (paramLocation === "path") {
18548
18611
  const escapedKey = escapeRegExp(key);
18549
- const paramRegex = new RegExp(`\\{${escapedKey}\\}|:${escapedKey}(?:\\/|$)`, "g");
18612
+ const paramRegex = new RegExp(
18613
+ `\\{${escapedKey}\\}|:${escapedKey}(?:\\/|$)|---${escapedKey}(?=__|/|$)`,
18614
+ "g"
18615
+ );
18550
18616
  if (paramRegex.test(resolvedPath)) {
18551
18617
  resolvedPath = resolvedPath.replace(
18552
18618
  paramRegex,
@@ -18562,7 +18628,10 @@ var ApiClient = class {
18562
18628
  for (const key of Object.keys(paramsCopy)) {
18563
18629
  const value = paramsCopy[key];
18564
18630
  const escapedKey = escapeRegExp(key);
18565
- const paramRegex = new RegExp(`\\{${escapedKey}\\}|:${escapedKey}(?:\\/|$)`, "g");
18631
+ const paramRegex = new RegExp(
18632
+ `\\{${escapedKey}\\}|:${escapedKey}(?:\\/|$)|---${escapedKey}(?=__|/|$)`,
18633
+ "g"
18634
+ );
18566
18635
  if (paramRegex.test(resolvedPath)) {
18567
18636
  resolvedPath = resolvedPath.replace(
18568
18637
  paramRegex,
@@ -18581,7 +18650,7 @@ var ApiClient = class {
18581
18650
  url: resolvedPath,
18582
18651
  headers: authHeaders
18583
18652
  };
18584
- if (["get", "delete", "head", "options"].includes(method.toLowerCase())) {
18653
+ if (isGetLikeMethod(method)) {
18585
18654
  config.params = this.processQueryParams(paramsCopy);
18586
18655
  } else {
18587
18656
  config.data = paramsCopy;
@@ -18631,6 +18700,194 @@ var ApiClient = class {
18631
18700
  }
18632
18701
  return result;
18633
18702
  }
18703
+ /**
18704
+ * Handle the LIST-API-ENDPOINTS meta-tool
18705
+ * Returns a list of all available API endpoints from the loaded tools
18706
+ */
18707
+ async handleListApiEndpoints() {
18708
+ const endpoints = [];
18709
+ if (this.openApiSpec) {
18710
+ for (const [path, pathItem] of Object.entries(this.openApiSpec.paths)) {
18711
+ if (!pathItem) continue;
18712
+ for (const [method, operation] of Object.entries(pathItem)) {
18713
+ if (method === "parameters" || !operation) continue;
18714
+ if (!isValidHttpMethod(method)) {
18715
+ continue;
18716
+ }
18717
+ const op = operation;
18718
+ endpoints.push({
18719
+ method: method.toUpperCase(),
18720
+ path,
18721
+ summary: op.summary || "",
18722
+ description: op.description || "",
18723
+ operationId: op.operationId || "",
18724
+ tags: op.tags || []
18725
+ });
18726
+ }
18727
+ }
18728
+ } else {
18729
+ for (const [toolId, tool] of this.toolsMap.entries()) {
18730
+ if (toolId.startsWith("LIST-API-ENDPOINTS") || toolId.startsWith("GET-API-ENDPOINT-SCHEMA") || toolId.startsWith("INVOKE-API-ENDPOINT")) {
18731
+ continue;
18732
+ }
18733
+ try {
18734
+ const { method, path } = this.parseToolId(toolId);
18735
+ endpoints.push({
18736
+ toolId,
18737
+ name: tool.name,
18738
+ description: tool.description,
18739
+ method: method.toUpperCase(),
18740
+ path
18741
+ });
18742
+ } catch (error) {
18743
+ continue;
18744
+ }
18745
+ }
18746
+ }
18747
+ return {
18748
+ endpoints,
18749
+ total: endpoints.length,
18750
+ note: this.openApiSpec ? "Use INVOKE-API-ENDPOINT to call specific endpoints with the path parameter" : "Limited endpoint information - OpenAPI spec not available"
18751
+ };
18752
+ }
18753
+ /**
18754
+ * Handle the GET-API-ENDPOINT-SCHEMA meta-tool
18755
+ * Returns the JSON schema for a specified API endpoint
18756
+ */
18757
+ handleGetApiEndpointSchema(toolId, params) {
18758
+ const { endpoint } = params;
18759
+ if (!endpoint) {
18760
+ throw new Error(`Missing required parameter 'endpoint' for tool '${toolId}'`);
18761
+ }
18762
+ if (this.openApiSpec) {
18763
+ const pathItem = this.openApiSpec.paths[endpoint];
18764
+ if (!pathItem) {
18765
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18766
+ }
18767
+ const operations = [];
18768
+ for (const [method, operation] of Object.entries(pathItem)) {
18769
+ if (method === "parameters" || !operation) continue;
18770
+ if (!isValidHttpMethod(method)) {
18771
+ continue;
18772
+ }
18773
+ const op = operation;
18774
+ operations.push({
18775
+ method: method.toUpperCase(),
18776
+ operationId: op.operationId || "",
18777
+ summary: op.summary || "",
18778
+ description: op.description || "",
18779
+ parameters: op.parameters || [],
18780
+ requestBody: op.requestBody || null,
18781
+ responses: op.responses || {},
18782
+ tags: op.tags || []
18783
+ });
18784
+ }
18785
+ if (operations.length === 0) {
18786
+ throw new Error(`No valid HTTP operations found for path '${endpoint}' in tool '${toolId}'`);
18787
+ }
18788
+ return {
18789
+ path: endpoint,
18790
+ operations,
18791
+ pathParameters: pathItem.parameters || []
18792
+ };
18793
+ } else {
18794
+ let matchingTool;
18795
+ let matchingToolId;
18796
+ for (const [toolId2, tool] of this.toolsMap.entries()) {
18797
+ try {
18798
+ const { path } = this.parseToolId(toolId2);
18799
+ if (path === endpoint) {
18800
+ matchingTool = tool;
18801
+ matchingToolId = toolId2;
18802
+ break;
18803
+ }
18804
+ } catch (error) {
18805
+ continue;
18806
+ }
18807
+ }
18808
+ if (!matchingTool || !matchingToolId) {
18809
+ throw new Error(`No endpoint found for path: ${endpoint}`);
18810
+ }
18811
+ return {
18812
+ toolId: matchingToolId,
18813
+ name: matchingTool.name,
18814
+ description: matchingTool.description,
18815
+ inputSchema: matchingTool.inputSchema,
18816
+ note: "Limited schema information - using tool definition instead of OpenAPI spec"
18817
+ };
18818
+ }
18819
+ }
18820
+ /**
18821
+ * Handle the INVOKE-API-ENDPOINT meta-tool
18822
+ * Dynamically invokes an API endpoint with the provided parameters
18823
+ */
18824
+ async handleInvokeApiEndpoint(toolId, params) {
18825
+ const { endpoint, method, params: endpointParams = {} } = params;
18826
+ if (!endpoint) {
18827
+ throw new Error(`Missing required parameter 'endpoint' for tool '${toolId}'`);
18828
+ }
18829
+ if (method) {
18830
+ const toolId2 = generateToolId(method, endpoint);
18831
+ if (this.toolsMap.has(toolId2)) {
18832
+ return this.executeApiCall(toolId2, endpointParams);
18833
+ } else if (this.openApiSpec) {
18834
+ const pathItem = this.openApiSpec.paths[endpoint];
18835
+ if (pathItem && pathItem[method.toLowerCase()]) {
18836
+ const { method: httpMethod, path } = { method: method.toUpperCase(), path: endpoint };
18837
+ return this.makeDirectHttpRequest(httpMethod, path, endpointParams);
18838
+ } else {
18839
+ throw new Error(
18840
+ `No endpoint found for path '${endpoint}' with method '${method}' in tool '${toolId2}'`
18841
+ );
18842
+ }
18843
+ } else {
18844
+ throw new Error(`Tool not found: ${toolId2}`);
18845
+ }
18846
+ }
18847
+ if (this.openApiSpec) {
18848
+ const pathItem = this.openApiSpec.paths[endpoint];
18849
+ if (pathItem) {
18850
+ for (const method2 of VALID_HTTP_METHODS) {
18851
+ if (pathItem[method2]) {
18852
+ return this.makeDirectHttpRequest(method2.toUpperCase(), endpoint, endpointParams);
18853
+ }
18854
+ }
18855
+ throw new Error(`No HTTP operations found for endpoint '${endpoint}' in tool '${toolId}'`);
18856
+ } else {
18857
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18858
+ }
18859
+ }
18860
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18861
+ }
18862
+ /**
18863
+ * Make a direct HTTP request without going through the tool system
18864
+ * Used by dynamic meta-tools when we have OpenAPI spec but no corresponding tool
18865
+ */
18866
+ async makeDirectHttpRequest(method, path, params) {
18867
+ const authHeaders = await this.authProvider.getAuthHeaders();
18868
+ const config = {
18869
+ method: method.toLowerCase(),
18870
+ url: path,
18871
+ headers: authHeaders
18872
+ };
18873
+ if (isGetLikeMethod(method)) {
18874
+ config.params = this.processQueryParams(params);
18875
+ } else {
18876
+ config.data = params;
18877
+ }
18878
+ try {
18879
+ const response = await this.axiosInstance.request(config);
18880
+ return response.data;
18881
+ } catch (error) {
18882
+ if (axios_default.isAxiosError(error)) {
18883
+ const axiosError = error;
18884
+ throw new Error(
18885
+ `API request failed: ${axiosError.message}${axiosError.response ? ` (${axiosError.response.status}: ${typeof axiosError.response.data === "object" ? JSON.stringify(axiosError.response.data) : axiosError.response.data})` : ""}`
18886
+ );
18887
+ }
18888
+ throw error;
18889
+ }
18890
+ }
18634
18891
  };
18635
18892
 
18636
18893
  // src/server.ts
@@ -18652,7 +18909,11 @@ var OpenAPIServer = class {
18652
18909
  );
18653
18910
  this.toolsManager = new ToolsManager(config);
18654
18911
  const authProviderOrHeaders = config.authProvider || new StaticAuthProvider(config.headers);
18655
- this.apiClient = new ApiClient(config.apiBaseUrl, authProviderOrHeaders);
18912
+ this.apiClient = new ApiClient(
18913
+ config.apiBaseUrl,
18914
+ authProviderOrHeaders,
18915
+ this.toolsManager.getSpecLoader()
18916
+ );
18656
18917
  this.initializeHandlers();
18657
18918
  }
18658
18919
  /**
@@ -18717,6 +18978,10 @@ var OpenAPIServer = class {
18717
18978
  toolsMap.set(toolId, tool);
18718
18979
  }
18719
18980
  this.apiClient.setTools(toolsMap);
18981
+ const spec = this.toolsManager.getOpenApiSpec();
18982
+ if (spec) {
18983
+ this.apiClient.setOpenApiSpec(spec);
18984
+ }
18720
18985
  await this.server.connect(transport);
18721
18986
  }
18722
18987
  };
package/dist/cli.js CHANGED
@@ -14381,10 +14381,15 @@ function parseToolId(toolId) {
14381
14381
  return { method, path: "/" + path };
14382
14382
  }
14383
14383
  function sanitizeForToolId(input) {
14384
- return input.replace(/[^A-Za-z0-9_-]/g, "").replace(/_{3,}/g, "__").replace(/^[_-]+|[_-]+$/g, "");
14384
+ let result = input.replace(/[^A-Za-z0-9_-]/g, "").replace(/_{3,}/g, "__");
14385
+ result = collapseExcessiveHyphens(result);
14386
+ return result.replace(/^[_-]+|[_-]+$/g, "");
14387
+ }
14388
+ function collapseExcessiveHyphens(input) {
14389
+ return input.replace(/-{4,}/g, "---");
14385
14390
  }
14386
14391
  function generateToolId(method, path) {
14387
- const cleanPath = path.replace(/^\//, "").replace(/\/+/g, "/").replace(/\{([^}]+)\}/g, "$1").replace(/\//g, "__");
14392
+ const cleanPath = path.replace(/^\//, "").replace(/\/+/g, "/").replace(/\{([^}]+)\}/g, "---$1").replace(/\//g, "__");
14388
14393
  const sanitizedPath = sanitizeForToolId(cleanPath);
14389
14394
  return `${method.toUpperCase()}::${sanitizedPath}`;
14390
14395
  }
@@ -14989,6 +14994,19 @@ var ToolsManager = class {
14989
14994
  }
14990
14995
  tools = /* @__PURE__ */ new Map();
14991
14996
  specLoader;
14997
+ loadedSpec;
14998
+ /**
14999
+ * Get the OpenAPI spec loader instance
15000
+ */
15001
+ getSpecLoader() {
15002
+ return this.specLoader;
15003
+ }
15004
+ /**
15005
+ * Get the loaded OpenAPI specification
15006
+ */
15007
+ getOpenApiSpec() {
15008
+ return this.loadedSpec;
15009
+ }
14992
15010
  /**
14993
15011
  * Create dynamic discovery meta-tools
14994
15012
  */
@@ -15016,7 +15034,12 @@ var ToolsManager = class {
15016
15034
  inputSchema: {
15017
15035
  type: "object",
15018
15036
  properties: {
15019
- endpoint: { type: "string", description: "Endpoint path to invoke" },
15037
+ endpoint: { type: "string", description: "Endpoint path to invoke (e.g. /users/{id})" },
15038
+ method: {
15039
+ type: "string",
15040
+ description: "HTTP method to use (optional, e.g. GET, POST, PUT, DELETE)",
15041
+ enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]
15042
+ },
15020
15043
  params: {
15021
15044
  type: "object",
15022
15045
  description: "Parameters for the API call",
@@ -15037,6 +15060,7 @@ var ToolsManager = class {
15037
15060
  this.config.specInputMethod,
15038
15061
  this.config.inlineSpecContent
15039
15062
  );
15063
+ this.loadedSpec = spec;
15040
15064
  if (this.config.toolsMode === "dynamic") {
15041
15065
  this.tools = this.createDynamicTools();
15042
15066
  return;
@@ -18472,18 +18496,39 @@ var StaticAuthProvider = class {
18472
18496
  }
18473
18497
  };
18474
18498
 
18499
+ // src/utils/http-methods.ts
18500
+ var VALID_HTTP_METHODS = [
18501
+ "get",
18502
+ "post",
18503
+ "put",
18504
+ "patch",
18505
+ "delete",
18506
+ "options",
18507
+ "head"
18508
+ ];
18509
+ var GET_LIKE_METHODS = ["get", "delete", "head", "options"];
18510
+ function isValidHttpMethod(method) {
18511
+ return VALID_HTTP_METHODS.includes(method.toLowerCase());
18512
+ }
18513
+ function isGetLikeMethod(method) {
18514
+ return GET_LIKE_METHODS.includes(method.toLowerCase());
18515
+ }
18516
+
18475
18517
  // src/api-client.ts
18476
18518
  var ApiClient = class {
18477
18519
  axiosInstance;
18478
18520
  toolsMap = /* @__PURE__ */ new Map();
18479
18521
  authProvider;
18522
+ specLoader;
18523
+ openApiSpec;
18480
18524
  /**
18481
18525
  * Create a new API client
18482
18526
  *
18483
18527
  * @param baseUrl - Base URL for the API
18484
18528
  * @param authProviderOrHeaders - AuthProvider instance or static headers for backward compatibility
18529
+ * @param specLoader - Optional OpenAPI spec loader for dynamic meta-tools
18485
18530
  */
18486
- constructor(baseUrl, authProviderOrHeaders) {
18531
+ constructor(baseUrl, authProviderOrHeaders, specLoader) {
18487
18532
  this.axiosInstance = axios_default.create({
18488
18533
  baseURL: baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`
18489
18534
  });
@@ -18494,6 +18539,7 @@ var ApiClient = class {
18494
18539
  } else {
18495
18540
  this.authProvider = authProviderOrHeaders;
18496
18541
  }
18542
+ this.specLoader = specLoader;
18497
18543
  }
18498
18544
  /**
18499
18545
  * Set the available tools for the client
@@ -18503,6 +18549,14 @@ var ApiClient = class {
18503
18549
  setTools(tools) {
18504
18550
  this.toolsMap = tools;
18505
18551
  }
18552
+ /**
18553
+ * Set the OpenAPI specification for dynamic meta-tools
18554
+ *
18555
+ * @param spec - The OpenAPI specification document
18556
+ */
18557
+ setOpenApiSpec(spec) {
18558
+ this.openApiSpec = spec;
18559
+ }
18506
18560
  /**
18507
18561
  * Get a tool definition by ID
18508
18562
  *
@@ -18532,6 +18586,15 @@ var ApiClient = class {
18532
18586
  */
18533
18587
  async executeApiCallWithRetry(toolId, params, isRetry) {
18534
18588
  try {
18589
+ if (toolId === "LIST-API-ENDPOINTS") {
18590
+ return await this.handleListApiEndpoints();
18591
+ }
18592
+ if (toolId === "GET-API-ENDPOINT-SCHEMA") {
18593
+ return this.handleGetApiEndpointSchema(toolId, params);
18594
+ }
18595
+ if (toolId === "INVOKE-API-ENDPOINT") {
18596
+ return this.handleInvokeApiEndpoint(toolId, params);
18597
+ }
18535
18598
  const { method, path } = this.parseToolId(toolId);
18536
18599
  const toolDef = this.getToolDefinition(toolId);
18537
18600
  const paramsCopy = { ...params };
@@ -18546,7 +18609,10 @@ var ApiClient = class {
18546
18609
  const paramLocation = paramDef_any?.["x-parameter-location"];
18547
18610
  if (paramLocation === "path") {
18548
18611
  const escapedKey = escapeRegExp(key);
18549
- const paramRegex = new RegExp(`\\{${escapedKey}\\}|:${escapedKey}(?:\\/|$)`, "g");
18612
+ const paramRegex = new RegExp(
18613
+ `\\{${escapedKey}\\}|:${escapedKey}(?:\\/|$)|---${escapedKey}(?=__|/|$)`,
18614
+ "g"
18615
+ );
18550
18616
  if (paramRegex.test(resolvedPath)) {
18551
18617
  resolvedPath = resolvedPath.replace(
18552
18618
  paramRegex,
@@ -18562,7 +18628,10 @@ var ApiClient = class {
18562
18628
  for (const key of Object.keys(paramsCopy)) {
18563
18629
  const value = paramsCopy[key];
18564
18630
  const escapedKey = escapeRegExp(key);
18565
- const paramRegex = new RegExp(`\\{${escapedKey}\\}|:${escapedKey}(?:\\/|$)`, "g");
18631
+ const paramRegex = new RegExp(
18632
+ `\\{${escapedKey}\\}|:${escapedKey}(?:\\/|$)|---${escapedKey}(?=__|/|$)`,
18633
+ "g"
18634
+ );
18566
18635
  if (paramRegex.test(resolvedPath)) {
18567
18636
  resolvedPath = resolvedPath.replace(
18568
18637
  paramRegex,
@@ -18581,7 +18650,7 @@ var ApiClient = class {
18581
18650
  url: resolvedPath,
18582
18651
  headers: authHeaders
18583
18652
  };
18584
- if (["get", "delete", "head", "options"].includes(method.toLowerCase())) {
18653
+ if (isGetLikeMethod(method)) {
18585
18654
  config.params = this.processQueryParams(paramsCopy);
18586
18655
  } else {
18587
18656
  config.data = paramsCopy;
@@ -18631,6 +18700,194 @@ var ApiClient = class {
18631
18700
  }
18632
18701
  return result;
18633
18702
  }
18703
+ /**
18704
+ * Handle the LIST-API-ENDPOINTS meta-tool
18705
+ * Returns a list of all available API endpoints from the loaded tools
18706
+ */
18707
+ async handleListApiEndpoints() {
18708
+ const endpoints = [];
18709
+ if (this.openApiSpec) {
18710
+ for (const [path, pathItem] of Object.entries(this.openApiSpec.paths)) {
18711
+ if (!pathItem) continue;
18712
+ for (const [method, operation] of Object.entries(pathItem)) {
18713
+ if (method === "parameters" || !operation) continue;
18714
+ if (!isValidHttpMethod(method)) {
18715
+ continue;
18716
+ }
18717
+ const op = operation;
18718
+ endpoints.push({
18719
+ method: method.toUpperCase(),
18720
+ path,
18721
+ summary: op.summary || "",
18722
+ description: op.description || "",
18723
+ operationId: op.operationId || "",
18724
+ tags: op.tags || []
18725
+ });
18726
+ }
18727
+ }
18728
+ } else {
18729
+ for (const [toolId, tool] of this.toolsMap.entries()) {
18730
+ if (toolId.startsWith("LIST-API-ENDPOINTS") || toolId.startsWith("GET-API-ENDPOINT-SCHEMA") || toolId.startsWith("INVOKE-API-ENDPOINT")) {
18731
+ continue;
18732
+ }
18733
+ try {
18734
+ const { method, path } = this.parseToolId(toolId);
18735
+ endpoints.push({
18736
+ toolId,
18737
+ name: tool.name,
18738
+ description: tool.description,
18739
+ method: method.toUpperCase(),
18740
+ path
18741
+ });
18742
+ } catch (error) {
18743
+ continue;
18744
+ }
18745
+ }
18746
+ }
18747
+ return {
18748
+ endpoints,
18749
+ total: endpoints.length,
18750
+ note: this.openApiSpec ? "Use INVOKE-API-ENDPOINT to call specific endpoints with the path parameter" : "Limited endpoint information - OpenAPI spec not available"
18751
+ };
18752
+ }
18753
+ /**
18754
+ * Handle the GET-API-ENDPOINT-SCHEMA meta-tool
18755
+ * Returns the JSON schema for a specified API endpoint
18756
+ */
18757
+ handleGetApiEndpointSchema(toolId, params) {
18758
+ const { endpoint } = params;
18759
+ if (!endpoint) {
18760
+ throw new Error(`Missing required parameter 'endpoint' for tool '${toolId}'`);
18761
+ }
18762
+ if (this.openApiSpec) {
18763
+ const pathItem = this.openApiSpec.paths[endpoint];
18764
+ if (!pathItem) {
18765
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18766
+ }
18767
+ const operations = [];
18768
+ for (const [method, operation] of Object.entries(pathItem)) {
18769
+ if (method === "parameters" || !operation) continue;
18770
+ if (!isValidHttpMethod(method)) {
18771
+ continue;
18772
+ }
18773
+ const op = operation;
18774
+ operations.push({
18775
+ method: method.toUpperCase(),
18776
+ operationId: op.operationId || "",
18777
+ summary: op.summary || "",
18778
+ description: op.description || "",
18779
+ parameters: op.parameters || [],
18780
+ requestBody: op.requestBody || null,
18781
+ responses: op.responses || {},
18782
+ tags: op.tags || []
18783
+ });
18784
+ }
18785
+ if (operations.length === 0) {
18786
+ throw new Error(`No valid HTTP operations found for path '${endpoint}' in tool '${toolId}'`);
18787
+ }
18788
+ return {
18789
+ path: endpoint,
18790
+ operations,
18791
+ pathParameters: pathItem.parameters || []
18792
+ };
18793
+ } else {
18794
+ let matchingTool;
18795
+ let matchingToolId;
18796
+ for (const [toolId2, tool] of this.toolsMap.entries()) {
18797
+ try {
18798
+ const { path } = this.parseToolId(toolId2);
18799
+ if (path === endpoint) {
18800
+ matchingTool = tool;
18801
+ matchingToolId = toolId2;
18802
+ break;
18803
+ }
18804
+ } catch (error) {
18805
+ continue;
18806
+ }
18807
+ }
18808
+ if (!matchingTool || !matchingToolId) {
18809
+ throw new Error(`No endpoint found for path: ${endpoint}`);
18810
+ }
18811
+ return {
18812
+ toolId: matchingToolId,
18813
+ name: matchingTool.name,
18814
+ description: matchingTool.description,
18815
+ inputSchema: matchingTool.inputSchema,
18816
+ note: "Limited schema information - using tool definition instead of OpenAPI spec"
18817
+ };
18818
+ }
18819
+ }
18820
+ /**
18821
+ * Handle the INVOKE-API-ENDPOINT meta-tool
18822
+ * Dynamically invokes an API endpoint with the provided parameters
18823
+ */
18824
+ async handleInvokeApiEndpoint(toolId, params) {
18825
+ const { endpoint, method, params: endpointParams = {} } = params;
18826
+ if (!endpoint) {
18827
+ throw new Error(`Missing required parameter 'endpoint' for tool '${toolId}'`);
18828
+ }
18829
+ if (method) {
18830
+ const toolId2 = generateToolId(method, endpoint);
18831
+ if (this.toolsMap.has(toolId2)) {
18832
+ return this.executeApiCall(toolId2, endpointParams);
18833
+ } else if (this.openApiSpec) {
18834
+ const pathItem = this.openApiSpec.paths[endpoint];
18835
+ if (pathItem && pathItem[method.toLowerCase()]) {
18836
+ const { method: httpMethod, path } = { method: method.toUpperCase(), path: endpoint };
18837
+ return this.makeDirectHttpRequest(httpMethod, path, endpointParams);
18838
+ } else {
18839
+ throw new Error(
18840
+ `No endpoint found for path '${endpoint}' with method '${method}' in tool '${toolId2}'`
18841
+ );
18842
+ }
18843
+ } else {
18844
+ throw new Error(`Tool not found: ${toolId2}`);
18845
+ }
18846
+ }
18847
+ if (this.openApiSpec) {
18848
+ const pathItem = this.openApiSpec.paths[endpoint];
18849
+ if (pathItem) {
18850
+ for (const method2 of VALID_HTTP_METHODS) {
18851
+ if (pathItem[method2]) {
18852
+ return this.makeDirectHttpRequest(method2.toUpperCase(), endpoint, endpointParams);
18853
+ }
18854
+ }
18855
+ throw new Error(`No HTTP operations found for endpoint '${endpoint}' in tool '${toolId}'`);
18856
+ } else {
18857
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18858
+ }
18859
+ }
18860
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18861
+ }
18862
+ /**
18863
+ * Make a direct HTTP request without going through the tool system
18864
+ * Used by dynamic meta-tools when we have OpenAPI spec but no corresponding tool
18865
+ */
18866
+ async makeDirectHttpRequest(method, path, params) {
18867
+ const authHeaders = await this.authProvider.getAuthHeaders();
18868
+ const config = {
18869
+ method: method.toLowerCase(),
18870
+ url: path,
18871
+ headers: authHeaders
18872
+ };
18873
+ if (isGetLikeMethod(method)) {
18874
+ config.params = this.processQueryParams(params);
18875
+ } else {
18876
+ config.data = params;
18877
+ }
18878
+ try {
18879
+ const response = await this.axiosInstance.request(config);
18880
+ return response.data;
18881
+ } catch (error) {
18882
+ if (axios_default.isAxiosError(error)) {
18883
+ const axiosError = error;
18884
+ throw new Error(
18885
+ `API request failed: ${axiosError.message}${axiosError.response ? ` (${axiosError.response.status}: ${typeof axiosError.response.data === "object" ? JSON.stringify(axiosError.response.data) : axiosError.response.data})` : ""}`
18886
+ );
18887
+ }
18888
+ throw error;
18889
+ }
18890
+ }
18634
18891
  };
18635
18892
 
18636
18893
  // src/server.ts
@@ -18652,7 +18909,11 @@ var OpenAPIServer = class {
18652
18909
  );
18653
18910
  this.toolsManager = new ToolsManager(config);
18654
18911
  const authProviderOrHeaders = config.authProvider || new StaticAuthProvider(config.headers);
18655
- this.apiClient = new ApiClient(config.apiBaseUrl, authProviderOrHeaders);
18912
+ this.apiClient = new ApiClient(
18913
+ config.apiBaseUrl,
18914
+ authProviderOrHeaders,
18915
+ this.toolsManager.getSpecLoader()
18916
+ );
18656
18917
  this.initializeHandlers();
18657
18918
  }
18658
18919
  /**
@@ -18717,6 +18978,10 @@ var OpenAPIServer = class {
18717
18978
  toolsMap.set(toolId, tool);
18718
18979
  }
18719
18980
  this.apiClient.setTools(toolsMap);
18981
+ const spec = this.toolsManager.getOpenApiSpec();
18982
+ if (spec) {
18983
+ this.apiClient.setOpenApiSpec(spec);
18984
+ }
18720
18985
  await this.server.connect(transport);
18721
18986
  }
18722
18987
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ivotoby/openapi-mcp-server",
3
- "version": "1.8.1",
3
+ "version": "1.8.3",
4
4
  "description": "An MCP server that exposes OpenAPI endpoints as resources",
5
5
  "license": "MIT",
6
6
  "type": "module",