开发者指南 - cso1z/Feishu-MCP GitHub Wiki

开发者指南

目录

环境搭建

前置要求

  • Node.js: >= 20.17.0
  • 包管理器: pnpm (推荐) 或 npm
  • TypeScript: ^5.7.3

安装步骤

  1. 克隆项目
git clone https://github.com/cso1z/Feishu-MCP.git
cd Feishu-MCP
  1. 安装依赖
pnpm install
#
npm install
  1. 配置环境变量
# 复制示例文件
cp .env.example .env

# 编辑 .env 文件
FEISHU_APP_ID=your_app_id
FEISHU_APP_SECRET=your_app_secret
FEISHU_AUTH_TYPE=tenant  # 或 user
PORT=3333
  1. 构建项目
pnpm run build
  1. 启动开发服务器
pnpm run dev

开发工具推荐

  • VS CodeCursor
  • TypeScript 扩展
  • ESLint 扩展
  • Prettier 扩展

项目结构

Feishu-MCP/
├── src/                    # 源代码目录
│   ├── index.ts           # 应用入口(HTTP/SSE 模式)
│   ├── cli.ts             # CLI 入口
│   ├── server.ts          # HTTP 服务器实现
│   ├── mcp/               # MCP 协议实现
│   │   ├── feishuMcp.ts   # MCP 服务器核心
│   │   └── tools/          # 工具函数
│   ├── services/           # 服务层
│   │   ├── baseService.ts      # 基础 API 服务
│   │   ├── feishuApiService.ts  # 飞书 API 服务
│   │   ├── blockFactory.ts     # 文档块工厂
│   │   ├── feishuAuthService.ts # 认证服务
│   │   └── callbackService.ts  # OAuth 回调处理
│   ├── utils/              # 工具层
│   │   ├── config.ts       # 配置管理
│   │   ├── logger.ts       # 日志系统
│   │   ├── cache.ts        # 缓存管理
│   │   ├── auth/           # 认证工具
│   │   └── ...
│   └── types/              # 类型定义
├── dist/                   # 编译输出
├── doc/                    # 文档
├── .env.example            # 环境变量示例
└── package.json            # 项目配置

开发流程

添加新工具

  1. 在对应的工具文件中添加工具函数
// 文件位置: src/mcp/tools/feishuTools.ts
import { z } from 'zod';

export function registerFeishuTools(server: McpServer, feishuService: FeishuApiService) {
  server.tool(
    'new_feishu_tool',
    '工具描述,说明工具的功能和使用场景',
    {
      param1: z.string().describe('参数1的说明'),
      param2: z.number().optional().describe('参数2的说明(可选)'),
    },
    async ({ param1, param2 }) => {
      try {
        // 调用 feishuService 的方法
        const result = await feishuService.someMethod(param1, param2);
        
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(result, null, 2)
            }
          ]
        };
      } catch (error) {
        Logger.error('工具执行失败:', error);
        const errorMessage = formatErrorMessage(error);
        return {
          content: [
            {
              type: 'text',
              text: `工具执行失败: ${errorMessage}`
            }
          ]
        };
      }
    }
  );
}
  1. 在 FeishuApiService 中添加对应的 API 方法(如需要)
// 文件位置: src/services/feishuApiService.ts
public async someMethod(param1: string, param2?: number): Promise<any> {
  try {
    const endpoint = '/some/endpoint';
    const params = { param1, param2 };
    const response = await this.get(endpoint, params);
    return response;
  } catch (error) {
    this.handleApiError(error, '操作失败');
  }
}
  1. 更新文档
    • README.md 的工具功能表中添加新工具
    • doc/api-reference.md 中添加详细的 API 文档

添加新的文档块类型

  1. 在 BlockFactory 中添加创建方法
// 文件位置: src/services/blockFactory.ts
export enum BlockType {
  // ... 现有类型
  NEW_TYPE = 'new_type'
}

export class BlockFactory {
  createNewTypeBlock(options: NewTypeOptions): any {
    return {
      block_type: BlockTypeMap[BlockType.NEW_TYPE],
      // ... 块内容结构
    };
  }
}
  1. 在 FeishuApiService 中添加创建方法
// 文件位置: src/services/feishuApiService.ts
public async createNewTypeBlock(
  documentId: string,
  parentBlockId: string,
  options: NewTypeOptions,
  index: number = 0
): Promise<any> {
  const blockContent = this.blockFactory.createNewTypeBlock(options);
  return this.createDocumentBlock(documentId, parentBlockId, blockContent, index);
}
  1. 添加对应的工具函数

代码规范

TypeScript 规范

  • 使用 严格模式 (strict: true)
  • 优先使用 interface 而非 type(除非需要联合类型)
  • 函数参数和返回值必须有类型注解
  • 避免使用 any,优先使用 unknown
// ✅ 好的做法
interface UserInfo {
  id: string;
  name: string;
}

function getUserInfo(id: string): Promise<UserInfo> {
  // ...
}

// ❌ 不好的做法
function getUserInfo(id: any): any {
  // ...
}

命名规范

  • 类名: PascalCase,如 FeishuApiService
  • 函数名: camelCase,如 getUserToken
  • 常量: UPPER_SNAKE_CASE,如 MAX_RETRY_COUNT
  • 文件名: camelCase,如 feishuApiService.ts

注释规范

  • 类和方法: 使用 JSDoc 注释
  • 复杂逻辑: 添加行内注释说明
/**
 * 获取用户访问令牌
 * @param clientKey 客户端缓存键
 * @param appId 应用ID(可选,如果tokenInfo中没有则使用此参数)
 * @returns 用户访问令牌
 * @throws 如果无法获取有效的token则抛出AuthRequiredError
 */
public async getUserAccessToken(
  clientKey: string,
  appId?: string,
  appSecret?: string
): Promise<string> {
  // ...
}

错误处理

  • 使用 try-catch 捕获异步错误
  • 使用 formatErrorMessage 统一格式化错误信息
  • 记录详细的错误日志
try {
  const result = await someAsyncOperation();
  return result;
} catch (error) {
  Logger.error('操作失败:', error);
  const errorMessage = formatErrorMessage(error);
  throw new Error(`操作失败: ${errorMessage}`);
}

调试技巧

本地调试

  1. 启动开发服务器
pnpm run dev
  1. 使用 VS Code 调试
    • 创建 .vscode/launch.json
    • 配置调试参数
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Server",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["run", "dev"],
      "console": "integratedTerminal"
    }
  ]
}
  1. 查看日志
    • 开发模式下,日志会输出到控制台
    • HTTP 模式下,可以通过浏览器访问日志端点(如实现)

MCP Inspector

使用 MCP Inspector 调试 MCP 协议通信:

pnpm run inspect

调试工具函数

在工具函数中添加日志:

server.tool('some_tool', '...', {...}, async (params) => {
  Logger.debug('工具调用参数:', params);
  
  try {
    const result = await feishuService.someMethod(params);
    Logger.debug('工具执行结果:', result);
    return { content: [{ type: 'text', text: JSON.stringify(result) }] };
  } catch (error) {
    Logger.error('工具执行失败:', error);
    throw error;
  }
});

测试

运行测试

# 运行所有测试
pnpm test

# 运行特定测试文件
pnpm test src/services/feishuApiService.test.ts

# 监视模式
pnpm test --watch

编写测试

// 文件位置: src/services/feishuApiService.test.ts
import { describe, it, expect, beforeEach } from '@jest/globals';
import { FeishuApiService } from './feishuApiService';

describe('FeishuApiService', () => {
  let service: FeishuApiService;

  beforeEach(() => {
    service = FeishuApiService.getInstance();
  });

  it('should create document', async () => {
    const result = await service.createDocument('Test', 'folder_token');
    expect(result).toBeDefined();
    expect(result.objToken).toBeDefined();
  });
});

提交代码

Git 提交规范

使用 Conventional Commits 规范:

<type>(<scope>): <subject>

<body>

<footer>

类型 (type):

  • feat: 新功能
  • fix: 修复 bug
  • docs: 文档更新
  • style: 代码格式(不影响功能)
  • refactor: 重构
  • test: 测试相关
  • chore: 构建/工具相关

示例:

feat(tools): 添加批量创建文档块工具

- 支持一次性创建多个文档块
- 优化批量操作的性能
- 添加错误处理机制

Closes #123

提交流程

  1. 创建功能分支
git checkout -b feat/new-feature
  1. 提交更改
git add .
git commit -m "feat: 添加新功能"
  1. 推送并创建 Pull Request
git push origin feat/new-feature

代码审查清单

  • 代码符合规范
  • 添加了必要的注释
  • 更新了相关文档
  • 通过了测试
  • 没有引入新的警告或错误

相关文档:

⚠️ **GitHub.com Fallback** ⚠️