UNPKG

rt-prompt-mcp

Version:

MCP Server for prompt engineering suggestions

342 lines (341 loc) 11.7 kB
/** @format */ // 飞书API相关配置 const FEISHU_APP_ID = "cli_a8bd472b69b7d00c"; const FEISHU_APP_SECRET = "HGPdj3rFdhgrVbNef5hTtdnVTBXZ1CTS"; const FEISHU_BASE_URL = "https://fpaq3raxke.feishu.cn/base/P0tMbKGeyaRkWKsmtxycQvvxndc?table=tblItjJMAju2R86Q&view=vewuMFx3gG"; /** * 获取飞书tenant_access_token * @returns Promise<string> token */ async function getTenantAccessToken() { try { const response = await fetch("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8", }, body: JSON.stringify({ app_id: FEISHU_APP_ID, app_secret: FEISHU_APP_SECRET, }), }); const data = (await response.json()); if (!data || data.code !== 0) { throw new Error(`获取tenant_access_token失败: ${data?.msg || "未知错误"}, 状态码: ${data?.code}`); } // 飞书API返回的tenant_access_token在顶层,不在data字段中 if (!data.tenant_access_token) { throw new Error(`响应中缺少tenant_access_token: ${JSON.stringify(data)}`); } return data.tenant_access_token; } catch (error) { throw error; } } /** * 解析飞书URL获取所需参数 * @param url 飞书文档URL * @returns 解析出的参数 */ function parseFeishuUrl(url) { try { const urlObj = new URL(url); const pathParts = urlObj.pathname.split("/"); const urlParams = new URLSearchParams(urlObj.search); // 从路径中获取appToken (在/base/后的部分) const baseIndex = pathParts.indexOf("base"); const appToken = baseIndex >= 0 && baseIndex < pathParts.length - 1 ? pathParts[baseIndex + 1] : ""; // 从URL参数中获取tableId和viewId const tableId = urlParams.get("table"); const viewId = urlParams.get("view"); if (!appToken) { throw new Error("URL中未找到有效的appToken"); } return { appToken, tableId, viewId }; } catch (error) { throw error; } } /** * 获取多维表格元数据 * @param token 访问令牌 * @param appToken 应用Token * @returns 多维表格元数据 */ async function getBitableAppInfo(token, appToken) { const response = await fetch(`https://open.feishu.cn/open-apis/bitable/v1/apps/${appToken}`, { method: "GET", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json; charset=utf-8", }, }); const responseData = (await response.json()); if (responseData.code !== 0) { throw new Error(`获取多维表格元数据失败: ${responseData.msg}`); } // 检查响应结构 if (!responseData.data) { throw new Error(`响应中缺少data字段: ${JSON.stringify(responseData)}`); } return responseData.data; } /** * 获取表格列表 * @param token 访问令牌 * @param appToken 应用Token * @returns 表格列表 */ async function getTables(token, appToken) { const response = await fetch(`https://open.feishu.cn/open-apis/bitable/v1/apps/${appToken}/tables`, { method: "GET", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json; charset=utf-8", }, }); const responseData = (await response.json()); if (responseData.code !== 0) { throw new Error(`获取表格列表失败: ${responseData.msg}`); } // 检查响应结构 if (!responseData.data || !responseData.data.items) { throw new Error(`响应中缺少表格数据: ${JSON.stringify(responseData)}`); } return responseData.data.items; } /** * 获取表格字段列表 * @param token 访问令牌 * @param appToken 应用Token * @param tableId 表格ID * @returns 字段列表 */ async function getFields(token, appToken, tableId) { const response = await fetch(`https://open.feishu.cn/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/fields`, { method: "GET", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json; charset=utf-8", }, }); const responseData = (await response.json()); if (responseData.code !== 0) { throw new Error(`获取字段列表失败: ${responseData.msg}`); } // 检查响应结构 if (!responseData.data || !responseData.data.items) { throw new Error(`响应中缺少字段数据: ${JSON.stringify(responseData)}`); } return responseData.data.items; } /** * 获取表格记录 * @param token 访问令牌 * @param appToken 应用Token * @param tableId 表格ID * @param viewId 视图ID * @param pageSize 每页记录数 * @param pageToken 分页标记 * @returns 记录数据 */ async function getRecords(token, appToken, tableId, viewId, pageSize = 100, pageToken = "") { let url = `https://open.feishu.cn/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/records?page_size=${pageSize}`; if (viewId) { url += `&view_id=${viewId}`; } if (pageToken) { url += `&page_token=${pageToken}`; } const response = await fetch(url, { method: "GET", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json; charset=utf-8", }, }); const responseData = (await response.json()); if (responseData.code !== 0) { throw new Error(`获取记录失败: ${responseData.msg}`); } // 检查响应结构 if (!responseData.data || !responseData.data.items) { throw new Error(`响应中缺少记录数据: ${JSON.stringify(responseData).substring(0, 200)}...`); } return responseData.data; } /** * 获取所有记录(处理分页) * @param token 访问令牌 * @param appToken 应用Token * @param tableId 表格ID * @param viewId 视图ID * @returns 所有记录 */ async function getAllRecords(token, appToken, tableId, viewId) { let allRecords = []; let pageToken = ""; const pageSize = 100; do { const data = await getRecords(token, appToken, tableId, viewId, pageSize, pageToken); allRecords = [...allRecords, ...data.items]; pageToken = data.page_token || ""; } while (pageToken); return allRecords; } /** * 格式化记录数据,将字段ID转换为字段名 * @param records 记录列表 * @param fields 字段列表 * @returns 格式化后的记录 */ function formatRecords(records, fields) { const fieldMap = {}; fields.forEach((field) => { fieldMap[field.field_id] = field.field_name; }); return records.map((record) => { const formattedRecord = { record_id: record.record_id, fields: {}, }; for (const fieldId in record.fields) { const fieldName = fieldMap[fieldId] || fieldId; formattedRecord.fields[fieldName] = record.fields[fieldId]; } return formattedRecord; }); } /** * 获取飞书多维表格数据的主函数 */ async function fetchFeishuTableData() { try { // 获取访问令牌 const token = await getTenantAccessToken(); // 解析URL获取参数 const { appToken, tableId, viewId } = parseFeishuUrl(FEISHU_BASE_URL); if (!tableId) { throw new Error("未找到有效的tableId"); } // 获取多维表格元数据 const appInfo = await getBitableAppInfo(token, appToken); // 获取表格列表 const tables = await getTables(token, appToken); // 获取字段列表 const fields = await getFields(token, appToken, tableId); // 获取所有记录 const records = await getAllRecords(token, appToken, tableId, viewId); // 格式化记录数据 const formattedRecords = formatRecords(records, fields); return { appInfo: appInfo.app, tableId, tableName: tables.find((t) => t.table_id === tableId)?.name, fields, records: formattedRecords, }; } catch (error) { throw error; } } /** * 根据字段名和字段值查找记录,返回另一个字段的值 * @param records 记录列表 * @param searchFieldName 要搜索的字段名 * @param searchValue 要搜索的字段值 * @param targetFieldName 要返回的字段名 * @returns 目标字段的值,如果未找到则返回null */ function findFieldValueByCondition(records, searchFieldName, searchValue, targetFieldName) { // 查找匹配的记录 const record = records.find((record) => { // 检查记录中是否存在指定字段名并且值匹配 for (const key in record.fields) { if (key === searchFieldName && record.fields[key] === searchValue) { return true; } } return false; }); // 如果找到记录,返回目标字段的值 if (record && record.fields[targetFieldName] !== undefined) { return record.fields[targetFieldName]; } // 未找到记录或目标字段不存在 return null; } /** * 根据多个条件查询记录并返回指定字段 * @param records 记录列表 * @param conditions 查询条件,格式为 {字段名: 字段值} * @param targetFieldName 要返回的目标字段名,如果不提供则返回整条记录 * @returns 查找到的目标字段值或记录,未找到则返回null */ function queryRecords(records, conditions, targetFieldName) { // 查找匹配所有条件的记录 const matchedRecords = records.filter((record) => { // 检查记录是否满足所有条件 for (const [fieldName, fieldValue] of Object.entries(conditions)) { let matched = false; // 检查记录中是否存在指定字段名并且值匹配 for (const key in record.fields) { if (key === fieldName && record.fields[key] === fieldValue) { matched = true; break; } } // 如果任一条件不满足,则整条记录不匹配 if (!matched) { return false; } } // 所有条件均满足 return true; }); // 如果未找到匹配记录 if (matchedRecords.length === 0) { return null; } // 如果指定了目标字段,则返回第一条匹配记录的该字段值 if (targetFieldName && matchedRecords[0].fields[targetFieldName] !== undefined) { return matchedRecords[0].fields[targetFieldName]; } // 否则返回匹配的第一条完整记录 return matchedRecords[0]; } /** * 根据ID获取内容字段值的简化函数 * @param id 记录ID * @returns Promise<内容字段值 | null> */ export async function getContentById(id) { try { // 获取数据 const data = await fetchFeishuTableData(); // 使用queryRecords查询内容 return queryRecords(data.records, { id }, "内容"); } catch (error) { throw error; } } /** * 根据名称获取内容字段值的简化函数 * @param name 记录名称 * @returns Promise<内容字段值 | null> */ export async function getContentByName(name) { try { // 获取数据 const data = await fetchFeishuTableData(); // 使用queryRecords查询内容 return queryRecords(data.records, { 名称: name }, "内容"); } catch (error) { throw error; } }