@ivotoby/openapi-mcp-server 1.8.0 → 1.8.2

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 +264 -6
  2. package/dist/cli.js +264 -6
  3. package/package.json +1 -1
package/dist/bundle.js CHANGED
@@ -14989,6 +14989,19 @@ var ToolsManager = class {
14989
14989
  }
14990
14990
  tools = /* @__PURE__ */ new Map();
14991
14991
  specLoader;
14992
+ loadedSpec;
14993
+ /**
14994
+ * Get the OpenAPI spec loader instance
14995
+ */
14996
+ getSpecLoader() {
14997
+ return this.specLoader;
14998
+ }
14999
+ /**
15000
+ * Get the loaded OpenAPI specification
15001
+ */
15002
+ getOpenApiSpec() {
15003
+ return this.loadedSpec;
15004
+ }
14992
15005
  /**
14993
15006
  * Create dynamic discovery meta-tools
14994
15007
  */
@@ -15016,7 +15029,12 @@ var ToolsManager = class {
15016
15029
  inputSchema: {
15017
15030
  type: "object",
15018
15031
  properties: {
15019
- endpoint: { type: "string", description: "Endpoint path to invoke" },
15032
+ endpoint: { type: "string", description: "Endpoint path to invoke (e.g. /users/{id})" },
15033
+ method: {
15034
+ type: "string",
15035
+ description: "HTTP method to use (optional, e.g. GET, POST, PUT, DELETE)",
15036
+ enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]
15037
+ },
15020
15038
  params: {
15021
15039
  type: "object",
15022
15040
  description: "Parameters for the API call",
@@ -15037,6 +15055,7 @@ var ToolsManager = class {
15037
15055
  this.config.specInputMethod,
15038
15056
  this.config.inlineSpecContent
15039
15057
  );
15058
+ this.loadedSpec = spec;
15040
15059
  if (this.config.toolsMode === "dynamic") {
15041
15060
  this.tools = this.createDynamicTools();
15042
15061
  return;
@@ -18472,18 +18491,39 @@ var StaticAuthProvider = class {
18472
18491
  }
18473
18492
  };
18474
18493
 
18494
+ // src/utils/http-methods.ts
18495
+ var VALID_HTTP_METHODS = [
18496
+ "get",
18497
+ "post",
18498
+ "put",
18499
+ "patch",
18500
+ "delete",
18501
+ "options",
18502
+ "head"
18503
+ ];
18504
+ var GET_LIKE_METHODS = ["get", "delete", "head", "options"];
18505
+ function isValidHttpMethod(method) {
18506
+ return VALID_HTTP_METHODS.includes(method.toLowerCase());
18507
+ }
18508
+ function isGetLikeMethod(method) {
18509
+ return GET_LIKE_METHODS.includes(method.toLowerCase());
18510
+ }
18511
+
18475
18512
  // src/api-client.ts
18476
18513
  var ApiClient = class {
18477
18514
  axiosInstance;
18478
18515
  toolsMap = /* @__PURE__ */ new Map();
18479
18516
  authProvider;
18517
+ specLoader;
18518
+ openApiSpec;
18480
18519
  /**
18481
18520
  * Create a new API client
18482
18521
  *
18483
18522
  * @param baseUrl - Base URL for the API
18484
18523
  * @param authProviderOrHeaders - AuthProvider instance or static headers for backward compatibility
18524
+ * @param specLoader - Optional OpenAPI spec loader for dynamic meta-tools
18485
18525
  */
18486
- constructor(baseUrl, authProviderOrHeaders) {
18526
+ constructor(baseUrl, authProviderOrHeaders, specLoader) {
18487
18527
  this.axiosInstance = axios_default.create({
18488
18528
  baseURL: baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`
18489
18529
  });
@@ -18494,6 +18534,7 @@ var ApiClient = class {
18494
18534
  } else {
18495
18535
  this.authProvider = authProviderOrHeaders;
18496
18536
  }
18537
+ this.specLoader = specLoader;
18497
18538
  }
18498
18539
  /**
18499
18540
  * Set the available tools for the client
@@ -18503,6 +18544,14 @@ var ApiClient = class {
18503
18544
  setTools(tools) {
18504
18545
  this.toolsMap = tools;
18505
18546
  }
18547
+ /**
18548
+ * Set the OpenAPI specification for dynamic meta-tools
18549
+ *
18550
+ * @param spec - The OpenAPI specification document
18551
+ */
18552
+ setOpenApiSpec(spec) {
18553
+ this.openApiSpec = spec;
18554
+ }
18506
18555
  /**
18507
18556
  * Get a tool definition by ID
18508
18557
  *
@@ -18532,6 +18581,15 @@ var ApiClient = class {
18532
18581
  */
18533
18582
  async executeApiCallWithRetry(toolId, params, isRetry) {
18534
18583
  try {
18584
+ if (toolId === "LIST-API-ENDPOINTS") {
18585
+ return await this.handleListApiEndpoints();
18586
+ }
18587
+ if (toolId === "GET-API-ENDPOINT-SCHEMA") {
18588
+ return this.handleGetApiEndpointSchema(toolId, params);
18589
+ }
18590
+ if (toolId === "INVOKE-API-ENDPOINT") {
18591
+ return this.handleInvokeApiEndpoint(toolId, params);
18592
+ }
18535
18593
  const { method, path } = this.parseToolId(toolId);
18536
18594
  const toolDef = this.getToolDefinition(toolId);
18537
18595
  const paramsCopy = { ...params };
@@ -18581,7 +18639,7 @@ var ApiClient = class {
18581
18639
  url: resolvedPath,
18582
18640
  headers: authHeaders
18583
18641
  };
18584
- if (["get", "delete", "head", "options"].includes(method.toLowerCase())) {
18642
+ if (isGetLikeMethod(method)) {
18585
18643
  config.params = this.processQueryParams(paramsCopy);
18586
18644
  } else {
18587
18645
  config.data = paramsCopy;
@@ -18631,6 +18689,194 @@ var ApiClient = class {
18631
18689
  }
18632
18690
  return result;
18633
18691
  }
18692
+ /**
18693
+ * Handle the LIST-API-ENDPOINTS meta-tool
18694
+ * Returns a list of all available API endpoints from the loaded tools
18695
+ */
18696
+ async handleListApiEndpoints() {
18697
+ const endpoints = [];
18698
+ if (this.openApiSpec) {
18699
+ for (const [path, pathItem] of Object.entries(this.openApiSpec.paths)) {
18700
+ if (!pathItem) continue;
18701
+ for (const [method, operation] of Object.entries(pathItem)) {
18702
+ if (method === "parameters" || !operation) continue;
18703
+ if (!isValidHttpMethod(method)) {
18704
+ continue;
18705
+ }
18706
+ const op = operation;
18707
+ endpoints.push({
18708
+ method: method.toUpperCase(),
18709
+ path,
18710
+ summary: op.summary || "",
18711
+ description: op.description || "",
18712
+ operationId: op.operationId || "",
18713
+ tags: op.tags || []
18714
+ });
18715
+ }
18716
+ }
18717
+ } else {
18718
+ for (const [toolId, tool] of this.toolsMap.entries()) {
18719
+ if (toolId.startsWith("LIST-API-ENDPOINTS") || toolId.startsWith("GET-API-ENDPOINT-SCHEMA") || toolId.startsWith("INVOKE-API-ENDPOINT")) {
18720
+ continue;
18721
+ }
18722
+ try {
18723
+ const { method, path } = this.parseToolId(toolId);
18724
+ endpoints.push({
18725
+ toolId,
18726
+ name: tool.name,
18727
+ description: tool.description,
18728
+ method: method.toUpperCase(),
18729
+ path
18730
+ });
18731
+ } catch (error) {
18732
+ continue;
18733
+ }
18734
+ }
18735
+ }
18736
+ return {
18737
+ endpoints,
18738
+ total: endpoints.length,
18739
+ note: this.openApiSpec ? "Use INVOKE-API-ENDPOINT to call specific endpoints with the path parameter" : "Limited endpoint information - OpenAPI spec not available"
18740
+ };
18741
+ }
18742
+ /**
18743
+ * Handle the GET-API-ENDPOINT-SCHEMA meta-tool
18744
+ * Returns the JSON schema for a specified API endpoint
18745
+ */
18746
+ handleGetApiEndpointSchema(toolId, params) {
18747
+ const { endpoint } = params;
18748
+ if (!endpoint) {
18749
+ throw new Error(`Missing required parameter 'endpoint' for tool '${toolId}'`);
18750
+ }
18751
+ if (this.openApiSpec) {
18752
+ const pathItem = this.openApiSpec.paths[endpoint];
18753
+ if (!pathItem) {
18754
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18755
+ }
18756
+ const operations = [];
18757
+ for (const [method, operation] of Object.entries(pathItem)) {
18758
+ if (method === "parameters" || !operation) continue;
18759
+ if (!isValidHttpMethod(method)) {
18760
+ continue;
18761
+ }
18762
+ const op = operation;
18763
+ operations.push({
18764
+ method: method.toUpperCase(),
18765
+ operationId: op.operationId || "",
18766
+ summary: op.summary || "",
18767
+ description: op.description || "",
18768
+ parameters: op.parameters || [],
18769
+ requestBody: op.requestBody || null,
18770
+ responses: op.responses || {},
18771
+ tags: op.tags || []
18772
+ });
18773
+ }
18774
+ if (operations.length === 0) {
18775
+ throw new Error(`No valid HTTP operations found for path '${endpoint}' in tool '${toolId}'`);
18776
+ }
18777
+ return {
18778
+ path: endpoint,
18779
+ operations,
18780
+ pathParameters: pathItem.parameters || []
18781
+ };
18782
+ } else {
18783
+ let matchingTool;
18784
+ let matchingToolId;
18785
+ for (const [toolId2, tool] of this.toolsMap.entries()) {
18786
+ try {
18787
+ const { path } = this.parseToolId(toolId2);
18788
+ if (path === endpoint) {
18789
+ matchingTool = tool;
18790
+ matchingToolId = toolId2;
18791
+ break;
18792
+ }
18793
+ } catch (error) {
18794
+ continue;
18795
+ }
18796
+ }
18797
+ if (!matchingTool || !matchingToolId) {
18798
+ throw new Error(`No endpoint found for path: ${endpoint}`);
18799
+ }
18800
+ return {
18801
+ toolId: matchingToolId,
18802
+ name: matchingTool.name,
18803
+ description: matchingTool.description,
18804
+ inputSchema: matchingTool.inputSchema,
18805
+ note: "Limited schema information - using tool definition instead of OpenAPI spec"
18806
+ };
18807
+ }
18808
+ }
18809
+ /**
18810
+ * Handle the INVOKE-API-ENDPOINT meta-tool
18811
+ * Dynamically invokes an API endpoint with the provided parameters
18812
+ */
18813
+ async handleInvokeApiEndpoint(toolId, params) {
18814
+ const { endpoint, method, params: endpointParams = {} } = params;
18815
+ if (!endpoint) {
18816
+ throw new Error(`Missing required parameter 'endpoint' for tool '${toolId}'`);
18817
+ }
18818
+ if (method) {
18819
+ const toolId2 = generateToolId(method, endpoint);
18820
+ if (this.toolsMap.has(toolId2)) {
18821
+ return this.executeApiCall(toolId2, endpointParams);
18822
+ } else if (this.openApiSpec) {
18823
+ const pathItem = this.openApiSpec.paths[endpoint];
18824
+ if (pathItem && pathItem[method.toLowerCase()]) {
18825
+ const { method: httpMethod, path } = { method: method.toUpperCase(), path: endpoint };
18826
+ return this.makeDirectHttpRequest(httpMethod, path, endpointParams);
18827
+ } else {
18828
+ throw new Error(
18829
+ `No endpoint found for path '${endpoint}' with method '${method}' in tool '${toolId2}'`
18830
+ );
18831
+ }
18832
+ } else {
18833
+ throw new Error(`Tool not found: ${toolId2}`);
18834
+ }
18835
+ }
18836
+ if (this.openApiSpec) {
18837
+ const pathItem = this.openApiSpec.paths[endpoint];
18838
+ if (pathItem) {
18839
+ for (const method2 of VALID_HTTP_METHODS) {
18840
+ if (pathItem[method2]) {
18841
+ return this.makeDirectHttpRequest(method2.toUpperCase(), endpoint, endpointParams);
18842
+ }
18843
+ }
18844
+ throw new Error(`No HTTP operations found for endpoint '${endpoint}' in tool '${toolId}'`);
18845
+ } else {
18846
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18847
+ }
18848
+ }
18849
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18850
+ }
18851
+ /**
18852
+ * Make a direct HTTP request without going through the tool system
18853
+ * Used by dynamic meta-tools when we have OpenAPI spec but no corresponding tool
18854
+ */
18855
+ async makeDirectHttpRequest(method, path, params) {
18856
+ const authHeaders = await this.authProvider.getAuthHeaders();
18857
+ const config = {
18858
+ method: method.toLowerCase(),
18859
+ url: path,
18860
+ headers: authHeaders
18861
+ };
18862
+ if (isGetLikeMethod(method)) {
18863
+ config.params = this.processQueryParams(params);
18864
+ } else {
18865
+ config.data = params;
18866
+ }
18867
+ try {
18868
+ const response = await this.axiosInstance.request(config);
18869
+ return response.data;
18870
+ } catch (error) {
18871
+ if (axios_default.isAxiosError(error)) {
18872
+ const axiosError = error;
18873
+ throw new Error(
18874
+ `API request failed: ${axiosError.message}${axiosError.response ? ` (${axiosError.response.status}: ${typeof axiosError.response.data === "object" ? JSON.stringify(axiosError.response.data) : axiosError.response.data})` : ""}`
18875
+ );
18876
+ }
18877
+ throw error;
18878
+ }
18879
+ }
18634
18880
  };
18635
18881
 
18636
18882
  // src/server.ts
@@ -18652,7 +18898,11 @@ var OpenAPIServer = class {
18652
18898
  );
18653
18899
  this.toolsManager = new ToolsManager(config);
18654
18900
  const authProviderOrHeaders = config.authProvider || new StaticAuthProvider(config.headers);
18655
- this.apiClient = new ApiClient(config.apiBaseUrl, authProviderOrHeaders);
18901
+ this.apiClient = new ApiClient(
18902
+ config.apiBaseUrl,
18903
+ authProviderOrHeaders,
18904
+ this.toolsManager.getSpecLoader()
18905
+ );
18656
18906
  this.initializeHandlers();
18657
18907
  }
18658
18908
  /**
@@ -18717,6 +18967,10 @@ var OpenAPIServer = class {
18717
18967
  toolsMap.set(toolId, tool);
18718
18968
  }
18719
18969
  this.apiClient.setTools(toolsMap);
18970
+ const spec = this.toolsManager.getOpenApiSpec();
18971
+ if (spec) {
18972
+ this.apiClient.setOpenApiSpec(spec);
18973
+ }
18720
18974
  await this.server.connect(transport);
18721
18975
  }
18722
18976
  };
@@ -23575,8 +23829,12 @@ function parseHeaders(headerStr) {
23575
23829
  const headers = {};
23576
23830
  if (headerStr) {
23577
23831
  headerStr.split(",").forEach((header) => {
23578
- const [key, value] = header.split(":");
23579
- if (key && value) headers[key.trim()] = value.trim();
23832
+ const colonIndex = header.indexOf(":");
23833
+ if (colonIndex > 0) {
23834
+ const key = header.substring(0, colonIndex).trim();
23835
+ const value = header.substring(colonIndex + 1).trim();
23836
+ if (key) headers[key] = value;
23837
+ }
23580
23838
  });
23581
23839
  }
23582
23840
  return headers;
package/dist/cli.js CHANGED
@@ -14989,6 +14989,19 @@ var ToolsManager = class {
14989
14989
  }
14990
14990
  tools = /* @__PURE__ */ new Map();
14991
14991
  specLoader;
14992
+ loadedSpec;
14993
+ /**
14994
+ * Get the OpenAPI spec loader instance
14995
+ */
14996
+ getSpecLoader() {
14997
+ return this.specLoader;
14998
+ }
14999
+ /**
15000
+ * Get the loaded OpenAPI specification
15001
+ */
15002
+ getOpenApiSpec() {
15003
+ return this.loadedSpec;
15004
+ }
14992
15005
  /**
14993
15006
  * Create dynamic discovery meta-tools
14994
15007
  */
@@ -15016,7 +15029,12 @@ var ToolsManager = class {
15016
15029
  inputSchema: {
15017
15030
  type: "object",
15018
15031
  properties: {
15019
- endpoint: { type: "string", description: "Endpoint path to invoke" },
15032
+ endpoint: { type: "string", description: "Endpoint path to invoke (e.g. /users/{id})" },
15033
+ method: {
15034
+ type: "string",
15035
+ description: "HTTP method to use (optional, e.g. GET, POST, PUT, DELETE)",
15036
+ enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]
15037
+ },
15020
15038
  params: {
15021
15039
  type: "object",
15022
15040
  description: "Parameters for the API call",
@@ -15037,6 +15055,7 @@ var ToolsManager = class {
15037
15055
  this.config.specInputMethod,
15038
15056
  this.config.inlineSpecContent
15039
15057
  );
15058
+ this.loadedSpec = spec;
15040
15059
  if (this.config.toolsMode === "dynamic") {
15041
15060
  this.tools = this.createDynamicTools();
15042
15061
  return;
@@ -18472,18 +18491,39 @@ var StaticAuthProvider = class {
18472
18491
  }
18473
18492
  };
18474
18493
 
18494
+ // src/utils/http-methods.ts
18495
+ var VALID_HTTP_METHODS = [
18496
+ "get",
18497
+ "post",
18498
+ "put",
18499
+ "patch",
18500
+ "delete",
18501
+ "options",
18502
+ "head"
18503
+ ];
18504
+ var GET_LIKE_METHODS = ["get", "delete", "head", "options"];
18505
+ function isValidHttpMethod(method) {
18506
+ return VALID_HTTP_METHODS.includes(method.toLowerCase());
18507
+ }
18508
+ function isGetLikeMethod(method) {
18509
+ return GET_LIKE_METHODS.includes(method.toLowerCase());
18510
+ }
18511
+
18475
18512
  // src/api-client.ts
18476
18513
  var ApiClient = class {
18477
18514
  axiosInstance;
18478
18515
  toolsMap = /* @__PURE__ */ new Map();
18479
18516
  authProvider;
18517
+ specLoader;
18518
+ openApiSpec;
18480
18519
  /**
18481
18520
  * Create a new API client
18482
18521
  *
18483
18522
  * @param baseUrl - Base URL for the API
18484
18523
  * @param authProviderOrHeaders - AuthProvider instance or static headers for backward compatibility
18524
+ * @param specLoader - Optional OpenAPI spec loader for dynamic meta-tools
18485
18525
  */
18486
- constructor(baseUrl, authProviderOrHeaders) {
18526
+ constructor(baseUrl, authProviderOrHeaders, specLoader) {
18487
18527
  this.axiosInstance = axios_default.create({
18488
18528
  baseURL: baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`
18489
18529
  });
@@ -18494,6 +18534,7 @@ var ApiClient = class {
18494
18534
  } else {
18495
18535
  this.authProvider = authProviderOrHeaders;
18496
18536
  }
18537
+ this.specLoader = specLoader;
18497
18538
  }
18498
18539
  /**
18499
18540
  * Set the available tools for the client
@@ -18503,6 +18544,14 @@ var ApiClient = class {
18503
18544
  setTools(tools) {
18504
18545
  this.toolsMap = tools;
18505
18546
  }
18547
+ /**
18548
+ * Set the OpenAPI specification for dynamic meta-tools
18549
+ *
18550
+ * @param spec - The OpenAPI specification document
18551
+ */
18552
+ setOpenApiSpec(spec) {
18553
+ this.openApiSpec = spec;
18554
+ }
18506
18555
  /**
18507
18556
  * Get a tool definition by ID
18508
18557
  *
@@ -18532,6 +18581,15 @@ var ApiClient = class {
18532
18581
  */
18533
18582
  async executeApiCallWithRetry(toolId, params, isRetry) {
18534
18583
  try {
18584
+ if (toolId === "LIST-API-ENDPOINTS") {
18585
+ return await this.handleListApiEndpoints();
18586
+ }
18587
+ if (toolId === "GET-API-ENDPOINT-SCHEMA") {
18588
+ return this.handleGetApiEndpointSchema(toolId, params);
18589
+ }
18590
+ if (toolId === "INVOKE-API-ENDPOINT") {
18591
+ return this.handleInvokeApiEndpoint(toolId, params);
18592
+ }
18535
18593
  const { method, path } = this.parseToolId(toolId);
18536
18594
  const toolDef = this.getToolDefinition(toolId);
18537
18595
  const paramsCopy = { ...params };
@@ -18581,7 +18639,7 @@ var ApiClient = class {
18581
18639
  url: resolvedPath,
18582
18640
  headers: authHeaders
18583
18641
  };
18584
- if (["get", "delete", "head", "options"].includes(method.toLowerCase())) {
18642
+ if (isGetLikeMethod(method)) {
18585
18643
  config.params = this.processQueryParams(paramsCopy);
18586
18644
  } else {
18587
18645
  config.data = paramsCopy;
@@ -18631,6 +18689,194 @@ var ApiClient = class {
18631
18689
  }
18632
18690
  return result;
18633
18691
  }
18692
+ /**
18693
+ * Handle the LIST-API-ENDPOINTS meta-tool
18694
+ * Returns a list of all available API endpoints from the loaded tools
18695
+ */
18696
+ async handleListApiEndpoints() {
18697
+ const endpoints = [];
18698
+ if (this.openApiSpec) {
18699
+ for (const [path, pathItem] of Object.entries(this.openApiSpec.paths)) {
18700
+ if (!pathItem) continue;
18701
+ for (const [method, operation] of Object.entries(pathItem)) {
18702
+ if (method === "parameters" || !operation) continue;
18703
+ if (!isValidHttpMethod(method)) {
18704
+ continue;
18705
+ }
18706
+ const op = operation;
18707
+ endpoints.push({
18708
+ method: method.toUpperCase(),
18709
+ path,
18710
+ summary: op.summary || "",
18711
+ description: op.description || "",
18712
+ operationId: op.operationId || "",
18713
+ tags: op.tags || []
18714
+ });
18715
+ }
18716
+ }
18717
+ } else {
18718
+ for (const [toolId, tool] of this.toolsMap.entries()) {
18719
+ if (toolId.startsWith("LIST-API-ENDPOINTS") || toolId.startsWith("GET-API-ENDPOINT-SCHEMA") || toolId.startsWith("INVOKE-API-ENDPOINT")) {
18720
+ continue;
18721
+ }
18722
+ try {
18723
+ const { method, path } = this.parseToolId(toolId);
18724
+ endpoints.push({
18725
+ toolId,
18726
+ name: tool.name,
18727
+ description: tool.description,
18728
+ method: method.toUpperCase(),
18729
+ path
18730
+ });
18731
+ } catch (error) {
18732
+ continue;
18733
+ }
18734
+ }
18735
+ }
18736
+ return {
18737
+ endpoints,
18738
+ total: endpoints.length,
18739
+ note: this.openApiSpec ? "Use INVOKE-API-ENDPOINT to call specific endpoints with the path parameter" : "Limited endpoint information - OpenAPI spec not available"
18740
+ };
18741
+ }
18742
+ /**
18743
+ * Handle the GET-API-ENDPOINT-SCHEMA meta-tool
18744
+ * Returns the JSON schema for a specified API endpoint
18745
+ */
18746
+ handleGetApiEndpointSchema(toolId, params) {
18747
+ const { endpoint } = params;
18748
+ if (!endpoint) {
18749
+ throw new Error(`Missing required parameter 'endpoint' for tool '${toolId}'`);
18750
+ }
18751
+ if (this.openApiSpec) {
18752
+ const pathItem = this.openApiSpec.paths[endpoint];
18753
+ if (!pathItem) {
18754
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18755
+ }
18756
+ const operations = [];
18757
+ for (const [method, operation] of Object.entries(pathItem)) {
18758
+ if (method === "parameters" || !operation) continue;
18759
+ if (!isValidHttpMethod(method)) {
18760
+ continue;
18761
+ }
18762
+ const op = operation;
18763
+ operations.push({
18764
+ method: method.toUpperCase(),
18765
+ operationId: op.operationId || "",
18766
+ summary: op.summary || "",
18767
+ description: op.description || "",
18768
+ parameters: op.parameters || [],
18769
+ requestBody: op.requestBody || null,
18770
+ responses: op.responses || {},
18771
+ tags: op.tags || []
18772
+ });
18773
+ }
18774
+ if (operations.length === 0) {
18775
+ throw new Error(`No valid HTTP operations found for path '${endpoint}' in tool '${toolId}'`);
18776
+ }
18777
+ return {
18778
+ path: endpoint,
18779
+ operations,
18780
+ pathParameters: pathItem.parameters || []
18781
+ };
18782
+ } else {
18783
+ let matchingTool;
18784
+ let matchingToolId;
18785
+ for (const [toolId2, tool] of this.toolsMap.entries()) {
18786
+ try {
18787
+ const { path } = this.parseToolId(toolId2);
18788
+ if (path === endpoint) {
18789
+ matchingTool = tool;
18790
+ matchingToolId = toolId2;
18791
+ break;
18792
+ }
18793
+ } catch (error) {
18794
+ continue;
18795
+ }
18796
+ }
18797
+ if (!matchingTool || !matchingToolId) {
18798
+ throw new Error(`No endpoint found for path: ${endpoint}`);
18799
+ }
18800
+ return {
18801
+ toolId: matchingToolId,
18802
+ name: matchingTool.name,
18803
+ description: matchingTool.description,
18804
+ inputSchema: matchingTool.inputSchema,
18805
+ note: "Limited schema information - using tool definition instead of OpenAPI spec"
18806
+ };
18807
+ }
18808
+ }
18809
+ /**
18810
+ * Handle the INVOKE-API-ENDPOINT meta-tool
18811
+ * Dynamically invokes an API endpoint with the provided parameters
18812
+ */
18813
+ async handleInvokeApiEndpoint(toolId, params) {
18814
+ const { endpoint, method, params: endpointParams = {} } = params;
18815
+ if (!endpoint) {
18816
+ throw new Error(`Missing required parameter 'endpoint' for tool '${toolId}'`);
18817
+ }
18818
+ if (method) {
18819
+ const toolId2 = generateToolId(method, endpoint);
18820
+ if (this.toolsMap.has(toolId2)) {
18821
+ return this.executeApiCall(toolId2, endpointParams);
18822
+ } else if (this.openApiSpec) {
18823
+ const pathItem = this.openApiSpec.paths[endpoint];
18824
+ if (pathItem && pathItem[method.toLowerCase()]) {
18825
+ const { method: httpMethod, path } = { method: method.toUpperCase(), path: endpoint };
18826
+ return this.makeDirectHttpRequest(httpMethod, path, endpointParams);
18827
+ } else {
18828
+ throw new Error(
18829
+ `No endpoint found for path '${endpoint}' with method '${method}' in tool '${toolId2}'`
18830
+ );
18831
+ }
18832
+ } else {
18833
+ throw new Error(`Tool not found: ${toolId2}`);
18834
+ }
18835
+ }
18836
+ if (this.openApiSpec) {
18837
+ const pathItem = this.openApiSpec.paths[endpoint];
18838
+ if (pathItem) {
18839
+ for (const method2 of VALID_HTTP_METHODS) {
18840
+ if (pathItem[method2]) {
18841
+ return this.makeDirectHttpRequest(method2.toUpperCase(), endpoint, endpointParams);
18842
+ }
18843
+ }
18844
+ throw new Error(`No HTTP operations found for endpoint '${endpoint}' in tool '${toolId}'`);
18845
+ } else {
18846
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18847
+ }
18848
+ }
18849
+ throw new Error(`No endpoint found for path '${endpoint}' in tool '${toolId}'`);
18850
+ }
18851
+ /**
18852
+ * Make a direct HTTP request without going through the tool system
18853
+ * Used by dynamic meta-tools when we have OpenAPI spec but no corresponding tool
18854
+ */
18855
+ async makeDirectHttpRequest(method, path, params) {
18856
+ const authHeaders = await this.authProvider.getAuthHeaders();
18857
+ const config = {
18858
+ method: method.toLowerCase(),
18859
+ url: path,
18860
+ headers: authHeaders
18861
+ };
18862
+ if (isGetLikeMethod(method)) {
18863
+ config.params = this.processQueryParams(params);
18864
+ } else {
18865
+ config.data = params;
18866
+ }
18867
+ try {
18868
+ const response = await this.axiosInstance.request(config);
18869
+ return response.data;
18870
+ } catch (error) {
18871
+ if (axios_default.isAxiosError(error)) {
18872
+ const axiosError = error;
18873
+ throw new Error(
18874
+ `API request failed: ${axiosError.message}${axiosError.response ? ` (${axiosError.response.status}: ${typeof axiosError.response.data === "object" ? JSON.stringify(axiosError.response.data) : axiosError.response.data})` : ""}`
18875
+ );
18876
+ }
18877
+ throw error;
18878
+ }
18879
+ }
18634
18880
  };
18635
18881
 
18636
18882
  // src/server.ts
@@ -18652,7 +18898,11 @@ var OpenAPIServer = class {
18652
18898
  );
18653
18899
  this.toolsManager = new ToolsManager(config);
18654
18900
  const authProviderOrHeaders = config.authProvider || new StaticAuthProvider(config.headers);
18655
- this.apiClient = new ApiClient(config.apiBaseUrl, authProviderOrHeaders);
18901
+ this.apiClient = new ApiClient(
18902
+ config.apiBaseUrl,
18903
+ authProviderOrHeaders,
18904
+ this.toolsManager.getSpecLoader()
18905
+ );
18656
18906
  this.initializeHandlers();
18657
18907
  }
18658
18908
  /**
@@ -18717,6 +18967,10 @@ var OpenAPIServer = class {
18717
18967
  toolsMap.set(toolId, tool);
18718
18968
  }
18719
18969
  this.apiClient.setTools(toolsMap);
18970
+ const spec = this.toolsManager.getOpenApiSpec();
18971
+ if (spec) {
18972
+ this.apiClient.setOpenApiSpec(spec);
18973
+ }
18720
18974
  await this.server.connect(transport);
18721
18975
  }
18722
18976
  };
@@ -23575,8 +23829,12 @@ function parseHeaders(headerStr) {
23575
23829
  const headers = {};
23576
23830
  if (headerStr) {
23577
23831
  headerStr.split(",").forEach((header) => {
23578
- const [key, value] = header.split(":");
23579
- if (key && value) headers[key.trim()] = value.trim();
23832
+ const colonIndex = header.indexOf(":");
23833
+ if (colonIndex > 0) {
23834
+ const key = header.substring(0, colonIndex).trim();
23835
+ const value = header.substring(colonIndex + 1).trim();
23836
+ if (key) headers[key] = value;
23837
+ }
23580
23838
  });
23581
23839
  }
23582
23840
  return headers;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ivotoby/openapi-mcp-server",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "description": "An MCP server that exposes OpenAPI endpoints as resources",
5
5
  "license": "MIT",
6
6
  "type": "module",