lhwapi-mcp-server 1.0.7 → 1.0.9
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 +92 -182
- package/dist/index.js +90 -140
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +104 -221
package/dist/bundle.js
CHANGED
|
@@ -30088,15 +30088,27 @@ var StdioServerTransport = class {
|
|
|
30088
30088
|
};
|
|
30089
30089
|
|
|
30090
30090
|
// src/index.ts
|
|
30091
|
+
console.error("LHW API MCP Server \u542F\u52A8\u4E2D...");
|
|
30091
30092
|
var APIFOX_BASE_URL = "https://api.apifox.com";
|
|
30092
30093
|
var args = process.argv.slice(2);
|
|
30093
30094
|
var projectId;
|
|
30095
|
+
var token;
|
|
30094
30096
|
for (let i = 0; i < args.length; i++) {
|
|
30095
30097
|
if (args[i] === "--project-id" && args[i + 1]) {
|
|
30096
30098
|
projectId = args[i + 1];
|
|
30099
|
+
} else if (args[i].startsWith("--project-id=")) {
|
|
30100
|
+
projectId = args[i].split("=")[1];
|
|
30101
|
+
}
|
|
30102
|
+
if (args[i] === "--token" && args[i + 1]) {
|
|
30103
|
+
token = args[i + 1];
|
|
30104
|
+
} else if (args[i].startsWith("--token=")) {
|
|
30105
|
+
token = args[i].split("=")[1];
|
|
30097
30106
|
}
|
|
30098
30107
|
}
|
|
30099
|
-
|
|
30108
|
+
token = token || process.env.APIFOX_ACCESS_TOKEN;
|
|
30109
|
+
projectId = projectId || process.env.APIFOX_PROJECT_ID;
|
|
30110
|
+
console.error("Project ID:", projectId || "\u672A\u8BBE\u7F6E");
|
|
30111
|
+
console.error("Token:", token ? "\u5DF2\u8BBE\u7F6E" : "\u672A\u8BBE\u7F6E");
|
|
30100
30112
|
async function fetchApifox(endpoint, authToken, options) {
|
|
30101
30113
|
const response = await fetch(`${APIFOX_BASE_URL}${endpoint}`, {
|
|
30102
30114
|
method: options?.method || "GET",
|
|
@@ -30119,241 +30131,139 @@ var server = new McpServer({
|
|
|
30119
30131
|
version: "1.0.0"
|
|
30120
30132
|
});
|
|
30121
30133
|
server.registerTool(
|
|
30122
|
-
"
|
|
30134
|
+
"export_openapi",
|
|
30123
30135
|
{
|
|
30124
|
-
description: "\
|
|
30136
|
+
description: "\u5BFC\u51FA\u9879\u76EE\u7684 OpenAPI (Swagger) \u683C\u5F0F\u6570\u636E\uFF0C\u652F\u6301 JSON \u6216 YAML \u683C\u5F0F",
|
|
30125
30137
|
inputSchema: external_exports3.object({
|
|
30126
|
-
|
|
30138
|
+
oasVersion: external_exports3.string().optional().describe("OpenAPI \u7248\u672C: 3.0, 3.1, 2.0\uFF0C\u9ED8\u8BA4 3.1"),
|
|
30139
|
+
exportFormat: external_exports3.string().optional().describe("\u5BFC\u51FA\u683C\u5F0F: JSON \u6216 YAML\uFF0C\u9ED8\u8BA4 JSON")
|
|
30127
30140
|
}).shape
|
|
30128
30141
|
},
|
|
30129
|
-
async ({
|
|
30130
|
-
|
|
30131
|
-
|
|
30142
|
+
async ({ oasVersion, exportFormat }) => {
|
|
30143
|
+
console.error("\u8C03\u7528 export_openapi \u5DE5\u5177");
|
|
30144
|
+
console.error("projectId:", projectId);
|
|
30145
|
+
if (!token) {
|
|
30132
30146
|
return {
|
|
30133
|
-
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF
|
|
30147
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF" }],
|
|
30134
30148
|
isError: true
|
|
30135
30149
|
};
|
|
30136
30150
|
}
|
|
30137
|
-
|
|
30138
|
-
const text = projects.map((p) => `\u9879\u76EE: ${p.name} (ID: ${p.id})
|
|
30139
|
-
\u63CF\u8FF0: ${p.description || "\u65E0"}`).join("\n\n");
|
|
30140
|
-
return {
|
|
30141
|
-
content: [{ type: "text", text: text || "\u6682\u65E0\u9879\u76EE" }]
|
|
30142
|
-
};
|
|
30143
|
-
}
|
|
30144
|
-
);
|
|
30145
|
-
server.registerTool(
|
|
30146
|
-
"listApis",
|
|
30147
|
-
{
|
|
30148
|
-
description: "\u83B7\u53D6\u6307\u5B9A\u9879\u76EE\u4E0B\u7684 API \u5217\u8868",
|
|
30149
|
-
inputSchema: external_exports3.object({
|
|
30150
|
-
projectId: external_exports3.string().optional().describe("\u9879\u76EE ID (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u547D\u4EE4\u884C\u53C2\u6570)"),
|
|
30151
|
-
token: external_exports3.string().optional().describe("Apifox \u8BBF\u95EE\u4EE4\u724C (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u73AF\u5883\u53D8\u91CF)")
|
|
30152
|
-
}).shape
|
|
30153
|
-
},
|
|
30154
|
-
async ({ projectId: inputProjectId, token: inputToken }) => {
|
|
30155
|
-
const authToken = inputToken || token;
|
|
30156
|
-
const authProjectId = inputProjectId || projectId;
|
|
30157
|
-
if (!authToken) {
|
|
30151
|
+
if (!projectId) {
|
|
30158
30152
|
return {
|
|
30159
|
-
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\
|
|
30153
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u901A\u8FC7 --project-id \u53C2\u6570\u6307\u5B9A\u9879\u76EE ID" }],
|
|
30160
30154
|
isError: true
|
|
30161
30155
|
};
|
|
30162
30156
|
}
|
|
30163
|
-
|
|
30157
|
+
try {
|
|
30158
|
+
const result = await fetchApifox(
|
|
30159
|
+
`/v1/projects/${projectId}/export-openapi`,
|
|
30160
|
+
token,
|
|
30161
|
+
{
|
|
30162
|
+
method: "POST",
|
|
30163
|
+
body: {
|
|
30164
|
+
scope: { type: "ALL" },
|
|
30165
|
+
oasVersion: oasVersion || "3.1",
|
|
30166
|
+
exportFormat: exportFormat || "JSON"
|
|
30167
|
+
}
|
|
30168
|
+
}
|
|
30169
|
+
);
|
|
30170
|
+
const format = exportFormat || "JSON";
|
|
30171
|
+
let output;
|
|
30172
|
+
if (format.toUpperCase() === "YAML") {
|
|
30173
|
+
output = result.openapi || "";
|
|
30174
|
+
} else {
|
|
30175
|
+
output = JSON.stringify(result, null, 2);
|
|
30176
|
+
}
|
|
30177
|
+
console.error("\u5BFC\u51FA\u6210\u529F");
|
|
30164
30178
|
return {
|
|
30165
|
-
content: [{ type: "text", text:
|
|
30179
|
+
content: [{ type: "text", text: output }]
|
|
30180
|
+
};
|
|
30181
|
+
} catch (error48) {
|
|
30182
|
+
console.error("\u5BFC\u51FA\u9519\u8BEF:", error48);
|
|
30183
|
+
return {
|
|
30184
|
+
content: [{ type: "text", text: `\u5BFC\u51FA\u5931\u8D25: ${error48 instanceof Error ? error48.message : String(error48)}` }],
|
|
30166
30185
|
isError: true
|
|
30167
30186
|
};
|
|
30168
30187
|
}
|
|
30169
|
-
const apis = await fetchApifox(
|
|
30170
|
-
`/v1/projects/${authProjectId}/apis`,
|
|
30171
|
-
authToken
|
|
30172
|
-
);
|
|
30173
|
-
const text = apis.map((api) => `[${api.method.toUpperCase()}] ${api.path} - ${api.name}`).join("\n");
|
|
30174
|
-
return {
|
|
30175
|
-
content: [{ type: "text", text: text || "\u6682\u65E0 API" }]
|
|
30176
|
-
};
|
|
30177
30188
|
}
|
|
30178
30189
|
);
|
|
30179
30190
|
server.registerTool(
|
|
30180
|
-
"
|
|
30191
|
+
"refresh_oas",
|
|
30181
30192
|
{
|
|
30182
|
-
description: "\
|
|
30183
|
-
inputSchema: external_exports3.object({
|
|
30184
|
-
apiId: external_exports3.string().describe("API ID"),
|
|
30185
|
-
projectId: external_exports3.string().optional().describe("\u9879\u76EE ID (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u547D\u4EE4\u884C\u53C2\u6570)"),
|
|
30186
|
-
token: external_exports3.string().optional().describe("Apifox \u8BBF\u95EE\u4EE4\u724C (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u73AF\u5883\u53D8\u91CF)")
|
|
30187
|
-
}).shape
|
|
30193
|
+
description: "\u5237\u65B0\u9879\u76EE\u7684 OpenAPI \u7F13\u5B58",
|
|
30194
|
+
inputSchema: external_exports3.object({}).shape
|
|
30188
30195
|
},
|
|
30189
|
-
async (
|
|
30190
|
-
|
|
30191
|
-
|
|
30192
|
-
if (!authToken) {
|
|
30196
|
+
async () => {
|
|
30197
|
+
console.error("\u8C03\u7528 refresh_oas \u5DE5\u5177");
|
|
30198
|
+
if (!token) {
|
|
30193
30199
|
return {
|
|
30194
|
-
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF
|
|
30200
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF" }],
|
|
30195
30201
|
isError: true
|
|
30196
30202
|
};
|
|
30197
30203
|
}
|
|
30198
|
-
if (!
|
|
30204
|
+
if (!projectId) {
|
|
30199
30205
|
return {
|
|
30200
30206
|
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u901A\u8FC7 --project-id \u53C2\u6570\u6307\u5B9A\u9879\u76EE ID" }],
|
|
30201
30207
|
isError: true
|
|
30202
30208
|
};
|
|
30203
30209
|
}
|
|
30204
|
-
|
|
30205
|
-
|
|
30206
|
-
|
|
30207
|
-
|
|
30208
|
-
|
|
30209
|
-
|
|
30210
|
-
|
|
30211
|
-
detail += `**\u65B9\u6CD5**: ${api.method.toUpperCase()}
|
|
30212
|
-
`;
|
|
30213
|
-
detail += `**\u8DEF\u5F84**: ${api.path}
|
|
30214
|
-
|
|
30215
|
-
`;
|
|
30216
|
-
if (api.description) {
|
|
30217
|
-
detail += `## \u63CF\u8FF0
|
|
30218
|
-
|
|
30219
|
-
${api.description}
|
|
30220
|
-
|
|
30221
|
-
`;
|
|
30222
|
-
}
|
|
30223
|
-
if (api.requestParams?.query?.length) {
|
|
30224
|
-
detail += `## Query \u53C2\u6570
|
|
30225
|
-
|
|
30226
|
-
`;
|
|
30227
|
-
api.requestParams.query.forEach((param) => {
|
|
30228
|
-
detail += `- ${param.name}: ${param.type || "string"} ${param.required ? "(\u5FC5\u586B)" : "(\u53EF\u9009)"}
|
|
30229
|
-
`;
|
|
30230
|
-
});
|
|
30231
|
-
detail += "\n";
|
|
30232
|
-
}
|
|
30233
|
-
if (api.requestBody?.schema) {
|
|
30234
|
-
detail += `## \u8BF7\u6C42\u4F53
|
|
30235
|
-
|
|
30236
|
-
\`\`\`json
|
|
30237
|
-
${JSON.stringify(api.requestBody.schema, null, 2)}
|
|
30238
|
-
\`\`\`
|
|
30239
|
-
|
|
30240
|
-
`;
|
|
30241
|
-
}
|
|
30242
|
-
if (api.responseList?.length) {
|
|
30243
|
-
detail += `## \u54CD\u5E94
|
|
30244
|
-
|
|
30245
|
-
`;
|
|
30246
|
-
api.responseList.forEach((resp) => {
|
|
30247
|
-
detail += `### ${resp.statusCode}
|
|
30248
|
-
`;
|
|
30249
|
-
if (resp.responseBodySchema) {
|
|
30250
|
-
detail += `\`\`\`json
|
|
30251
|
-
${JSON.stringify(resp.responseBodySchema, null, 2)}
|
|
30252
|
-
\`\`\`
|
|
30253
|
-
`;
|
|
30254
|
-
}
|
|
30255
|
-
});
|
|
30256
|
-
}
|
|
30257
|
-
return {
|
|
30258
|
-
content: [{ type: "text", text: detail }]
|
|
30259
|
-
};
|
|
30260
|
-
}
|
|
30261
|
-
);
|
|
30262
|
-
server.registerTool(
|
|
30263
|
-
"searchApis",
|
|
30264
|
-
{
|
|
30265
|
-
description: "\u641C\u7D22 API",
|
|
30266
|
-
inputSchema: external_exports3.object({
|
|
30267
|
-
keyword: external_exports3.string().describe("\u641C\u7D22\u5173\u952E\u8BCD"),
|
|
30268
|
-
projectId: external_exports3.string().optional().describe("\u9879\u76EE ID (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u547D\u4EE4\u884C\u53C2\u6570)"),
|
|
30269
|
-
token: external_exports3.string().optional().describe("Apifox \u8BBF\u95EE\u4EE4\u724C (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u73AF\u5883\u53D8\u91CF)")
|
|
30270
|
-
}).shape
|
|
30271
|
-
},
|
|
30272
|
-
async ({ keyword, projectId: inputProjectId, token: inputToken }) => {
|
|
30273
|
-
const authToken = inputToken || token;
|
|
30274
|
-
const authProjectId = inputProjectId || projectId;
|
|
30275
|
-
if (!authToken) {
|
|
30210
|
+
try {
|
|
30211
|
+
await fetchApifox(
|
|
30212
|
+
`/v1/projects/${projectId}/refresh-openapi-cache`,
|
|
30213
|
+
token,
|
|
30214
|
+
{ method: "POST" }
|
|
30215
|
+
);
|
|
30216
|
+
console.error("\u5237\u65B0\u6210\u529F");
|
|
30276
30217
|
return {
|
|
30277
|
-
content: [{ type: "text", text: "
|
|
30278
|
-
isError: true
|
|
30218
|
+
content: [{ type: "text", text: "OpenAPI \u7F13\u5B58\u5DF2\u5237\u65B0" }]
|
|
30279
30219
|
};
|
|
30280
|
-
}
|
|
30281
|
-
|
|
30220
|
+
} catch (error48) {
|
|
30221
|
+
console.error("\u5237\u65B0\u9519\u8BEF:", error48);
|
|
30282
30222
|
return {
|
|
30283
|
-
content: [{ type: "text", text:
|
|
30223
|
+
content: [{ type: "text", text: `\u5237\u65B0\u5931\u8D25: ${error48 instanceof Error ? error48.message : String(error48)}` }],
|
|
30284
30224
|
isError: true
|
|
30285
30225
|
};
|
|
30286
30226
|
}
|
|
30287
|
-
const apis = await fetchApifox(
|
|
30288
|
-
`/v1/projects/${authProjectId}/apis`,
|
|
30289
|
-
authToken
|
|
30290
|
-
);
|
|
30291
|
-
const filtered = apis.filter(
|
|
30292
|
-
(api) => api.name.toLowerCase().includes(keyword.toLowerCase()) || api.path.toLowerCase().includes(keyword.toLowerCase())
|
|
30293
|
-
);
|
|
30294
|
-
const text = filtered.map((api) => `[${api.method.toUpperCase()}] ${api.path} - ${api.name}`).join("\n");
|
|
30295
|
-
return {
|
|
30296
|
-
content: [{ type: "text", text: text || "\u672A\u627E\u5230\u5339\u914D\u7684 API" }]
|
|
30297
|
-
};
|
|
30298
30227
|
}
|
|
30299
30228
|
);
|
|
30300
30229
|
server.registerTool(
|
|
30301
|
-
"
|
|
30230
|
+
"list_apis",
|
|
30302
30231
|
{
|
|
30303
|
-
description: "\
|
|
30304
|
-
inputSchema: external_exports3.object({
|
|
30305
|
-
oasVersion: external_exports3.string().optional().describe("OpenAPI \u7248\u672C: 3.0, 3.1, 2.0"),
|
|
30306
|
-
exportFormat: external_exports3.string().optional().describe("\u5BFC\u51FA\u683C\u5F0F: JSON \u6216 YAML"),
|
|
30307
|
-
projectId: external_exports3.string().optional().describe("\u9879\u76EE ID (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u547D\u4EE4\u884C\u53C2\u6570)"),
|
|
30308
|
-
token: external_exports3.string().optional().describe("Apifox \u8BBF\u95EE\u4EE4\u724C (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u73AF\u5883\u53D8\u91CF)")
|
|
30309
|
-
}).shape
|
|
30232
|
+
description: "\u83B7\u53D6\u9879\u76EE\u4E0B\u7684 API \u63A5\u53E3\u5217\u8868",
|
|
30233
|
+
inputSchema: external_exports3.object({}).shape
|
|
30310
30234
|
},
|
|
30311
|
-
async (
|
|
30312
|
-
|
|
30313
|
-
|
|
30314
|
-
if (!authToken) {
|
|
30235
|
+
async () => {
|
|
30236
|
+
console.error("\u8C03\u7528 list_apis \u5DE5\u5177");
|
|
30237
|
+
if (!token) {
|
|
30315
30238
|
return {
|
|
30316
|
-
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF
|
|
30239
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF" }],
|
|
30317
30240
|
isError: true
|
|
30318
30241
|
};
|
|
30319
30242
|
}
|
|
30320
|
-
if (!
|
|
30243
|
+
if (!projectId) {
|
|
30321
30244
|
return {
|
|
30322
30245
|
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u901A\u8FC7 --project-id \u53C2\u6570\u6307\u5B9A\u9879\u76EE ID" }],
|
|
30323
30246
|
isError: true
|
|
30324
30247
|
};
|
|
30325
30248
|
}
|
|
30326
|
-
|
|
30327
|
-
`/v1/projects/${
|
|
30328
|
-
|
|
30329
|
-
|
|
30330
|
-
|
|
30331
|
-
|
|
30332
|
-
|
|
30333
|
-
|
|
30334
|
-
|
|
30335
|
-
|
|
30336
|
-
|
|
30337
|
-
|
|
30338
|
-
|
|
30339
|
-
let output;
|
|
30340
|
-
if (format.toUpperCase() === "YAML") {
|
|
30341
|
-
output = result.openapi || "";
|
|
30342
|
-
} else {
|
|
30343
|
-
output = JSON.stringify(result, null, 2);
|
|
30249
|
+
try {
|
|
30250
|
+
const apis = await fetchApifox(`/v1/projects/${projectId}/apis`, token);
|
|
30251
|
+
const text = apis.map((api) => `[${api.method?.toUpperCase() || "GET"}] ${api.path} - ${api.name}`).join("\n");
|
|
30252
|
+
console.error("\u83B7\u53D6 API \u5217\u8868\u6210\u529F\uFF0C\u6570\u91CF:", apis.length);
|
|
30253
|
+
return {
|
|
30254
|
+
content: [{ type: "text", text: text || "\u6682\u65E0 API" }]
|
|
30255
|
+
};
|
|
30256
|
+
} catch (error48) {
|
|
30257
|
+
console.error("\u83B7\u53D6 API \u5217\u8868\u9519\u8BEF:", error48);
|
|
30258
|
+
return {
|
|
30259
|
+
content: [{ type: "text", text: `\u83B7\u53D6\u5931\u8D25: ${error48 instanceof Error ? error48.message : String(error48)}` }],
|
|
30260
|
+
isError: true
|
|
30261
|
+
};
|
|
30344
30262
|
}
|
|
30345
|
-
return {
|
|
30346
|
-
content: [{ type: "text", text: output }]
|
|
30347
|
-
};
|
|
30348
30263
|
}
|
|
30349
30264
|
);
|
|
30350
30265
|
async function main() {
|
|
30351
|
-
|
|
30352
|
-
console.error("\u8B66\u544A: \u672A\u6307\u5B9A --project-id \u53C2\u6570");
|
|
30353
|
-
}
|
|
30354
|
-
if (!token) {
|
|
30355
|
-
console.error("\u8B66\u544A: \u672A\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF");
|
|
30356
|
-
}
|
|
30266
|
+
console.error("MCP Server \u521D\u59CB\u5316\u5B8C\u6210\uFF0C\u7B49\u5F85\u8FDE\u63A5...");
|
|
30357
30267
|
const transport = new StdioServerTransport();
|
|
30358
30268
|
await server.connect(transport);
|
|
30359
30269
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
console.error("LHW API MCP Server 启动中...");
|
|
4
5
|
const APIFOX_BASE_URL = "https://api.apifox.com";
|
|
5
6
|
const args = process.argv.slice(2);
|
|
6
7
|
let projectId;
|
|
8
|
+
let token;
|
|
7
9
|
for (let i = 0; i < args.length; i++) {
|
|
8
10
|
if (args[i] === "--project-id" && args[i + 1]) {
|
|
9
11
|
projectId = args[i + 1];
|
|
10
12
|
}
|
|
13
|
+
else if (args[i].startsWith("--project-id=")) {
|
|
14
|
+
projectId = args[i].split("=")[1];
|
|
15
|
+
}
|
|
16
|
+
if (args[i] === "--token" && args[i + 1]) {
|
|
17
|
+
token = args[i + 1];
|
|
18
|
+
}
|
|
19
|
+
else if (args[i].startsWith("--token=")) {
|
|
20
|
+
token = args[i].split("=")[1];
|
|
21
|
+
}
|
|
11
22
|
}
|
|
12
|
-
|
|
23
|
+
token = token || process.env.APIFOX_ACCESS_TOKEN;
|
|
24
|
+
projectId = projectId || process.env.APIFOX_PROJECT_ID;
|
|
25
|
+
console.error("Project ID:", projectId || "未设置");
|
|
26
|
+
console.error("Token:", token ? "已设置" : "未设置");
|
|
13
27
|
async function fetchApifox(endpoint, authToken, options) {
|
|
14
28
|
const response = await fetch(`${APIFOX_BASE_URL}${endpoint}`, {
|
|
15
29
|
method: options?.method || "GET",
|
|
@@ -31,190 +45,126 @@ const server = new McpServer({
|
|
|
31
45
|
name: "lhwapi-mcp-server",
|
|
32
46
|
version: "1.0.0",
|
|
33
47
|
});
|
|
34
|
-
server.registerTool("
|
|
35
|
-
description: "
|
|
48
|
+
server.registerTool("export_openapi", {
|
|
49
|
+
description: "导出项目的 OpenAPI (Swagger) 格式数据,支持 JSON 或 YAML 格式",
|
|
36
50
|
inputSchema: z.object({
|
|
37
|
-
|
|
51
|
+
oasVersion: z.string().optional().describe("OpenAPI 版本: 3.0, 3.1, 2.0,默认 3.1"),
|
|
52
|
+
exportFormat: z.string().optional().describe("导出格式: JSON 或 YAML,默认 JSON"),
|
|
38
53
|
}).shape,
|
|
39
|
-
}, async ({
|
|
40
|
-
|
|
41
|
-
|
|
54
|
+
}, async ({ oasVersion, exportFormat }) => {
|
|
55
|
+
console.error("调用 export_openapi 工具");
|
|
56
|
+
console.error("projectId:", projectId);
|
|
57
|
+
if (!token) {
|
|
42
58
|
return {
|
|
43
|
-
content: [{ type: "text", text: "错误: 请设置 APIFOX_ACCESS_TOKEN
|
|
59
|
+
content: [{ type: "text", text: "错误: 请设置 APIFOX_ACCESS_TOKEN 环境变量" }],
|
|
44
60
|
isError: true,
|
|
45
61
|
};
|
|
46
62
|
}
|
|
47
|
-
|
|
48
|
-
const text = projects
|
|
49
|
-
.map((p) => `项目: ${p.name} (ID: ${p.id})\n描述: ${p.description || "无"}`)
|
|
50
|
-
.join("\n\n");
|
|
51
|
-
return {
|
|
52
|
-
content: [{ type: "text", text: text || "暂无项目" }],
|
|
53
|
-
};
|
|
54
|
-
});
|
|
55
|
-
server.registerTool("listApis", {
|
|
56
|
-
description: "获取指定项目下的 API 列表",
|
|
57
|
-
inputSchema: z.object({
|
|
58
|
-
projectId: z.string().optional().describe("项目 ID (可选,默认使用命令行参数)"),
|
|
59
|
-
token: z.string().optional().describe("Apifox 访问令牌 (可选,默认使用环境变量)"),
|
|
60
|
-
}).shape,
|
|
61
|
-
}, async ({ projectId: inputProjectId, token: inputToken }) => {
|
|
62
|
-
const authToken = inputToken || token;
|
|
63
|
-
const authProjectId = inputProjectId || projectId;
|
|
64
|
-
if (!authToken) {
|
|
63
|
+
if (!projectId) {
|
|
65
64
|
return {
|
|
66
|
-
content: [{ type: "text", text: "错误:
|
|
65
|
+
content: [{ type: "text", text: "错误: 请通过 --project-id 参数指定项目 ID" }],
|
|
67
66
|
isError: true,
|
|
68
67
|
};
|
|
69
68
|
}
|
|
70
|
-
|
|
69
|
+
try {
|
|
70
|
+
const result = await fetchApifox(`/v1/projects/${projectId}/export-openapi`, token, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
body: {
|
|
73
|
+
scope: { type: "ALL" },
|
|
74
|
+
oasVersion: oasVersion || "3.1",
|
|
75
|
+
exportFormat: exportFormat || "JSON",
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
const format = exportFormat || "JSON";
|
|
79
|
+
let output;
|
|
80
|
+
if (format.toUpperCase() === "YAML") {
|
|
81
|
+
output = result.openapi || "";
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
output = JSON.stringify(result, null, 2);
|
|
85
|
+
}
|
|
86
|
+
console.error("导出成功");
|
|
71
87
|
return {
|
|
72
|
-
content: [{ type: "text", text:
|
|
88
|
+
content: [{ type: "text", text: output }],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
console.error("导出错误:", error);
|
|
93
|
+
return {
|
|
94
|
+
content: [{ type: "text", text: `导出失败: ${error instanceof Error ? error.message : String(error)}` }],
|
|
73
95
|
isError: true,
|
|
74
96
|
};
|
|
75
97
|
}
|
|
76
|
-
const apis = await fetchApifox(`/v1/projects/${authProjectId}/apis`, authToken);
|
|
77
|
-
const text = apis
|
|
78
|
-
.map((api) => `[${api.method.toUpperCase()}] ${api.path} - ${api.name}`)
|
|
79
|
-
.join("\n");
|
|
80
|
-
return {
|
|
81
|
-
content: [{ type: "text", text: text || "暂无 API" }],
|
|
82
|
-
};
|
|
83
98
|
});
|
|
84
|
-
server.registerTool("
|
|
85
|
-
description: "
|
|
86
|
-
inputSchema: z.object({
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}).shape,
|
|
91
|
-
}, async ({ apiId, projectId: inputProjectId, token: inputToken }) => {
|
|
92
|
-
const authToken = inputToken || token;
|
|
93
|
-
const authProjectId = inputProjectId || projectId;
|
|
94
|
-
if (!authToken) {
|
|
99
|
+
server.registerTool("refresh_oas", {
|
|
100
|
+
description: "刷新项目的 OpenAPI 缓存",
|
|
101
|
+
inputSchema: z.object({}).shape,
|
|
102
|
+
}, async () => {
|
|
103
|
+
console.error("调用 refresh_oas 工具");
|
|
104
|
+
if (!token) {
|
|
95
105
|
return {
|
|
96
|
-
content: [{ type: "text", text: "错误: 请设置 APIFOX_ACCESS_TOKEN
|
|
106
|
+
content: [{ type: "text", text: "错误: 请设置 APIFOX_ACCESS_TOKEN 环境变量" }],
|
|
97
107
|
isError: true,
|
|
98
108
|
};
|
|
99
109
|
}
|
|
100
|
-
if (!
|
|
110
|
+
if (!projectId) {
|
|
101
111
|
return {
|
|
102
112
|
content: [{ type: "text", text: "错误: 请通过 --project-id 参数指定项目 ID" }],
|
|
103
113
|
isError: true,
|
|
104
114
|
};
|
|
105
115
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
detail += `**路径**: ${api.path}\n\n`;
|
|
110
|
-
if (api.description) {
|
|
111
|
-
detail += `## 描述\n\n${api.description}\n\n`;
|
|
112
|
-
}
|
|
113
|
-
if (api.requestParams?.query?.length) {
|
|
114
|
-
detail += `## Query 参数\n\n`;
|
|
115
|
-
api.requestParams.query.forEach((param) => {
|
|
116
|
-
detail += `- ${param.name}: ${param.type || "string"} ${param.required ? "(必填)" : "(可选)"}\n`;
|
|
117
|
-
});
|
|
118
|
-
detail += "\n";
|
|
119
|
-
}
|
|
120
|
-
if (api.requestBody?.schema) {
|
|
121
|
-
detail += `## 请求体\n\n\`\`\`json\n${JSON.stringify(api.requestBody.schema, null, 2)}\n\`\`\`\n\n`;
|
|
122
|
-
}
|
|
123
|
-
if (api.responseList?.length) {
|
|
124
|
-
detail += `## 响应\n\n`;
|
|
125
|
-
api.responseList.forEach((resp) => {
|
|
126
|
-
detail += `### ${resp.statusCode}\n`;
|
|
127
|
-
if (resp.responseBodySchema) {
|
|
128
|
-
detail += `\`\`\`json\n${JSON.stringify(resp.responseBodySchema, null, 2)}\n\`\`\`\n`;
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
return {
|
|
133
|
-
content: [{ type: "text", text: detail }],
|
|
134
|
-
};
|
|
135
|
-
});
|
|
136
|
-
server.registerTool("searchApis", {
|
|
137
|
-
description: "搜索 API",
|
|
138
|
-
inputSchema: z.object({
|
|
139
|
-
keyword: z.string().describe("搜索关键词"),
|
|
140
|
-
projectId: z.string().optional().describe("项目 ID (可选,默认使用命令行参数)"),
|
|
141
|
-
token: z.string().optional().describe("Apifox 访问令牌 (可选,默认使用环境变量)"),
|
|
142
|
-
}).shape,
|
|
143
|
-
}, async ({ keyword, projectId: inputProjectId, token: inputToken }) => {
|
|
144
|
-
const authToken = inputToken || token;
|
|
145
|
-
const authProjectId = inputProjectId || projectId;
|
|
146
|
-
if (!authToken) {
|
|
116
|
+
try {
|
|
117
|
+
await fetchApifox(`/v1/projects/${projectId}/refresh-openapi-cache`, token, { method: "POST" });
|
|
118
|
+
console.error("刷新成功");
|
|
147
119
|
return {
|
|
148
|
-
content: [{ type: "text", text: "
|
|
149
|
-
isError: true,
|
|
120
|
+
content: [{ type: "text", text: "OpenAPI 缓存已刷新" }],
|
|
150
121
|
};
|
|
151
122
|
}
|
|
152
|
-
|
|
123
|
+
catch (error) {
|
|
124
|
+
console.error("刷新错误:", error);
|
|
153
125
|
return {
|
|
154
|
-
content: [{ type: "text", text:
|
|
126
|
+
content: [{ type: "text", text: `刷新失败: ${error instanceof Error ? error.message : String(error)}` }],
|
|
155
127
|
isError: true,
|
|
156
128
|
};
|
|
157
129
|
}
|
|
158
|
-
const apis = await fetchApifox(`/v1/projects/${authProjectId}/apis`, authToken);
|
|
159
|
-
const filtered = apis.filter((api) => api.name.toLowerCase().includes(keyword.toLowerCase()) ||
|
|
160
|
-
api.path.toLowerCase().includes(keyword.toLowerCase()));
|
|
161
|
-
const text = filtered
|
|
162
|
-
.map((api) => `[${api.method.toUpperCase()}] ${api.path} - ${api.name}`)
|
|
163
|
-
.join("\n");
|
|
164
|
-
return {
|
|
165
|
-
content: [{ type: "text", text: text || "未找到匹配的 API" }],
|
|
166
|
-
};
|
|
167
130
|
});
|
|
168
|
-
server.registerTool("
|
|
169
|
-
description: "
|
|
170
|
-
inputSchema: z.object({
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
token: z.string().optional().describe("Apifox 访问令牌 (可选,默认使用环境变量)"),
|
|
175
|
-
}).shape,
|
|
176
|
-
}, async ({ oasVersion, exportFormat, projectId: inputProjectId, token: inputToken }) => {
|
|
177
|
-
const authToken = inputToken || token;
|
|
178
|
-
const authProjectId = inputProjectId || projectId;
|
|
179
|
-
if (!authToken) {
|
|
131
|
+
server.registerTool("list_apis", {
|
|
132
|
+
description: "获取项目下的 API 接口列表",
|
|
133
|
+
inputSchema: z.object({}).shape,
|
|
134
|
+
}, async () => {
|
|
135
|
+
console.error("调用 list_apis 工具");
|
|
136
|
+
if (!token) {
|
|
180
137
|
return {
|
|
181
|
-
content: [{ type: "text", text: "错误: 请设置 APIFOX_ACCESS_TOKEN
|
|
138
|
+
content: [{ type: "text", text: "错误: 请设置 APIFOX_ACCESS_TOKEN 环境变量" }],
|
|
182
139
|
isError: true,
|
|
183
140
|
};
|
|
184
141
|
}
|
|
185
|
-
if (!
|
|
142
|
+
if (!projectId) {
|
|
186
143
|
return {
|
|
187
144
|
content: [{ type: "text", text: "错误: 请通过 --project-id 参数指定项目 ID" }],
|
|
188
145
|
isError: true,
|
|
189
146
|
};
|
|
190
147
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
let output;
|
|
201
|
-
if (format.toUpperCase() === "YAML") {
|
|
202
|
-
output = result.openapi || "";
|
|
148
|
+
try {
|
|
149
|
+
const apis = await fetchApifox(`/v1/projects/${projectId}/apis`, token);
|
|
150
|
+
const text = apis
|
|
151
|
+
.map((api) => `[${api.method?.toUpperCase() || 'GET'}] ${api.path} - ${api.name}`)
|
|
152
|
+
.join("\n");
|
|
153
|
+
console.error("获取 API 列表成功,数量:", apis.length);
|
|
154
|
+
return {
|
|
155
|
+
content: [{ type: "text", text: text || "暂无 API" }],
|
|
156
|
+
};
|
|
203
157
|
}
|
|
204
|
-
|
|
205
|
-
|
|
158
|
+
catch (error) {
|
|
159
|
+
console.error("获取 API 列表错误:", error);
|
|
160
|
+
return {
|
|
161
|
+
content: [{ type: "text", text: `获取失败: ${error instanceof Error ? error.message : String(error)}` }],
|
|
162
|
+
isError: true,
|
|
163
|
+
};
|
|
206
164
|
}
|
|
207
|
-
return {
|
|
208
|
-
content: [{ type: "text", text: output }],
|
|
209
|
-
};
|
|
210
165
|
});
|
|
211
166
|
async function main() {
|
|
212
|
-
|
|
213
|
-
console.error("警告: 未指定 --project-id 参数");
|
|
214
|
-
}
|
|
215
|
-
if (!token) {
|
|
216
|
-
console.error("警告: 未设置 APIFOX_ACCESS_TOKEN 环境变量");
|
|
217
|
-
}
|
|
167
|
+
console.error("MCP Server 初始化完成,等待连接...");
|
|
218
168
|
const transport = new StdioServerTransport();
|
|
219
169
|
await server.connect(transport);
|
|
220
170
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,eAAe,GAAG,wBAAwB,CAAC;AAEjD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,IAAI,SAA6B,CAAC;AAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;IACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,cAAc,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC9C,SAAS,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,48 +2,32 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
|
+
console.error("LHW API MCP Server 启动中...");
|
|
6
|
+
|
|
5
7
|
const APIFOX_BASE_URL = "https://api.apifox.com";
|
|
6
8
|
|
|
7
9
|
const args = process.argv.slice(2);
|
|
8
10
|
let projectId: string | undefined;
|
|
11
|
+
let token: string | undefined;
|
|
12
|
+
|
|
9
13
|
for (let i = 0; i < args.length; i++) {
|
|
10
14
|
if (args[i] === "--project-id" && args[i + 1]) {
|
|
11
15
|
projectId = args[i + 1];
|
|
16
|
+
} else if (args[i].startsWith("--project-id=")) {
|
|
17
|
+
projectId = args[i].split("=")[1];
|
|
18
|
+
}
|
|
19
|
+
if (args[i] === "--token" && args[i + 1]) {
|
|
20
|
+
token = args[i + 1];
|
|
21
|
+
} else if (args[i].startsWith("--token=")) {
|
|
22
|
+
token = args[i].split("=")[1];
|
|
12
23
|
}
|
|
13
24
|
}
|
|
14
25
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
interface ApifoxResponse<T> {
|
|
18
|
-
data: T;
|
|
19
|
-
message?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface Project {
|
|
23
|
-
id: string;
|
|
24
|
-
name: string;
|
|
25
|
-
description?: string;
|
|
26
|
-
}
|
|
26
|
+
token = token || process.env.APIFOX_ACCESS_TOKEN;
|
|
27
|
+
projectId = projectId || process.env.APIFOX_PROJECT_ID;
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
name: string;
|
|
31
|
-
path: string;
|
|
32
|
-
method: string;
|
|
33
|
-
description?: string;
|
|
34
|
-
requestParams?: {
|
|
35
|
-
query?: any[];
|
|
36
|
-
path?: any[];
|
|
37
|
-
header?: any[];
|
|
38
|
-
};
|
|
39
|
-
requestBody?: {
|
|
40
|
-
schema?: any;
|
|
41
|
-
};
|
|
42
|
-
responseList?: {
|
|
43
|
-
statusCode: number;
|
|
44
|
-
responseBodySchema?: any;
|
|
45
|
-
}[];
|
|
46
|
-
}
|
|
29
|
+
console.error("Project ID:", projectId || "未设置");
|
|
30
|
+
console.error("Token:", token ? "已设置" : "未设置");
|
|
47
31
|
|
|
48
32
|
async function fetchApifox<T>(endpoint: string, authToken: string, options?: {
|
|
49
33
|
method?: string;
|
|
@@ -64,7 +48,7 @@ async function fetchApifox<T>(endpoint: string, authToken: string, options?: {
|
|
|
64
48
|
throw new Error(`Apifox API error: ${response.status} - ${error}`);
|
|
65
49
|
}
|
|
66
50
|
|
|
67
|
-
const data = await response.json() as
|
|
51
|
+
const data = await response.json() as { data: T };
|
|
68
52
|
return data.data;
|
|
69
53
|
}
|
|
70
54
|
|
|
@@ -74,256 +58,155 @@ const server = new McpServer({
|
|
|
74
58
|
});
|
|
75
59
|
|
|
76
60
|
server.registerTool(
|
|
77
|
-
"
|
|
61
|
+
"export_openapi",
|
|
78
62
|
{
|
|
79
|
-
description: "
|
|
63
|
+
description: "导出项目的 OpenAPI (Swagger) 格式数据,支持 JSON 或 YAML 格式",
|
|
80
64
|
inputSchema: z.object({
|
|
81
|
-
|
|
65
|
+
oasVersion: z.string().optional().describe("OpenAPI 版本: 3.0, 3.1, 2.0,默认 3.1"),
|
|
66
|
+
exportFormat: z.string().optional().describe("导出格式: JSON 或 YAML,默认 JSON"),
|
|
82
67
|
}).shape,
|
|
83
68
|
},
|
|
84
|
-
async ({
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
const projects = await fetchApifox<Project[]>("/v1/projects", authToken);
|
|
93
|
-
const text = projects
|
|
94
|
-
.map((p) => `项目: ${p.name} (ID: ${p.id})\n描述: ${p.description || "无"}`)
|
|
95
|
-
.join("\n\n");
|
|
96
|
-
return {
|
|
97
|
-
content: [{ type: "text" as const, text: text || "暂无项目" }],
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
server.registerTool(
|
|
103
|
-
"listApis",
|
|
104
|
-
{
|
|
105
|
-
description: "获取指定项目下的 API 列表",
|
|
106
|
-
inputSchema: z.object({
|
|
107
|
-
projectId: z.string().optional().describe("项目 ID (可选,默认使用命令行参数)"),
|
|
108
|
-
token: z.string().optional().describe("Apifox 访问令牌 (可选,默认使用环境变量)"),
|
|
109
|
-
}).shape,
|
|
110
|
-
},
|
|
111
|
-
async ({ projectId: inputProjectId, token: inputToken }: { projectId?: string; token?: string }) => {
|
|
112
|
-
const authToken = inputToken || token;
|
|
113
|
-
const authProjectId = inputProjectId || projectId;
|
|
69
|
+
async ({ oasVersion, exportFormat }: {
|
|
70
|
+
oasVersion?: string;
|
|
71
|
+
exportFormat?: string;
|
|
72
|
+
}) => {
|
|
73
|
+
console.error("调用 export_openapi 工具");
|
|
74
|
+
console.error("projectId:", projectId);
|
|
114
75
|
|
|
115
|
-
if (!
|
|
76
|
+
if (!token) {
|
|
116
77
|
return {
|
|
117
|
-
content: [{ type: "text" as const, text: "错误: 请设置 APIFOX_ACCESS_TOKEN
|
|
78
|
+
content: [{ type: "text" as const, text: "错误: 请设置 APIFOX_ACCESS_TOKEN 环境变量" }],
|
|
118
79
|
isError: true,
|
|
119
80
|
};
|
|
120
81
|
}
|
|
121
|
-
if (!
|
|
82
|
+
if (!projectId) {
|
|
122
83
|
return {
|
|
123
84
|
content: [{ type: "text" as const, text: "错误: 请通过 --project-id 参数指定项目 ID" }],
|
|
124
85
|
isError: true,
|
|
125
86
|
};
|
|
126
87
|
}
|
|
127
88
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
89
|
+
try {
|
|
90
|
+
const result = await fetchApifox<any>(
|
|
91
|
+
`/v1/projects/${projectId}/export-openapi`,
|
|
92
|
+
token,
|
|
93
|
+
{
|
|
94
|
+
method: "POST",
|
|
95
|
+
body: {
|
|
96
|
+
scope: { type: "ALL" },
|
|
97
|
+
oasVersion: oasVersion || "3.1",
|
|
98
|
+
exportFormat: exportFormat || "JSON",
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const format = exportFormat || "JSON";
|
|
104
|
+
let output: string;
|
|
105
|
+
if (format.toUpperCase() === "YAML") {
|
|
106
|
+
output = result.openapi || "";
|
|
107
|
+
} else {
|
|
108
|
+
output = JSON.stringify(result, null, 2);
|
|
109
|
+
}
|
|
140
110
|
|
|
141
|
-
|
|
142
|
-
"getApiDetail",
|
|
143
|
-
{
|
|
144
|
-
description: "获取单个 API 的详细信息",
|
|
145
|
-
inputSchema: z.object({
|
|
146
|
-
apiId: z.string().describe("API ID"),
|
|
147
|
-
projectId: z.string().optional().describe("项目 ID (可选,默认使用命令行参数)"),
|
|
148
|
-
token: z.string().optional().describe("Apifox 访问令牌 (可选,默认使用环境变量)"),
|
|
149
|
-
}).shape,
|
|
150
|
-
},
|
|
151
|
-
async ({ apiId, projectId: inputProjectId, token: inputToken }: { apiId: string; projectId?: string; token?: string }) => {
|
|
152
|
-
const authToken = inputToken || token;
|
|
153
|
-
const authProjectId = inputProjectId || projectId;
|
|
154
|
-
|
|
155
|
-
if (!authToken) {
|
|
111
|
+
console.error("导出成功");
|
|
156
112
|
return {
|
|
157
|
-
content: [{ type: "text" as const, text:
|
|
158
|
-
isError: true,
|
|
113
|
+
content: [{ type: "text" as const, text: output }],
|
|
159
114
|
};
|
|
160
|
-
}
|
|
161
|
-
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error("导出错误:", error);
|
|
162
117
|
return {
|
|
163
|
-
content: [{ type: "text" as const, text:
|
|
118
|
+
content: [{ type: "text" as const, text: `导出失败: ${error instanceof Error ? error.message : String(error)}` }],
|
|
164
119
|
isError: true,
|
|
165
120
|
};
|
|
166
121
|
}
|
|
167
|
-
|
|
168
|
-
const api = await fetchApifox<ApiDetail>(
|
|
169
|
-
`/v1/projects/${authProjectId}/apis/${apiId}`,
|
|
170
|
-
authToken
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
let detail = `# ${api.name}\n\n`;
|
|
174
|
-
detail += `**方法**: ${api.method.toUpperCase()}\n`;
|
|
175
|
-
detail += `**路径**: ${api.path}\n\n`;
|
|
176
|
-
|
|
177
|
-
if (api.description) {
|
|
178
|
-
detail += `## 描述\n\n${api.description}\n\n`;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (api.requestParams?.query?.length) {
|
|
182
|
-
detail += `## Query 参数\n\n`;
|
|
183
|
-
api.requestParams.query.forEach((param: any) => {
|
|
184
|
-
detail += `- ${param.name}: ${param.type || "string"} ${
|
|
185
|
-
param.required ? "(必填)" : "(可选)"
|
|
186
|
-
}\n`;
|
|
187
|
-
});
|
|
188
|
-
detail += "\n";
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (api.requestBody?.schema) {
|
|
192
|
-
detail += `## 请求体\n\n\`\`\`json\n${JSON.stringify(api.requestBody.schema, null, 2)}\n\`\`\`\n\n`;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (api.responseList?.length) {
|
|
196
|
-
detail += `## 响应\n\n`;
|
|
197
|
-
api.responseList.forEach((resp) => {
|
|
198
|
-
detail += `### ${resp.statusCode}\n`;
|
|
199
|
-
if (resp.responseBodySchema) {
|
|
200
|
-
detail += `\`\`\`json\n${JSON.stringify(resp.responseBodySchema, null, 2)}\n\`\`\`\n`;
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
content: [{ type: "text" as const, text: detail }],
|
|
207
|
-
};
|
|
208
122
|
}
|
|
209
123
|
);
|
|
210
124
|
|
|
211
125
|
server.registerTool(
|
|
212
|
-
"
|
|
126
|
+
"refresh_oas",
|
|
213
127
|
{
|
|
214
|
-
description: "
|
|
215
|
-
inputSchema: z.object({
|
|
216
|
-
keyword: z.string().describe("搜索关键词"),
|
|
217
|
-
projectId: z.string().optional().describe("项目 ID (可选,默认使用命令行参数)"),
|
|
218
|
-
token: z.string().optional().describe("Apifox 访问令牌 (可选,默认使用环境变量)"),
|
|
219
|
-
}).shape,
|
|
128
|
+
description: "刷新项目的 OpenAPI 缓存",
|
|
129
|
+
inputSchema: z.object({}).shape,
|
|
220
130
|
},
|
|
221
|
-
async (
|
|
222
|
-
|
|
223
|
-
const authProjectId = inputProjectId || projectId;
|
|
131
|
+
async () => {
|
|
132
|
+
console.error("调用 refresh_oas 工具");
|
|
224
133
|
|
|
225
|
-
if (!
|
|
134
|
+
if (!token) {
|
|
226
135
|
return {
|
|
227
|
-
content: [{ type: "text" as const, text: "错误: 请设置 APIFOX_ACCESS_TOKEN
|
|
136
|
+
content: [{ type: "text" as const, text: "错误: 请设置 APIFOX_ACCESS_TOKEN 环境变量" }],
|
|
228
137
|
isError: true,
|
|
229
138
|
};
|
|
230
139
|
}
|
|
231
|
-
if (!
|
|
140
|
+
if (!projectId) {
|
|
232
141
|
return {
|
|
233
142
|
content: [{ type: "text" as const, text: "错误: 请通过 --project-id 参数指定项目 ID" }],
|
|
234
143
|
isError: true,
|
|
235
144
|
};
|
|
236
145
|
}
|
|
237
146
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
};
|
|
147
|
+
try {
|
|
148
|
+
await fetchApifox<any>(
|
|
149
|
+
`/v1/projects/${projectId}/refresh-openapi-cache`,
|
|
150
|
+
token,
|
|
151
|
+
{ method: "POST" }
|
|
152
|
+
);
|
|
153
|
+
console.error("刷新成功");
|
|
154
|
+
return {
|
|
155
|
+
content: [{ type: "text" as const, text: "OpenAPI 缓存已刷新" }],
|
|
156
|
+
};
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error("刷新错误:", error);
|
|
159
|
+
return {
|
|
160
|
+
content: [{ type: "text" as const, text: `刷新失败: ${error instanceof Error ? error.message : String(error)}` }],
|
|
161
|
+
isError: true,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
256
164
|
}
|
|
257
165
|
);
|
|
258
166
|
|
|
259
167
|
server.registerTool(
|
|
260
|
-
"
|
|
168
|
+
"list_apis",
|
|
261
169
|
{
|
|
262
|
-
description: "
|
|
263
|
-
inputSchema: z.object({
|
|
264
|
-
oasVersion: z.string().optional().describe("OpenAPI 版本: 3.0, 3.1, 2.0"),
|
|
265
|
-
exportFormat: z.string().optional().describe("导出格式: JSON 或 YAML"),
|
|
266
|
-
projectId: z.string().optional().describe("项目 ID (可选,默认使用命令行参数)"),
|
|
267
|
-
token: z.string().optional().describe("Apifox 访问令牌 (可选,默认使用环境变量)"),
|
|
268
|
-
}).shape,
|
|
170
|
+
description: "获取项目下的 API 接口列表",
|
|
171
|
+
inputSchema: z.object({}).shape,
|
|
269
172
|
},
|
|
270
|
-
async (
|
|
271
|
-
|
|
272
|
-
exportFormat?: string;
|
|
273
|
-
projectId?: string;
|
|
274
|
-
token?: string
|
|
275
|
-
}) => {
|
|
276
|
-
const authToken = inputToken || token;
|
|
277
|
-
const authProjectId = inputProjectId || projectId;
|
|
173
|
+
async () => {
|
|
174
|
+
console.error("调用 list_apis 工具");
|
|
278
175
|
|
|
279
|
-
if (!
|
|
176
|
+
if (!token) {
|
|
280
177
|
return {
|
|
281
|
-
content: [{ type: "text" as const, text: "错误: 请设置 APIFOX_ACCESS_TOKEN
|
|
178
|
+
content: [{ type: "text" as const, text: "错误: 请设置 APIFOX_ACCESS_TOKEN 环境变量" }],
|
|
282
179
|
isError: true,
|
|
283
180
|
};
|
|
284
181
|
}
|
|
285
|
-
if (!
|
|
182
|
+
if (!projectId) {
|
|
286
183
|
return {
|
|
287
184
|
content: [{ type: "text" as const, text: "错误: 请通过 --project-id 参数指定项目 ID" }],
|
|
288
185
|
isError: true,
|
|
289
186
|
};
|
|
290
187
|
}
|
|
291
188
|
|
|
292
|
-
|
|
293
|
-
`/v1/projects/${
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (format.toUpperCase() === "YAML") {
|
|
308
|
-
output = result.openapi || "";
|
|
309
|
-
} else {
|
|
310
|
-
output = JSON.stringify(result, null, 2);
|
|
189
|
+
try {
|
|
190
|
+
const apis = await fetchApifox<any[]>(`/v1/projects/${projectId}/apis`, token);
|
|
191
|
+
const text = apis
|
|
192
|
+
.map((api) => `[${api.method?.toUpperCase() || 'GET'}] ${api.path} - ${api.name}`)
|
|
193
|
+
.join("\n");
|
|
194
|
+
console.error("获取 API 列表成功,数量:", apis.length);
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: "text" as const, text: text || "暂无 API" }],
|
|
197
|
+
};
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error("获取 API 列表错误:", error);
|
|
200
|
+
return {
|
|
201
|
+
content: [{ type: "text" as const, text: `获取失败: ${error instanceof Error ? error.message : String(error)}` }],
|
|
202
|
+
isError: true,
|
|
203
|
+
};
|
|
311
204
|
}
|
|
312
|
-
|
|
313
|
-
return {
|
|
314
|
-
content: [{ type: "text" as const, text: output }],
|
|
315
|
-
};
|
|
316
205
|
}
|
|
317
206
|
);
|
|
318
207
|
|
|
319
208
|
async function main() {
|
|
320
|
-
|
|
321
|
-
console.error("警告: 未指定 --project-id 参数");
|
|
322
|
-
}
|
|
323
|
-
if (!token) {
|
|
324
|
-
console.error("警告: 未设置 APIFOX_ACCESS_TOKEN 环境变量");
|
|
325
|
-
}
|
|
326
|
-
|
|
209
|
+
console.error("MCP Server 初始化完成,等待连接...");
|
|
327
210
|
const transport = new StdioServerTransport();
|
|
328
211
|
await server.connect(transport);
|
|
329
212
|
}
|