ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从零搭建MCP服务:用AI驱动Excel自动化处理实战

从零搭建MCP服务:用AI驱动Excel自动化处理实战 Excel 处理这件事几乎每个和数据打交道的人都绕不开。我做过一段时间的数据运营每天的工作就是打开十几个 Excel 文件复制、粘贴、筛选、汇总一套流程下来两三个小时就没了。后来学了 Python想着用脚本自动化结果发现写脚本本身也要花不少时间而且每次需求一变就得改代码。直到我开始接触 MCP才真正找到了一条既灵活又高效的路子——用 AI 来驱动 Excel 处理把那些重复性的操作交给模型去决策和执行。这篇文章要聊的就是怎么从零开始搭建一个属于自己的 MCP 服务让它能够理解你对 Excel 的操作意图自动完成数据读取、清洗、计算和输出。不管你是完全没接触过 MCP 的新手还是已经用过一些现成工具想深入定制的开发者下面的内容都能给你一条清晰的路径。我会把踩过的坑、试过的方案、最终跑通的流程都摊开来讲尽量让你少走弯路。1. 先搞清楚 MCP 到底解决了 Excel 处理中的什么问题1.1 传统 Excel 自动化的三条路各有各的痛点在动手写代码之前有必要先理一理我们到底在解决什么问题。Excel 自动化这件事市面上已经有很多方案了但每一种都有它的局限。第一条路是Excel 自带的 VBA 宏。这东西确实能自动化录制宏也很方便但问题在于 VBA 的语法老旧调试体验差而且一旦文件结构变化宏就很容易报错。更麻烦的是VBA 脚本很难和外部系统交互比如你想从数据库拉数据再写回 Excel用 VBA 写起来就非常别扭。第二条路是Python 脚本 openpyxl/pandas。这是目前最主流的方案灵活性高生态也成熟。但它的痛点在于每次业务逻辑变化你都得改代码、重新运行。比如今天要统计“销售额大于 5000 的订单数量”明天要改成“销售额大于 8000 且地区为华东的订单数量”你就得重新写一个脚本。对于非程序员来说这几乎不可行。第三条路是低代码平台的工作流。像一些在线表格工具或者 RPA 平台提供了拖拽式的流程编排。这种方式对小白友好但灵活性受限遇到复杂逻辑就抓瞎而且数据往往要上传到第三方平台有隐私顾虑。这三条路的共同问题是它们都要求你提前把逻辑写死。而现实中的 Excel 处理需求往往是模糊的、多变的、需要根据数据情况动态调整的。1.2 MCP 带来的范式转变让 AI 成为你的 Excel 操作助手MCP 的全称是 Model Context Protocol翻译过来叫“模型上下文协议”。你可以把它理解成一套标准化的接口规范让 AI 模型能够调用外部工具、访问外部数据。打个比方以前 AI 就像一个只会聊天的朋友你问它问题它能回答但它没法帮你动手做事。有了 MCP 之后AI 就像有了手和脚你告诉它“帮我把这个 Excel 里销售额超过 5000 的订单筛出来按地区汇总”它就能真的去操作文件、执行计算、返回结果。这里的关键在于你不需要提前把逻辑写死。你只需要用自然语言描述你的需求AI 会根据当前的数据情况自主决定调用哪些工具、按什么顺序执行。比如同样是“汇总销售额”如果数据里有缺失值AI 可以先调用清洗工具处理如果地区字段格式不统一AI 可以先做标准化。这种动态决策的能力是传统脚本和低代码平台做不到的。对于 Excel 处理来说MCP 的价值尤其明显。因为 Excel 数据的结构千变万化列名不统一、格式混乱、合并单元格、隐藏行……这些都是家常便饭。用固定脚本处理你得写一大堆异常处理逻辑而用 MCP AI模型可以根据实际情况灵活应对。1.3 为什么选择自己开发而不是用现成的你可能会问市面上不是已经有一些 Excel 相关的 AI 工具了吗为什么还要自己开发 MCP我的体会是现成工具通常只覆盖通用场景比如“读取表格”“生成图表”这类基础操作。但实际工作中每个团队的数据规范、业务逻辑、输出格式都不一样。比如我们团队要求所有报表必须包含“数据更新时间”和“数据来源”两个字段还要按照特定的命名规则保存到指定目录。这些个性化需求现成工具很难满足。自己开发 MCP 的好处在于你可以把团队内部的规范、常用的数据处理逻辑、特定的输出格式都封装成工具让 AI 按照你的规则来执行。而且一旦搭好框架后续增加新功能只需要添加新的工具函数扩展性非常好。2. 动手之前环境准备与核心依赖的选型逻辑2.1 Python 环境与包管理器的选择开发 MCP 服务Python 是最顺手的选择因为 MCP 的官方 SDK 对 Python 支持最好而且 Python 在数据处理领域的生态无可替代。Python 版本建议用3.10 或以上因为 MCP SDK 用到了一些较新的类型注解特性。安装 Python 本身没什么好说的官网下载安装包一路下一步就行。但包管理器我强烈建议用uv而不是 pip。uv 的速度比 pip 快一个数量级而且它能自动管理虚拟环境省去了手动创建 venv 的麻烦。安装 uv 的命令很简单pip install uv装好之后创建一个新项目目录然后初始化uv init excel-mcp-server cd excel-mcp-serveruv 会自动生成pyproject.toml文件后续添加依赖都用uv add命令它会自动更新依赖文件并安装。2.2 MCP SDK 与 Excel 处理库的搭配核心依赖有两个方向一是 MCP 协议相关的库二是 Excel 处理相关的库。MCP 方面安装官方 SDKuv add mcp这个包提供了服务端和客户端的实现我们主要用服务端部分把工具函数暴露出去。Excel 处理方面我推荐openpyxl pandas的组合。openpyxl 擅长处理单元格级别的操作比如读写特定单元格、设置格式、处理合并单元格pandas 擅长表格级别的操作比如筛选、分组、聚合、透视。两者配合使用基本能覆盖所有场景。uv add openpyxl pandas如果你的 Excel 文件包含复杂的公式或者需要保留原有格式可能还需要xlwings它可以直接调用 Excel 应用程序本身来操作文件。不过 xlwings 依赖本机安装 Excel在服务器环境不太适用按需选择。2.3 项目目录结构的设计思路一个清晰的项目结构能让后续维护轻松很多。我习惯这样组织excel-mcp-server/ ├── pyproject.toml ├── src/ │ └── excel_mcp/ │ ├── __init__.py │ ├── server.py # MCP 服务入口 │ ├── tools/ │ │ ├── __init__.py │ │ ├── read.py # 读取相关工具 │ │ ├── write.py # 写入相关工具 │ │ ├── transform.py # 数据转换工具 │ │ └── analyze.py # 分析统计工具 │ └── utils/ │ ├── __init__.py │ └── helpers.py # 通用辅助函数 └── tests/ └── test_tools.py这样分模块的好处是每个工具函数职责单一方便测试和复用。比如read.py里只放读取 Excel 的函数transform.py里只放数据清洗和转换的函数。当 AI 需要完成一个复杂任务时它会自动组合调用这些工具。3. 从零搭建 MCP 服务端核心代码逐层拆解3.1 初始化 MCP 服务与工具注册机制MCP 服务端的核心逻辑是创建一个 Server 实例然后把各个工具函数注册上去。每个工具函数都需要有清晰的名称、描述和参数定义因为 AI 是根据这些信息来决定调用哪个工具的。先看服务入口server.py的基本骨架from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent import asyncio app Server(excel-mcp-server) app.list_tools() async def list_tools(): return [ Tool( nameread_excel, description读取 Excel 文件返回指定工作表的数据, inputSchema{ type: object, properties: { file_path: {type: string, description: Excel 文件的绝对路径}, sheet_name: {type: string, description: 工作表名称不填则读取第一个} }, required: [file_path] } ), # 其他工具... ] app.call_tool() async def call_tool(name: str, arguments: dict): if name read_excel: result await handle_read_excel(arguments) return [TextContent(typetext, textresult)] # 其他工具的分发... async def main(): async with stdio_server() as (read_stream, write_stream): await app.run(read_stream, write_stream, app.create_initialization_options()) if __name__ __main__: asyncio.run(main())这里有几个关键点需要注意。工具描述要写得像给同事交代任务一样清楚。AI 是根据description字段来判断这个工具是干什么的。如果你只写“读取 Excel”AI 可能不确定它能不能读特定工作表、能不能处理大文件。所以描述里要尽量把能力边界说清楚。参数定义要完整。inputSchema遵循 JSON Schema 规范每个参数都要有类型和描述。对于可选参数不要放进required数组。AI 会根据这些定义来构造调用参数如果定义不清晰AI 可能会传错参数类型。返回值统一用 TextContent 包装。MCP 协议要求工具返回内容列表文本内容用TextContent包装。如果你返回的是结构化数据可以先转成 JSON 字符串再包装。3.2 读取工具让 AI 能“看见”Excel 里的数据读取是后续所有操作的基础。如果 AI 连数据长什么样都不知道就没法做任何决策。所以读取工具的设计要尽可能灵活。import pandas as pd from openpyxl import load_workbook async def handle_read_excel(args: dict) - str: file_path args[file_path] sheet_name args.get(sheet_name) try: # 用 pandas 快速读取数据 df pd.read_excel(file_path, sheet_namesheet_name or 0) # 返回数据概览列名、行数、前几行数据 info { columns: df.columns.tolist(), row_count: len(df), dtypes: {col: str(dtype) for col, dtype in df.dtypes.items()}, preview: df.head(5).to_dict(orientrecords) } return json.dumps(info, ensure_asciiFalse, indent2) except Exception as e: return f读取失败{str(e)}这里我特意没有返回全部数据而是返回了列名、行数、数据类型和前 5 行预览。原因是如果数据量很大把全部数据塞给 AI 会消耗大量 token而且 AI 也不需要看到每一行才能做决策。它只需要知道数据的结构然后根据需要再调用其他工具来获取具体数据。这个设计思路很重要工具的输出要精简且信息密度高。AI 的上下文窗口是有限的如果每个工具都返回一大堆数据很快就会把上下文撑爆。3.3 筛选与查询工具把自然语言变成数据操作筛选是 Excel 处理中最常用的操作。传统做法是写df[df[销售额] 5000]但有了 MCP你可以让 AI 根据自然语言来生成筛选条件。async def handle_filter_data(args: dict) - str: file_path args[file_path] sheet_name args.get(sheet_name) conditions args[conditions] # 例如 [{column: 销售额, op: , value: 5000}] df pd.read_excel(file_path, sheet_namesheet_name or 0) mask pd.Series([True] * len(df)) for cond in conditions: col cond[column] op cond[op] val cond[value] if op : mask df[col] val elif op : mask df[col] val elif op : mask df[col] val elif op : mask df[col] val elif op : mask df[col] val elif op contains: mask df[col].astype(str).str.contains(str(val), naFalse) elif op in: mask df[col].isin(val) result df[mask] return json.dumps({ matched_count: len(result), data: result.head(20).to_dict(orientrecords) }, ensure_asciiFalse, indent2)这个工具的设计要点在于把筛选条件抽象成结构化参数。AI 不需要生成 Python 代码只需要输出一个 JSON 数组来描述筛选条件。这样做的好处是安全可控不会因为 AI 生成错误代码导致意外行为。支持的运算符我列了一个表方便你对照实现运算符含义示例大于{column: 金额, op: , value: 1000}大于等于{column: 数量, op: , value: 10}小于{column: 库存, op: , value: 50}小于等于{column: 年龄, op: , value: 30}等于{column: 状态, op: , value: 已完成}contains包含关键词{column: 备注, op: contains, value: 紧急}in在列表中{column: 地区, op: in, value: [华东, 华南]}3.4 聚合统计工具分组汇总与透视分组汇总是我用得最多的功能。以前用 Excel 的数据透视表每次都要手动拖字段现在用 MCP直接说“按地区汇总销售额”就行。async def handle_group_aggregate(args: dict) - str: file_path args[file_path] sheet_name args.get(sheet_name) group_by args[group_by] # 分组列例如 [地区] agg_column args[agg_column] # 聚合列例如 销售额 agg_func args.get(agg_func, sum) # 聚合函数 df pd.read_excel(file_path, sheet_namesheet_name or 0) if agg_func sum: result df.groupby(group_by)[agg_column].sum() elif agg_func mean: result df.groupby(group_by)[agg_column].mean() elif agg_func count: result df.groupby(group_by)[agg_column].count() elif agg_func max: result df.groupby(group_by)[agg_column].max() elif agg_func min: result df.groupby(group_by)[agg_column].min() result result.reset_index() result.columns group_by [f{agg_column}_{agg_func}] return json.dumps({ group_count: len(result), data: result.to_dict(orientrecords) }, ensure_asciiFalse, indent2)这个工具支持多种聚合函数AI 会根据用户的描述自动选择。比如用户说“统计每个地区的订单数量”AI 就会用count说“计算每个地区的平均销售额”AI 就会用mean。3.5 写入与导出工具把处理结果落盘处理完数据之后最终还是要写回 Excel。写入工具需要支持两种模式覆盖原文件和另存为新文件。async def handle_write_excel(args: dict) - str: data args[data] # 要写入的数据列表形式 output_path args[output_path] sheet_name args.get(sheet_name, Sheet1) mode args.get(mode, new) # new 或 append df_new pd.DataFrame(data) if mode append and os.path.exists(output_path): df_old pd.read_excel(output_path, sheet_namesheet_name) df_final pd.concat([df_old, df_new], ignore_indexTrue) else: df_final df_new df_final.to_excel(output_path, sheet_namesheet_name, indexFalse) return f已写入 {len(df_final)} 行数据到 {output_path}写入操作要特别注意数据类型的一致性。比如日期字段如果 AI 传过来的是字符串写入 Excel 后可能变成文本格式后续再做日期计算就会出错。我的做法是在写入前做一次类型推断和转换确保数值列是数值类型日期列是日期类型。4. 让 AI 真正理解你的意图工具描述与提示词设计4.1 工具描述怎么写才能让 AI 选对工具工具描述是 AI 选择工具的唯一依据。如果描述写得含糊AI 就可能选错工具或者传错参数。我总结了几个写描述的原则。第一说清楚“做什么”和“不做什么”。比如读取工具的描述不要只写“读取 Excel”而要写“读取 Excel 文件并返回列名、行数和前几行预览数据。不返回全部数据如需获取具体数据请配合筛选工具使用”。这样 AI 就知道读取工具只负责概览不会期望它返回全部数据。第二参数描述要包含示例。比如conditions参数的描述可以写成“筛选条件列表每个条件包含 column列名、op运算符、value值三个字段。例如 [{column: 销售额, op: , value: 5000}]”。有了示例AI 构造参数时就不容易出错。第三说明返回值的格式。AI 需要知道工具返回的是什么才能决定下一步怎么处理。比如“返回 JSON 字符串包含 matched_count匹配行数和 data匹配数据列表两个字段”。4.2 系统提示词给 AI 立规矩除了工具描述系统提示词也很关键。它相当于给 AI 的一份工作手册告诉它在这个场景下应该怎么做事。我通常会在系统提示词里写清楚这几件事角色定位你是一个 Excel 数据处理助手负责帮助用户完成数据读取、清洗、筛选、汇总和导出任务。工作流程先读取文件了解数据结构再根据用户需求选择合适的工具最后汇总结果并说明做了什么。注意事项操作前确认文件路径正确处理大量数据时先筛选再读取写入文件前确认输出路径不会覆盖重要文件。输出格式用简洁的中文回复说明执行了哪些操作、得到了什么结果。如果数据量较大只展示摘要和关键数据。这些提示词看起来简单但能显著提升 AI 的表现。没有这些约束AI 可能会一次性读取全部数据、或者忘记确认文件路径就执行操作。4.3 多轮对话中的上下文管理MCP 服务通常是在多轮对话中使用的。用户可能先说“读取这个文件”然后说“筛选出销售额大于 5000 的”再说“按地区汇总”。AI 需要记住之前的操作和文件路径才能连贯地完成任务。这里有个坑AI 的上下文窗口有限如果每轮都把完整数据塞进去很快就会超限。我的做法是让工具只返回摘要信息具体数据通过文件路径来引用。比如读取工具返回列名和行数筛选工具返回匹配行数和前 20 条数据汇总工具返回汇总结果。这样即使经过多轮对话上下文也不会膨胀得太厉害。另外可以在系统提示词里要求 AI 在每次回复中简要复述当前的操作状态比如“当前文件sales.xlsx已筛选出 120 条记录接下来进行分组汇总”。这样即使用户中途切换话题再回来AI 也能快速恢复上下文。5. 实测中踩过的坑与解决方案5.1 中文列名与特殊字符的处理Excel 里的列名经常包含中文、空格、括号、斜杠等特殊字符。pandas 读取这些列名没问题但在构造筛选条件时如果 AI 传过来的列名和实际列名有细微差异比如多了个空格就会报 KeyError。我的解决方案是在读取工具里做一次列名标准化去掉首尾空格、统一全角半角、把连续空格替换成单个空格。然后在返回的列名列表里给出标准化后的名称AI 后续操作都用标准化后的列名。def normalize_columns(df): df.columns [ str(col).strip().replace( , ).replace( , ) for col in df.columns ] return df5.2 大文件读取的内存与性能问题有一次我处理一个 50 万行的 Excel 文件用 pandas 直接读取内存直接爆了。后来改成用 openpyxl 的只读模式逐行读取配合分块处理才解决了问题。from openpyxl import load_workbook def read_large_excel(file_path, sheet_nameNone, chunk_size10000): wb load_workbook(file_path, read_onlyTrue, data_onlyTrue) ws wb[sheet_name] if sheet_name else wb.active rows [] for i, row in enumerate(ws.iter_rows(values_onlyTrue)): rows.append(row) if len(rows) chunk_size: yield rows rows [] if rows: yield rows wb.close()对于超过 10 万行的文件我建议在读取工具里加一个判断如果文件行数超过阈值就提示 AI 先做筛选再读取避免一次性加载全部数据。5.3 日期格式的识别与转换Excel 里的日期格式五花八门有的是2024-01-15有的是2024/1/15有的是15-Jan-2024还有的是 Excel 内部的序列号比如45306。pandas 读取时经常把日期列识别成字符串或数字导致后续日期计算出错。我的做法是在读取工具里加一个日期列自动识别的逻辑尝试用pd.to_datetime转换如果转换成功率超过 80%就认定这是日期列并统一转换成标准格式。def auto_parse_dates(df): for col in df.columns: if df[col].dtype object: try: converted pd.to_datetime(df[col], errorscoerce) if converted.notna().sum() / len(df) 0.8: df[col] converted except: pass return df5.4 写入时格式丢失的问题用 pandas 的to_excel写入文件时原有的单元格格式比如字体、颜色、边框、列宽会全部丢失。如果用户对格式有要求这就很麻烦。解决方案有两种一是用 openpyxl 在写入后重新设置格式二是先用 openpyxl 加载原文件只修改数据区域保留其他格式。我通常用第二种方式from openpyxl import load_workbook def write_with_format(file_path, data, sheet_name, start_row2): wb load_workbook(file_path) ws wb[sheet_name] for i, row in enumerate(data): for j, value in enumerate(row): ws.cell(rowstart_row i, columnj 1, valuevalue) wb.save(file_path)这种方式适合在原有模板上填充数据的场景比如每月报表格式已经定好了只需要更新数据。6. 进阶玩法把 MCP 服务接入实际工作流6.1 与 AI 客户端对接的配置方式MCP 服务写好后需要接入 AI 客户端才能使用。不同的客户端配置方式略有差异但核心都是告诉客户端有一个 MCP 服务通过什么命令启动需要什么参数。以常见的配置文件为例通常是在客户端的配置文件中添加一段{ mcpServers: { excel-mcp: { command: uv, args: [--directory, /path/to/excel-mcp-server, run, python, -m, excel_mcp.server] } } }配置好之后重启客户端AI 就能发现这个 MCP 服务提供的工具了。你可以在对话中直接说“帮我读取 sales.xlsx 文件”AI 就会自动调用read_excel工具。6.2 组合多个工具完成复杂任务单个工具只能完成简单操作真正的价值在于组合。比如“把 sales.xlsx 里华东地区销售额大于 5000 的订单按产品类别汇总然后导出到新文件”这个任务AI 会自动分解成调用read_excel读取文件了解列名和数据结构。调用filter_data筛选出华东地区且销售额大于 5000 的记录。调用group_aggregate按产品类别汇总销售额。调用write_excel将结果写入新文件。整个过程不需要你写任何代码只需要用自然语言描述需求。AI 会根据工具描述自动选择调用顺序和参数。6.3 扩展新工具的思路与建议框架搭好之后增加新工具就是水到渠成的事。我后来陆续加了几个工具数据清洗工具处理缺失值、去重、格式标准化。图表生成工具根据数据生成柱状图、折线图、饼图保存为图片。多文件合并工具把多个结构相同的 Excel 文件合并成一个。条件格式工具根据规则给单元格设置颜色比如销售额低于目标值的标红。每加一个工具AI 的能力边界就扩大一圈。我的建议是从最常用的操作开始逐步扩展。不要一开始就追求大而全先把读取、筛选、汇总、写入这四个核心工具做扎实后续根据实际需求慢慢加。7. 关于性能与安全的一些实操心得7.1 工具调用的超时与重试MCP 工具调用是异步的如果某个操作耗时太长比如读取大文件可能会超时。我的做法是在工具函数里加超时控制对于可能耗时的操作先返回一个“任务已提交”的响应然后异步处理处理完再通知。不过大多数 Excel 操作都在秒级完成真正需要担心的是网络文件或者超大文件。对于本地文件一般不需要特别处理。7.2 文件路径的安全校验让 AI 操作文件有一个风险它可能会访问不该访问的文件或者覆盖重要文件。所以我在工具函数里加了路径校验import os ALLOWED_DIRS [/data/excel, /tmp/excel_workspace] def validate_path(file_path): abs_path os.path.abspath(file_path) if not any(abs_path.startswith(d) for d in ALLOWED_DIRS): raise ValueError(f路径不在允许范围内{abs_path}) return abs_path这样即使 AI 传了一个奇怪的路径也会被拦截下来。对于写入操作还可以加一个“文件已存在时是否覆盖”的确认机制。7.3 数据隐私的边界控制Excel 文件里经常包含敏感数据比如客户信息、财务数据。在使用 MCP AI 处理时要注意数据不会泄露到外部。我的做法是所有数据处理都在本地完成AI 只负责决策和调度不接触原始数据。具体来说工具函数在本地读取和处理数据只把摘要信息返回给 AI原始数据不出本地。另外在系统提示词里明确要求 AI 不要将数据内容复述到对话中只描述操作和结果。这样即使对话记录被保存也不会包含敏感数据。8. 我实际跑通后的几点体会这套 MCP 服务我已经在团队内部用了几个月处理了上百个 Excel 文件整体体验下来有几个感受。第一前期投入值得。搭建框架花了大概两天时间但后续每次处理新需求从原来的半小时缩短到几分钟。而且非技术同事也能用他们只需要在对话框里描述需求就行。第二工具描述的质量决定一切。我一开始工具描述写得很简单AI 经常选错工具或者传错参数。后来花时间把每个工具的描述写清楚包括能力边界、参数示例、返回值格式AI 的准确率立刻上了一个台阶。第三不要追求一步到位。我最初想做一个“万能 Excel 工具”结果发现工具太复杂AI 反而不知道怎么用。后来拆成多个单一职责的小工具每个工具只做一件事AI 的组合能力反而更强。第四日志很重要。我在每个工具函数里都加了日志记录记录调用时间、参数、执行结果。这样当 AI 行为不符合预期时可以查日志定位是工具的问题还是 AI 决策的问题。如果你也想搭一套自己的 MCP 服务我的建议是从最简单的读取工具开始跑通整个链路然后再逐步添加筛选、汇总、写入工具。每加一个工具就测试一下 AI 能不能正确调用确保每一步都扎实。这个过程本身也是对 MCP 协议理解不断加深的过程等你把四五个工具都跑通之后再回头看那些复杂的业务场景思路会清晰很多。
返回列表