杨帆 2025-10-26 08:03 广东
MCP厉害的地方在于,不用重复造轮子。

🔧 MCP是一个开放的协议标准,旨在简化AI模型与外部工具和数据的交互,提供统一的接口,让AI模型能够无缝连接各种数据源和工具。
🛠️ MCP解决了传统function call的平台依赖问题,提供了更统一、开放、安全、灵活的工具调用机制,让用户和开发者都能从中受益。
🚀 通过MCP,开发者可以更快速地构建更强大的AI应用,而普通用户则可以在不了解技术细节的情况下使用丰富的现成工具。
🔗 MCP的核心是让LLM能够方便地调用多个工具,模型通过prompt engineering,即提供所有工具的结构化描述和few-shot的example来决定该使用哪些工具。
🔍 工具的执行和结果反馈机制简单直接:模型分析用户请求后,会决定是否需要调用工具,无需工具时直接生成自然语言回复,需要工具时输出结构化JSON格式的工具调用请求,客户端根据这个json代码执行对应的工具。
杨帆 2025-10-26 08:03 广东
MCP厉害的地方在于,不用重复造轮子。
1、MCP——开放、通用、有共识的协议标准
MCP 是 Anthropic (Claude) 主导发布的一个开放的、通用的、有共识的协议标准。
MCP 是一个标准协议,就像给 AI 大模型装了一个 “万能接口”,让 AI 模型能够与不同的数据源和工具进行无缝交互。它就像 USB-C 接口一样,提供了一种标准化的方法,将 AI 模型连接到各种数据源和工具。
MCP 旨在替换碎片化的 Agent 代码集成,从而使 AI 系统更可靠,更有效。通过建立通用标准,服务商可以基于协议来推出它们自己服务的 AI 能力,从而支持开发者更快的构建更强大的 AI 应用。开发者也不需要重复造轮子,通过开源项目可以建立强大的 AI Agent 生态。MCP 可以在不同的应用 / 服务之间保持上下文,增强整体自主执行任务的能力。
2、MCP架构的五大核心部分
3、为什么需要 MCP 呢?
假设你正在使用一个 AI 编程助手来帮助你写代码。这个 AI 助手就是一个 MCP 主机。它需要访问一些外部资源,比如代码库、文档或者调试工具。MCP 服务器就像是一个中介,它连接了这些资源和 AI 助手。
1、VS Function Call
2、模型如何智能选择Agent/工具
那 tool 的描述和代码中的 input_schema 是从哪里来的呢?通过进一步分析 MCP 的 Python SDK 源代码可以发现:大部分情况下,当使用装饰器 @mcp.tool() 来装饰函数时,对应的 name 和 description 等其实直接源自用户定义函数的函数名以及函数的 docstring 等。这里仅截取一小部分片段,想了解更多请参考原始代码。... # 省略了无关的代码async def start(self):# 初始化所有的 mcp serverfor server in self.servers:await server.initialize()# 获取所有的 tools 命名为 all_toolsall_tools = []for server in self.servers:tools = await server.list_tools()all_tools.extend(tools)# 将所有的 tools 的功能描述格式化成字符串供 LLM 使用# tool.format_for_llm() 我放到了这段代码最后,方便阅读。tools_description = "\n".join([tool.format_for_llm() for tool in all_tools])# 询问 LLM(Claude) 应该使用哪些工具。system_message = ("You are a helpful assistant with access to these tools:\n\n"f"{tools_description}\n""Choose the appropriate tool based on the user's question. ""If no tool is needed, reply directly.\n\n""IMPORTANT: When you need to use a tool, you must ONLY respond with ""the exact JSON object format below, nothing else:\n""{\n"' "tool": "tool-name",\n'' "arguments": {\n'' "argument-name": "value"\n'" }\n""}\n\n""After receiving a tool's response:\n""1. Transform the raw data into a natural, conversational response\n""2. Keep responses concise but informative\n""3. Focus on the most relevant information\n""4. Use appropriate context from the user's question\n""5. Avoid simply repeating the raw data\n\n""Please use only the tools that are explicitly defined above.")messages = [{"role": "system", "content": system_message}]while True:# Final... 假设这里已经处理了用户消息输入.messages.append({"role": "user", "content": user_input})# 将 system_message 和用户消息输入一起发送给 LLMllm_response = self.llm_client.get_response(messages)... # 后面和确定使用哪些工具无关class Tool:"""Represents a tool with its properties and formatting."""def __init__(self, name: str, description: str, input_schema: dict[str, Any]) -> None:self.name: str = nameself.description: str = descriptionself.input_schema: dict[str, Any] = input_schema# 把工具的名字 / 工具的用途(description)和工具所需要的参数(args_desc)转化为文本def format_for_llm(self) -> str:"""Format tool information for LLM.Returns:A formatted string describing the tool."""args_desc = []if "properties" in self.input_schema:for param_name, param_info in self.input_schema["properties"].items():arg_desc = (f"- {param_name}: {param_info.get('description', 'No description')}")if param_name in self.input_schema.get("required", []):arg_desc += " (required)"args_desc.append(arg_desc)return f"""Tool: {self.name}Description: {self.description}Arguments:{chr(10).join(args_desc)}"""
总结:模型是通过 prompt engineering,即提供所有工具的结构化描述和 few-shot 的 example 来确定该使用哪些工具。另一方面,Anthropic 肯定对 Claude 做了专门的训练,毕竟是自家协议,Claude 更能理解工具的 prompt 以及输出结构化的 tool call json 代码。def from_function(cls,fn: Callable,name: str | None = None,description: str | None = None,context_kwarg: str | None = None,) -> "Tool":"""Create a Tool from a function."""func_name = name or fn.__name__ # 获取函数名if func_name == "<lambda>":raise ValueError("You must provide a name for lambda functions")func_doc = description or fn.__doc__ or "" # 获取函数 docstringis_async = inspect.iscoroutinefunction(fn)... # 更多请参考原始代码...
3、工具执行与结果反馈机制
... # 省略无关的代码async def start(self):... # 上面已经介绍过了,模型如何选择工具while True:# 假设这里已经处理了用户消息输入.messages.append({"role": "user", "content": user_input})# 获取 LLM 的输出llm_response = self.llm_client.get_response(messages)# 处理 LLM 的输出(如果有 tool call 则执行对应的工具)result = await self.process_llm_response(llm_response)# 如果 result 与 llm_response 不同,说明执行了 tool call (有额外信息了)# 则将 tool call 的结果重新发送给 LLM 进行处理。if result != llm_response:messages.append({"role": "assistant", "content": llm_response})messages.append({"role": "system", "content": result})final_response = self.llm_client.get_response(messages)logging.info("\nFinal response: %s", final_response)messages.append({"role": "assistant", "content": final_response})# 否则代表没有执行 tool call,则直接将 LLM 的输出返回给用户。else:messages.append({"role": "assistant", "content": llm_response})
1、使用 LLM 构建 MCP 的最佳实践
剩下的部分也很重要,但是偏重于方法论,实践性较弱,这里就不展开了,推荐直接看链接:https://modelcontextprotocol.io/tutorials/building-mcp-with-llms。... (这里是已经引入的 domain knowledge)打造一个 MCP 服务器,它能够:- 连接到我公司的 PostgreSQL 数据库- 将表格结构作为资源开放出来- 提供运行只读 SQL 查询的工具- 包含常见数据分析任务的引导
2、手动实践
# 安装 uvcurl -LsSf https://astral.sh/uv/install.sh | sh# 创建项目目录uv init txt_countercd txt_counter# 设置 Python 3.10+ 环境echo "3.11" > .python-version# 创建虚拟环境并激活uv venvsource .venv/bin/activate# Install dependenciesuv add "mcp[cli]" httpx# Create our server filetouch txt_counter.py
Answer:一个用 Rust 编写的超快速 (100x) Python 包管理器和环境管理工具,由 Astral 开发。定位为 pip 和 venv 的替代品,专注于速度、简单性和现代 Python 工作流。
Domain Knowledge 复制于 MCP Python SDK的README文件(https://raw.githubusercontent.com/modelcontextprotocol/python-sdk/refs/heads/main/README.md)Step4. 实现 MCP Server以下代码由 Claude 3.7 直接生成。当然这里主要是因为需求足够简单,当需要实现一个复杂的 MCP Server 时,可能需要多步的引导和 Debug 才能得到最终的代码。"""... (这里是已经引入的 domain knowledge)"""打造一个 MCP 服务器,它能够:- 功能:- 统计当前桌面上的 txt 文件数量- 获取对应文件的名字要求:- 不需要给出 prompt 和 resource 相关代码。- 你可以假设我的桌面路径为 /Users/{username}/Desktop
任务非常简单,只需要调用非常基本的os就可以完成。Step5. 测试 MCP Serverimport osfrom pathlib import Pathfrom mcp.server.fastmcp import FastMCP# 创建 MCP Servermcp = FastMCP("桌面 TXT 文件统计器")def count_desktop_txt_files() -> int:"""Count the number of .txt files on the desktop."""# Get the desktop pathusername = os.getenv("USER") or os.getenv("USERNAME")desktop_path = Path(f"/Users/{username}/Desktop")# Count .txt filestxt_files = list(desktop_path.glob("*.txt"))return len(txt_files)def list_desktop_txt_files() -> str:"""Get a list of all .txt filenames on the desktop."""# Get the desktop pathusername = os.getenv("USER") or os.getenv("USERNAME")desktop_path = Path(f"/Users/{username}/Desktop")# Get all .txt filestxt_files = list(desktop_path.glob("*.txt"))# Return the filenamesif not txt_files:return "No .txt files found on desktop."# Format the list of filenamesfile_list = "\n".join([f"- {file.name}" for file in txt_files])return f"Found {len(txt_files)} .txt files on desktop:\n{file_list}"if __name__ == "__main__":# Initialize and run the servermcp.run()
之后进入到给出的链接中,你大概能按下图进行操作:$ mcp dev txt_counter.pyStarting MCP inspector...Proxy server listening on port 3000MCP Inspector is up and running at http://localhost:5173
Step6. 接入 Claude最后一步就是把我们写好的 MCP 接入到 Claude Desktop 中。流程如下:
在配置文件中添加以下内容,记得替换相关路径为实际路径。# 打开 claude_desktop_config.json (MacOS / Linux)# 如果你用的是 cursor 或者 vim 请更换对应的命令code ~/Library/Application\ Support/Claude/claude_desktop_config.json
uv最好是绝对路径,推荐使用 which uv 获取。配置好后重启 Claude Desktop,如果没问题就能看到对应的 MCP Server 了。{"mcpServers": {"txt_counter": {"command": "/opt/homebrew/bin/uv","args": ["--directory","/Users/yangfan/mcp/txt_counter","run","txt_counter.py"]}}}
Step7. 实际使用接下来,我们通过一个简单的 prompt 进行实际测试:
能推测我当前桌面上 txt 文件名的含义吗?它可能会请求你的使用权限,如图一所示,你可以点击 Allow for This Chat看起来我们 MCP Server 已经正常工作了!
3、MCP Server Debug
AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。
鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑