1. 项目概述:一个为AI IDE赋能的MCP开发框架
如果你和我一样,长期在Cursor、Claude Desktop这类AI原生IDE里“摸爬滚打”,那你肯定有过这样的体验:想让AI帮你分析一份PDF报告,它说“我无法读取文件”;想让它总结一个网页内容,它只能干瞪眼;想让它处理Excel里的数据,更是无从下手。AI模型本身很强大,但它与外部世界(你的文件、网络、数据库)之间,似乎总隔着一道无形的墙。
这就是MCP(Model Context Protocol)要解决的问题。简单来说,MCP就是一套标准协议,它定义了AI模型如何安全、可控地调用外部工具和资源。你可以把它想象成AI的“USB接口”或“插件系统”。而今天要聊的这个aigo666/mcp-framework,就是一个基于MCP协议,功能强大且开箱即用的开发框架。它不是一个简单的工具集合,而是一个完整的“脚手架”,让你能快速、规范地为你的AI助手(尤其是在Cursor IDE里)开发各种自定义工具,比如文件解析、网页抓取,甚至是连接你自己的知识库(如MaxKB)进行对话。
这个框架的核心价值在于“标准化”和“易扩展”。它把与MCP服务器交互的底层细节、工具注册、错误处理、资源管理等繁琐工作都封装好了。作为开发者,你只需要关注工具本身的业务逻辑:比如怎么从PDF里提取文字和图片,怎么解析Excel表格。剩下的“脏活累活”,框架都替你干了。这意味着,即使你对MCP协议本身不熟悉,也能基于这个框架,在半小时内开发出一个能被Cursor直接识别和调用的专业工具。
2. 框架核心设计思路与架构解析
2.1 为什么选择MCP?协议层解析
在深入代码之前,有必要先理解MCP协议的设计哲学。传统的AI应用集成外部能力,要么通过复杂的API编排,要么需要针对特定模型(如OpenAI的Function Calling)做适配,耦合度高,迁移成本大。MCP的诞生,就是为了统一这个混乱的局面。
MCP协议的核心思想是声明式工具描述和标准化通信。工具提供者(也就是我们的框架)不需要关心对面是Cursor、Claude Desktop还是其他什么客户端,它只需要按照MCP的规范,声明自己有哪些工具(name,description,input_schema),以及如何调用(execute方法)。客户端则按照同样的规范来发现和调用这些工具。这就像USB设备只需要遵循USB协议,就能被任何有USB接口的电脑识别,而不需要知道电脑是Windows还是Mac。
mcp-framework在这个基础上,做了几层关键的抽象:
- 工具基类(
BaseTool):所有自定义工具都继承自这个类。它强制要求子类实现name,description,input_schema和execute方法,确保了每个工具都符合MCP的规范。 - 工具注册器(
ToolRegistry):一个全局的注册中心,采用装饰器模式(@ToolRegistry.register)。开发者只需要用这个装饰器标记自己的工具类,框架启动时就会自动扫描、加载并注册到MCP服务器中。这种设计极大地简化了工具的集成流程。 - 统一的服务器入口(
server.py):它基于官方的mcpPython SDK,创建了一个SSE(Server-Sent Events)服务器。SSE是一种轻量级的、服务器向客户端推送更新的协议,非常适合AI工具调用这种“请求-响应”或“流式响应”的场景。服务器负责处理来自客户端的连接、协议握手、工具列表查询和具体的工具调用请求。
这种架构带来的直接好处是解耦和可维护性。你的工具逻辑是独立的Python类,服务器逻辑是固定的,两者通过清晰的接口(注册器)连接。未来MCP协议升级,你可能只需要更新底层的mcpSDK和服务器适配逻辑,而你的业务工具代码大概率不需要改动。
2.2 项目结构深度解读:模块化与职责分离
打开项目目录,其结构清晰地体现了上述设计思想:
mcp_tool/ ├── tools/ # 工具实现层 │ ├── __init__.py # 定义BaseTool和ToolRegistry,是工具层的“宪法” │ ├── loader.py # 自动扫描并加载tools目录下所有工具模块 │ ├── file_tool.py # 综合文件处理(路由分发器) │ ├── pdf_tool.py # PDF专精工具 │ ├── word_tool.py # Word专精工具 │ └── ... # 其他专精工具 ├── __main__.py # 命令行入口,处理启动参数 └── server.py # MCP协议服务器实现我们来重点看几个关键文件:
tools/__init__.py:这是工具层的基石。BaseTool抽象类定义了工具的“宪法”,任何工具都必须遵守。ToolRegistry类则是一个单例模式的注册表,它内部维护着一个工具名字到工具类的映射字典。@register装饰器的作用,就是把被装饰的类添加到这个字典里。tools/loader.py:这是框架“自动化”的魔法所在。它利用Python的pkgutil模块,在运行时动态遍历tools目录下所有以_tool.py结尾的模块,并导入它们。因为每个模块在导入时,其内部的@ToolRegistry.register装饰器就会执行,从而完成工具类的自动注册。这意味着你新增一个new_tool.py文件,框架启动时就能自动识别,无需修改任何注册代码。tools/file_tool.py:这是一个设计巧妙的“门面”(Facade)工具。它本身不处理具体文件,而是根据文件后缀名(.pdf,.docx等),将请求路由到对应的专精工具(pdf_tool,word_tool)。这为用户提供了一个统一的、易记的接口parse_file,而内部则保持了各格式处理逻辑的独立和纯粹。
这种“核心协议层 + 自动化工具加载 + 统一门面”的三层架构,使得框架在保持强大功能的同时,具备了极佳的扩展性和可读性。
3. 核心工具实现细节与实战技巧
框架内置的工具覆盖了文档处理、网络获取和AI对话三大场景,每一个工具的实现都包含了不少值得细品的“小心思”。
3.1 文件处理工具链:从通用接口到专精解析
file_tool.py:智能路由器的实现这个工具的execute方法是一个典型的路由逻辑。它首先从参数中获取file_path,然后使用os.path.splitext提取文件扩展名并转为小写。接着,一个预定义的映射字典将后缀映射到具体的工具名和参数。
# 伪代码展示路由逻辑 _extension_to_tool = { '.pdf': ('parse_pdf', {}), '.doc': ('parse_word', {}), '.docx': ('parse_word', {}), # ... 其他格式 }如果找不到映射,则返回错误。如果找到,它就构造一个对自身MCP服务器的内部调用,模拟客户端去调用对应的专精工具。这里的关键是,它利用了MCP服务器可以处理自身工具调用的特性,实现了工具间的组合。这种做法比直接在代码里导入和调用其他工具类更优雅,因为它保持了工具间的松耦合,每个工具都只通过标准的MCP协议进行通信。
pdf_tool.py:超越文本提取的OCR集成PDF处理是文档处理的难点,因为PDF可能包含纯文本、扫描图像、混合图层等。该工具的parse_pdf提供了两种模式:
quick模式:使用pymupdf(PyMuPDF)的get_text()方法快速提取文本。这适用于数字生成的、文字可选的PDF,速度极快。full模式(默认):这是精华所在。它不仅提取文本,还会遍历每一页,使用get_images()方法获取页面中的所有原始图片。更关键的一步是OCR(光学字符识别):它将这些图片数据通过pytesseract库发送给Tesseract引擎进行文字识别。最终,它返回一个结构化的结果,包含原始文本、图片的Base64编码数据以及从图片中识别出的文字。
实操心得:OCR的配置与优化要让
full模式正常工作,系统必须安装Tesseract OCR引擎及其中文语言包。在Ubuntu上,除了tesseract-ocr,务必安装tesseract-ocr-chi-sim(简体中文)。在代码中,可以通过pytesseract.image_to_string(image, lang='chi_sim+eng')指定多语言识别,提高混合图文PDF的识别准确率。对于复杂的版面,full模式可能会比较慢,这是正常的,因为它进行了图像处理和OCR计算。
word_tool.py与excel_tool.py:结构化数据的提取对于Word和Excel,框架分别采用了python-docx和pandas+openpyxl的组合。
- Word工具:它遍历文档的所有段落(
document.paragraphs)和表格(document.tables),将文本和表格数据(转换为Markdown格式的表格字符串)拼接起来。同时,它也会提取文档中的所有内嵌图片,将其作为资源返回。这里的一个细节是,它处理了表格中的合并单元格,确保提取的数据结构完整。 - Excel工具:它利用
pandas的read_excel函数,配合openpyxl引擎,可以读取.xlsx和.xlsm文件。它会遍历所有工作表(sheet),将每个工作表转换成一个字典列表(每行一个字典),并附带列名。这种方式非常适合AI模型进行后续的分析和问答,因为数据是以结构化的JSON-like格式提供的。
避坑指南:文件路径与权限所有文件处理工具都要求提供
file_path。在Docker部署模式下,这个路径是容器内的路径。框架通过环境变量HOST_MOUNT_TARGET将宿主机的目录映射到了容器内(如/host_files)。因此,如果你在宿主机上的文件路径是/home/user/docs/report.pdf,并且你将/home/user/docs映射到了/host_files,那么在Cursor中调用工具时,参数应该写为parse_file /host_files/report.pdf。务必确保Docker容器有对该挂载目录的读取权限。
3.2 网络与AI对话工具:稳定性的艺术
url_tool.py:健壮的网页内容获取这个工具虽然逻辑简单——使用httpx库获取URL内容——但实现上充分考虑到了健壮性。
- 超时控制:设置了连接超时和读取超时(例如各10秒),防止因网络问题导致客户端长时间等待。
- 异常处理:捕获了
httpx可能抛出的所有异常(如连接错误、超时、HTTP状态错误),并返回格式友好的错误信息,而不是让整个MCP调用崩溃。 - 编码处理:它会优先使用HTTP响应头中声明的编码,如果失败,则使用
chardet库进行猜测,最后再尝试UTF-8。这能有效避免中文等非ASCII内容出现乱码。
maxkb_tool.py:与私有知识库的流式对话这是连接私有AI知识库(MaxKB)的桥梁。它的设计亮点在于对流式响应(Streaming)的支持。
- 流式 vs 非流式:在
execute方法中,它根据stream参数决定返回方式。如果为true(默认),它不会等待MaxKB API返回完整响应,而是边接收边通过yield关键字逐步返回文本块。这在Cursor中体验极佳,用户能看到答案逐字打出,感知延迟低。如果为false,则收集所有响应后再一次性返回。 - 会话管理:通过
re_chat参数可以控制是否开启新会话。工具内部应该维护了一个会话ID或类似的机制(具体取决于MaxKB API的设计),来实现多轮对话的上下文保持。 - 完备的错误处理:同样,它对网络请求、API错误、JSON解析错误等都有捕获,并确保任何情况下都会返回一个有效的
TextContent给客户端,而不是抛出未处理的异常。
注意事项:配置是关键
maxkb工具严重依赖环境变量配置(MAXKB_HOST,MAXKB_CHAT_ID,MAXKB_APPLICATION_ID,MAXKB_AUTHORIZATION)。在部署时,务必在.env文件中正确填写这些值。特别是MAXKB_AUTHORIZATION,通常是Bearer Token,格式要正确。一个常见的错误是忘记在Token前加上Bearer前缀,或者主机地址(MAXKB_HOST)的端口号写错。
4. 从零开始:开发、部署与鉴权全流程
4.1 开发一个新工具:以“天气查询”为例
假设我们要开发一个查询天气的工具weather,它接收城市名参数,返回天气信息。按照框架规范,步骤如下:
- 创建文件:在
mcp_tool/tools/目录下新建weather_tool.py。 - 编写工具类:
import mcp.types as types from . import BaseTool, ToolRegistry import httpx @ToolRegistry.register # 关键:使用装饰器注册 class WeatherTool(BaseTool): name = "weather" description = "查询指定城市的当前天气情况。" input_schema = { "type": "object", "required": ["city"], # 声明必需参数 "properties": { "city": { "type": "string", "description": "城市名称,例如:北京、Shanghai", } }, } async def execute(self, arguments: dict): # 1. 参数验证(框架会部分验证,这里可做补充) city = arguments.get("city") if not city: return [types.TextContent(type="text", text="错误:必须提供城市参数。")] # 2. 业务逻辑:调用外部天气API api_key = "YOUR_API_KEY" # 实践中应从环境变量读取 url = f"https://api.weatherapi.com/v1/current.json?key={api_key}&q={city}" try: async with httpx.AsyncClient(timeout=10.0) as client: resp = await client.get(url) resp.raise_for_status() data = resp.json() # 3. 格式化结果 location = data['location']['name'] temp_c = data['current']['temp_c'] condition = data['current']['condition']['text'] result_text = f"{location}的当前天气:{condition},气温{temp_c}°C。" except httpx.RequestError as e: result_text = f"请求天气API失败:{e}" except (KeyError, ValueError) as e: result_text = f"解析天气数据失败:{e}" # 4. 返回MCP标准格式的结果 return [types.TextContent(type="text", text=result_text)]- 无需额外注册:由于
loader.py会自动导入tools下的模块,你的工具在下次启动服务时就会自动可用。 - 在Cursor中测试:重启MCP服务器后,在Cursor的聊天框输入
/weather 北京,就能看到工具的返回结果。
4.2 两种部署方式详解与对比
框架提供了Docker和传统Python两种部署方式,适用于不同场景。
Docker部署(推荐用于生产与快速体验)这是最省心、环境最统一的方式。项目提供的docker-compose.yml文件已经配置好了所有依赖。
# 1. 克隆项目并配置 git clone https://github.com/aigo666/mcp-framework.git cd mcp-framework cp .env.example .env # 编辑 .env 文件,设置你的MaxKB密钥和挂载目录 # 2. 一键启动 docker compose up --build -d- 优势:完全隔离的环境,无需在宿主机安装Poppler、Tesseract等复杂的系统依赖。特别是OCR功能,在Windows上配置Tesseract非常麻烦,Docker镜像里已经全部配好。
- 本地文件访问:通过配置
.env中的HOST_MOUNT_SOURCE和HOST_MOUNT_TARGET,你可以将本地文件夹映射到容器内。这样,在Cursor中就可以用容器内路径(如/host_files/doc.pdf)访问你电脑上的文件了。 - 服务管理:使用
docker compose logs -f查看实时日志,docker compose restart重启服务,非常方便。
传统Python部署(适合开发与调试)如果你需要频繁修改工具代码并进行调试,在本地Python环境中运行会更方便。
# 1. 安装系统依赖(以Ubuntu为例) sudo apt-get update sudo apt-get install -y poppler-utils tesseract-ocr tesseract-ocr-chi-sim # 2. 创建虚拟环境并安装Python包 python -m venv venv source venv/bin/activate pip install -r requirements.txt # 3. 启动服务(可指定主机和端口) python -m mcp_tool --host 0.0.0.0 --port 8000- 优势:代码修改后立即生效,无需重新构建Docker镜像。可以使用IDE的调试器(如VSCode、PyCharm)设置断点,单步调试你的工具逻辑,这对于开发复杂工具至关重要。
- 劣势:需要手动处理所有系统级和Python级的依赖,跨平台一致性差。
4.3 鉴权配置:为你的工具加上安全锁
MCP框架支持SSE端点鉴权,这是一个非常重要的生产级特性。默认配置使用了一个测试用的鉴权服务地址,但为了安全,你应该部署自己的鉴权服务。
鉴权流程梳理:
- 用户在Cursor中配置MCP服务器URL:
http://your-server:8000/sse?token=eyJhbGciOi... - Cursor发起SSE连接,携带这个URL。
server.py中的SSE端点处理函数会从请求的查询参数中提取出token。- 服务器向
MCP_AUTH_URL(在.env中配置)发起一个HTTP请求,请求头中包含Authorization: Bearer <token>。 - 鉴权服务验证Token的有效性(是否过期、签名是否正确、用户是否存在等),返回HTTP状态码。200表示成功,401/403表示失败。
- MCP服务器根据鉴权结果决定是否建立SSE连接。
自行部署JWT鉴权服务: 项目文档推荐了 Jason Watmore 的 Node.js JWT 教程。你可以按照该教程快速搭建一个服务。核心是提供一个/users/authenticate或类似的验证端点,接收Bearer Token并返回验证结果。将你的服务地址填入.env文件的MCP_AUTH_URL中。
安全警告:切勿在生产环境中使用默认的测试鉴权地址。务必部署自己的鉴权服务,并使用强密钥(JWT Secret)来签发和验证Token。同时,建议将SSE服务部署在内部网络或通过VPN访问,不要直接暴露在公网。
5. 常见问题排查与性能优化实录
在实际使用和开发过程中,你可能会遇到以下问题。这里记录了我踩过的一些坑和解决方案。
5.1 连接与工具调用问题
问题1:Cursor中提示“无法连接到MCP服务器”或工具列表为空。
- 检查1:服务器是否运行?执行
docker compose ps或查看进程,确认mcp-framework容器或进程正在运行。 - 检查2:端口和主机配置?确保
.env中的MCP_SERVER_HOST和MCP_SERVER_PORT与Cursor中配置的URL一致。如果服务器运行在容器内,主机需设为0.0.0.0。 - 检查3:Cursor配置格式?Cursor中MCP服务器类型应选“sse”(或“SSE”),URL格式为
http://<host>:<port>/sse?token=<your_token>。注意是/sse路径,不是根路径。 - 检查4:防火墙/网络策略?确保服务器所在机器的防火墙放行了对应端口(如8000)。
问题2:调用工具时,返回“Tool not found”错误。
- 原因:工具名称不匹配。MCP工具调用是大小写敏感的。
- 解决:在Cursor中使用
/list命令(如果支持)或查看服务器启动日志,确认工具注册的正确名称。调用时使用完全一致的名字和参数格式。
问题3:处理大文件(如100MB的PDF)时,服务无响应或内存溢出。
- 原因:默认情况下,工具可能会尝试将整个文件读入内存。
- 优化:在工具实现中,对于大文件应采用流式或分块处理。例如,PDF工具可以一页一页地处理,而不是一次性加载整个文档。框架本身在部分工具中已做了临时文件处理,但作为开发者,在自定义工具中也应有此意识。
- 临时解决:对于现有工具,可以尝试先手动拆分大文件。
5.2 功能相关问题
问题4:PDF的full模式OCR识别不出中文,或识别乱码。
- 确认系统语言包:在Docker中,确保镜像安装了中文语言包。如果是本地部署,运行
tesseract --list-langs查看是否包含chi_sim。 - 检查代码:在
pdf_tool.py的OCR调用处,确认lang参数包含了chi_sim,例如lang='chi_sim+eng'。 - 图片预处理:有时PDF中的图片质量差会影响OCR。可以考虑在OCR前对图片进行简单的预处理,如二值化、降噪,但这需要修改工具代码。
问题5:maxkb工具返回“连接失败”或“认证失败”。
- 检查环境变量:这是最常见的原因。确保
.env文件中的MAXKB_HOST、MAXKB_APPLICATION_ID、MAXKB_AUTHORIZATION全部正确无误。MAXKB_HOST通常是http://<maxkb-server-ip>:8080。 - 测试MaxKB API:使用
curl或 Postman 直接测试MaxKB的聊天接口,确认其本身工作正常,并且你的授权信息有效。 - 查看容器日志:运行
docker compose logs mcp-framework查看详细的错误信息,通常会有更具体的网络错误或API响应内容。
问题6:挂载的本地文件在容器内无法访问。
- 检查挂载配置:确认
.env中HOST_MOUNT_SOURCE的路径存在且可读。在Docker Compose文件中,检查volumes映射是否正确。 - 权限问题:Docker容器通常以非root用户运行。确保宿主机上
HOST_MOUNT_SOURCE目录对容器用户(或所有用户)有读取权限。可以尝试chmod 755 /your/host/path。 - 路径使用:在Cursor中调用工具时,必须使用容器内的映射路径(
/host_files/...),而不是宿主机路径。
5.3 性能优化建议
- 工具懒加载:当前框架启动时会加载所有工具模块。如果工具数量非常多,可以考虑改为按需加载,即只在第一次被调用时加载其模块,以加快启动速度。
- 缓存机制:对于
url工具获取的网页内容,或者parse_file处理的某些不常变动的文件,可以引入一个简单的内存缓存(如functools.lru_cache)或磁盘缓存,避免重复请求和解析。 - 异步处理:框架基于
anyio,天然支持异步。确保你的自定义工具在执行网络I/O或文件I/O时,使用异步库(如aiofiles读文件,httpx.AsyncClient请求网络),而不是阻塞的同步调用,这样可以极大提升服务器在高并发下的吞吐量。 - 资源清理:特别是在处理图片、临时文件时,确保在
execute方法执行完毕后,显式地关闭文件描述符、删除临时文件,避免内存泄漏。
这个框架就像一个功能齐全的“工具箱”,并提供了制作新工具的“车床”。它解决了AI助手与本地环境交互的核心痛点。通过理解其架构、掌握工具开发范式、并妥善处理部署与安全问题,你就能为自己的AI工作流打造出强大而个性化的增强插件。无论是快速分析数据、总结文档,还是连接企业内部知识库,其可能性完全取决于你的想象力。