
一 背景在很长一段时间里我对MCP (Model Context Protocol)的认知还停留在概念层面。坦白说迟迟没有动手开发主要是因为没有找到一个既有实际痛点、又适合本地练手的刚需场景。直到最近一个高频的日常需求摆在了面前业务场景需要定期从多个本地 Excel 文档中提取特定列的数据重新组装生成新表格最后通过邮件发送给指定人员。实现权衡虽然用传统 Python 脚本也能快速堆一个出来但每次都要切到终端手动敲命令显得不够优雅也无法让 AI 助手直接替我代劳。为了彻底打通大模型与本地文件系统的交互链路顺便系统性地掌握 MCP 的开发全貌我决定从零到一动手打造一个定制化的生产力工具——smart-excel-mcp。二 实现思路2.1 整体架构与技术选型通信协议层采用模型上下文协议Model Context Protocol, MCP的Stdio 传输模式。由 IDE 客户端如 Antigravity、Cursor 等在本地按需拉起 Python 进程实现零配置、安全且高效的网络开销交互。核心框架基于 Python 官方mcpSDK配合MCPServer构建服务。数据处理层依托pandas与openpyxl实现高效的文件扫描、动态表头解析、列过滤及带有高精度时间戳的新表归档。邮件服务层基于内置的smtplib与email.mime模块采用SSL 安全加密传输对接主流企业/个人邮箱如 126.com 等。2.2 核心业务模块拆分2.2.1 智能路径解析与文件扫描模块需求痛点用户往往难以精准记忆文件的绝对路径且人工盘点目录效率低下。实现方案默认路径策略内置标准工作目录映射如E:/work。当用户未指定目录时支持智能引导或默认扫描。递归遍历过滤利用os.walk深度遍历目标路径自动屏蔽 Office 产生的临时缓存锁文件以~$开头的临时文件精准锁定.xlsx与.xls格式。2.2.2 防御性字段提取与清洗模块需求痛点源表格表头常伴随前后空格、特殊字符或拼写差异极易导致程序崩溃。实现方案双向对齐清洗锁定有效表头行后对原表列名与用户输入的列名统一执行.strip()清洗。防御性容错校验对用户指定的列名进行严格校验。若存在缺失列主动抛出明确的错误提示并返回当前文件的“全量可用字段列表”驱动大模型自动纠偏。2.2.3 内存态动态缓存与时间戳自动归档模块需求痛点处理后的报表需要规范命名沉淀且后续的邮件发送工具必须能够自动关联最新文件避免繁琐的手动复制。实现方案高精度时间戳归档提取原文件名与扩展名利用datetime.now().strftime(%y%m%d%H%M%S)生成时间戳按照原文件名_YYMMDDHHMMSS.xlsx的规范自动输出并保存至用户桌面。会话内存状态机 (Session State)在后端配置模块设立LATEST_GENERATED_FILE缓存变量。当列提取成功时自动暂存新文件路径当邮件发送工具检测到文件路径输入为空时运行时动态从内存中智能捕获最新生成的文件确保端到端流程无缝闭环。2.3 高风险操作的前置预览与安全防护模块需求痛点邮件发送属于不可逆的高风险操作若无确认机制极易造成误发或隐私泄露。实现方案职责解耦预览 vs 发送preview_email前置安全校验仅在后端组装发件人、收件人、主题、附件大小及完整路径摘要绝不实际触发 SMTP 发送。send_excel_by_email正式授权发送强制要求必须在核对预览信息无误后或通过大模型工作流引导用户确认后方可调用。参数可定制与可观测性收件人邮箱支持配置默认值如DEFAULT_RECIPIENT_EMAIL同时全面支持运行时自定义修改或输入兼顾易用性与安全性。三 开发实现先看看项目目录划分smart-excel-mcp/ ├── .venv/ # Python 虚拟环境目录 ├── config.py # 全局配置文件包含默认目录、邮箱 SMTP 配置及内存态缓存 ├── excel_handler.py # Excel 核心处理逻辑目录扫描、防御性列提取、时间戳文件归档 ├── email_handler.py # 邮件处理逻辑包含预览生成、安全校验及真实 SMTP 发送 ├── server.py # MCP 服务入口注册并暴露所有 MCP Tool └── requirements.txt # 项目 Python 依赖包清单2.1 创建并激活虚拟python环境打开 Git Bash切换到项目根目录下运行以下命令创建名为.venv的虚拟环境python -m venv .venv在 Git Bash 中激活虚拟环境的命令为source .venv/Scripts/activate这种方式每次进入项目的时候需要手动激活一劳永逸的解决方法是: 进入用户根目录使用文本编辑器如notepad创建并打开.bashrc文件此时会弹出一个记事本窗口询问是否创建新文件点击“是”。cd ~ notepad .bashrc将以下代码复制并粘贴到刚刚打开的记事本中以后新开git-bash终端脚本检测到项目下存在.venv目录就会进入python虚拟环境if [ -d .venv ]; then source .venv/Scripts/activate fi安装项目依赖包Python 自带的smtplib、email、os、datetime等模块均为标准库无需额外通过 pip 安装pip install mcp pandas openpyxl依赖项固化pip freeze requirements.txt2.2 编写主文件server.py 是整个 MCP 项目的“神经中枢”主要承担三大核心职责协议通信层初始化基于 MCPServer 构建服务采用 Stdio 模式与 IDE 客户端如 Antigravity、Cursor建立安全、零配置的本地通信。工具注册与暴露 (mcp.tool)将底层独立的业务逻辑目录扫描、防御性列提取、邮件前置预览、加密安全发送封装并注册为标准化的 AI 工具。工作流闭环串联作为胶水层将各模块有机衔接支撑大模型在本地完成从 Excel 智能清洗到高风险邮件确认分发的全流程自动化闭环。from config import DEFAULT_RECIPIENT_EMAIL from email_handler import preview_email_logic, send_excel_by_email_logic from excel_handler import extract_excel_columns_logic, scan_excel_files_logic from mcp.server.mcpserver import MCPServer mcp MCPServer(Smart Excel Processor MCP) mcp.tool() def scan_excel_files(directory_path: str ) - str: 扫描指定文件夹下的所有 Excel 文件获取各文件的可用字段。 return scan_excel_files_logic(directory_path) mcp.tool() def extract_excel_columns(file_path: str, columns: list[str]) - str: 读取指定的 Excel 文件提取用户指定的若干列数据并保存至桌面。 return extract_excel_columns_logic(file_path, columns) mcp.tool() def preview_email( 文件路径: str , 收件人邮箱: str DEFAULT_RECIPIENT_EMAIL, 邮件主题: str 数据报表 ) - str: 【高风险操作前置步骤】在发送邮件前生成邮件预览摘要。 AI 规则要求在执行正式发送send_excel_by_email之前必须先调用此工具展示摘要并等待用户确认。 Args: 文件路径: 需要发送的 Excel 文件路径。如果留空将自动使用上一步刚刚生成的新文件路径。 收件人邮箱: 目标收件人邮箱 邮件主题: 邮件主题 return preview_email_logic(文件路径, 收件人邮箱, 邮件主题) mcp.tool() def send_excel_by_email( 文件路径: str , 收件人邮箱: str DEFAULT_RECIPIENT_EMAIL, 邮件主题: str 数据报表 ) - str: 【高风险操作】将指定的 Excel 文件通过 SMTP 邮件服务真正发送到指定邮箱。 安全警示请务必先通过 preview_email 确认无误并获得用户明确授权后再调用此工具。 Args: 文件路径: 需要发送的 Excel 文件路径。如果留空将自动使用上一步刚刚生成的新文件路径。 收件人邮箱: 目标收件人邮箱 邮件主题: 邮件主题 return send_excel_by_email_logic(文件路径, 收件人邮箱, 邮件主题) if __name__ __main__: mcp.run()2.3 编写excel目录扫描、防御性列提取、时间戳文件归档在本地自动化办公场景中代码往往面临路径难找、表头不规范、误操作多等痛点。excel_handler.py通过以下三大核心机制实现了高效、鲁棒的数据处理智能目录扫描与过滤 通过递归遍历指定目录自动过滤掉 Office 产生的临时缓存文件如以~$开头的锁文件精准锁定.xlsx与.xls文件并自动提取可用字段供大模型参考。防御性表头清洗与纠偏 针对用户输入或表格中常见的空格与拼写差异代码会对列名统一执行.strip()清洗。若发现用户指定的列名在表中不存在会主动抛出异常并反馈全量可用字段驱动大模型自动纠偏。高精度时间戳自动归档 处理后的数据不覆盖原表而是利用datetime生成高精度时间戳如_YYMMDDHHMMSS按照原文件名_时间戳.xlsx的规范自动输出并归档至用户桌面确保数据可追溯。from datetime import datetime import os import pandas as pd from config import DEFAULT_DIR import config # 引入 config 用于更新全局变量 def scan_excel_files_logic(directory_path: str ) - str: target_dir ( DEFAULT_DIR if not directory_path or directory_path.strip() else directory_path ) full_dir os.path.expanduser(target_dir) if not os.path.exists(full_dir) or not os.path.isdir(full_dir): return f错误目录不存在或不是有效的文件夹: {full_dir} excel_files [] for root, _, files in os.walk(full_dir): for file in files: if file.lower().endswith((.xlsx, .xls)) and not file.startswith( ~$ ): excel_files.append(os.path.join(root, file)) if not excel_files: return f在目录 {full_dir} 中没有找到任何 Excel 文件。 result f已为您扫描目录 {full_dir}找到以下 Excel 文件及其实际字段\n\n for path in excel_files: try: df pd.read_excel(path, header0, nrows0) columns [str(c).strip() for c in df.columns if Unnamed not in str(c)] result f **文件名称**: {os.path.basename(path)}\n result f - **完整路径**: {path}\n result f - **可用字段**: {columns}\n\n except Exception as e: result f **文件名称**: {os.path.basename(path)} (解析失败: {str(e)})\n\n return result def extract_excel_columns_logic(file_path: str, columns: list[str]) - str: try: full_path os.path.expanduser(file_path) if not os.path.exists(full_path): return f错误找不到文件 {full_path} base_name, ext os.path.splitext(os.path.basename(full_path)) if not ext: ext .xlsx df pd.read_excel(full_path, header0) df.columns [str(c).strip() for c in df.columns] missing_cols [col for col in columns if col not in df.columns] if missing_cols: return ( f错误原文件中不存在以下列{missing_cols}。当前文件的有效列名为{list(df.columns)} ) new_df df[columns] timestamp datetime.now().strftime(%y%m%d%H%M%S) new_filename f{base_name}_{timestamp}{ext} output_path os.path.expanduser(f~/Desktop/{new_filename}) new_df.to_excel(output_path, indexFalse) # 【核心修改】将成功生成的新文件路径记录到全局配置中 config.LATEST_GENERATED_FILE output_path return ( f成功已提取指定列。\n- 新文件名: {new_filename}\n- 已保存至桌面: {output_path} ) except Exception as e: return f处理 Excel 失败原因{str(e)}2.4 编写邮件发送功能email_handler.py是项目的安全防线与邮件自动化分发模块主要实现两大核心功能高风险前置预览 (preview_email_logic) 在真正发送邮件前自动组装发件人、收件人、主题及附件摘要包含内存中缓存的最新文件路径与文件大小形成可视化摘要避免误发。安全加密发送 (send_excel_by_email_logic) 基于smtplib采用SSL 安全加密通道对接主流邮箱如 126.com将清洗后的报表安全地作为附件发送给指定收件人。import os import smtplib from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText import config from config import DEFAULT_RECIPIENT_EMAIL, SENDER_AUTH_CODE, SENDER_EMAIL, SMTP_PORT, SMTP_SERVER def preview_email_logic( 文件路径: str , 收件人邮箱: str DEFAULT_RECIPIENT_EMAIL, 邮件主题: str 数据报表 ) - str: 仅生成邮件预览信息不执行真实发送 try: if not 文件路径 or 文件路径.strip() : if config.LATEST_GENERATED_FILE and os.path.exists(config.LATEST_GENERATED_FILE): 文件路径 config.LATEST_GENERATED_FILE else: return 【预览失败】错误未指定文件路径且当前会话中尚未生成新的 Excel 文件。 full_path os.path.expanduser(文件路径) if not os.path.exists(full_path): return f【预览失败】错误找不到要发送的文件 {full_path} file_size os.path.getsize(full_path) / 1024 # KB return ( f **邮件发送前预览确认**\n f- **发件人**: {SENDER_EMAIL}\n f- **收件人**: {收件人邮箱}\n f- **邮件主题**: {邮件主题}\n f- **附件文件**: {os.path.basename(full_path)} (大小: {file_size:.2f} KB)\n f- **完整路径**: {full_path}\n\n f⚠️ **安全提示**: 以上信息核对无误后请执行 send_excel_by_email 工具进行最终发送。 ) except Exception as e: return f预览生成失败原因{str(e)} def send_excel_by_email_logic( 文件路径: str , 收件人邮箱: str DEFAULT_RECIPIENT_EMAIL, 邮件主题: str 数据报表 ) - str: try: if not 文件路径 or 文件路径.strip() : if config.LATEST_GENERATED_FILE and os.path.exists(config.LATEST_GENERATED_FILE): 文件路径 config.LATEST_GENERATED_FILE else: return 错误未指定文件路径且当前会话中尚未通过工具生成新的 Excel 文件。 full_path os.path.expanduser(文件路径) if not os.path.exists(full_path): return f错误找不到要发送的文件 {full_path} msg MIMEMultipart() msg[From] SENDER_EMAIL msg[To] 收件人邮箱 msg[Subject] 邮件主题 body_text 您好这是由 smart-excel-mcp 自动为您清洗并生成的最新数据报表请查收附件。 msg.attach(MIMEText(body_text, plain, utf-8)) with open(full_path, rb) as f: part MIMEApplication(f.read(), Nameos.path.basename(full_path)) part.add_header(Content-Disposition, attachment, filenameos.path.basename(full_path)) msg.attach(part) server smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT) server.login(SENDER_EMAIL, SENDER_AUTH_CODE) server.sendmail(SENDER_EMAIL, [收件人邮箱], msg.as_string()) server.quit() return f✅ 邮件发送成功已将文件 {os.path.basename(full_path)} 安全发送至 {收件人邮箱} except Exception as e: return f❌ 邮件发送失败原因{str(e)}2.5 配置文件# 全局配置文件 # 默认扫描的 Excel 文件夹路径 DEFAULT_DIR rE:\work # 126 邮箱 SMTP 配置 SMTP_SERVER smtp.126.com SMTP_PORT 465 SENDER_EMAIL xxx126.com # 替换为你的真实 126 邮箱 SENDER_AUTH_CODE xxx # 替换为你的 126 16位客户端授权码 # 默认收件人邮箱可在下方配置中修改或在界面中直接重写 DEFAULT_RECIPIENT_EMAIL xxxx126.com # 【新增】全局缓存记录最近一次通过 extract_excel_columns 生成的新文件路径 LATEST_GENERATED_FILE 四 接入Anti-Gravity并进行调试4.1 在Anti-Gravity IDE中配置MCPIDE的菜单路径为设置齿轮Open Antigravity IDE User Settings Customizations点击Open MPC Config,会在IDE编辑窗口打开mcp_config.json文件配置mcp服务器启动命令和参数如果服务器要验证token的话在env字段中配置。{ mcpServers: { smart-excel: { command: D:\\smart-excel-mcp\\.venv\\Scripts\\python.exe, args: [ D:\\smart-excel-mcp\\server.py ], env: {} } } }配完之后要打开开关才能生效4.2 测试工作流步骤一扫描文件 (scan_excel_files)输入目标文件夹路径或留空使用默认路径查看目录下的有效 Excel 文件及其全量可用字段列表。在代码agent聊天对话框输入我想扫描excel文件就会看到调用smart-excel mcp, 执行结果如下步骤二提取指定列 (extract_excel_columns)传入目标文件路径与需要提取的列名字段。程序会自动清洗表头、校验字段、提取数据并以高精度时间戳命名如xxx_260504163744.xlsx保存至用户桌面同时自动将该路径写入后端内存缓存 (LATEST_GENERATED_FILE)。在agent对话框输入“提取 4月-报名名单-20260504163744.xlsx中的姓名字段”执行结果如下可以看到桌面生成了新的文件内容只有提取字段。步骤三邮件前置预览 (preview_email)检查收件人、主题以及自动关联的最新文件路径与附件大小摘要。输入需要发邮件核对邮件发送地址发送的文件步骤四正式授权发送 (send_excel_by_email)确认无误后触发正式发送将处理好的报表安全送达指定邮箱。在对话框输入确认发送之后邮件才会真的发送。登录126邮箱可以看到收到了一封新邮件。结尾在本次smart-excel-mcp项目的实战开发与调试过程中我有以下三点深刻的体会与反思生产力跃升意图驱动的自动化AI 最大的价值在于通过自然语言理解人类意图将原本枯燥、重复的本地表格清洗与数据捞取工作转化为“所见即所得”的自动化体验彻底解放了日常办公的体力劳动。场景演进从单文件到多文件批处理当前版本主要聚焦于单文件的精准提取与归档。但在实际生产环境中多文件、跨表格的聚合提取往往频次更高这也将是该项目后续迭代升级的重点方向。性能与成本不可忽视的 Token 消耗MCP 架构下的 Agent 交互对上下文和工具描述的依赖较高Token 消耗相对较快例如在本地几次简单的联调测试中就能明显感知到客户端额度的波动在后续实际落地和复杂工作流设计中需要合理评估成本与提示词开销。资料展示下面是我整理的AI大模型 学习资料和工具包预览适合收藏后按主题逐步学习