mcp-apifox
Version:
一个用于将 Apifox API 文档与 AI 助手集成的 Model Context Protocol (MCP) 服务器。
136 lines (132 loc) • 3.48 kB
JavaScript
// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// src/utils/apifox.ts
import { request } from "undici";
async function fetchApiInfoApi(projectId, apiId, accessToken) {
const response = await request(`https://api.apifox.com/v1/projects/${projectId}/export-openapi`, {
method: "POST",
headers: {
"X-Apifox-Api-Version": "2024-03-28",
"Authorization": `Bearer ${accessToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
scope: {
type: "SELECTED_ENDPOINTS",
selectedEndpointIds: [apiId]
},
options: {
includeApifoxExtensionProperties: false,
addFoldersToTags: false
},
oasVersion: "3.1",
exportFormat: "JSON"
})
});
const result = await response.body.text();
return result;
}
function extractProjectIdAndApiIdFromText(text) {
const urlPattern = /apifox\.com\/link\/project\/(\d+)\/apis\/api-(\d+)/;
const contentPattern = /project\/(\d+).*api-(\d+)/;
let projectId = "";
let apiId = "";
const lines = text.split("\n");
for (const line of lines) {
const trimmedLine = line.trim();
const urlMatch = trimmedLine.match(urlPattern);
if (urlMatch) {
projectId = urlMatch[1];
apiId = urlMatch[2];
break;
}
const contentMatch = trimmedLine.match(contentPattern);
if (contentMatch) {
projectId = contentMatch[1];
apiId = contentMatch[2];
break;
}
}
return {
projectId,
apiId
};
}
// src/index.ts
var server = new McpServer({
name: "apifox",
version: "0.0.1"
});
server.tool(
"get_apifox_project_id_and_api_id_from_url",
"Get the project ID and API ID of Apifox from the URL.",
{
text: z.string().describe("The text to extract the project id and api id from")
},
({ text }) => {
const { projectId, apiId } = extractProjectIdAndApiIdFromText(text);
return {
content: [
{
type: "text",
text: JSON.stringify({ projectId, apiId })
}
]
};
}
);
server.tool(
"get_apifox_api_info",
"Get the info of Apifox API.",
{
projectId: z.string().describe("The project ID of Apifox"),
apiId: z.string().describe("The API ID of Apifox")
},
async ({ projectId, apiId }) => {
try {
let token = process.env.APIFOX_ACCESS_TOKEN;
const args = process.argv.slice(2);
for (const arg of args) {
const tokenArg = arg.match(/^--(?:apifox-)?token=(.+)$/);
if (tokenArg) {
token = tokenArg[1];
break;
}
}
if (!token) {
throw new Error("No token provided");
}
const result = await fetchApiInfoApi(projectId, apiId, token);
return {
content: [
{
type: "text",
text: result
}
]
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error fetching API info: ${error.message}`
}
],
isError: true
};
}
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Apifox MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});