feishu-mcp
Version:
Model Context Protocol server for Feishu integration
135 lines (134 loc) • 6.55 kB
JavaScript
import { Logger } from '../../utils/logger.js';
import { FeishuBaseApiService } from './FeishuBaseApiService.js';
/**
* 飞书知识库(Wiki)服务
* 负责知识空间及节点的查询与创建
*/
export class FeishuWikiService extends FeishuBaseApiService {
constructor(authService) {
super(authService);
}
/**
* 获取当前用户可见的所有知识空间列表,自动处理分页直到获取全部数据
* @param pageSize 每次分页请求的数量,默认 20
* @returns 知识空间对象数组,每项包含 space_id、name、description 等字段
*/
async getAllWikiSpacesList(pageSize = 20) {
try {
Logger.info(`开始获取所有知识空间列表,每页数量: ${pageSize}`);
const endpoint = '/wiki/v2/spaces';
let allItems = [];
let pageToken = undefined;
let hasMore = true;
while (hasMore) {
const params = { page_size: pageSize };
if (pageToken)
params.page_token = pageToken;
Logger.debug(`请求知识空间列表,page_token: ${pageToken || 'null'}, page_size: ${pageSize}`);
const response = await this.get(endpoint, params);
if (response?.items) {
allItems = [...allItems, ...response.items];
hasMore = response.has_more || false;
pageToken = response.page_token;
Logger.debug(`当前页获取到 ${response.items.length} 个知识空间,累计 ${allItems.length} 个,hasMore: ${hasMore}`);
}
else {
hasMore = false;
Logger.warn('知识空间列表响应格式异常:', JSON.stringify(response, null, 2));
}
}
Logger.info(`知识空间列表获取完成,共 ${allItems.length} 个空间`);
return allItems;
}
catch (error) {
this.handleApiError(error, '获取知识空间列表失败');
}
}
/**
* 获取指定知识空间下的所有子节点,自动处理分页直到获取全部数据
* @param spaceId 知识空间 ID
* @param parentNodeToken 父节点 Token,不传则获取根节点下的直属子节点
* @param pageSize 每次分页请求的数量,默认 20
* @returns 节点对象数组,每项包含 node_token、obj_token、title、obj_type 等字段
*/
async getAllWikiSpaceNodes(spaceId, parentNodeToken, pageSize = 20) {
try {
Logger.info(`开始获取知识空间子节点列表,space_id: ${spaceId}, parent_node_token: ${parentNodeToken || 'null'}`);
const endpoint = `/wiki/v2/spaces/${spaceId}/nodes`;
let allItems = [];
let pageToken = undefined;
let hasMore = true;
while (hasMore) {
const params = { page_size: pageSize };
if (parentNodeToken)
params.parent_node_token = parentNodeToken;
if (pageToken)
params.page_token = pageToken;
Logger.debug(`请求知识空间子节点列表,page_token: ${pageToken || 'null'}`);
const response = await this.get(endpoint, params);
if (response?.items) {
allItems = [...allItems, ...response.items];
hasMore = response.has_more || false;
pageToken = response.page_token;
Logger.debug(`当前页获取到 ${response.items.length} 个子节点,累计 ${allItems.length} 个,hasMore: ${hasMore}`);
}
else {
hasMore = false;
Logger.warn('知识空间子节点列表响应格式异常:', JSON.stringify(response, null, 2));
}
}
Logger.info(`知识空间子节点列表获取完成,共 ${allItems.length} 个节点`);
return allItems;
}
catch (error) {
this.handleApiError(error, '获取知识空间子节点列表失败');
}
}
/**
* 获取指定知识空间的详细信息
* 当 spaceId 传入 'my_library' 时,获取当前用户的"我的知识库"信息
* @param spaceId 知识空间 ID,或传入 'my_library' 获取个人知识库
* @param lang 语言参数,仅当 spaceId 为 'my_library' 时有效,默认 'en'
* @returns 知识空间信息对象,包含 space_id、name、description 等字段
*/
async getWikiSpaceInfo(spaceId, lang = 'en') {
try {
const params = {};
if (spaceId === 'my_library')
params.lang = lang;
const response = await this.get(`/wiki/v2/spaces/${spaceId}`, params);
Logger.debug(`获取知识空间信息成功 (space_id: ${spaceId}):`, response);
return response?.space || response;
}
catch (error) {
this.handleApiError(error, `获取知识空间信息失败 (space_id: ${spaceId})`);
}
}
/**
* 在知识空间中创建一个新的文档节点(类型固定为 docx)
* @param spaceId 目标知识空间 ID
* @param title 节点标题
* @param parentNodeToken 父节点 Token,不传则在根目录下创建
* @returns 创建的节点信息,包含 node_token(节点 ID)和 obj_token(关联文档 ID)
*/
async createWikiSpaceNode(spaceId, title, parentNodeToken) {
try {
Logger.info(`开始创建知识空间节点,space_id: ${spaceId}, title: ${title}, parent_node_token: ${parentNodeToken || 'null(根节点)'}`);
const endpoint = `/wiki/v2/spaces/${spaceId}/nodes`;
const payload = { title, obj_type: 'docx', node_type: 'origin' };
if (parentNodeToken)
payload.parent_node_token = parentNodeToken;
const response = await this.post(endpoint, payload);
if (response?.data?.node) {
const node = response.data.node;
Logger.info(`知识空间节点创建成功,node_token: ${node.node_token}, obj_token: ${node.obj_token}`);
return node;
}
Logger.info('知识空间节点创建成功');
return response;
}
catch (error) {
this.handleApiError(error, '创建知识空间节点失败');
}
}
}