@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.
- package/dist/bundle.js +273 -8
- package/dist/cli.js +273 -8
- 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
|
-
|
|
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, "
|
|
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(
|
|
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(
|
|
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 (
|
|
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(
|
|
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
|
-
|
|
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, "
|
|
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(
|
|
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(
|
|
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 (
|
|
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(
|
|
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
|
};
|