// 进程内 MCP 工具(Claude 专用);M2 起同样的能力经 MCP Gateway 提供给 Codex import path from 'node:path' import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk' import { z } from 'zod' import { CONVERT_FORMATS, convertDocument, recalcWorkbook } from '../../services/office' export const OFFICE_SERVER = 'office' export const CONVERT_TOOL = `mcp__${OFFICE_SERVER}__convert_document` export const RECALC_TOOL = `mcp__${OFFICE_SERVER}__recalc_workbook` export function officeServer(workspaceDir: string, pythonRuntime: string) { return createSdkMcpServer({ name: OFFICE_SERVER, version: '0.1.0', tools: [ tool( 'convert_document', '用 LibreOffice 转换文档格式(例如 docx→pdf、xlsx→pdf、pptx→pdf、doc→docx、html→pdf)。' + '输入和输出都必须在工作区内;输出文件名与输入相同、扩展名换成目标格式。不要在 Bash 里直接调用 soffice,它在沙箱中无法运行。', { input: z.string().describe('输入文件路径(相对工作区或绝对路径)'), format: z.enum(CONVERT_FORMATS).describe('目标格式'), out_dir: z.string().optional().describe('输出目录,默认 outputs'), }, async args => { try { const out = await convertDocument({ workspaceDir, input: args.input, format: args.format, outDir: args.out_dir }) return { content: [{ type: 'text', text: `已生成:${path.relative(workspaceDir, out)}` }] } } catch (err) { return { content: [{ type: 'text', text: (err as Error).message }], isError: true } } }, ), tool( 'recalc_workbook', '用 LibreOffice 重新计算 Excel 文件中的所有公式并写回原文件,然后报告公式数量和错误单元格(#REF!、#DIV/0! 等)。' + '用 openpyxl 写入公式后必须调用它,否则文件里没有计算结果。不要在 Bash 里调用 soffice 或 recalc.py,它们在沙箱中无法运行。', { input: z.string().describe('xlsx/xlsm 文件路径(相对工作区或绝对路径)') }, async args => { try { const r = await recalcWorkbook({ workspaceDir, input: args.input, pythonRuntime }) const lines = [`已重算:${path.relative(workspaceDir, r.path)},公式 ${r.formulas} 个,错误 ${r.errorCount} 个`] for (const e of r.errors) lines.push(`- ${e.sheet}!${e.cell}: ${e.error}`) return { content: [{ type: 'text', text: lines.join('\n') }], isError: r.errorCount > 0 } } catch (err) { return { content: [{ type: 'text', text: (err as Error).message }], isError: true } } }, ), ], }) }