资讯详情

从零开发MCP服务:用Python让AI直接处理Excel数据

📅 2026/11/2 8:59:21 | 华诺云谱 👁 阅读
从零开发MCP服务:用Python让AI直接处理Excel数据
1. 为什么我要自己动手写一个 MCP1.1 从一次崩溃的 Excel 合并说起上个月帮朋友处理一批销售数据二十多个 Excel 文件每个文件结构还不完全一样有的表头在第一行有的在第三行有的列名是销售额有的是销售金额。我一开始想用 Python 脚本硬写结果光是对齐列名就改了七八版中间还因为某个文件里混了个合并单元格直接报错中断。那天晚上我盯着屏幕想这种活儿明明有规律为什么每次都要重新写一遍胶水代码。后来接触到 MCP 这个概念才意识到问题的本质我缺的不是脚本而是一个能被 AI 直接调用的能力接口。传统做法是我写脚本、我调试、我运行AI 只能在旁边看着。而 MCP 的思路是我把读 Excel写 Excel统计某列关键词求和这些能力封装成标准工具AI 自己决定什么时候调用哪个工具、传什么参数。这就把我指挥 AI 写代码变成了AI 指挥我的工具干活效率完全不是一个量级。这篇文章就是记录我从零开发第一个 MCP 服务的完整过程。核心目标很明确用 Python 写一个能处理 Excel 的 MCP 服务让 AI 能直接读取表格、筛选数据、做统计、写回结果。适合有 Python 基础、被 Excel 重复劳动折磨过、想搞明白 MCP 到底怎么落地的人。如果你连 Python 都没装过建议先花半小时把环境搞定再回来后面会提到具体怎么装。1.2 MCP 到底是个什么东西先把概念说清楚不然后面全是糊涂账。MCP 全称 Model Context Protocol翻译过来叫模型上下文协议。你可以把它理解成 AI 和外部工具之间的USB 接口标准。以前每个 AI 应用要调用外部能力都得自己定一套私有协议A 家的插件 B 家用不了。MCP 就是把这个接口标准化了只要你的服务按 MCP 规范暴露能力任何支持 MCP 的 AI 客户端都能直接调用。它跟工作流的区别在哪像 Coze 工作流、ComfyUI 工作流那种是你提前把步骤编排好AI 按固定路线走。MCP 更灵活它只提供工具箱具体用哪个工具、按什么顺序用由 AI 在对话过程中动态决定。比如你说帮我把这三个表里销售额超过一万的记录挑出来合并AI 会自己判断先调读取工具、再调筛选工具、最后调写入工具。这个决策过程是它自己完成的不需要你预先画流程图。MCP 服务端一般暴露三类东西工具Tools就是可执行的动作比如读文件、算数据资源Resources就是可读取的数据比如某个文件的内容提示Prompts就是预设的指令模板。我们这次主要用工具这一类因为 Excel 处理的核心就是执行动作。注意MCP 是软件层面的协议跟硬件接口那个概念不是一回事。别被名字里的协议两个字带偏它本质是一套 JSON-RPC 风格的通信规范跑在本地进程或者网络连接上。2. 开发前的环境准备与工具选型2.1 Python 环境怎么装才不踩坑Python 安装这件事看着简单坑不少。我推荐直接用官网的安装包别用某些第三方整合包那些包经常自带一堆你用不上的库还容易和系统环境打架。去 python.org 下载 3.10 以上的版本安装的时候务必勾选Add Python to PATH这一步漏了后面命令行里敲 python 会提示找不到命令新手最容易卡在这。装完之后验证一下打开终端敲python --version pip --version两个都能正常输出版本号才算成功。如果 pip 报错多半是环境变量没配好重新跑一遍安装程序选Modify把 PATH 选项勾上。接下来建虚拟环境。这一步很多人嫌麻烦跳过结果项目依赖和系统 Python 混在一起后面出问题很难排查。我的习惯是每个项目单独建一个python -m venv mcp_excel_env # Windows mcp_excel_env\Scripts\activate # macOS / Linux source mcp_excel_env/bin/activate激活之后命令行前面会出现(mcp_excel_env)字样说明你在这个隔离环境里了。后面所有 pip 安装都装在这里面不会污染全局。2.2 核心依赖库的选择理由处理 Excel 的库有好几个我选的是openpyxl理由很实在它同时支持读写.xlsx能处理公式、样式、合并单元格而且纯 Python 实现不依赖 Excel 软件本身。pandas 虽然强大但它更适合做数据分析写回 Excel 时对格式的控制不如 openpyxl 精细。至于 xlrd它只读不写而且新版已经不支持 xlsx 了直接排除。MCP 服务端我用官方推荐的mcpPython SDK它把协议细节都封装好了我们只需要用装饰器定义工具函数就行。安装命令pip install mcp openpyxl如果你还想让 AI 能处理更复杂的表格统计可以顺手装上 pandaspip install pandas但注意pandas 读进来的 DataFrame 和 openpyxl 的 workbook 是两套对象混用的时候要清楚自己在操作哪个。我一般用 openpyxl 做读写用 pandas 做复杂聚合中间用列表或字典转换。2.3 目录结构规划别小看目录结构一开始乱放文件后面改起来想死。我的项目结构是这样的mcp-excel-server/ ├── server.py # MCP 服务主入口 ├── excel_tools.py # Excel 处理逻辑 ├── requirements.txt # 依赖清单 └── test_data/ # 测试用的 Excel 文件把业务逻辑和协议入口分开好处是以后想换协议实现比如从 stdio 换成别的传输方式业务代码一行不用动。requirements.txt里写清楚版本方便复现mcp1.0.0 openpyxl3.1.0 pandas2.0.0实操心得虚拟环境 requirements.txt 这套组合是我踩过无数次换台电脑就跑不起来的坑之后养成的习惯。多花两分钟省下两小时。3. 核心工具的设计与实现细节3.1 工具划分的思路MCP 服务好不好用关键看工具怎么切分。切得太粗一个工具干十件事AI 传参容易出错切得太细AI 要调十几次效率低还容易乱。我的划分原则是一个工具对应一个明确的、可独立验证的动作。针对 Excel 处理我设计了四个核心工具工具名功能输入输出read_excel读取表格内容文件路径、工作表名二维数组filter_rows按条件筛选行数据、列名、条件筛选后数据sum_by_keyword某列含关键词的对应列求和数据、关键词列、求和列、关键词求和结果write_excel写入数据到表格数据、输出路径成功状态这四个覆盖了读-筛-算-写的完整链路。你会发现sum_by_keyword这个工具很具体它对应的正是热搜里那个Excel 同一列中统计含关键词对应数据求和的需求。与其让 AI 自己写公式不如把这个高频操作封装成专用工具一次调用搞定。3.2 读取工具的实现与边界处理先看read_excel的实现。核心逻辑不复杂但边界情况必须处理from openpyxl import load_workbook def read_excel(file_path: str, sheet_name: str None) - list: wb load_workbook(file_path, data_onlyTrue) ws wb[sheet_name] if sheet_name else wb.active data [] for row in ws.iter_rows(values_onlyTrue): data.append(list(row)) wb.close() return data这里有几个关键点。data_onlyTrue很重要它让 openpyxl 读取公式的计算结果而不是公式本身。如果你不加这个参数读到的单元格值是SUM(A1:A10)这种字符串根本没法用。我第一次就栽在这统计出来全是公式文本排查了半天。sheet_name默认取活动工作表但实际文件里活动表不一定是你要的那张。所以我在工具描述里明确告诉 AI如果不确定表名先调用一个列出所有工作表名的工具。这就引出了第五个工具list_sheets虽然简单但很实用def list_sheets(file_path: str) - list: wb load_workbook(file_path, read_onlyTrue) names wb.sheetnames wb.close() return namesread_onlyTrue让 openpyxl 用只读模式打开大文件时内存占用低很多。一个几十兆的 Excel只读模式几秒就能列出表名普通模式可能要等十几秒。3.3 关键词求和工具的参数设计sum_by_keyword是这次的重头戏因为热搜里明确提到了这个需求。它的逻辑是在指定列里找包含关键词的行把这些行对应的另一列数值加起来。def sum_by_keyword(data: list, keyword_col: int, sum_col: int, keyword: str) - float: total 0.0 for row in data[1:]: # 跳过表头 cell row[keyword_col] if cell is not None and keyword in str(cell): value row[sum_col] if isinstance(value, (int, float)): total value return total参数用列索引而不是列名是因为列名可能有重复或者带空格AI 传参时容易出错。但列索引对人不友好所以我在工具的 schema 描述里写清楚keyword_col 是关键词所在列的索引从 0 开始计数。同时提供一个辅助工具get_headers返回表头让 AI 先看表头再决定索引。这里有个细节keyword in str(cell)用的是包含匹配不是相等匹配。因为实际数据里关键词往往嵌在长文本里比如华东区-销售额包含华东。如果你要精确匹配得改成。我在工具描述里注明了默认是包含匹配需要精确匹配时传一个exact参数。注意数值判断用isinstance(value, (int, float))因为 Excel 里有些单元格看着是数字实际是文本格式直接相加会报错。这个坑我在处理财务数据时踩过明明看着是 1000加进去变成字符串拼接了。3.4 写入工具的格式保留策略write_excel最容易出问题的地方是格式丢失。如果你用 openpyxl 新建一个 workbook 写入原来的字体、颜色、列宽全没了。我的做法是如果目标文件已存在先加载它再往指定位置写这样原有格式能保留。def write_excel(data: list, output_path: str, sheet_name: str None, start_row: int 1): import os if os.path.exists(output_path): wb load_workbook(output_path) ws wb[sheet_name] if sheet_name else wb.active else: from openpyxl import Workbook wb Workbook() ws wb.active if sheet_name: ws.title sheet_name for i, row in enumerate(data): for j, value in enumerate(row): ws.cell(rowstart_row i, columnj 1, valuevalue) wb.save(output_path) wb.close() return f已写入 {len(data)} 行到 {output_path}start_row参数是为了支持追加写入。比如你想把筛选结果写到原表下方传start_row原表行数2就行。这个参数不加的话每次写入都从第一行开始会覆盖原有数据。4. 把工具接入 MCP 服务端4.1 用装饰器注册工具MCP Python SDK 的用法很直观用server.tool()装饰器把普通函数变成 MCP 工具from mcp.server import Server from mcp.server.stdio import stdio_server server Server(excel-processor) server.tool() def read_excel(file_path: str, sheet_name: str None) - list: 读取 Excel 文件内容返回二维数组。sheet_name 不传则读活动工作表。 # ... 实现同上 server.tool() def sum_by_keyword(data: list, keyword_col: int, sum_col: int, keyword: str) - float: 在 keyword_col 列中查找包含 keyword 的行对 sum_col 列求和。 # ... 实现同上装饰器会自动读取函数的类型注解和文档字符串生成 MCP 协议需要的工具描述。所以类型注解一定要写全文档字符串要写清楚每个参数的含义。AI 就是靠这些信息判断该传什么参数的。我见过有人函数写得没问题但文档字符串就一句处理数据结果 AI 完全不知道该传什么调用一直失败。4.2 启动服务与传输方式MCP 服务启动用 stdio 传输也就是标准输入输出。这是本地开发最常用的方式AI 客户端把服务当子进程启动通过管道通信import asyncio async def main(): async with stdio_server() as (read_stream, write_stream): await server.run(read_stream, write_stream, server.create_initialization_options()) if __name__ __main__: asyncio.run(main())这段是模板代码基本不用改。server.run会进入事件循环等待客户端发来的请求。你不需要自己处理 JSON 解析、请求路由这些SDK 全包了。启动命令就是python server.py。但注意直接运行的话它会在那等着不会输出任何东西这是正常的。它是在等客户端连接。你要测试的话得用 MCP 客户端或者支持 MCP 的 AI 工具来连。4.3 在 AI 客户端里配置不同客户端的配置方式不一样但核心都是告诉它启动哪个命令来运行这个 MCP 服务。以常见的配置文件为例大概长这样{ mcpServers: { excel-processor: { command: python, args: [/path/to/mcp-excel-server/server.py] } } }command是启动命令args是参数。如果你用了虚拟环境command要指向虚拟环境里的 python比如/path/to/mcp_excel_env/bin/python否则可能找不到安装的依赖。这个路径问题是我调试时最常遇到的服务启动就报ModuleNotFoundError一查就是用了系统 python 而不是虚拟环境的。配置好之后重启客户端它应该能列出你的工具。你可以直接对话测试帮我读一下 test_data/sales.xlsx 里有哪些工作表看它会不会调用list_sheets。5. 实战演练一个完整的处理流程5.1 准备测试数据我造了一个销售数据表sales.xlsx结构如下区域产品销售额备注华东区A产品12000重点客户华北区B产品8500华东区调货华东区C产品15000新客户华南区A产品9800常规华东区B产品11000续约这个表故意设计了一个陷阱第三行备注列里也出现了华东区如果统计时不指定列可能会把备注里的关键词也算进去。这正是考验工具参数设计的地方。5.2 对话式调用全过程我在 AI 客户端里输入读取 sales.xlsx统计区域列中包含华东区的行的销售额总和。AI 的决策过程是这样的先调list_sheets确认表名再调read_excel拿到数据然后调get_headers确认区域在第 0 列、销售额在第 2 列最后调sum_by_keyword传参keyword_col0, sum_col2, keyword华东区。返回结果 38000120001500011000。整个过程我一句话没说参数AI 自己搞定的。这就是 MCP 的价值我把能力封装好AI 负责编排。如果换成传统脚本我得自己写if 华东区 in row[0]这样的判断还得处理表头、列索引这些琐事。5.3 处理结果写回接着我说把销售额大于 10000 的行筛选出来写到新文件 result.xlsx。AI 调filter_rows拿到筛选结果再调write_excel写入。这里有个细节filter_rows返回的数据包含表头因为写回时也需要表头。所以筛选工具的实现里我特意保留了第一行def filter_rows(data: list, col: int, threshold: float) - list: result [data[0]] # 保留表头 for row in data[1:]: value row[col] if isinstance(value, (int, float)) and value threshold: result.append(row) return result如果不保留表头写出来的文件第一行就是数据看着很别扭。这种小细节只有真正用过的人才会注意到。实操心得工具之间的数据格式要统一。我所有工具都约定第一行是表头这样工具可以随意串联不用每次转换格式。这个约定写在每个工具的文档字符串里AI 也能理解。6. 常见问题与排查技巧6.1 服务启动失败怎么查最常见的问题是依赖找不到。排查顺序先确认虚拟环境激活了没再确认pip list里有没有 mcp 和 openpyxl最后确认客户端配置里的 python 路径是不是虚拟环境的。我遇到过配置文件里写了相对路径结果客户端工作目录不对找不到 server.py改成绝对路径就好了。第二个常见问题是端口或管道冲突。stdio 方式一般不会冲突但如果你同时开了多个客户端连同一个服务可能会互相干扰。解决办法是每个客户端配一个独立的服务实例。6.2 工具调用报参数错误AI 传参错误通常是因为工具描述不清楚。比如keyword_col我写的是列索引AI 可能传列名区域进来。后来我在描述里加了示例例如区域在第 0 列就传 0错误率明显下降。还有一种情况是数据类型不匹配。AI 可能把数字传成字符串比如threshold10000而不是10000。我在函数内部加了类型转换兜底threshold float(threshold)这样即使 AI 传字符串也能正常工作。防御性编程在 MCP 开发里特别重要因为你无法控制 AI 怎么传参。6.3 大文件处理超时Excel 文件超过几万行时读取和写入会变慢。我的优化手段是读取用read_onlyTrue写入用write_onlyTrue如果不需要保留格式。另外可以在工具里加一个max_rows参数只读前 N 行做预览确认没问题再全量处理。问题现象可能原因解决办法启动报 ModuleNotFoundErrorpython 路径不对配置里用虚拟环境绝对路径读到公式文本而非结果没加 data_onlyload_workbook 加 data_onlyTrue求和结果为 0单元格是文本格式加 isinstance 判断或转换写入后格式丢失新建了 workbook先加载原文件再写AI 传参类型错误描述不清晰文档字符串加示例函数内做转换6.4 工具描述怎么写才让 AI 不迷糊这是最容易被忽视但影响最大的环节。好的工具描述要包含三要素做什么、参数含义、返回什么。我对比过两种写法差的写法筛选数据好的写法按数值条件筛选行。col 是列索引从0开始threshold 是阈值返回包含表头的筛选结果。后者 AI 一次就能传对参数前者要试好几次。工具描述就是给 AI 看的说明书写得越清楚它用得越准。7. 后续可以怎么扩展这套基础框架搭好之后扩展方向很多。比如接入 pandas 做分组聚合封装一个group_by_sum工具或者加一个merge_sheets工具把多个工作表按某列合并。热搜里提到的markdown 表格转换 Excel也可以做成一个工具输入 markdown 文本输出 xlsx。再往深了走可以结合其他 MCP 服务做联动。比如一个负责读数据库的 MCP一个负责处理 Excel 的 MCPAI 可以先把数据库查出来再调 Excel 工具生成报表。这就是 MCP 生态的威力每个服务专注一件事AI 负责把它们串起来。我自己下一步打算加的是条件格式工具让 AI 能根据数值自动给单元格上色。这个用 openpyxl 的PatternFill就能实现但参数设计要花点心思得让 AI 容易表达大于 10000 标红这种规则。最后分享一个我调试 MCP 的小技巧在工具函数里加日志输出到文件因为 stdio 传输时 print 会干扰协议通信。用logging写到单独的文件出问题时翻日志比猜快得多。这个习惯帮我省了大量排查时间。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑