#
tokens: 2955/50000 9/9 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .env.example
├── .eslintrc
├── .gitignore
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── README.md
├── src
│   ├── cli.ts
│   ├── config.ts
│   ├── index.ts
│   └── server.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
.cursor/
.history/
node_modules/
.env
build/

```

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
###
 # @Descripttion: 
 # @version: 
 # @Author: wangmin
 # @Date: 2025-03-21 11:25:26
 # @LastEditors: wangmin
 # @LastEditTime: 2025-03-21 11:26:18
### 
# your apifox api key
APIFOX_API_KEY=your_apifox_api_key
# your apifox project id
PROJECT_ID=your_apifox_project_id
# your apifox server 
PORT=your_apifox_server_port
```

--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------

```
{
  "parser": "@typescript-eslint/parser",
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier"
  ],
  "plugins": ["@typescript-eslint"],
  "parserOptions": {
    "ecmaVersion": 2022,
    "sourceType": "module"
  },
  "rules": {
    "@typescript-eslint/explicit-function-return-type": "warn",
    "@typescript-eslint/no-unused-vars": [
      "error",
      { "argsIgnorePattern": "^_" }
    ],
    "@typescript-eslint/no-explicit-any": "warn"
  }
}

```

--------------------------------------------------------------------------------
/src/cli.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

import { resolve } from "path";
import { config } from "dotenv";
import { startServer } from "./index.js";

// Load .env from the current working directory
config({ path: resolve(process.cwd(), ".env") });

startServer().catch((error: unknown) => {
  if (error instanceof Error) {
    console.error("Failed to start server:", error.message);
  } else {
    console.error("Failed to start server with unknown error:", error);
  }
  process.exit(1);
});

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "lib": ["ES2022", "DOM"],
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "sourceMap": true,
    "baseUrl": ".",
    "paths": {
      "~/*": ["src/*"]
    },
    "preserveSymlinks": true,
    "allowJs": true,
    "removeComments": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "build"]
}

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
/*
 * @Descripttion:
 * @version:
 * @Author: wangmin
 * @Date: 2025-03-20 14:39:11
 * @LastEditors: wangmin
 * @LastEditTime: 2025-04-03 09:33:34
 */
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { getServerConfig } from "./config.js";
import { ApiFoxServer } from "./server.js";
// // 创建 MCP 服务器

// 启动服务器
export async function startServer(): Promise<void> {
  const isLocalMode =
    process.env.NODE_ENV === "cli" || process.argv.includes("--local");
  const config = getServerConfig();

  const server = new ApiFoxServer(config.apifoxApiKey, config.projectId);

  if (isLocalMode) {
    const transport = new StdioServerTransport();
    await server.connect(transport);
  } else {
    console.error(
      `初始化HTTP模式下的ApiFox MCP Server服务器在端口 ${config.port}`
    );
    await server.startHttpServer(config.port);
  }
}

startServer().catch((error) => {
  console.error("启动接口信息服务器失败:", error);
  process.exit(1);
});

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "@wangmhaha/apifox-mcp-server",
  "version": "1.6.8",
  "main": "build/index.js",
  "type": "module",
  "bin": {
    "@wangmhaha/apifox-mcp-server": "build/cli.js"
  },
  "scripts": {
    "build": "tsc && tsc-alias",
    "start": "node build/index.js",
    "start:cli": "cross-env NODE_ENV=cli node build/index.js",
    "start:http": "node build/index.js"
  },
  "files": [
    "build",
    "README.md"
  ],
  "repository": {
    "type": "git",
    "url": "git+https://github.com/wangmhaha/apifox-mcp-server"
  },
  "keywords": [],
  "author": "wangmhaha",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "@types/express": "^5.0.1",
    "@types/express-serve-static-core": "^5.0.6",
    "@types/node": "^22.10.0",
    "tsc-alias": "^1.8.11",
    "tsx": "^4.19.3",
    "typescript": "^5.7.2"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.6.1",
    "@types/yargs": "^17.0.33",
    "@typescript-eslint/eslint-plugin": "^8.27.0",
    "@typescript-eslint/parser": "^8.27.0",
    "cross-env": "^7.0.3",
    "dotenv": "^16.4.7",
    "eslint": "^9.22.0",
    "express": "^4.21.2",
    "yargs": "^17.7.2",
    "zod": "^3.24.2"
  }
}
```

--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------

```typescript
/*
 * @Descripttion:
 * @version:
 * @Author: wangmin
 * @Date: 2025-03-20 17:26:55
 * @LastEditors: wangmin
 * @LastEditTime: 2025-03-21 09:16:43
 */
import { config } from "dotenv";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";

config();

interface ServerConfig {
  port: number;
  apifoxApiKey: string;
  projectId: string;
}

interface CliArgs {
  "apifox-api-key"?: string;
  port?: number;
  "project-id"?: string;
}

export function getServerConfig(): ServerConfig {
  const argv = yargs(hideBin(process.argv))
    .options({
      "apifox-api-key": {
        type: "string",
        describe: "apifox api key",
      },
      "project-id": {
        type: "string",
        describe: "apifox project id",
      },
      port: {
        type: "number",
        describe: "Prot to run the server on",
      },
    })
    .help()
    .parseSync() as CliArgs;

  const config: ServerConfig = {
    apifoxApiKey: "",
    projectId: "",
    port: 3000,
  };

  if (argv["apifox-api-key"]) {
    config.apifoxApiKey = argv["apifox-api-key"];
  } else if (process.env.APIFOX_API_KEY) {
    config.apifoxApiKey = process.env.APIFOX_API_KEY;
  }

  if (argv["project-id"]) {
    config.projectId = argv["project-id"];
  } else if (process.env.PROJECT_ID) {
    config.projectId = process.env.PROJECT_ID;
  }

  if (argv.port) {
    config.port = argv.port;
  } else if (process.env.PORT) {
    config.port = parseInt(process.env.PORT, 10);
  }

  if (!config.apifoxApiKey) {
    console.error("请提供 apifox api key");
    process.exit(1);
  }

  if (!config.projectId) {
    console.error("请提供 apifox project id");
    process.exit(1);
  }

  return config;
}

```

--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------

```typescript
/*
 * @Descripttion:
 * @version:
 * @Author: wangmin
 * @Date: 2025-03-20 17:49:38
 * @LastEditors: wangmin
 * @LastEditTime: 2025-04-03 14:30:44
 */
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
import { IncomingMessage, ServerResponse } from "http";
import express from "express";
import { Response, Request } from "express-serve-static-core";
import { z } from "zod";

export class ApiFoxServer {
  private readonly server: McpServer;
  private sseTransport: SSEServerTransport | null = null;

  constructor(apifoxApiKey: string, projectId: string) {
    this.server = new McpServer({
      name: "ApiFox MCP Server",
      version: "1.0.0",
      capabilities: {
        notifications: true,
      },
    });

    this.registerTools(apifoxApiKey, projectId);
  }

  // 注册工具
  private registerTools(key: string, projectId: string): void {
    this.server.tool(
      "get-interface",
      "获取apiFox接口信息",
      {
        moduleId: z.string().describe("要查询模块id"),
        moduleName: z.string().describe("要查询模块名称"),
      },
      async (args: { moduleId: string; moduleName?: string }) => {
        try {
          const response = await fetch(
            `https://api.apifox.com/v1/projects/${projectId}/export-openapi`,
            {
              method: "POST",
              headers: {
                "Content-Type": "application/json",
                Authorization: `Bearer ${key}`,
                "X-Apifox-Api-Version": "2024-03-28",
              },
              body: JSON.stringify({
                scope: {
                  type: "SELECTED_FOLDERS",
                  selectedFolderIds: [args.moduleId],
                  excludedByTags: ["pet"],
                },
                options: {
                  includeApifoxExtensionProperties: false,
                  addFoldersToTags: true,
                },
                oasVersion: "3.1",
                exportFormat: "JSON",
              }),
            }
          );

          // 检查响应状态
          if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
          }

          // 解析响应数据
          const data = await response.json();

          if (!data) {
            return {
              content: [
                {
                  type: "text",
                  text: `无法找到${args.moduleName}的接口信息`,
                },
              ],
            };
          }

          return {
            content: [
              {
                type: "text",
                text: `基于openapi3.1.0的规范,${
                  args.moduleName
                }的接口信息如下: ${JSON.stringify(data)}`,
              },
            ],
          };
        } catch (error) {
          console.error("获取接口信息失败:", error);
          return {
            content: [
              {
                type: "text",
                text: `获取接口信息失败: ${error}`,
              },
            ],
          };
        }
      }
    );
  }

  async connect(transport: Transport): Promise<void> {
    await this.server.connect(transport);
    console.error("服务器已连接并准备处理请求");
  }

  async startHttpServer(port: number): Promise<void> {
    const app = express();

    app.get("/sse", async (req: Request, res: Response) => {
      console.error("SSE连接建立");
      this.sseTransport = new SSEServerTransport(
        "/messages",
        res as unknown as ServerResponse<IncomingMessage>
      );
      await this.connect(this.sseTransport);
    });

    app.post("/messages", async (req: Request, res: Response) => {
      if (!this.sseTransport) {
        res.status(400).send();
        return;
      }
      await this.sseTransport.handlePostMessage(
        req as unknown as IncomingMessage,
        res as unknown as ServerResponse<IncomingMessage>
      );
    });

    app.listen(port, () => {
      console.error(`HTTP服务器监听端口 ${port}`);
      console.error(`SSE 端点可用于 http://localhost:${port}/sse`);
      console.error(
        `消息端点可在以下位置访问: http://localhost:${port}/messages`
      );
    });
  }
}

```