关于 xlsx-tools-mcp
xlsx-tools-mcp 是面向 MCP 客户端的 Excel .xlsx 读写服务,适用于需要让 Agent 操作工作簿的场景。它支持保留未修改内容,并可用 LibreOffice 重算公式。
主要功能
- 01读取单元格、搜索与汇总数据
- 02写入值和公式并管理工作表
- 03创建 Excel 表格与图表
- 04样式、合并单元格等结构保留
- 05可选公式重算与错误扫描
- 06访问目录限制和自动备份
适用场景
- 01让 Agent 查询工作簿数据
- 02批量更新单元格或公式
- 03维护工作表、行列与格式
- 04重算公式并检查错误
MCP 相关
作为 stdio MCP 服务,向 MCP 客户端提供 29 种 Excel 文件读写工具,包括读取、查询、编辑和工作表管理。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx xlsx-tools-mcpXLSX_MCP_FILESComma or semicolon-separated alias=path pairs of workbooks to preload at startupXLSX_MCP_RECALC_TIMEOUTTimeout in seconds for the LibreOffice recalculation pass (default 60)XLSX_MCP_LOCK_TIMEOUTTimeout in seconds to acquire the per-file lock (default 10)XLSX_MCP_ALLOWED_DIRSComma or semicolon-separated list of allowed directories to restrict file access (supports case-insensitivity on Windows)XLSX_MCP_AUTO_BACKUPEnable automatic backup creation before workbook mutations (default true)XLSX_MCP_BACKUP_DIROptional custom directory to store backup files instead of beside the target file本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
29收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| backup_path | string | 是 | |
| target_path | string | 是 |
详细介绍
xlsx-tools-mcp
一个用于高精度读取和写入 Excel(.xlsx)文件的 MCP 服务,同时保留文件现有的结构、样式和公式。
概述
xlsx-tools-mcp 提供 29 个模型上下文协议(MCP)工具,让大模型 Agent 能够准确地读取和写入 Excel .xlsx 文件,同时保留其结构。它作为标准的 stdio MCP 服务运行:安装后,将其注册到 MCP 客户端(Claude Code、OpenCode 等),客户端的 Agent 即可列出工作表、读取单元格范围、搜索值、汇总数据、写入单元格和公式、管理工作表/行/列、应用样式,以及强制重新计算公式。
它秉持的原则是:编辑现有工作簿时,不应破坏未触及的内容。
功能
- 通过 openpyxl 保留结构地写入 — 写入时会加载现有工作簿并保存回去,保留样式、合并单元格、批注,以及编辑未触及的所有内容。
- 通过 LibreOffice 重新计算,确保公式结果始终最新 — openpyxl 会写入公式字符串,但不会计算公式。每次写入值或公式后,服务都会运行无头 LibreOffice 进程重新计算真实结果,然后返回
errors_found— 重新计算产生的任何 Excel 错误值(#REF!、#DIV/0!、#N/A等)。 - 通过 python-calamine 快速读取 — 这是一个由 Rust 驱动的解析器,可准确、快速地推断类型;当需要公式、样式或批注,或者 calamine 无法解析文件时,会自动回退到 openpyxl。
- 基于 pandas 的分组、聚合与查询 —
aggregate_sheet和query_sheet可在不破坏源结构的情况下执行操作,并通过 AST 表达式验证阻止代码注入。 - 原生 Excel 对象 — 创建带自动筛选和样式的正式 Excel 表格(
create_table),以及原生图表(create_chart,支持条形图、折线图、饼图和散点图)。 - 工作表生命周期与格式设置 — 重命名工作表(
rename_sheet)、复制并保留样式(copy_sheet)、清除区域(clear_range),以及自动调整列宽(autofit_columns)。 - 跨平台可靠性(Windows、macOS、Linux) — 不区分大小写的路径限制、逗号或分号
;环境变量分隔符、安全的原子文件替换并在临时锁定时重试(防范WinError 32/ 防病毒软件干扰),以及不会导致窗口闪烁的静默无头重新计算。 - 按文件加锁 — 通过同级
<path>.lock文件(filelock)串行处理针对同一工作簿的并发工具调用(或其他进程),确保写入不会交错并损坏文件。 - 路径限制 —
XLSX_MCP_ALLOWED_DIRS(以逗号或分号分隔)将所有读写操作限制在指定目录内。设置后,任何访问这些边界之外文件的尝试都会引发AccessDeniedError。支持不区分大小写的路径比较和 Windows 上的多驱动器路径。 - 自动备份与还原 — 如果
backup=True(默认值由XLSX_MCP_AUTO_BACKUP=true设置),写入工具会在修改工作簿前自动创建同级.bak文件。使用restore_backup可原子地还原更改。 - 公式注入防护 — 默认情况下,
write_cells和append_rows会拒绝包含WEBSERVICE、HYPERLINK、INDIRECT、RTD、CALL、REGISTER或 DDE 命令(|)的公式,以防止数据外泄或恶意链接。传入allow_external_formulas=True可绕过此检查。 - XML 炸弹防护 —
defusedxml包是自动安装的依赖项;openpyxl 会检测到它并使用经过强化的 XML 解析器,防止恶意xlsxXML 扩展并耗尽资源。 - 启动时预加载文件 — 设置
XLSX_MCP_FILES可预加载一个或多个工作簿(以逗号或分号分隔);之后调用工具时可以省略path,或使用简短别名代替完整文件系统路径。
下载统计
数据由 pypistats.org 实时提供,统计非镜像下载量。这些数据统计的是下载事件,而非独立用户或安装次数——单个用户可能触发多次下载(CI、重新安装、Docker 重建、镜像)。
架构
┌──────────────────────── Supervisor (MCP transport, stdio)
│ src/xlsx_tools_mcp/server.py 29 MCP tools + instructions
│ src/xlsx_tools_mcp/settings.py env vars, preloaded files, path resolution
│ src/xlsx_tools_mcp/locking.py per-file <path>.lock serialization
│ src/xlsx_tools_mcp/errors.py domain error types
│ src/xlsx_tools_mcp/recalc.py LibreOffice headless recalc + error scanning
│
├─ Read path
│ src/xlsx_tools_mcp/io/reader.py calamine primary → openpyxl fallback
│ src/xlsx_tools_mcp/io/transform.py pandas aggregation on read results
│
└─ Write path
src/xlsx_tools_mcp/io/writer.py openpyxl → LibreOffice recalc → scan errors
io 层(io/)与 MCP 传输层(server.py)刻意解耦。每个 MCP 工具都是一个轻量包装器,负责解析目标路径、获取按文件设置的锁,并调用一个 io 层函数。这样核心逻辑就独立于 MCP,可以直接进行测试(参见 tests/)。
重新计算的权衡
写入涉及单元格值或公式的内容后,服务会对文件运行 soffice --headless --convert-to xlsx,以便为每个公式计算出实际值。此往返操作会重新计算公式,但也会重新导出整个工作簿——这是一种权衡,并不保证完美保留所有内容。openpyxl 原本可以保留的某些功能可能无法保持原样:数据透视表、图表、数据验证、某些格式和某些定义名称。
如果你正在处理结构复杂且这类风险不容忽视的工作簿,可以在值/公式写入工具(write_cells、append_rows、insert_rows、delete_rows、insert_columns、delete_columns)中传入 recalculate=False,仅使用 openpyxl 保存并完全跳过往返操作。
要求
- Python ≥ 3.10
- LibreOffice——可选,但建议安装。仅公式重新计算需要它。没有 LibreOffice 时,写入仍会成功(通过 openpyxl 保存),但公式不会重新计算,并且会在
message字段中返回警告。
安装 LibreOffice:
# macOS
brew install --cask libreoffice
# Debian / Ubuntu
sudo apt-get install -y libreoffice-calc
# Windows
winget install TheDocumentFoundation.LibreOffice
服务会通过以下方式查找 LibreOffice:检查 PATH 中的 soffice / libreoffice、macOS 标准安装位置(/Applications/LibreOffice.app/Contents/MacOS/soffice),以及 Windows 标准路径(%ProgramFiles%\LibreOffice\program\soffice.exe 等)。在 Windows 上,无窗口闪烁(CREATE_NO_WINDOW)且使用隔离的用户配置文件静默运行无界面重新计算。
安装
服务使用 stdio 传输(标准 MCP):安装后会等待 MCP 客户端连接并调用工具。通常无需自行运行;只需在客户端中注册即可。
1. 通过 PyPI 使用 uvx(推荐——无需克隆)
uvx xlsx-tools-mcp
uvx 会获取并运行已发布的软件包,不会污染你的项目。这是为 MCP 客户端启用该服务最简单的方式(见下方的配置片段)。
2. 从源代码安装
git clone https://github.com/ruriazz/xlsx-tools-mcp.git
cd xlsx-tools-mcp
uv sync
# run the server (useful for local dev / debugging):
uv run xlsx-tools-mcp
3. 通过 pip 安装
pip install xlsx-tools-mcp
此操作会安装控制台入口,因此你可以直接运行服务:
xlsx-tools-mcp
MCP 客户端配置
对所有客户端来说,最简单的注册方式是使用 uvx xlsx-tools-mcp(无需克隆,始终使用已发布的版本)。
Windows 提示: 在客户端的 JSON 配置文件(
.mcp.json、opencode.json)中,请使用正斜杠(例如"C:/path/to/data.xlsx")或经过转义的反斜杠(例如"C:\\\\path\\\\to\\\\data.xlsx"),以避免 JSON 转义序列错误。
Claude Code
claude mcp add xlsx-tools-mcp -- uvx xlsx-tools-mcp
也可以通过项目中的 .mcp.json 配置:
{
"mcpServers": {
"xlsx-tools-mcp": { "command": "uvx", "args": ["xlsx-tools-mcp"] }
}
}
OpenCode
在项目级 opencode.json 或全局 ~/.config/opencode/opencode.json 中:
{
"mcp": {
"xlsx-tools-mcp": { "type": "local", "command": ["uvx", "xlsx-tools-mcp"], "enabled": true }
}
}
从源代码克隆运行
如果你克隆了仓库,而不是从 PyPI 安装,请将客户端指向本地检出目录,将 uvx xlsx-tools-mcp 替换为动态的 uv run 形式(请使用克隆目录的绝对路径):
Claude Code .mcp.json:
{
"mcpServers": {
"xlsx-tools-mcp": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/xlsx-reader", "run", "xlsx-tools-mcp"]
}
}
}
OpenCode:
{
"mcp": {
"xlsx-tools-mcp": {
"type": "local",
"command": ["uv", "--directory", "/absolute/path/to/xlsx-reader", "run", "xlsx-tools-mcp"],
"enabled": true
}
}
}
将 /absolute/path/to/xlsx-reader 替换为克隆目录的实际位置。
预加载文件(XLSX_MCP_FILES)
在 MCP 服务配置的 env 部分设置 XLSX_MCP_FILES 环境变量(而不是在交互式 shell 中设置——服务由客户端启动),即可在启动时预加载工作簿。格式:以逗号或分号分隔的 alias=absolute/path 条目,或不带别名的绝对路径:
XLSX_MCP_FILES=name=/abs/path/to/name.xlsx,report=/data/report.xlsx
不带别名的路径会默认使用文件名作为别名:
XLSX_MCP_FILES=/abs/path/to/sales.xlsx
将 alias/filename 作为别名时:
- 配置了一个文件 → 调用任何工具时都可以完全省略
path。 - 配置了多个文件 → 将别名(或文件名)作为
path传入。 list_configured_files()会返回别名到绝对路径的映射。- 对未预加载的文件,仍可使用原始绝对路径和相对路径。
Claude Code——使用预加载的 .mcp.json:
{
"mcpServers": {
"xlsx-tools-mcp": {
"command": "uvx",
"args": ["xlsx-tools-mcp"],
"env": {
"XLSX_MCP_FILES": "report=/data/report.xlsx,sales=/data/sales.xlsx"
}
}
}
}
使用预加载的 OpenCode:
{
"mcp": {
"xlsx-tools-mcp": {
"type": "local",
"command": ["uvx", "xlsx-tools-mcp"],
"env": { "XLSX_MCP_FILES": "report=/data/report.xlsx,sales=/data/sales.xlsx" },
"enabled": true
}
}
}
工具参考
全部 29 个工具。除非另有说明,path 接受文件系统路径、预加载的别名或文件名;如果恰好预加载了一个文件,也可以省略。create_workbook 是例外——其 path 为必填项,因为新文件不会预加载。
响应结构(所有写入工具): 每个写入工具都会返回
{"saved": bool, "recalculated": bool, "errors_found": list, "message": str}。当errors_found非空时,它是一个包含{"sheet": "...", "cell": "B2", "error": "#DIV/0!"}的列表。
检查 / 读取
| 工具 | 描述 |
|---|---|
list_configured_files() | 列出通过 XLSX_MCP_FILES 在启动时预加载的文件,返回别名 → 绝对路径映射。不确定有哪些文件可用时,请先调用此工具。 |
list_sheets(path?) | 列出工作簿中的所有工作表及其近似行列数(calamine)。 |
get_workbook_info(path?) | 工作簿级元数据:每个工作表的精确尺寸、max_row/max_column、工作表状态、活动工作表和已定义名称。 |
read_sheet(sheet, cell_range?, max_rows?, offset_row?, format?, path?) | 读取单元格值。cell_range 用于限制读取区域。max_rows 用于限制行数。offset_row 用于分页时跳过指定行数。format 可以是 "array"(二维列表)、"records"(以首行为键的字典列表)或 "markdown"(表格字符串)。 |
get_cell(sheet, cell, path?) | 获取单个单元格的完整详情:值(缓存的计算结果)、公式、数字格式、字体(粗体/斜体/字号/颜色)、填充颜色、合并状态和批注。 |
search_workbook(query, sheet?, match_case?, limit?, path?) | 在一个或所有工作表中进行子字符串搜索。sheet 用于限定单个工作表;match_case=True 表示区分大小写;limit 用于限制匹配数量。返回 {"sheet", "cell", "value"}。 |
aggregate_sheet(sheet, group_by, agg, cell_range?, has_header?, path?) | 使用 pandas 进行分组和汇总。group_by 是列名列表(取自标题行);agg 将列名映射到聚合函数,例如 {"amount": "sum"}。has_header=True(默认值)表示从首行读取列名。返回 {columns, records, row_count}。 |
profile_sheet(sheet, sample_rows?, path?) | 分析工作表元数据:推断类型、空值数量、最小值/最大值和样本值。 |
query_sheet(sheet, filter_query, columns?, max_rows?, cell_range?, path?) | 使用 pandas 表达式筛选工作表数据,并通过 AST 强化代码注入防护。 |
写入
| 工具 | 描述 |
|---|---|
create_workbook(path, sheets?, overwrite?) | 创建新的 .xlsx/.xlsm 工作簿。sheets 默认为 ["Sheet1"]。overwrite=True 会替换现有文件。path 必填(新文件不会预先加载)。 |
write_cells(sheet, cells, create_sheet_if_missing?, recalculate?, allow_external_formulas?, backup?, path?) | 向指定单元格写入值和/或公式。cells 是一个列表,其中包含 {"cell": "A1", "value": ...} 或 {"cell": "B1", "formula": "=A1*2"}。可选择先创建工作表;recalculate=True(默认值)会运行 LibreOffice 重新计算。 |
append_rows(sheet, rows, create_sheet_if_missing?, recalculate?, allow_external_formulas?, backup?, path?) | 在最后一个已使用行之后追加行。rows 是一个行列表,每行都是按列顺序排列的单元格值列表。 |
create_sheet(sheet, index?, path?) | 添加新的空工作表。index 是从零开始的插入位置;省略时会追加到末尾。 |
delete_sheet(sheet, path?) | 删除工作表。如果这是剩下的唯一工作表,则操作失败。 |
rename_sheet(old_name, new_name, path?) | 重命名现有工作表。 |
copy_sheet(source_sheet, target_sheet, path?) | 复制工作表及其内容、公式和样式。 |
insert_rows(sheet, start_row, count?, recalculate?, path?) | 在 start_row(从 1 开始计数)之前插入空行,将现有行向下移动。count 默认为 1。 |
delete_rows(sheet, start_row, count?, recalculate?, path?) | 删除从 start_row(从 1 开始计数)开始的行,将下方的行向上移动。count 默认为 1。 |
insert_columns(sheet, start_column, count?, recalculate?, path?) | 在 start_column(从 1 开始计数)之前插入空列,将现有列向右移动。count 默认为 1。 |
delete_columns(sheet, start_column, count?, recalculate?, path?) | 删除从 start_column(从 1 开始计数)开始的列,将右侧的列向左移动。count 默认为 1。 |
merge_cells(sheet, cell_range, path?) | 将矩形区域(例如 "A1:C1")合并为一个单元格。 |
unmerge_cells(sheet, cell_range, path?) | 取消合并先前已合并的区域。 |
set_cell_style(sheet, cell_range, style, path?) | 为区域(例如 "A1:D1")应用格式。style 键:bold、italic、font_size、font_color(十六进制 RGB,例如 "FF0000")、bg_color(十六进制 RGB)、horizontal、vertical(对齐方式)、border("thin"、"medium"、"thick" 等)、number_format(例如 "#,##0.00")。 |
autofit_columns(sheet, min_width?, max_width?, padding?, path?) | 根据最大文本长度自动调整列宽,以防内容被截断。 |
clear_range(sheet, cell_range, clear_values?, clear_styles?, path?) | 清除指定区域内的单元格值和/或样式,但不删除行或列。 |
create_table(sheet, cell_range, table_name, style_name?, show_filter?, show_row_stripes?, path?) | 在区域上创建正式的 Excel 表格。 |
create_chart(sheet, chart_type, data_range, categories_range?, title?, target_cell?, path?) | 添加原生 Excel 图表(柱形图、折线图、饼图、散点图)。 |
recalculate_workbook(path?) | 强制执行 LibreOffice 无头重新计算,并报告发现的公式错误。 |
restore_backup(backup_path, target_path) | 将 .bak 备份文件原子性地还原到目标工作簿。 |
示例载荷 — write_cells
写入一个公式和一个值的调用:
{
"sheet": "Sheet1",
"cells": [
{ "cell": "A1", "value": 100 },
{ "cell": "B1", "formula": "=A1*2" }
],
"recalculate": true,
"path": "/data/budget.xlsx"
}
对应的响应:
{
"saved": true,
"recalculated": true,
"errors_found": [],
"message": "Recalculated with LibreOffice headless."
}
如果此操作涉及的公式产生错误,errors_found 将如下所示:
{
"saved": true,
"recalculated": true,
"errors_found": [
{ "sheet": "Sheet1", "cell": "C5", "error": "#DIV/0!" }
],
"message": "Recalculated with LibreOffice headless."
}
示例载荷 — query_sheet
{
"sheet": "Sales",
"filter_query": "Revenue > 500 and Status == 'PAID'",
"columns": ["Customer", "Revenue"],
"max_rows": 10
}
示例载荷 — create_table 和 create_chart
{
"sheet": "Summary",
"cell_range": "A1:D10",
"table_name": "SalesTable",
"style_name": "TableStyleMedium9"
}
{
"sheet": "Summary",
"chart_type": "bar",
"data_range": "B1:B10",
"categories_range": "A2:A10",
"title": "Monthly Revenue",
"target_cell": "F2"
}
环境变量
| 变量 | 默认值 | 描述 |
|---|---|---|
XLSX_MCP_FILES | 空 | 以逗号或分号分隔的 alias=path 或 path,用于在启动时预加载。 |
XLSX_MCP_ALLOWED_DIRS | 空 | 以逗号或分号分隔的目录,用于限制文件访问范围(访问路径之外的文件会引发 AccessDeniedError)。在 Windows 上支持不区分大小写。 |
XLSX_MCP_AUTO_BACKUP | true | 修改工作簿前自动创建 .bak 副本。 |
XLSX_MCP_BACKUP_DIR | 空 | 备份文件的自定义目录(默认位于目标文件旁)。 |
XLSX_MCP_RECALC_TIMEOUT | 60 | 使用无头模式的 LibreOffice 重新计算公式时的超时时间(秒)。 |
XLSX_MCP_LOCK_TIMEOUT | 10 | 获取每个文件的锁后再报错前的超时时间(秒)。 |
安全与并发
- 路径限制 —
XLSX_MCP_ALLOWED_DIRS(以逗号或分号分隔)会将所有读取和写入限制在指定目录内。设置后,任何访问这些目录之外文件的尝试都会引发AccessDeniedError。支持在 Windows 上进行不区分大小写的路径比较及多驱动器路径。 - 自动备份与还原 — 如果
backup=True(默认值由XLSX_MCP_AUTO_BACKUP=true决定),写入工具会在修改工作簿前自动创建一个同目录的.bak文件。使用XLSX_MCP_BACKUP_DIR将备份保存到指定文件夹。使用restore_backup可原子地还原更改。 - 公式注入防护 — 默认情况下,
write_cells和append_rows会拒绝包含WEBSERVICE、HYPERLINK、INDIRECT、RTD、CALL、REGISTER或 DDE 命令(|)的公式,以防止数据外泄或命令执行。传入allow_external_formulas=True可绕过此检查。 - AST 安全查询验证 —
query_sheet使用 AST 解析器,仅允许比较、布尔逻辑和安全运算符,阻止@变量查找、代码执行调用和私有双下划线属性。 - XML 炸弹防护 —
defusedxml是此软件包的自动依赖项。openpyxl 会自动检测并使用其强化的 XML 解析器,因此恶意.xlsx(XML 文件的压缩包)无法通过实体扩展开启资源耗尽攻击。无需配置。 - 逐文件锁定 — 每次读取或写入都会获取同级
<path>.lock文件(通过filelock)。并发工具调用或其他进程对同一工作簿的操作会依次执行,因此写入不会交错并损坏文件。 - 瞬时锁冲突恢复能力 — 在 Windows NTFS 上,杀毒软件或搜索索引器检查新保存的文件时,原子文件交换可能会短暂遇到共享冲突(
WinError 32/33)。服务会执行指数退避重试,在文件解除占用后自动成功;如果文件一直被 Microsoft Excel 打开,则会提供清晰的指导信息。 - 重新计算超时 —
XLSX_MCP_RECALC_TIMEOUT(秒,默认值为60)限制 LibreOffice 重新计算流程的运行时长。 - 锁超时 —
XLSX_MCP_LOCK_TIMEOUT(秒,默认值为10)限制工具获取逐文件锁前的等待时长,超时后将报错。
故障排除
- 尽管公式有误,
errors_found仍为空 — 可能未运行重新计算。检查message字段:如果其中写着未找到 LibreOffice,则文件已通过 openpyxl 原样保存,公式未重新计算(缓存值可能已过时)。请安装 LibreOffice(参见要求)。 - 重新计算速度慢或超时 — 提高
XLSX_MCP_RECALC_TIMEOUT(默认值为 60s)。超时时,文件仍会保存,但recalculated将为false,且message会说明重新计算超时。 - 并发访问时出现
LockTimeoutError— 另一个操作持有锁。提高XLSX_MCP_LOCK_TIMEOUT(默认值为 10s),或等另一个操作完成后重试。 Workbook is locked by another process/WinError 32— 在 Windows 上,Microsoft Excel 和某些应用会独占锁定打开的文件。请在 Excel 中关闭工作簿后重试。服务器会在失败前自动重试由防病毒扫描程序或搜索索引器造成的临时锁定。- “找不到工作表” — 错误消息会列出可用的工作表名称,供你选择正确的工作表。
- 需要
path/ 未配置文件 — 你调用工具时未提供path,且没有(或配置了多个)预加载文件。请通过XLSX_MCP_FILES预加载一个文件、传入明确的别名,或传入原始路径。
开发 / 贡献
请参阅 CONTRIBUTING.md。运行测试套件:
uv run pytest
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。