fmea-api-mcp-server 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/index.js +56 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,5 +99,9 @@ When the package is published to NPM:
|
|
|
99
99
|
## Features
|
|
100
100
|
- **Resources**: Can read JSON files in the `endpoints` folder.
|
|
101
101
|
- **Tools**:
|
|
102
|
-
|
|
102
|
+
- **Tools**:
|
|
103
|
+
- `search_apis`:
|
|
104
|
+
- Smart search with relevance scoring (Summary > Description > Path).
|
|
105
|
+
- Supports filters: `query`, `method` (GET/POST), `version` (v1/v2).
|
|
106
|
+
- Results limited to top 10 by default to prevent context pollution.
|
|
103
107
|
- `get_api_details`: Get full details (schema, parameters) for a specific endpoint.
|
package/dist/index.js
CHANGED
|
@@ -104,7 +104,15 @@ class ApiDocsServer {
|
|
|
104
104
|
properties: {
|
|
105
105
|
query: {
|
|
106
106
|
type: "string",
|
|
107
|
-
description: "Search query (e.g. 'user login', '
|
|
107
|
+
description: "Search query (e.g. 'user login', 'create project').",
|
|
108
|
+
},
|
|
109
|
+
method: {
|
|
110
|
+
type: "string",
|
|
111
|
+
description: "Filter by HTTP method (e.g. 'GET', 'POST'). Optional.",
|
|
112
|
+
},
|
|
113
|
+
version: {
|
|
114
|
+
type: "string",
|
|
115
|
+
description: "Filter by API version (e.g. 'v1', 'v2'). Optional.",
|
|
108
116
|
},
|
|
109
117
|
},
|
|
110
118
|
required: ["query"],
|
|
@@ -134,7 +142,9 @@ class ApiDocsServer {
|
|
|
134
142
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
135
143
|
if (request.params.name === "search_apis") {
|
|
136
144
|
const query = String(request.params.arguments?.query).toLowerCase();
|
|
137
|
-
const
|
|
145
|
+
const method = request.params.arguments?.method ? String(request.params.arguments?.method).toUpperCase() : undefined;
|
|
146
|
+
const version = request.params.arguments?.version ? String(request.params.arguments?.version).toLowerCase() : undefined;
|
|
147
|
+
const results = await this.searchInFiles(query, method, version);
|
|
138
148
|
return {
|
|
139
149
|
content: [
|
|
140
150
|
{
|
|
@@ -195,20 +205,45 @@ class ApiDocsServer {
|
|
|
195
205
|
}
|
|
196
206
|
return results;
|
|
197
207
|
}
|
|
198
|
-
//
|
|
199
|
-
async searchInFiles(query) {
|
|
208
|
+
// Smart search helper with scoring, filtering, and limits
|
|
209
|
+
async searchInFiles(query, filterMethod, filterVersion) {
|
|
200
210
|
const files = await this.getAllFiles(ENDPOINTS_DIR);
|
|
201
|
-
|
|
211
|
+
let allMatches = [];
|
|
202
212
|
for (const filePath of files) {
|
|
203
213
|
try {
|
|
204
214
|
const content = await fs.readFile(filePath, "utf-8");
|
|
205
215
|
const json = JSON.parse(content);
|
|
206
216
|
const fileName = path.relative(ENDPOINTS_DIR, filePath);
|
|
217
|
+
// Version Filtering (File level or content level check)
|
|
218
|
+
// Check if file path contains version (e.g. "v1/...") or json has version field
|
|
219
|
+
if (filterVersion) {
|
|
220
|
+
const fileVersion = fileName.split(path.sep)[0]; // e.g. "v1" from "v1/projects/..."
|
|
221
|
+
if (fileVersion !== filterVersion && json.version !== filterVersion) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
207
225
|
if (json.endpoints && Array.isArray(json.endpoints)) {
|
|
208
226
|
for (const endpoint of json.endpoints) {
|
|
209
|
-
|
|
210
|
-
if (
|
|
211
|
-
|
|
227
|
+
// Method Filtering
|
|
228
|
+
if (filterMethod && endpoint.method.toUpperCase() !== filterMethod) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
// Scoring Logic
|
|
232
|
+
let score = 0;
|
|
233
|
+
const summary = (endpoint.summary || "").toLowerCase();
|
|
234
|
+
const description = (endpoint.description || "").toLowerCase();
|
|
235
|
+
const apiPath = (endpoint.path || "").toLowerCase();
|
|
236
|
+
const operationId = (endpoint.operationId || "").toLowerCase();
|
|
237
|
+
// 1. Exact/High relevance matches
|
|
238
|
+
if (summary.includes(query) || operationId.includes(query))
|
|
239
|
+
score += 10;
|
|
240
|
+
if (description.includes(query))
|
|
241
|
+
score += 5;
|
|
242
|
+
if (apiPath.includes(query))
|
|
243
|
+
score += 3;
|
|
244
|
+
if (score > 0) {
|
|
245
|
+
allMatches.push({
|
|
246
|
+
score,
|
|
212
247
|
file: fileName,
|
|
213
248
|
method: endpoint.method,
|
|
214
249
|
path: endpoint.path,
|
|
@@ -223,7 +258,19 @@ class ApiDocsServer {
|
|
|
223
258
|
// Ignore parse errors
|
|
224
259
|
}
|
|
225
260
|
}
|
|
226
|
-
|
|
261
|
+
// Sort by score descending
|
|
262
|
+
allMatches.sort((a, b) => b.score - a.score);
|
|
263
|
+
// Limit results
|
|
264
|
+
const LIMIT = 10;
|
|
265
|
+
const totalFound = allMatches.length;
|
|
266
|
+
const limitedResults = allMatches.slice(0, LIMIT).map(({ score, ...rest }) => rest); // Remove score from output
|
|
267
|
+
if (totalFound > LIMIT) {
|
|
268
|
+
return {
|
|
269
|
+
results: limitedResults,
|
|
270
|
+
warning: `Found ${totalFound} results. Showing top ${LIMIT}. Please refine your search query or use filters.`
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return limitedResults;
|
|
227
274
|
}
|
|
228
275
|
// Helper to get full details of an API
|
|
229
276
|
async getApiDetails(apiPath, method) {
|