lhwapi-mcp-server 1.0.3 → 1.0.4
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 +277 -0
- package/dist/cli.cjs +1 -1
- package/package.json +9 -4
- package/scripts/build.mjs +12 -0
package/dist/bundle.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
var APIFOX_BASE_URL = "https://api.apifox.com";
|
|
6
|
+
var args = process.argv.slice(2);
|
|
7
|
+
var projectId;
|
|
8
|
+
for (let i = 0; i < args.length; i++) {
|
|
9
|
+
if (args[i] === "--project-id" && args[i + 1]) {
|
|
10
|
+
projectId = args[i + 1];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
var token = process.env.APIFOX_ACCESS_TOKEN;
|
|
14
|
+
async function fetchApifox(endpoint, authToken, options) {
|
|
15
|
+
const response = await fetch(`${APIFOX_BASE_URL}${endpoint}`, {
|
|
16
|
+
method: options?.method || "GET",
|
|
17
|
+
headers: {
|
|
18
|
+
"Authorization": `Bearer ${authToken}`,
|
|
19
|
+
"Content-Type": "application/json",
|
|
20
|
+
"X-Apifox-Api-Version": "2024-03-28"
|
|
21
|
+
},
|
|
22
|
+
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
23
|
+
});
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
const error = await response.text();
|
|
26
|
+
throw new Error(`Apifox API error: ${response.status} - ${error}`);
|
|
27
|
+
}
|
|
28
|
+
const data = await response.json();
|
|
29
|
+
return data.data;
|
|
30
|
+
}
|
|
31
|
+
var server = new McpServer({
|
|
32
|
+
name: "lhwapi-mcp-server",
|
|
33
|
+
version: "1.0.0"
|
|
34
|
+
});
|
|
35
|
+
server.registerTool(
|
|
36
|
+
"listProjects",
|
|
37
|
+
{
|
|
38
|
+
description: "\u83B7\u53D6 Apifox \u9879\u76EE\u5217\u8868",
|
|
39
|
+
inputSchema: z.object({
|
|
40
|
+
token: z.string().optional().describe("Apifox \u8BBF\u95EE\u4EE4\u724C (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u73AF\u5883\u53D8\u91CF)")
|
|
41
|
+
}).shape
|
|
42
|
+
},
|
|
43
|
+
async ({ token: inputToken }) => {
|
|
44
|
+
const authToken = inputToken || token;
|
|
45
|
+
if (!authToken) {
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF\u6216\u4F20\u5165 token \u53C2\u6570" }],
|
|
48
|
+
isError: true
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const projects = await fetchApifox("/v1/projects", authToken);
|
|
52
|
+
const text = projects.map((p) => `\u9879\u76EE: ${p.name} (ID: ${p.id})
|
|
53
|
+
\u63CF\u8FF0: ${p.description || "\u65E0"}`).join("\n\n");
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: text || "\u6682\u65E0\u9879\u76EE" }]
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
server.registerTool(
|
|
60
|
+
"listApis",
|
|
61
|
+
{
|
|
62
|
+
description: "\u83B7\u53D6\u6307\u5B9A\u9879\u76EE\u4E0B\u7684 API \u5217\u8868",
|
|
63
|
+
inputSchema: z.object({
|
|
64
|
+
projectId: z.string().optional().describe("\u9879\u76EE ID (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u547D\u4EE4\u884C\u53C2\u6570)"),
|
|
65
|
+
token: z.string().optional().describe("Apifox \u8BBF\u95EE\u4EE4\u724C (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u73AF\u5883\u53D8\u91CF)")
|
|
66
|
+
}).shape
|
|
67
|
+
},
|
|
68
|
+
async ({ projectId: inputProjectId, token: inputToken }) => {
|
|
69
|
+
const authToken = inputToken || token;
|
|
70
|
+
const authProjectId = inputProjectId || projectId;
|
|
71
|
+
if (!authToken) {
|
|
72
|
+
return {
|
|
73
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF\u6216\u4F20\u5165 token \u53C2\u6570" }],
|
|
74
|
+
isError: true
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (!authProjectId) {
|
|
78
|
+
return {
|
|
79
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u901A\u8FC7 --project-id \u53C2\u6570\u6307\u5B9A\u9879\u76EE ID" }],
|
|
80
|
+
isError: true
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const apis = await fetchApifox(
|
|
84
|
+
`/v1/projects/${authProjectId}/apis`,
|
|
85
|
+
authToken
|
|
86
|
+
);
|
|
87
|
+
const text = apis.map((api) => `[${api.method.toUpperCase()}] ${api.path} - ${api.name}`).join("\n");
|
|
88
|
+
return {
|
|
89
|
+
content: [{ type: "text", text: text || "\u6682\u65E0 API" }]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
server.registerTool(
|
|
94
|
+
"getApiDetail",
|
|
95
|
+
{
|
|
96
|
+
description: "\u83B7\u53D6\u5355\u4E2A API \u7684\u8BE6\u7EC6\u4FE1\u606F",
|
|
97
|
+
inputSchema: z.object({
|
|
98
|
+
apiId: z.string().describe("API ID"),
|
|
99
|
+
projectId: z.string().optional().describe("\u9879\u76EE ID (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u547D\u4EE4\u884C\u53C2\u6570)"),
|
|
100
|
+
token: z.string().optional().describe("Apifox \u8BBF\u95EE\u4EE4\u724C (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u73AF\u5883\u53D8\u91CF)")
|
|
101
|
+
}).shape
|
|
102
|
+
},
|
|
103
|
+
async ({ apiId, projectId: inputProjectId, token: inputToken }) => {
|
|
104
|
+
const authToken = inputToken || token;
|
|
105
|
+
const authProjectId = inputProjectId || projectId;
|
|
106
|
+
if (!authToken) {
|
|
107
|
+
return {
|
|
108
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF\u6216\u4F20\u5165 token \u53C2\u6570" }],
|
|
109
|
+
isError: true
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (!authProjectId) {
|
|
113
|
+
return {
|
|
114
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u901A\u8FC7 --project-id \u53C2\u6570\u6307\u5B9A\u9879\u76EE ID" }],
|
|
115
|
+
isError: true
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const api = await fetchApifox(
|
|
119
|
+
`/v1/projects/${authProjectId}/apis/${apiId}`,
|
|
120
|
+
authToken
|
|
121
|
+
);
|
|
122
|
+
let detail = `# ${api.name}
|
|
123
|
+
|
|
124
|
+
`;
|
|
125
|
+
detail += `**\u65B9\u6CD5**: ${api.method.toUpperCase()}
|
|
126
|
+
`;
|
|
127
|
+
detail += `**\u8DEF\u5F84**: ${api.path}
|
|
128
|
+
|
|
129
|
+
`;
|
|
130
|
+
if (api.description) {
|
|
131
|
+
detail += `## \u63CF\u8FF0
|
|
132
|
+
|
|
133
|
+
${api.description}
|
|
134
|
+
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
if (api.requestParams?.query?.length) {
|
|
138
|
+
detail += `## Query \u53C2\u6570
|
|
139
|
+
|
|
140
|
+
`;
|
|
141
|
+
api.requestParams.query.forEach((param) => {
|
|
142
|
+
detail += `- ${param.name}: ${param.type || "string"} ${param.required ? "(\u5FC5\u586B)" : "(\u53EF\u9009)"}
|
|
143
|
+
`;
|
|
144
|
+
});
|
|
145
|
+
detail += "\n";
|
|
146
|
+
}
|
|
147
|
+
if (api.requestBody?.schema) {
|
|
148
|
+
detail += `## \u8BF7\u6C42\u4F53
|
|
149
|
+
|
|
150
|
+
\`\`\`json
|
|
151
|
+
${JSON.stringify(api.requestBody.schema, null, 2)}
|
|
152
|
+
\`\`\`
|
|
153
|
+
|
|
154
|
+
`;
|
|
155
|
+
}
|
|
156
|
+
if (api.responseList?.length) {
|
|
157
|
+
detail += `## \u54CD\u5E94
|
|
158
|
+
|
|
159
|
+
`;
|
|
160
|
+
api.responseList.forEach((resp) => {
|
|
161
|
+
detail += `### ${resp.statusCode}
|
|
162
|
+
`;
|
|
163
|
+
if (resp.responseBodySchema) {
|
|
164
|
+
detail += `\`\`\`json
|
|
165
|
+
${JSON.stringify(resp.responseBodySchema, null, 2)}
|
|
166
|
+
\`\`\`
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
content: [{ type: "text", text: detail }]
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
server.registerTool(
|
|
177
|
+
"searchApis",
|
|
178
|
+
{
|
|
179
|
+
description: "\u641C\u7D22 API",
|
|
180
|
+
inputSchema: z.object({
|
|
181
|
+
keyword: z.string().describe("\u641C\u7D22\u5173\u952E\u8BCD"),
|
|
182
|
+
projectId: z.string().optional().describe("\u9879\u76EE ID (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u547D\u4EE4\u884C\u53C2\u6570)"),
|
|
183
|
+
token: z.string().optional().describe("Apifox \u8BBF\u95EE\u4EE4\u724C (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u73AF\u5883\u53D8\u91CF)")
|
|
184
|
+
}).shape
|
|
185
|
+
},
|
|
186
|
+
async ({ keyword, projectId: inputProjectId, token: inputToken }) => {
|
|
187
|
+
const authToken = inputToken || token;
|
|
188
|
+
const authProjectId = inputProjectId || projectId;
|
|
189
|
+
if (!authToken) {
|
|
190
|
+
return {
|
|
191
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF\u6216\u4F20\u5165 token \u53C2\u6570" }],
|
|
192
|
+
isError: true
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (!authProjectId) {
|
|
196
|
+
return {
|
|
197
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u901A\u8FC7 --project-id \u53C2\u6570\u6307\u5B9A\u9879\u76EE ID" }],
|
|
198
|
+
isError: true
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
const apis = await fetchApifox(
|
|
202
|
+
`/v1/projects/${authProjectId}/apis`,
|
|
203
|
+
authToken
|
|
204
|
+
);
|
|
205
|
+
const filtered = apis.filter(
|
|
206
|
+
(api) => api.name.toLowerCase().includes(keyword.toLowerCase()) || api.path.toLowerCase().includes(keyword.toLowerCase())
|
|
207
|
+
);
|
|
208
|
+
const text = filtered.map((api) => `[${api.method.toUpperCase()}] ${api.path} - ${api.name}`).join("\n");
|
|
209
|
+
return {
|
|
210
|
+
content: [{ type: "text", text: text || "\u672A\u627E\u5230\u5339\u914D\u7684 API" }]
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
server.registerTool(
|
|
215
|
+
"exportOpenApi",
|
|
216
|
+
{
|
|
217
|
+
description: "\u5BFC\u51FA OpenAPI/Swagger \u683C\u5F0F\u6570\u636E",
|
|
218
|
+
inputSchema: z.object({
|
|
219
|
+
oasVersion: z.string().optional().describe("OpenAPI \u7248\u672C: 3.0, 3.1, 2.0"),
|
|
220
|
+
exportFormat: z.string().optional().describe("\u5BFC\u51FA\u683C\u5F0F: JSON \u6216 YAML"),
|
|
221
|
+
projectId: z.string().optional().describe("\u9879\u76EE ID (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u547D\u4EE4\u884C\u53C2\u6570)"),
|
|
222
|
+
token: z.string().optional().describe("Apifox \u8BBF\u95EE\u4EE4\u724C (\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u73AF\u5883\u53D8\u91CF)")
|
|
223
|
+
}).shape
|
|
224
|
+
},
|
|
225
|
+
async ({ oasVersion, exportFormat, projectId: inputProjectId, token: inputToken }) => {
|
|
226
|
+
const authToken = inputToken || token;
|
|
227
|
+
const authProjectId = inputProjectId || projectId;
|
|
228
|
+
if (!authToken) {
|
|
229
|
+
return {
|
|
230
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF\u6216\u4F20\u5165 token \u53C2\u6570" }],
|
|
231
|
+
isError: true
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
if (!authProjectId) {
|
|
235
|
+
return {
|
|
236
|
+
content: [{ type: "text", text: "\u9519\u8BEF: \u8BF7\u901A\u8FC7 --project-id \u53C2\u6570\u6307\u5B9A\u9879\u76EE ID" }],
|
|
237
|
+
isError: true
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const result = await fetchApifox(
|
|
241
|
+
`/v1/projects/${authProjectId}/export-openapi`,
|
|
242
|
+
authToken,
|
|
243
|
+
{
|
|
244
|
+
method: "POST",
|
|
245
|
+
body: {
|
|
246
|
+
scope: { type: "ALL" },
|
|
247
|
+
oasVersion: oasVersion || "3.1",
|
|
248
|
+
exportFormat: exportFormat || "JSON"
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
const format = exportFormat || "JSON";
|
|
253
|
+
let output;
|
|
254
|
+
if (format.toUpperCase() === "YAML") {
|
|
255
|
+
output = result.openapi || "";
|
|
256
|
+
} else {
|
|
257
|
+
output = JSON.stringify(result, null, 2);
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
content: [{ type: "text", text: output }]
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
async function main() {
|
|
265
|
+
if (!projectId) {
|
|
266
|
+
console.error("\u8B66\u544A: \u672A\u6307\u5B9A --project-id \u53C2\u6570");
|
|
267
|
+
}
|
|
268
|
+
if (!token) {
|
|
269
|
+
console.error("\u8B66\u544A: \u672A\u8BBE\u7F6E APIFOX_ACCESS_TOKEN \u73AF\u5883\u53D8\u91CF");
|
|
270
|
+
}
|
|
271
|
+
const transport = new StdioServerTransport();
|
|
272
|
+
await server.connect(transport);
|
|
273
|
+
}
|
|
274
|
+
main().catch((error) => {
|
|
275
|
+
console.error("Server error:", error);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
});
|
package/dist/cli.cjs
CHANGED
|
@@ -3,7 +3,7 @@ const { spawn } = require("child_process");
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
|
|
5
5
|
const args = process.argv.slice(2);
|
|
6
|
-
const child = spawn("node", [path.join(__dirname, "
|
|
6
|
+
const child = spawn("node", [path.join(__dirname, "bundle.js"), ...args], {
|
|
7
7
|
stdio: "inherit",
|
|
8
8
|
shell: true,
|
|
9
9
|
});
|
package/package.json
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lhwapi-mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "MCP server for API documentation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"bin": {
|
|
9
|
-
"lhwapi-mcp-server": "
|
|
9
|
+
"lhwapi-mcp-server": "dist/cli.cjs"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc",
|
|
13
|
-
"
|
|
13
|
+
"bundle": "node scripts/build.mjs",
|
|
14
|
+
"start": "node dist/bundle.js",
|
|
14
15
|
"dev": "tsx src/index.ts"
|
|
15
16
|
},
|
|
16
|
-
"keywords": [
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"api-docs"
|
|
20
|
+
],
|
|
17
21
|
"author": "",
|
|
18
22
|
"license": "MIT",
|
|
19
23
|
"dependencies": {
|
|
@@ -22,6 +26,7 @@
|
|
|
22
26
|
},
|
|
23
27
|
"devDependencies": {
|
|
24
28
|
"@types/node": "^25.3.2",
|
|
29
|
+
"esbuild": "^0.27.3",
|
|
25
30
|
"tsx": "^4.21.0",
|
|
26
31
|
"typescript": "^5.9.3"
|
|
27
32
|
}
|