核心模块详解 - cso1z/Feishu-MCP GitHub Wiki
项目采用分层架构,主要模块如下:
src/
├── index.ts # 应用入口
├── server.ts # HTTP 服务器
├── cli.ts # CLI 入口
├── mcp/ # MCP 协议实现
│ ├── feishuMcp.ts # MCP 服务器核心
│ └── tools/ # 工具函数注册
├── services/ # 服务层
│ ├── baseService.ts # 基础 API 服务
│ ├── feishuApiService.ts # 飞书 API 服务
│ ├── blockFactory.ts # 文档块工厂
│ └── feishuAuthService.ts # 认证服务
└── utils/ # 工具层
├── config.ts # 配置管理
├── logger.ts # 日志系统
├── cache.ts # 缓存管理
└── auth/ # 认证工具
位置: src/mcp/feishuMcp.ts
这是 MCP 服务器的核心类,继承自 McpServer。
export class FeishuMcp extends McpServer {
private feishuService: FeishuApiService | null = null;
constructor() {
super(serverInfo, serverOptions);
this.initFeishuService();
this.registerAllTools();
}
private initFeishuService(): void {
this.feishuService = FeishuApiService.getInstance();
}
private registerAllTools(): void {
registerFeishuTools(this, this.feishuService);
registerFeishuBlockTools(this, this.feishuService);
registerFeishuFolderTools(this, this.feishuService);
}
}工具函数分为三类:
-
基础工具 (
feishuTools.ts)- 文档创建和查询
- 文档信息获取
- Wiki 链接转换
- 搜索功能
-
文档块工具 (
feishuBlockTools.ts)- 文档块创建(文本、代码、标题等)
- 文档块更新
- 文档块删除
- 批量操作
-
文件夹工具 (
feishuFolderTools.ts)- 文件夹列表获取
- 文件夹创建
- 根文件夹获取
位置: src/services/baseService.ts
抽象基类,提供通用的 HTTP 请求处理能力。
export abstract class BaseApiService {
// 执行 HTTP 请求的核心方法
protected async request<T>(
endpoint: string,
method: string = 'GET',
data?: any,
needsAuth: boolean = true,
additionalHeaders?: Record<string, string>,
responseType?: 'json' | 'arraybuffer' | 'blob' | 'document' | 'text' | 'stream',
retry: boolean = false
): Promise<T>
// HTTP 方法封装
protected async get<T>(endpoint: string, params?: any, needsAuth?: boolean): Promise<T>
protected async post<T>(endpoint: string, data?: any, needsAuth?: boolean): Promise<T>
protected async patch<T>(endpoint: string, data?: any, needsAuth?: boolean): Promise<T>
protected async delete<T>(endpoint: string, data?: any, needsAuth?: boolean): Promise<T>
}// Token 过期自动刷新
private async handleTenantTokenExpired<T>(...): Promise<T> {
// 清除过期 token
tokenCacheManager.removeTenantToken(clientKey);
// 重试请求(会自动获取新 token)
return await this.request<T>(endpoint, method, data, needsAuth, additionalHeaders, responseType, true);
}
private async handleUserTokenExpired<T>(...): Promise<T> {
// 检查是否可以刷新
if (tokenStatus.canRefresh && !tokenStatus.isExpired) {
// 设置 token 为过期状态,触发刷新
tokenInfo.expires_at = Math.floor(Date.now() / 1000) - 1;
// 重试请求
return await this.request<T>(...);
}
}位置: src/services/feishuApiService.ts
单例模式,提供所有飞书 API 操作的封装。
// 创建文档
public async createDocument(title: string, folderToken: string): Promise<any>
// 获取文档信息
public async getDocumentInfo(documentId: string): Promise<any>
// 获取文档内容
public async getDocumentContent(documentId: string, lang: number = 0): Promise<string>
// 获取文档块结构
public async getDocumentBlocks(documentId: string, pageSize: number = 500): Promise<any[]>// 创建文本块
public async createTextBlock(
documentId: string,
parentBlockId: string,
textContents: Array<{text?: string, equation?: string, style?: any}>,
align: number = 1,
index: number = 0
): Promise<any>
// 创建代码块
public async createCodeBlock(
documentId: string,
parentBlockId: string,
code: string,
language: number = 0,
wrap: boolean = false,
index: number = 0
): Promise<any>
// 批量创建文档块
public async createDocumentBlocks(
documentId: string,
parentBlockId: string,
blockContents: any[],
index: number = 0
): Promise<any>// 创建图片块(完整流程)
public async createImageBlock(
documentId: string,
parentBlockId: string,
imagePathOrUrl: string,
options: ImageBlockOptions
): Promise<any>
// 流程:
// 1. 获取图片 Base64 (本地文件或网络 URL)
// 2. 创建空图片块
// 3. 上传图片素材到飞书
// 4. 设置图片块内容位置: src/services/blockFactory.ts
工厂类,用于创建符合飞书 API 规范的文档块对象。
export enum BlockType {
TEXT = 'text',
CODE = 'code',
HEADING = 'heading',
LIST = 'list',
IMAGE = 'image',
MERMAID = 'mermaid'
}// 创建文本块
createTextBlock(options: {
textContents: Array<{text: string, style?: any}>,
align: number
}): any
// 创建代码块
createCodeBlock(options: {
code: string,
language: number,
wrap: boolean
}): any
// 创建标题块
createHeadingBlock(options: {
text: string,
level: number,
align: number
}): any// 应用默认文本样式
public static applyDefaultTextStyle(style?: any): any {
const defaultStyle = {
bold: false,
italic: false,
strikethrough: false,
underline: false,
code_inline: false,
background_color: 0,
text_color: 0,
link: undefined
};
return { ...defaultStyle, ...style };
}位置: src/utils/config.ts
负责加载和管理应用配置。
-
环境变量 (
.env文件) - 命令行参数
- 默认值
优先级:命令行参数 > 环境变量 > 默认值
interface Config {
feishu: {
appId: string;
appSecret: string;
baseUrl: string;
authType: 'tenant' | 'user';
};
server: {
port: number;
};
}位置: src/utils/logger.ts
统一的日志接口,支持集成 MCP 日志系统。
Logger.info('信息日志');
Logger.warn('警告日志');
Logger.error('错误日志');
Logger.debug('调试日志');// 在 stdio 模式下,日志会通过 MCP 协议发送
Logger.info = (...args: any[]) => {
server.server.sendLoggingMessage({ level: 'info', data: args });
};位置: src/utils/cache.ts
提供内存缓存功能,主要用于 Wiki 到文档 ID 的转换缓存。
export class CacheManager {
// 缓存 Wiki 转换结果
cacheWikiToDocId(wikiToken: string, documentId: string): void
// 获取缓存的转换结果
getWikiToDocId(wikiToken: string): string | null
}位置: src/utils/auth/tokenCacheManager.ts
管理用户 token 和租户 token 的缓存,支持文件持久化。
- 内存缓存:快速访问
- 文件持久化:服务重启后保留 token
- 自动清理:定期清理过期 token
- 状态检查:检查 token 是否有效、是否需要刷新
// 缓存用户 token
cacheUserToken(key: string, tokenInfo: UserTokenInfo, customTtl?: number): boolean
// 获取用户 token
getUserToken(key: string): string | null
// 检查用户 token 状态
checkUserTokenStatus(key: string): TokenStatus
// 缓存租户 token
cacheTenantToken(key: string, tokenInfo: TenantTokenInfo, customTtl?: number): boolean
// 获取租户 token
getTenantToken(key: string): string | null位置: src/utils/auth/userAuthManager.ts
管理用户会话映射,将 SSE sessionId 映射到 userKey。
export class UserAuthManager {
// 创建会话映射
createSession(sessionId: string, userKey: string): void
// 获取用户标识
getUserKeyBySessionId(sessionId: string): string | null
// 移除会话映射
removeSession(sessionId: string): void
}位置: src/utils/auth/userContextManager.ts
使用 AsyncLocalStorage 管理请求级别的用户上下文。
export class UserContextManager {
// 在用户上下文中执行函数
run(context: {userKey: string, baseUrl: string}, fn: () => Promise<void>): void
// 获取当前用户标识
getUserKey(): string | null
// 获取当前基础 URL
getBaseUrl(): string | null
}位置: src/utils/auth/tokenRefreshManager.ts
后台定时任务,自动刷新即将过期的用户 token。
export class TokenRefreshManager {
start(): void {
// 每 5 分钟检查一次所有用户 token
setInterval(() => {
this.refreshExpiringTokens();
}, 5 * 60 * 1000);
}
}位置: src/utils/paramUtils.ts
处理各种参数格式,统一转换为 API 需要的格式。
// 处理文档 ID(支持 URL 和纯 ID)
processDocumentId(documentId: string): string
// 处理块 ID
processBlockId(blockId: string): string
// 处理 Wiki Token
processWikiToken(wikiUrl: string): string
// 处理画板 ID
processWhiteboardId(whiteboardId: string): string位置: src/utils/document.ts
提供文档内容处理的工具函数。
相关文档: