[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"similar-liaokongVFX--MCP-Chinese-Getting-Started-Guide":3,"tool-liaokongVFX--MCP-Chinese-Getting-Started-Guide":61},[4,18,26,36,44,53],{"id":5,"name":6,"github_repo":7,"description_zh":8,"stars":9,"difficulty_score":10,"last_commit_at":11,"category_tags":12,"status":17},4358,"openclaw","openclaw\u002Fopenclaw","OpenClaw 是一款专为个人打造的本地化 AI 助手，旨在让你在自己的设备上拥有完全可控的智能伙伴。它打破了传统 AI 助手局限于特定网页或应用的束缚，能够直接接入你日常使用的各类通讯渠道，包括微信、WhatsApp、Telegram、Discord、iMessage 等数十种平台。无论你在哪个聊天软件中发送消息，OpenClaw 都能即时响应，甚至支持在 macOS、iOS 和 Android 设备上进行语音交互，并提供实时的画布渲染功能供你操控。\n\n这款工具主要解决了用户对数据隐私、响应速度以及“始终在线”体验的需求。通过将 AI 部署在本地，用户无需依赖云端服务即可享受快速、私密的智能辅助，真正实现了“你的数据，你做主”。其独特的技术亮点在于强大的网关架构，将控制平面与核心助手分离，确保跨平台通信的流畅性与扩展性。\n\nOpenClaw 非常适合希望构建个性化工作流的技术爱好者、开发者，以及注重隐私保护且不愿被单一生态绑定的普通用户。只要具备基础的终端操作能力（支持 macOS、Linux 及 Windows WSL2），即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你",349277,3,"2026-04-06T06:32:30",[13,14,15,16],"Agent","开发框架","图像","数据工具","ready",{"id":19,"name":20,"github_repo":21,"description_zh":22,"stars":23,"difficulty_score":10,"last_commit_at":24,"category_tags":25,"status":17},3808,"stable-diffusion-webui","AUTOMATIC1111\u002Fstable-diffusion-webui","stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面，旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点，将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。\n\n无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师，还是想要深入探索模型潜力的开发者与研究人员，都能从中获益。其核心亮点在于极高的功能丰富度：不仅支持文生图、图生图、局部重绘（Inpainting）和外绘（Outpainting）等基础模式，还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外，它内置了 GFPGAN 和 CodeFormer 等人脸修复工具，支持多种神经网络放大算法，并允许用户通过插件系统无限扩展能力。即使是显存有限的设备，stable-diffusion-webui 也提供了相应的优化选项，让高质量的 AI 艺术创作变得触手可及。",162132,"2026-04-05T11:01:52",[14,15,13],{"id":27,"name":28,"github_repo":29,"description_zh":30,"stars":31,"difficulty_score":32,"last_commit_at":33,"category_tags":34,"status":17},1381,"everything-claude-code","affaan-m\u002Feverything-claude-code","everything-claude-code 是一套专为 AI 编程助手（如 Claude Code、Codex、Cursor 等）打造的高性能优化系统。它不仅仅是一组配置文件，而是一个经过长期实战打磨的完整框架，旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。\n\n通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能，everything-claude-code 能显著提升 AI 在复杂任务中的表现，帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略，使得模型响应更快、成本更低，同时有效防御潜在的攻击向量。\n\n这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库，还是需要 AI 协助进行安全审计与自动化测试，everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目，它融合了多语言支持与丰富的实战钩子（hooks），让 AI 真正成长为懂上",141543,2,"2026-04-06T11:32:54",[14,13,35],"语言模型",{"id":37,"name":38,"github_repo":39,"description_zh":40,"stars":41,"difficulty_score":32,"last_commit_at":42,"category_tags":43,"status":17},2271,"ComfyUI","Comfy-Org\u002FComfyUI","ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎，专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式，采用直观的节点式流程图界面，让用户通过连接不同的功能模块即可构建个性化的生成管线。\n\n这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景，也能自由组合模型、调整参数并实时预览效果，轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性，不仅支持 Windows、macOS 和 Linux 全平台，还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构，并率先支持 SDXL、Flux、SD3 等前沿模型。\n\n无论是希望深入探索算法潜力的研究人员和开发者，还是追求极致创作自由度的设计师与资深 AI 绘画爱好者，ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能，使其成为当前最灵活、生态最丰富的开源扩散模型工具之一，帮助用户将创意高效转化为现实。",107888,"2026-04-06T11:32:50",[14,15,13],{"id":45,"name":46,"github_repo":47,"description_zh":48,"stars":49,"difficulty_score":32,"last_commit_at":50,"category_tags":51,"status":17},4721,"markitdown","microsoft\u002Fmarkitdown","MarkItDown 是一款由微软 AutoGen 团队打造的轻量级 Python 工具，专为将各类文件高效转换为 Markdown 格式而设计。它支持 PDF、Word、Excel、PPT、图片（含 OCR）、音频（含语音转录）、HTML 乃至 YouTube 链接等多种格式的解析，能够精准提取文档中的标题、列表、表格和链接等关键结构信息。\n\n在人工智能应用日益普及的今天，大语言模型（LLM）虽擅长处理文本，却难以直接读取复杂的二进制办公文档。MarkItDown 恰好解决了这一痛点，它将非结构化或半结构化的文件转化为模型“原生理解”且 Token 效率极高的 Markdown 格式，成为连接本地文件与 AI 分析 pipeline 的理想桥梁。此外，它还提供了 MCP（模型上下文协议）服务器，可无缝集成到 Claude Desktop 等 LLM 应用中。\n\n这款工具特别适合开发者、数据科学家及 AI 研究人员使用，尤其是那些需要构建文档检索增强生成（RAG）系统、进行批量文本分析或希望让 AI 助手直接“阅读”本地文件的用户。虽然生成的内容也具备一定可读性，但其核心优势在于为机器",93400,"2026-04-06T19:52:38",[52,14],"插件",{"id":54,"name":55,"github_repo":56,"description_zh":57,"stars":58,"difficulty_score":10,"last_commit_at":59,"category_tags":60,"status":17},4487,"LLMs-from-scratch","rasbt\u002FLLMs-from-scratch","LLMs-from-scratch 是一个基于 PyTorch 的开源教育项目，旨在引导用户从零开始一步步构建一个类似 ChatGPT 的大型语言模型（LLM）。它不仅是同名技术著作的官方代码库，更提供了一套完整的实践方案，涵盖模型开发、预训练及微调的全过程。\n\n该项目主要解决了大模型领域“黑盒化”的学习痛点。许多开发者虽能调用现成模型，却难以深入理解其内部架构与训练机制。通过亲手编写每一行核心代码，用户能够透彻掌握 Transformer 架构、注意力机制等关键原理，从而真正理解大模型是如何“思考”的。此外，项目还包含了加载大型预训练权重进行微调的代码，帮助用户将理论知识延伸至实际应用。\n\nLLMs-from-scratch 特别适合希望深入底层原理的 AI 开发者、研究人员以及计算机专业的学生。对于不满足于仅使用 API，而是渴望探究模型构建细节的技术人员而言，这是极佳的学习资源。其独特的技术亮点在于“循序渐进”的教学设计：将复杂的系统工程拆解为清晰的步骤，配合详细的图表与示例，让构建一个虽小但功能完备的大模型变得触手可及。无论你是想夯实理论基础，还是为未来研发更大规模的模型做准备",90106,"2026-04-06T11:19:32",[35,15,13,14],{"id":62,"github_repo":63,"name":64,"description_en":65,"description_zh":66,"ai_summary_zh":66,"readme_en":67,"readme_zh":68,"quickstart_zh":69,"use_case_zh":70,"hero_image_url":71,"owner_login":72,"owner_name":73,"owner_avatar_url":74,"owner_bio":75,"owner_company":75,"owner_location":76,"owner_email":75,"owner_twitter":75,"owner_website":75,"owner_url":77,"languages":75,"stars":78,"forks":79,"last_commit_at":80,"license":75,"difficulty_score":32,"env_os":81,"env_gpu":82,"env_ram":82,"env_deps":83,"category_tags":93,"github_topics":94,"view_count":32,"oss_zip_url":75,"oss_zip_packed_at":75,"status":17,"created_at":100,"updated_at":101,"faqs":102,"releases":143},4620,"liaokongVFX\u002FMCP-Chinese-Getting-Started-Guide","MCP-Chinese-Getting-Started-Guide","Model Context Protocol(MCP) 编程极速入门","MCP-Chinese-Getting-Started-Guide 是一份专为中文开发者打造的 Model Context Protocol (MCP) 极速入门指南。MCP 被誉为 AI 应用的\"USB-C 接口”，旨在解决大语言模型与外部数据源、工具之间连接标准不统一、集成难度高的问题，让 AI 能无缝访问和处理现实世界信息。\n\n本指南通过实战案例，手把手教用户如何使用 Python 和 uv 工具链快速构建一个支持网络搜索功能的 MCP 服务器，并演示了如何利用官方 Inspector 工具进行可视化调试，以及如何编写客户端代码来调用这些能力。其独特亮点在于跳过了仅服务于特定客户端的功能，聚焦于通用的“工具”模块与最常用的 stdio 传输协议，降低了学习门槛，使开发者能更专注于构建跨模型兼容的 AI 应用。\n\n这份资源非常适合希望深入理解 AI 代理架构、想要为大模型扩展自定义能力的软件工程师、AI 研究员及技术爱好者。无论你是想为现有项目接入实时数据，还是探索下一代 AI 应用开发模式，都能从中获得清晰的技术路径和可落地的代码参考，轻松开启标准化 AI 集成之旅。","# Model Context Protocol(MCP) 编程极速入门\n\n\n\n[TOC]\n\n## 简介\n\n模型上下文协议（MCP）是一个创新的开源协议，它重新定义了大语言模型（LLM）与外部世界的互动方式。MCP 提供了一种标准化方法，使任意大语言模型能够轻松连接各种数据源和工具，实现信息的无缝访问和处理。MCP 就像是 AI 应用程序的 USB-C 接口，为 AI 模型提供了一种标准化的方式来连接不同的数据源和工具。\n\n![image-20250223214308430](.assets\u002Fimage-20250223214308430.png)\n\nMCP 有以下几个核心功能：\n\n- Resources 资源\n- Prompts 提示词\n- Tools 工具\n- Sampling 采样\n- Roots 根目录\n- Transports 传输层\n\n因为大部分功能其实都是服务于 Claude 客户端的，本文更希望编写的 MCP 服务器服务与通用大语言模型，所以本文将会主要以“工具”为重点，其他功能会放到最后进行简单讲解。\n\n其中 MCP 的传输层支持了 2 种协议的实现：stdio（标准输入\u002F输出）和 SSE（服务器发送事件），因为 stdio 更为常用，所以本文会以 stdio 为例进行讲解。\n\n本文将会使用 3.11 的 Python 版本，并使用 uv 来管理 Python 项目。同时代码将会在文末放到 Github 上，废话不多说，我们这就开始吧~\n\n## 开发 MCP 服务器\n\n在这一小节中，我们将会实现一个用于网络搜索的服务器。首先，我们先来通过 uv 初始化我们的项目。\n\n> uv 官方文档：https:\u002F\u002Fdocs.astral.sh\u002Fuv\u002F\n\n```shell\n# 初始化项目\nuv init mcp_getting_started\ncd mcp_getting_started\n\n# 创建虚拟环境并进入虚拟环境\nuv venv\n.venv\\Scripts\\activate.bat\n\n# 安装依赖\nuv add \"mcp[cli]\" httpx openai\n\n```\n\n然后我们来创建一个叫 `web_search.py` 文件，来实现我们的服务。MCP 为我们提供了2个对象：`mcp.server.FastMCP` 和 `mcp.server.Server`，`mcp.server.FastMCP` 是更高层的封装，我们这里就来使用它。\n\n```python\nimport httpx\nfrom mcp.server import FastMCP\n\n# # 初始化 FastMCP 服务器\napp = FastMCP('web-search')\n```\n\n实现执行的方法非常简单，MCP 为我们提供了一个 `@mcp.tool()` 我们只需要将实现函数用这个装饰器装饰即可。函数名称将作为工具名称，参数将作为工具参数，并通过注释来描述工具与参数，以及返回值。\n\n这里我们直接使用智谱的接口，它这个接口不仅能帮我们搜索到相关的结果链接，并帮我们生成了对应链接中文章总结后的内容的，~~并且现阶段是免费的~~(目前已经开始收费，0.03元\u002F次)，非常适合我们。\n\n>官方文档：https:\u002F\u002Fbigmodel.cn\u002Fdev\u002Fapi\u002Fsearch-tool\u002Fweb-search-pro\n>\n>API Key 生成地址：https:\u002F\u002Fbigmodel.cn\u002Fusercenter\u002Fproj-mgmt\u002Fapikeys\n\n```python\n@app.tool()\nasync def web_search(query: str) -> str:\n    \"\"\"\n    搜索互联网内容\n\n    Args:\n        query: 要搜索内容\n\n    Returns:\n        搜索结果的总结\n    \"\"\"\n\n    async with httpx.AsyncClient() as client:\n        response = await client.post(\n            'https:\u002F\u002Fopen.bigmodel.cn\u002Fapi\u002Fpaas\u002Fv4\u002Ftools',\n            headers={'Authorization': '换成你自己的API KEY'},\n            json={\n                'tool': 'web-search-pro',\n                'messages': [\n                    {'role': 'user', 'content': query}\n                ],\n                'stream': False\n            }\n        )\n\n        res_data = []\n        for choice in response.json()['choices']:\n            for message in choice['message']['tool_calls']:\n                search_results = message.get('search_result')\n                if not search_results:\n                    continue\n                for result in search_results:\n                    res_data.append(result['content'])\n\n        return '\\n\\n\\n'.join(res_data)\n```\n\n最后，我们来添加运行服务器的代码。\n\n```python\nif __name__ == \"__main__\":\n    app.run(transport='stdio')\n```\n\n## 调试 MCP 服务器\n\n此时，我们就完成了 MCP 服务端的编写。下面，我们来使用官方提供的 `Inspector` 可视化工具来调试我们的服务器。\n\n我们可以通过两种方法来运行`Inspector`：\n\n> 请先确保已经安装了 node 环境。\n\n通过 npx：\n\n```shell\nnpx -y @modelcontextprotocol\u002Finspector \u003Ccommand> \u003Carg1> \u003Carg2>\n```\n\n我们的这个代码运行命令为：\n\n```shell\nnpx -y @modelcontextprotocol\u002Finspector uv run web_search.py\n```\n\n\n\n通过 mcp dev 来运行：\n\n```shell\nmcp dev PYTHONFILE\n```\n\n我们的这个代码运行命令为：\n\n```shell\nmcp dev web_search.py\n```\n\n当出现如下提示则代表运行成功。如果提示连接出错，可能是端口被占用，可以看这个 issue 的解决方法：https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide\u002Fissues\u002F6\n\n![image-20250223223638135](.assets\u002Fimage-20250223223638135.png)\n\n然后，我们打开这个地址，点击左侧的 `Connect` 按钮，即可连接我们刚写的服务。然后我们切换到 `Tools` 栏中，点击 `List Tools` 按钮即可看到我们刚写的工具，我们就可以开始进行调试啦。\n\n![image-20250223224052795](.assets\u002Fimage-20250223224052795.png)\n\n## 开发 MCP 客户端\n\n首先，我们先来看看如何在客户端如何调用我们刚才开发的 MCP 服务器中的工具。\n\n```python\nimport asyncio\n\nfrom mcp.client.stdio import stdio_client\nfrom mcp import ClientSession, StdioServerParameters\n\n# 为 stdio 连接创建服务器参数\nserver_params = StdioServerParameters(\n    # 服务器执行的命令，这里我们使用 uv 来运行 web_search.py\n    command='uv',\n    # 运行的参数\n    args=['run', 'web_search.py'],\n    # 环境变量，默认为 None，表示使用当前环境变量\n    # env=None\n)\n\n\nasync def main():\n    # 创建 stdio 客户端\n    async with stdio_client(server_params) as (stdio, write):\n        # 创建 ClientSession 对象\n        async with ClientSession(stdio, write) as session:\n            # 初始化 ClientSession\n            await session.initialize()\n\n            # 列出可用的工具\n            response = await session.list_tools()\n            print(response)\n\n            # 调用工具\n            response = await session.call_tool('web_search', {'query': '今天杭州天气'})\n            print(response)\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n\n```\n\n因为我们的python脚本需要在虚拟环境中才能运行，所以这里我们通过 `uv` 来启动我们的脚本。\n\n下面我们来通过一个小例子来看看如何让 `DeepSeek` 来调用我们 MCP 服务器中的方法。\n\n这里我们会用 `dotenv` 来管理我们相关的环境变量。.env 文件内容如下：\n\n```shell\nOPENAI_API_KEY=sk-89baxxxxxxxxxxxxxxxxxx\nOPENAI_BASE_URL=https:\u002F\u002Fapi.deepseek.com\nOPENAI_MODEL=deepseek-chat\n```\n\n首先我们来编写我们的 `MCPClient` 类。\n\n```python\nimport json\nimport asyncio\nimport os\nfrom typing import Optional\nfrom contextlib import AsyncExitStack\n\nfrom openai import OpenAI\nfrom dotenv import load_dotenv\n\nfrom mcp import ClientSession, StdioServerParameters\nfrom mcp.client.stdio import stdio_client\n\n\nload_dotenv()\n\n\nclass MCPClient:\n    def __init__(self):\n        self.session: Optional[ClientSession] = None\n        self.exit_stack = AsyncExitStack()\n        self.client = OpenAI()\n```\n\n然后我们添加 `connect_to_server` 方法来初始化我们的 MCP 服务器的 session。\n\n```python\n    async def connect_to_server(self):\n        server_params = StdioServerParameters(\n            command='uv',\n            args=['run', 'web_search.py'],\n            env=None\n        )\n\n        stdio_transport = await self.exit_stack.enter_async_context(\n            stdio_client(server_params))\n        stdio, write = stdio_transport\n        self.session = await self.exit_stack.enter_async_context(\n            ClientSession(stdio, write))\n\n        await self.session.initialize()\n```\n\n然后我们再实现一个用于调用 MCP 服务器的方法来处理和 DeepSeek 之间的交互。\n\n```python\n    async def process_query(self, query: str) -> str:\n        # 这里需要通过 system prompt 来约束一下大语言模型，\n        # 否则会出现不调用工具，自己乱回答的情况\n        system_prompt = (\n            \"You are a helpful assistant.\"\n            \"You have the function of online search. \"\n            \"Please MUST call web_search tool to search the Internet content before answering.\"\n            \"Please do not lose the user's question information when searching,\"\n            \"and try to maintain the completeness of the question content as much as possible.\"\n            \"When there is a date related question in the user's question,\" \n            \"please use the search function directly to search and PROHIBIT inserting specific time.\"\n        )\n        \n        messages = [\n            {\"role\": \"system\", \"content\": system_prompt},\n            {\"role\": \"user\", \"content\": query}\n        ]\n\n        # 获取所有 mcp 服务器 工具列表信息\n        response = await self.session.list_tools()\n        # 生成 function call 的描述信息\n        available_tools = [{\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": tool.name,\n                \"description\": tool.description,\n                \"input_schema\": tool.inputSchema\n            }\n        } for tool in response.tools]\n\n        # 请求 deepseek，function call 的描述信息通过 tools 参数传入\n        response = self.client.chat.completions.create(\n            model=os.getenv(\"OPENAI_MODEL\"),\n            messages=messages,\n            tools=available_tools\n        )\n\n        # 处理返回的内容\n        content = response.choices[0]\n        if content.finish_reason == \"tool_calls\":\n            # 如何是需要使用工具，就解析工具\n            tool_call = content.message.tool_calls[0]\n            tool_name = tool_call.function.name\n            tool_args = json.loads(tool_call.function.arguments)\n\n            # 执行工具\n            result = await self.session.call_tool(tool_name, tool_args)\n            print(f\"\\n\\n[Calling tool {tool_name} with args {tool_args}]\\n\\n\")\n\t\t\t\n            # 将 deepseek 返回的调用哪个工具数据和工具执行完成后的数据都存入messages中\n            messages.append(content.message.model_dump())\n            messages.append({\n                \"role\": \"tool\",\n                \"content\": result.content[0].text,\n                \"tool_call_id\": tool_call.id,\n            })\n\n            # 将上面的结果再返回给 deepseek 用于生产最终的结果\n            response = self.client.chat.completions.create(\n                model=os.getenv(\"OPENAI_MODEL\"),\n                messages=messages,\n            )\n            return response.choices[0].message.content\n\n        return content.message.content\n```\n\n接着，我们来实现循环提问和最后退出后关闭session的操作。\n\n```python\n    async def chat_loop(self):\n        while True:\n            try:\n                query = input(\"\\nQuery: \").strip()\n\n                if query.lower() == 'quit':\n                    break\n\n                response = await self.process_query(query)\n                print(\"\\n\" + response)\n\n            except Exception as e:\n                import traceback\n                traceback.print_exc()\n\n    async def cleanup(self):\n        \"\"\"Clean up resources\"\"\"\n        await self.exit_stack.aclose()\n```\n\n最后，我们来完成运行这个客户端相关的代码\n\n```python\nasync def main():\n    client = MCPClient()\n    try:\n        await client.connect_to_server()\n        await client.chat_loop()\n    finally:\n        await client.cleanup()\n\n\nif __name__ == \"__main__\":\n    import sys\n\n    asyncio.run(main())\n\n```\n\n这是一个最精简的代码，里面没有实现记录上下文消息等功能，只是为了用最简单的代码来了解如何通过大语言模型来调动 MCP 服务器。这里只演示了如何连接单服务器，如果你期望连接多个 MCP 服务器，无非就是循环一下 `connect_to_server` 中的代码，可以将他们封装成一个类，然后将所有的 MCP 服务器中的工具循环遍历生成一个大的 `available_tools`，然后在通过大语言模型的返回结果进行调用即可，这里就不再赘述了。\n> 可以参考官方案例：https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Fpython-sdk\u002Fblob\u002Fmain\u002Fexamples\u002Fclients\u002Fsimple-chatbot\u002Fmcp_simple_chatbot\u002Fmain.py\n\n## Sampling 讲解\n\nMCP 还为我们提供了一个 `Sampling` 的功能，这个如果从字面来理解会让人摸不到头脑，但实际上这个功能就给了我们一个在执行工具的前后的接口，我们可以在工具执行前后来执行一些操作。比如，当调用本地文件的删除的工具的时候，肯定是期望我们确认后再进行删除。那么，此时就可以使用这个功能。\n\n下面我们就来实现这个人工监督的小功能。\n\n首先，我们来创建个模拟拥有删除文件的 MCP 服务器：\n\n```python\n# 服务端\nfrom mcp.server import FastMCP\nfrom mcp.types import SamplingMessage, TextContent\n\napp = FastMCP('file_server')\n\n\n@app.tool()\nasync def delete_file(file_path: str):\n    # 创建 SamplingMessage 用于触发 sampling callback 函数\n    result = await app.get_context().session.create_message(\n        messages=[\n            SamplingMessage(\n                role='user', content=TextContent(\n                    type='text', text=f'是否要删除文件: {file_path} (Y)')\n            )\n        ],\n        max_tokens=100\n    )\n\n    # 获取到 sampling callback 函数的返回值，并根据返回值进行处理\n    if result.content.text == 'Y':\n        return f'文件 {file_path} 已被删除！！'\n\n\nif __name__ == '__main__':\n    app.run(transport='stdio')\n\n```\n\n这里最重要的就是需要通过`create_message`方法来创建一个 `SamplingMessage` 类型的 message，他会将这个 message 发送给 sampling callback 对应的函数中。\n\n接着，我们来创建客户端的代码：\n\n```python\n# 客户端\nimport asyncio\n\nfrom mcp.client.stdio import stdio_client\nfrom mcp import ClientSession, StdioServerParameters\nfrom mcp.shared.context import RequestContext\nfrom mcp.types import (\n    TextContent,\n    CreateMessageRequestParams,\n    CreateMessageResult,\n)\n\nserver_params = StdioServerParameters(\n    command='uv',\n    args=['run', 'file_server.py'],\n)\n\n\nasync def sampling_callback(\n        context: RequestContext[ClientSession, None],\n        params: CreateMessageRequestParams,\n):\n    # 获取工具发送的消息并显示给用户\n    input_message = input(params.messages[0].content.text)\n    # 将用户输入发送回工具\n    return CreateMessageResult(\n        role='user',\n        content=TextContent(\n            type='text',\n            text=input_message.strip().upper() or 'Y'\n        ),\n        model='user-input',\n        stopReason='endTurn'\n    )\n\n\nasync def main():\n    async with stdio_client(server_params) as (stdio, write):\n        async with ClientSession(\n                stdio, write,\n                # 设置 sampling_callback 对应的方法\n                sampling_callback=sampling_callback\n        ) as session:\n            await session.initialize()\n            res = await session.call_tool(\n                'delete_file',\n                {'file_path': 'C:\u002Fxxx.txt'}\n            )\n            # 获取工具最后执行完的返回结果\n            print(res)\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n\n```\n\n特别要注意的是，目前在工具里面打印的内容实际上使用 `stdio_client` 是无法显示到命令行窗口的。所以，我们调试的话，可以使用 `mcp.shared.memory.create_connected_server_and_client_session`。\n\n具体代码如下：\n\n```python\n# 客户端\nfrom mcp.shared.memory import (\n    create_connected_server_and_client_session as create_session\n)\n# 这里需要引入服务端的 app 对象\nfrom file_server import app\n\nasync def sampling_callback(context, params):\n    ...\n\nasync def main():\n    async with create_session(\n        app._mcp_server,\n        sampling_callback=sampling_callback\n    ) as client_session:\n        await client_session.call_tool(\n            'delete_file', \n            {'file_path': 'C:\u002Fxxx.txt'}\n        )\n\nif __name__ == '__main__':\n    asyncio.run(main())\n```\n\n\n\n## Claude Desktop 加载 MCP Server\n\n因为后面的两个功能实际上都是为了提供给 Claude 桌面端用的，所以这里先说下如何加载我们自定义的 MCP Server 到 Claude 桌面端。\n\n首先，我们先打开配置。\n\n![image-20250227221154638](.assets\u002Fimage-20250227221154638.png)\n\n我们点击 `Developer` 菜单，然后点击 `Edit Config` 按钮打开 Claude 桌面端的配置文件 `claude_desktop_config.json`\n\n![image-20250227221302174](.assets\u002Fimage-20250227221302174.png)\n\n然后开始添加我们的服务器，服务器需要在 `mcpServers` 层级下，参数有 `command`、`args`、`env`。实际上，参数和 `StdioServerParameters` 对象初始化时候的参数是一样的。 \n\n```json\n{\n  \"mcpServers\": {\n    \"web-search-server\": {\n      \"command\": \"uv\",\n      \"args\": [\n        \"--directory\",\n        \"D:\u002Fprojects\u002Fmcp_getting_started\",\n        \"run\",\n        \"web_search.py\"\n      ]\n    }\n  }\n}\n```\n\n最后，我们保存文件后重启 Claude 桌面端就可以在这里看到我们的插件了。\n\n![image-20250227221911231](.assets\u002Fimage-20250227221911231.png)\n\n![image-20250227221921036](.assets\u002Fimage-20250227221921036.png)\n\n当然，我们也可以直接在我们插件的目录下运行以下命令来直接安装：\n\n```shell\nmcp install web_search.py\n```\n\n\n\n## 其他功能\n\n### Prompt\n\nMCP 还为我们提供了一个生成 Prompt 模板的功能。他使用起来也很简单，只需要使用 `prompt` 装饰器装饰一下即可，代码如下：\n\n```python\nfrom mcp.server import FastMCP\n\napp = FastMCP('prompt_and_resources')\n\n@app.prompt('翻译专家')\nasync def translate_expert(\n        target_language: str = 'Chinese',\n) -> str:\n    return f'你是一个翻译专家，擅长将任何语言翻译成{target_language}。请翻译以下内容：'\n\n\nif __name__ == '__main__':\n    app.run(transport='stdio')\n\n```\n\n然后我们用上一节讲到的配置 Claude 桌面端 MCP 服务器的方法添加下我们的新 MCP 服务器。然后我们就可以点击右下角的图标开始使用啦。\n\n他会让我们设置一下我们传入的参数，然后他会在我们的聊天窗口上生成一个附件。\n\n![mcp001](.assets\u002Fmcp001-1740666812436-2.gif)\n\n\n\n### Resource\n\n我们还可以在 Claude 客户端上选择我们为用户提供的预设资源，同时也支持自定义的协议。具体代码如下：\n\n```python\nfrom mcp.server import FastMCP\n\napp = FastMCP('prompt_and_resources')\n\n@app.resource('echo:\u002F\u002Fstatic')\nasync def echo_resource():\n    # 返回的是，当用户使用这个资源时，资源的内容\n    return 'Echo!'\n\n@app.resource('greeting:\u002F\u002F{name}')\nasync def get_greeting(name):\n    return f'Hello, {name}!'\n\n\nif __name__ == '__main__':\n    app.run(transport='stdio')\n\n```\n\n然后，我们到 Claude 桌面端上看看。\n\n![mcp002](.assets\u002Fmcp002.gif)\n\n这里要特别注意的是，目前 Claude 桌面端是没法读到资源装饰器设置 `greeting:\u002F\u002F{name}` 这种通配符的路径，未来将会被支持。但是，在我们的客户端代码中是可以当做资源模板来使用的，具体代码如下：\n\n```python\nimport asyncio\nfrom pydantic import AnyUrl\n\nfrom mcp.client.stdio import stdio_client\nfrom mcp import ClientSession, StdioServerParameters\n\nserver_params = StdioServerParameters(\n    command='uv',\n    args=['run', 'prompt_and_resources.py'],\n)\n\n\nasync def main():\n    async with stdio_client(server_params) as (stdio, write):\n        async with ClientSession(stdio, write) as session:\n            await session.initialize()\n\n            # 获取无通配符的资源列表\n            res = await session.list_resources()\n            print(res)\n\n            # 获取有通配符的资源列表(资源模板)\n            res = await session.list_resource_templates()\n            print(res)\n\n            # 读取资源，会匹配通配符\n            res = await session.read_resource(AnyUrl('greeting:\u002F\u002Fliming'))\n            print(res)\n\n            # 获取 Prompt 模板列表\n            res = await session.list_prompts()\n            print(res)\n\n            # 使用 Prompt 模板\n            res = await session.get_prompt(\n                '翻译专家', arguments={'target_language': '英语'})\n            print(res)\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n\n```\n\n\n\n### 生命周期\n\nMCP 生命周期分为3个阶段：\n\n- 初始化\n- 交互通信中\n- 服务被关闭\n\n因此，我们可以在这个三个阶段的开始和结束来做一些事情，比如创建数据库连接和关闭数据库连接、记录日志、记录工具使用信息等。\n\n下面我们将以网页搜索工具，把工具调用时的查询和查询到的结果存储到一个全局上下文中作为缓存为例，来看看生命周期如何使用。完整代码如下：\n\n```python\nimport httpx\nfrom dataclasses import dataclass\nfrom contextlib import asynccontextmanager\n\nfrom mcp.server import FastMCP\nfrom mcp.server.fastmcp import Context\n\n\n@dataclass\n# 初始化一个生命周期上下文对象\nclass AppContext:\n    # 里面有一个字段用于存储请求历史\n    histories: dict\n\n\n@asynccontextmanager\nasync def app_lifespan(server):\n    # 在 MCP 初始化时执行\n    histories = {}\n    try:\n        # 每次通信会把这个上下文通过参数传入工具\n        yield AppContext(histories=histories)\n    finally:\n        # 当 MCP 服务关闭时执行\n        print(histories)\n\n\napp = FastMCP(\n    'web-search', \n    # 设置生命周期监听函数\n    lifespan=app_lifespan\n)\n\n\n@app.tool()\n# 第一个参数会被传入上下文对象\nasync def web_search(ctx: Context, query: str) -> str:\n    \"\"\"\n    搜索互联网内容\n\n    Args:\n        query: 要搜索内容\n\n    Returns:\n        搜索结果的总结\n    \"\"\"\n    # 如果之前问过同样的问题，就直接返回缓存\n    histories = ctx.request_context.lifespan_context.histories\n    if query in histories：\n    \treturn histories[query]\n\n    async with httpx.AsyncClient() as client:\n        response = await client.post(\n            'https:\u002F\u002Fopen.bigmodel.cn\u002Fapi\u002Fpaas\u002Fv4\u002Ftools',\n            headers={'Authorization': 'YOUR API KEY'},\n            json={\n                'tool': 'web-search-pro',\n                'messages': [\n                    {'role': 'user', 'content': query}\n                ],\n                'stream': False\n            }\n        )\n\n        res_data = []\n        for choice in response.json()['choices']:\n            for message in choice['message']['tool_calls']:\n                search_results = message.get('search_result')\n                if not search_results:\n                    continue\n                for result in search_results:\n                    res_data.append(result['content'])\n\n        return_data = '\\n\\n\\n'.join(res_data)\n\n        # 将查询值和返回值存入到 histories 中\n        ctx.request_context.lifespan_context.histories[query] = return_data\n        return return_data\n\n\nif __name__ == \"__main__\":\n    app.run()\n\n```\n\n\n\n## 在 LangChain 中使用 MCP 服务器\n\n最近 LangChain 发布了一个新的开源项目 `langchain-mcp-adapters`，可以很方便的将 MCP 服务器集成到 LangChain 中。下面我们来看看如何使用它:\n\n```python\nfrom mcp import ClientSession, StdioServerParameters\nfrom mcp.client.stdio import stdio_client\n\nfrom langchain_mcp_adapters.tools import load_mcp_tools\nfrom langgraph.prebuilt import create_react_agent\n\nfrom langchain_openai import ChatOpenAI\nmodel = ChatOpenAI(model=\"gpt-4o\")\n\nserver_params = StdioServerParameters(\n    command='uv',\n    args=['run', 'web_search.py'],\n)\n\nasync with stdio_client(server_params) as (read, write):\n    async with ClientSession(read, write) as session:\n        await session.initialize()\n\n        # 获取工具列表\n        tools = await load_mcp_tools(session)\n\n        # 创建并使用 ReAct agent\n        agent = create_react_agent(model, tools)\n        agent_response = await agent.ainvoke({'messages': '杭州今天天气怎么样？'})\n```\n\n更详细的使用方法请参考：https:\u002F\u002Fgithub.com\u002Flangchain-ai\u002Flangchain-mcp-adapters\n\n\n\n## DeepSeek + cline + 自定义MCP = 图文大师\n\n最后，我们来使用 VsCode 的 cline 插件，来通过 DeepSeek 和我们自定义的一个图片生成的 mcp 服务器来构建一个图文大师的应用。废话不多说，我们直接开始。\n\n首先先来构建我们的图片生成的 mcp server，这里我们直接用 huggingface 上的 `FLUX.1-schnell` 模型，地址是：https:\u002F\u002Fhuggingface.co\u002Fspaces\u002Fblack-forest-labs\u002FFLUX.1-schnell 。这里我们不使用 `gradio_client` 库，而是会使用 `httpx` 手搓一个，因为使用 `gradio_client` 库可能会出现编码错误的bug。具体代码如下：\n\n```python\n# image_server.py\n\nimport json\nimport httpx\nfrom mcp.server import FastMCP\n\n\napp = FastMCP('image_server')\n\n\n@app.tool()\nasync def image_generation(image_prompt: str):\n    \"\"\"\n    生成图片\n    :param image_prompt: 图片描述，需要是英文\n    :return: 图片保存到的本地路径\n    \"\"\"\n    async with httpx.AsyncClient() as client:\n        data = {'data': [image_prompt, 0, True, 512, 512, 3]}\n\n        # 创建生成图片任务\n        response1 = await client.post(\n            'https:\u002F\u002Fblack-forest-labs-flux-1-schnell.hf.space\u002Fcall\u002Finfer',\n            json=data,\n            headers={\"Content-Type\": \"application\u002Fjson\"}\n        )\n\n        # 解析响应获取事件 ID\n        response_data = response1.json()\n        event_id = response_data.get('event_id')\n\n        if not event_id:\n            return '无法获取事件 ID'\n\n        # 通过流式的方式拿到返回数据\n        url = f'https:\u002F\u002Fblack-forest-labs-flux-1-schnell.hf.space\u002Fcall\u002Finfer\u002F{event_id}'\n        full_response = ''\n        async with client.stream('GET', url) as response2:\n            async for chunk in response2.aiter_text():\n                full_response += chunk\n\n        return json.loads(full_response.split('data: ')[-1])[0]['url']\n\nif __name__ == '__main__':\n    app.run(transport='stdio')\n\n```\n\n然后我们可以在虚拟环境下使用下面的命令打开 MCP Inspector 进行调试下我们的工具。\n\n```shell\nmcp dev image_server.py\n```\n\n![image-20250301231332749](.assets\u002Fimage-20250301231332749.png)\n\n接着我们在 VsCode 中安装 cline 插件，当安装完插件后，我们配置一下我们的 deepseek 的 api key。接着，我们点击右上角的 `MCP Server` 按钮打开 mcp server 列表。\n\n![image-20250301232248034](.assets\u002Fimage-20250301232248034.png)\n\n然后切换到 `Installed` Tab 点击 `Configure MCP Servers` 按钮来编辑自定义的 mcp 服务器。\n\n![image-20250301232417966](.assets\u002Fimage-20250301232417966.png)\n\n配置如下：\n\n```json\n{\n  \"mcpServers\": {\n    \"image_server\": {\n      \"command\": \"uv\",\n      \"args\": [\n        \"--directory\",\n        \"D:\u002Fprojects\u002Fmcp_getting_started\",\n        \"run\",\n        \"image_server.py\"\n      ],\n      \"env\": {},\n      \"disabled\": false,\n      \"autoApprove\": []\n    }\n  }\n}\n```\n\n我们保存后，这里的这个小点是绿色的就表示我们的服务器已连接，然后我们就可以开始使用啦。\n\n![image-20250301232809433](.assets\u002Fimage-20250301232809433.png)\n\n然后，我们就打开输入框，来输入我们的要写的文章的内容：\n\n![image-20250301233421292](.assets\u002Fimage-20250301233421292.png)\n\n我们可以看到，他正确的调用了我们的工具\n\n![image-20250301233726301](.assets\u002Fimage-20250301233726301.png)\n\n最后，就是可以看到生成的文章啦。\n\n![image-20250301234532249](.assets\u002Fimage-20250301234532249.png)\n\n\n\n## 借助 serverless 将 MCP 服务部署到云端\n\n上面我们讲的都是如何使用本地的 MCP 服务，但是有时我们希望直接把 MCP 服务部署到云端来直接调用，就省去了本地下载启动的烦恼了。此时，我们就需要来使用 MCP 的 SSE 的协议来实现了。\n\n此时，我们先来写 SSE 协议的 MCP 服务。实现起来很简单，只需要将我们最后的 `run` 命令中的 `transport` 参数设置为 `sse` 即可。下面还是以上面的网络搜索为例子，来实现一下 ，具体代码如下：\n\n```python\n# sse_web_search.py\nimport httpx\n\nfrom mcp.server import FastMCP\n\n\napp = FastMCP('web-search', port=9000)\n\n\n@app.tool()\nasync def web_search(query: str) -> str:\n    \"\"\"\n    搜索互联网内容\n\n    Args:\n        query: 要搜索内容\n\n    Returns:\n        搜索结果的总结\n    \"\"\"\n    async with httpx.AsyncClient() as client:\n        response = await client.post(\n            'https:\u002F\u002Fopen.bigmodel.cn\u002Fapi\u002Fpaas\u002Fv4\u002Ftools',\n            headers={'Authorization': 'YOUR API KEY'},\n            json={\n                'tool': 'web-search-pro',\n                'messages': [\n                    {'role': 'user', 'content': query}\n                ],\n                'stream': False\n            }\n        )\n\n        res_data = []\n        for choice in response.json()['choices']:\n            for message in choice['message']['tool_calls']:\n                search_results = message.get('search_result')\n                if not search_results:\n                    continue\n                for result in search_results:\n                    res_data.append(result['content'])\n\n        return '\\n\\n\\n'.join(res_data)\n\n\nif __name__ == \"__main__\":\n    app.run(transport='sse')\n\n```\n\n在 `FastMCP` 中，有几个可以设置 SSE 协议相关的参数：\n\n- host: 服务地址，默认为 `0.0.0.0`\n- port: 服务端口，默认为 8000。上述代码中，我设置为 `9000`\n- sse_path：sse 的路由，默认为 `\u002Fsse`\n\n此时，我们就可以直接写一个客户端的代码来进行测试了。具体代码如下：\n\n```python\nimport asyncio\nfrom mcp.client.sse import sse_client\nfrom mcp import ClientSession\n\n\nasync def main():\n    async with sse_client('http:\u002F\u002Flocalhost:9000\u002Fsse') as streams:\n        async with ClientSession(*streams) as session:\n            await session.initialize()\n\n            res = await session.call_tool('web_search', {'query': '杭州今天天气'})\n            print(res)\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n\n```\n\n我们可以看到，他正常工作了，并搜索到了内容：\n\n![image-20250406152518223](.assets\u002Fimage-20250406152518223.png)\n\n当然，我们也可以使用 `mcp dev sse_web_search.py` 的方式来测试。这里要注意的是，`Transport Type` 需要改成 `SSE`，然后下面填写我们的本地服务地址。\n\n![image-20250406153106098](.assets\u002Fimage-20250406153106098.png)\n\n当一切都测试没有问题后，我们就来将他通过 severless 的方式来部署到云端。这里我们选择的是阿里云的函数计算服务。首先我们先进入到阿里云的 `函数计算 FC 3.0` 的 `函数` 菜单，并点击 `创建函数` 来创建我们的服务。地址是：https:\u002F\u002Ffcnext.console.aliyun.com\u002Fcn-hangzhou\u002Ffunctions\n\n![image-20250406153655185](.assets\u002Fimage-20250406153655185.png)\n\n我们这里选择 `Web函数` ，运行环境我们选择 `Python 10`。代码上传方式这里可以根据大家需求来，因为我这里就一个 python 文件，所以我这里就直接选择`使用示例代码`了，这样我后面直接把我的代码覆盖进去了就行了。启动命令和监听端口我这里都保留为默认(**端口需要和代码中一致**)。\n\n环境变量大家可以将代码中用到的 apikey 可以设置为一个环境变量，这里我就不设置了。最后设置完成截图如下：\n\n![image-20250406154115438](.assets\u002Fimage-20250406154115438.png)\n\n在高级设置中，为了方便调试，我启动了日志功能。\n\n![image-20250406154228341](.assets\u002Fimage-20250406154228341.png)\n\n设置完成后，点创建即可。他就跳转到代码编辑部分，然后我们把之前的代码复制进去即可。\n\n![image-20250406154441634](.assets\u002Fimage-20250406154441634.png)\n\n完成后，我们来安装下依赖。我们点击右上角的`编辑层`。这里默认会有个默认的 flask 的层，因为开始的模板用的是 flask，这里我们就不需要了。我们删除他，再添加一个 mcp 的层。选择`添加官方公共层`，然后搜索 `mcp` 就能看到了一个 python 版的 MCP 层，里面包含了 MCP 所有用到的依赖。\n\n![image-20250406154753623](.assets\u002Fimage-20250406154753623.png)\n\n如果你还有其他第三方的，可以先搜索下看看公共层中是否有，没有就可以自行构建一个自定义的层。点击这里就可以，只需要提供一个 `requirements` 列表就可以了，这里就不赘述了。 \n\n![image-20250406154935751](.assets\u002Fimage-20250406154935751.png)\n\n当我们都设置完成后，点击右下角的部署即可。\n\n然后我们又回到了我们代码编辑的页面，此时，我们再点击左上角的部署代码。稍等一两秒就会提示代码部署成功。此时，我们的 MCP 服务就被部署到了云端。\n\n![image-20250406155135563](.assets\u002Fimage-20250406155135563.png)\n\n\n\n> 20250409 更新：不知道是不是官方看到了这篇文章，现在运行时可以直接选择 `MCP 运行时` 了，就不用再在层那里手动添加 `MCP 层` 了。\n>\n> ![image-20250409213302652](.assets\u002Fimage-20250409213302652.png)\n\n\n\n然后，我们切换到`配置`的`触发器`中，就可以看到我们用来访问的 URL 地址了。当然，你也可以绑定自己的域名。\n\n![image-20250406155353662](.assets\u002Fimage-20250406155353662.png)\n\n然后，我们就可以用我们上面的客户端代码进行测试了。\n\n```python\nimport asyncio\nfrom mcp.client.sse import sse_client\nfrom mcp import ClientSession\n\n\nasync def main():\n    async with sse_client('https:\u002F\u002Fmcp-test-whhergsbso.cn-hangzhou.fcapp.run\u002Fsse') as streams:\n        async with ClientSession(*streams) as session:\n            await session.initialize()\n\n            res = await session.call_tool('web_search', {'query': '杭州今天天气'})\n            print(res)\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n```\n\n如果我们发现在客户端有报错也不用慌，我们可以直接在日志中找到对应出错的请求点击`请求日志`查看报错来修复。\n\n![image-20250406155803071](.assets\u002Fimage-20250406155803071.png)\n\n到这里，我们的 MCP 服务就被部署到了云端，我们就可以在任何地方直接来使用它了。\n\n比如，在 `Cherry-Studio` 中，我们可以这样来设置：\n\n![image-20250406160152782](.assets\u002Fimage-20250406160152782.png)\n\n在 `Cline` 中：\n\n![image-20250406160709759](.assets\u002Fimage-20250406160709759.png)\n\n在 `Cursor` 中：\n\n![image-20250406161055717](.assets\u002Fimage-20250406161055717.png)\n\n```json\n{\n  \"mcpServers\": {\n    \"web-search\": {\n      \"url\": \"https:\u002F\u002Fmcp-test-whhergsbso.cn-hangzhou.fcapp.run\u002Fsse\"\n    }\n  }\n}\n```\n\n\n\n至此，整个 MCP 入门教程就到这里啦，后续有其他的再进行更新。相关代码会放到 github 仓库中：https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide\n\n","# 模型上下文协议(MCP) 编程极速入门\n\n\n\n[TOC]\n\n## 简介\n\n模型上下文协议（MCP）是一个创新的开源协议，它重新定义了大语言模型（LLM）与外部世界的互动方式。MCP 提供了一种标准化方法，使任意大语言模型能够轻松连接各种数据源和工具，实现信息的无缝访问和处理。MCP 就像是 AI 应用程序的 USB-C 接口，为 AI 模型提供了一种标准化的方式来连接不同的数据源和工具。\n\n![image-20250223214308430](.assets\u002Fimage-20250223214308430.png)\n\nMCP 有以下几个核心功能：\n\n- Resources 资源\n- Prompts 提示词\n- Tools 工具\n- Sampling 采样\n- Roots 根目录\n- Transports 传输层\n\n因为大部分功能其实都是服务于 Claude 客户端的，本文更希望编写的 MCP 服务器服务与通用大语言模型，所以本文将会主要以“工具”为重点，其他功能会放到最后进行简单讲解。\n\n其中 MCP 的传输层支持了 2 种协议的实现：stdio（标准输入\u002F输出）和 SSE（服务器发送事件），因为 stdio 更为常用，所以本文会以 stdio 为例进行讲解。\n\n本文将会使用 3.11 的 Python 版本，并使用 uv 来管理 Python 项目。同时代码将会在文末放到 Github 上，废话不多说，我们这就开始吧~\n\n## 开发 MCP 服务器\n\n在这一小节中，我们将会实现一个用于网络搜索的服务器。首先，我们先来通过 uv 初始化我们的项目。\n\n> uv 官方文档：https:\u002F\u002Fdocs.astral.sh\u002Fuv\u002F\n\n```shell\n# 初始化项目\nuv init mcp_getting_started\ncd mcp_getting_started\n\n# 创建虚拟环境并进入虚拟环境\nuv venv\n.venv\\Scripts\\activate.bat\n\n# 安装依赖\nuv add \"mcp[cli]\" httpx openai\n\n```\n\n然后我们来创建一个叫 `web_search.py` 文件，来实现我们的服务。MCP 为我们提供了2个对象：`mcp.server.FastMCP` 和 `mcp.server.Server`，`mcp.server.FastMCP` 是更高层的封装，我们这里就来使用它。\n\n```python\nimport httpx\nfrom mcp.server import FastMCP\n\n# # 初始化 FastMCP 服务器\napp = FastMCP('web-search')\n```\n\n实现执行的方法非常简单，MCP 为我们提供了一个 `@mcp.tool()` 我们只需要将实现函数用这个装饰器装饰即可。函数名称将作为工具名称，参数将作为工具参数，并通过注释来描述工具与参数，以及返回值。\n\n这里我们直接使用智谱的接口，它这个接口不仅能帮我们搜索到相关的结果链接，并帮我们生成了对应链接中文章总结后的内容的，~~并且现阶段是免费的~~(目前已经开始收费，0.03元\u002F次)，非常适合我们。\n\n>官方文档：https:\u002F\u002Fbigmodel.cn\u002Fdev\u002Fapi\u002Fsearch-tool\u002Fweb-search-pro\n>\n>API Key 生成地址：https:\u002F\u002Fbigmodel.cn\u002Fusercenter\u002Fproj-mgmt\u002Fapikeys\n\n```python\n@app.tool()\nasync def web_search(query: str) -> str:\n    \"\"\"\n    搜索互联网内容\n\n    Args:\n        query: 要搜索内容\n\n    Returns:\n        搜索结果的总结\n    \"\"\"\n\n    async with httpx.AsyncClient() as client:\n        response = await client.post(\n            'https:\u002F\u002Fopen.bigmodel.cn\u002Fapi\u002Fpaas\u002Fv4\u002Ftools',\n            headers={'Authorization': '换成你自己的API KEY'},\n            json={\n                'tool': 'web-search-pro',\n                'messages': [\n                    {'role': 'user', 'content': query}\n                ],\n                'stream': False\n            }\n        )\n\n        res_data = []\n        for choice in response.json()['choices']:\n            for message in choice['message']['tool_calls']:\n                search_results = message.get('search_result')\n                if not search_results:\n                    continue\n                for result in search_results:\n                    res_data.append(result['content'])\n\n        return '\\n\\n\\n'.join(res_data)\n```\n\n最后，我们来添加运行服务器的代码。\n\n```python\nif __name__ == \"__main__\":\n    app.run(transport='stdio')\n```\n\n## 调试 MCP 服务器\n\n此时，我们就完成了 MCP 服务端的编写。下面，我们来使用官方提供的 `Inspector` 可视化工具来调试我们的服务器。\n\n我们可以通过两种方法来运行`Inspector`：\n\n> 请先确保已经安装了 node 环境。\n\n通过 npx：\n\n```shell\nnpx -y @modelcontextprotocol\u002Finspector \u003Ccommand> \u003Carg1> \u003Carg2>\n```\n\n我们的这个代码运行命令为：\n\n```shell\nnpx -y @modelcontextprotocol\u002Finspector uv run web_search.py\n```\n\n\n\n通过 mcp dev 来运行：\n\n```shell\nmcp dev PYTHONFILE\n```\n\n我们的这个代码运行命令为：\n\n```shell\nmcp dev web_search.py\n```\n\n当出现如下提示则代表运行成功。如果提示连接出错，可能是端口被占用，可以看这个 issue 的解决方法：https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide\u002Fissues\u002F6\n\n![image-20250223223638135](.assets\u002Fimage-20250223223638135.png)\n\n然后，我们打开这个地址，点击左侧的 `Connect` 按钮，即可连接我们刚写的服务。然后我们切换到 `Tools` 栏中，点击 `List Tools` 按钮即可看到我们刚写的工具，我们就可以开始进行调试啦。\n\n![image-20250223224052795](.assets\u002Fimage-20250223224052795.png)\n\n## 开发 MCP 客户端\n\n首先，我们先来看看如何在客户端如何调用我们刚才开发的 MCP 服务器中的工具。\n\n```python\nimport asyncio\n\nfrom mcp.client.stdio import stdio_client\nfrom mcp import ClientSession, StdioServerParameters\n\n# 为 stdio 连接创建服务器参数\nserver_params = StdioServerParameters(\n    # 服务器执行的命令，这里我们使用 uv 来运行 web_search.py\n    command='uv',\n    # 运行的参数\n    args=['run', 'web_search.py'],\n    # 环境变量，默认为 None，表示使用当前环境变量\n    # env=None\n)\n\n\nasync def main():\n    # 创建 stdio 客户端\n    async with stdio_client(server_params) as (stdio, write):\n        # 创建 ClientSession 对象\n        async with ClientSession(stdio, write) as session:\n            # 初始化 ClientSession\n            await session.initialize()\n\n            # 列出可用的工具\n            response = await session.list_tools()\n            print(response)\n\n            # 调用工具\n            response = await session.call_tool('web_search', {'query': '今天杭州天气'})\n            print(response)\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n\n```\n\n因为我们的python脚本需要在虚拟环境中才能运行，所以这里我们通过 `uv` 来启动我们的脚本。\n\n下面我们来通过一个小例子来看看如何让 `DeepSeek` 来调用我们 MCP 服务器中的方法。\n\n这里我们会用 `dotenv` 来管理我们相关的环境变量。.env 文件内容如下：\n\n```shell\nOPENAI_API_KEY=sk-89baxxxxxxxxxxxxxxxxxx\nOPENAI_BASE_URL=https:\u002F\u002Fapi.deepseek.com\nOPENAI_MODEL=deepseek-chat\n```\n\n首先我们来编写我们的 `MCPClient` 类。\n\n```python\nimport json\nimport asyncio\nimport os\nfrom typing import Optional\nfrom contextlib import AsyncExitStack\n\nfrom openai import OpenAI\nfrom dotenv import load_dotenv\n\nfrom mcp import ClientSession, StdioServerParameters\nfrom mcp.client.stdio import stdio_client\n\n\nload_dotenv()\n\n\nclass MCPClient:\n    def __init__(self):\n        self.session: Optional[ClientSession] = None\n        self.exit_stack = AsyncExitStack()\n        self.client = OpenAI()\n```\n\n然后我们添加 `connect_to_server` 方法来初始化我们的 MCP 服务器的 session。\n\n```python\n    async def connect_to_server(self):\n        server_params = StdioServerParameters(\n            command='uv',\n            args=['run', 'web_search.py'],\n            env=None\n        )\n\n        stdio_transport = await self.exit_stack.enter_async_context(\n            stdio_client(server_params))\n        stdio, write = stdio_transport\n        self.session = await self.exit_stack.enter_async_context(\n            ClientSession(stdio, write))\n\n        await self.session.initialize()\n```\n\n然后我们再实现一个用于调用 MCP 服务器的方法来处理和 DeepSeek 之间的交互。\n\n```python\n    async def process_query(self, query: str) -> str:\n        # 这里需要通过 system prompt 来约束一下大语言模型，\n        # 否则会出现不调用工具，自己乱回答的情况\n        system_prompt = (\n            \"You are a helpful assistant.\"\n            \"You have the function of online search. \"\n            \"Please MUST call web_search tool to search the Internet content before answering.\"\n            \"Please do not lose the user's question information when searching,\"\n            \"and try to maintain the completeness of the question content as much as possible.\"\n            \"When there is a date related question in the user's question,\" \n            \"please use the search function directly to search and PROHIBIT inserting specific time.\"\n        )\n        \n        messages = [\n            {\"role\": \"system\", \"content\": system_prompt},\n            {\"role\": \"user\", \"content\": query}\n        ]\n\n        # 获取所有 mcp 服务器 工具列表信息\n        response = await self.session.list_tools()\n        # 生成 function call 的描述信息\n        available_tools = [{\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": tool.name,\n                \"description\": tool.description,\n                \"input_schema\": tool.inputSchema\n            }\n        } for tool in response.tools]\n\n        # 请求 deepseek，function call 的描述信息通过 tools 参数传入\n        response = self.client.chat.completions.create(\n            model=os.getenv(\"OPENAI_MODEL\"),\n            messages=messages,\n            tools=available_tools\n        )\n\n        # 处理返回的内容\n        content = response.choices[0]\n        if content.finish_reason == \"tool_calls\":\n            # 如何是需要使用工具，就解析工具\n            tool_call = content.message.tool_calls[0]\n            tool_name = tool_call.function.name\n            tool_args = json.loads(tool_call.function.arguments)\n\n            # 执行工具\n            result = await self.session.call_tool(tool_name, tool_args)\n            print(f\"\\n\\n[Calling tool {tool_name} with args {tool_args}]\\n\\n\")\n\t\t\t\n            # 将 deepseek 返回的调用哪个工具数据和工具执行完成后的数据都存入messages中\n            messages.append(content.message.model_dump())\n            messages.append({\n                \"role\": \"tool\",\n                \"content\": result.content[0].text,\n                \"tool_call_id\": tool_call.id,\n            })\n\n            # 将上面的结果再返回给 deepseek 用于生产最终的结果\n            response = self.client.chat.completions.create(\n                model=os.getenv(\"OPENAI_MODEL\"),\n                messages=messages,\n            )\n            return response.choices[0].message.content\n\n        return content.message.content\n```\n\n接着，我们来实现循环提问和最后退出后关闭session的操作。\n\n```python\n    async def chat_loop(self):\n        while True:\n            try:\n                query = input(\"\\nQuery: \").strip()\n\n                if query.lower() == 'quit':\n                    break\n\n                response = await self.process_query(query)\n                print(\"\\n\" + response)\n\n            except Exception as e:\n                import traceback\n                traceback.print_exc()\n\n    async def cleanup(self):\n        \"\"\"Clean up resources\"\"\"\n        await self.exit_stack.aclose()\n```\n\n最后，我们来完成运行这个客户端相关的代码\n\n```python\nasync def main():\n    client = MCPClient()\n    try:\n        await client.connect_to_server()\n        await client.chat_loop()\n    finally:\n        await client.cleanup()\n\n\nif __name__ == \"__main__\":\n    import sys\n\n    asyncio.run(main())\n\n```\n\n这是一个最精简的代码，里面没有实现记录上下文消息等功能，只是为了用最简单的代码来了解如何通过大语言模型来调动 MCP 服务器。这里只演示了如何连接单服务器，如果你期望连接多个 MCP 服务器，无非就是循环一下 `connect_to_server` 中的代码，可以将他们封装成一个类，然后将所有的 MCP 服务器中的工具循环遍历生成一个大的 `available_tools`，然后在通过大语言模型的返回结果进行调用即可，这里就不再赘述了。\n> 可以参考官方案例：https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Fpython-sdk\u002Fblob\u002Fmain\u002Fexamples\u002Fclients\u002Fsimple-chatbot\u002Fmcp_simple_chatbot\u002Fmain.py\n\n## Sampling 讲解\n\nMCP 还为我们提供了一个 `Sampling` 的功能，这个如果从字面来理解会让人摸不到头脑，但实际上这个功能就给了我们一个在执行工具的前后的接口，我们可以在工具执行前后来执行一些操作。比如，当调用本地文件的删除的工具的时候，肯定是期望我们确认后再进行删除。那么，此时就可以使用这个功能。\n\n下面我们就来实现这个人工监督的小功能。\n\n首先，我们来创建个模拟拥有删除文件的 MCP 服务器：\n\n```python\n# 服务端\nfrom mcp.server import FastMCP\nfrom mcp.types import SamplingMessage, TextContent\n\napp = FastMCP('file_server')\n\n\n@app.tool()\nasync def delete_file(file_path: str):\n    # 创建 SamplingMessage 用于触发 sampling callback 函数\n    result = await app.get_context().session.create_message(\n        messages=[\n            SamplingMessage(\n                role='user', content=TextContent(\n                    type='text', text=f'是否要删除文件: {file_path} (Y)')\n            )\n        ],\n        max_tokens=100\n    )\n\n    # 获取到 sampling callback 函数的返回值，并根据返回值进行处理\n    if result.content.text == 'Y':\n        return f'文件 {file_path} 已被删除！！'\n\n\nif __name__ == '__main__':\n    app.run(transport='stdio')\n\n```\n\n这里最重要的就是需要通过`create_message`方法来创建一个 `SamplingMessage` 类型的 message，他会将这个 message 发送给 sampling callback 对应的函数中。\n\n接着，我们来创建客户端的代码：\n\n```python\n# 客户端\nfrom mcp.client import ClientSession\nfrom mcp.types import SamplingMessage\n\nasync def main():\n    # 连接到 MCP 服务器\n    async with ClientSession('file_server') as session:\n        # 发送 sampling message\n        await session.create_message(\n            messages=[\n                SamplingMessage(\n                    role='user', content=TextContent(\n                        type='text', text='是否要删除文件: \u002Fpath\u002Fto\u002Ffile? (Y\u002FN)')\n            ]\n        )\n\n        # 等待回调函数响应\n        response = await session.wait_for_callback()\n\n        # 根据响应决定是否继续执行\n        if response == 'Y':\n            print('文件已成功删除！')\n        else:\n            print('操作已取消。')\n\n# 运行程序\nasyncio.run(main())\n\n# 客户端\nimport asyncio\n\nfrom mcp.client.stdio import stdio_client\nfrom mcp import ClientSession, StdioServerParameters\nfrom mcp.shared.context import RequestContext\nfrom mcp.types import (\n    TextContent,\n    CreateMessageRequestParams,\n    CreateMessageResult,\n)\n\nserver_params = StdioServerParameters(\n    command='uv',\n    args=['run', 'file_server.py'],\n)\n\n\nasync def sampling_callback(\n        context: RequestContext[ClientSession, None],\n        params: CreateMessageRequestParams,\n):\n    # 获取工具发送的消息并显示给用户\n    input_message = input(params.messages[0].content.text)\n    # 将用户输入发送回工具\n    return CreateMessageResult(\n        role='user',\n        content=TextContent(\n            type='text',\n            text=input_message.strip().upper() or 'Y'\n        ),\n        model='user-input',\n        stopReason='endTurn'\n    )\n\n\nasync def main():\n    async with stdio_client(server_params) as (stdio, write):\n        async with ClientSession(\n                stdio, write,\n                # 设置 sampling_callback 对应的方法\n                sampling_callback=sampling_callback\n        ) as session:\n            await session.initialize()\n            res = await session.call_tool(\n                'delete_file',\n                {'file_path': 'C:\u002Fxxx.txt'}\n            )\n            # 获取工具最后执行完的返回结果\n            print(res)\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n\n```\n\n特别要注意的是，目前在工具里面打印的内容实际上使用 `stdio_client` 是无法显示到命令行窗口的。所以，我们调试的话，可以使用 `mcp.shared.memory.create_connected_server_and_client_session`。\n\n具体代码如下：\n\n```python\n# 客户端\nfrom mcp.shared.memory import (\n    create_connected_server_and_client_session as create_session\n)\n# 这里需要引入服务端的 app 对象\nfrom file_server import app\n\nasync def sampling_callback(context, params):\n    ...\n\nasync def main():\n    async with create_session(\n        app._mcp_server,\n        sampling_callback=sampling_callback\n    ) as client_session:\n        await client_session.call_tool(\n            'delete_file', \n            {'file_path': 'C:\u002Fxxx.txt'}\n        )\n\nif __name__ == '__main__':\n    asyncio.run(main())\n```\n\n\n\n## Claude Desktop 加载 MCP Server\n\n因为后面的两个功能实际上都是为了提供给 Claude 桌面端用的，所以这里先说下如何加载我们自定义的 MCP Server 到 Claude 桌面端。\n\n首先，我们先打开配置。\n\n![image-20250227221154638](.assets\u002Fimage-20250227221154638.png)\n\n我们点击 `Developer` 菜单，然后点击 `Edit Config` 按钮打开 Claude 桌面端的配置文件 `claude_desktop_config.json`\n\n![image-20250227221302174](.assets\u002Fimage-20250227221302174.png)\n\n然后开始添加我们的服务器，服务器需要在 `mcpServers` 层级下，参数有 `command`、`args`、`env`。实际上，参数和 `StdioServerParameters` 对象初始化时候的参数是一样的。 \n\n```json\n{\n  \"mcpServers\": {\n    \"web-search-server\": {\n      \"command\": \"uv\",\n      \"args\": [\n        \"--directory\",\n        \"D:\u002Fprojects\u002Fmcp_getting_started\",\n        \"run\",\n        \"web_search.py\"\n      ]\n    }\n  }\n}\n```\n\n最后，我们保存文件后重启 Claude 桌面端就可以在这里看到我们的插件了。\n\n![image-20250227221911231](.assets\u002Fimage-20250227221911231.png)\n\n![image-20250227221921036](.assets\u002Fimage-20250227221921036.png)\n\n当然，我们也可以直接在我们插件的目录下运行以下命令来直接安装：\n\n```shell\nmcp install web_search.py\n```\n\n\n\n## 其他功能\n\n### Prompt\n\nMCP 还为我们提供了一个生成 Prompt 模板的功能。他使用起来也很简单，只需要使用 `prompt` 装饰器装饰一下即可，代码如下：\n\n```python\nfrom mcp.server import FastMCP\n\napp = FastMCP('prompt_and_resources')\n\n@app.prompt('翻译专家')\nasync def translate_expert(\n        target_language: str = 'Chinese',\n) -> str:\n    return f'你是一个翻译专家，擅长将任何语言翻译成{target_language}。请翻译以下内容：'\n\n\nif __name__ == '__main__':\n    app.run(transport='stdio')\n\n```\n\n然后我们用上一节讲到的配置 Claude 桌面端 MCP 服务器的方法添加下我们的新 MCP 服务器。然后我们就可以点击右下角的图标开始使用啦。\n\n他会让我们设置一下我们传入的参数，然后他会在我们的聊天窗口上生成一个附件。\n\n![mcp001](.assets\u002Fmcp001-1740666812436-2.gif)\n\n\n\n### Resource\n\n我们还可以在 Claude 客户端上选择我们为用户提供的预设资源，同时也支持自定义的协议。具体代码如下：\n\n```python\nfrom mcp.server import FastMCP\n\napp = FastMCP('prompt_and_resources')\n\n@app.resource('echo:\u002F\u002Fstatic')\nasync def echo_resource():\n    # 返回的是，当用户使用这个资源时，资源的内容\n    return 'Echo!'\n\n@app.resource('greeting:\u002F\u002F{name}')\nasync def get_greeting(name):\n    return f'Hello, {name}!'\n\n\nif __name__ == '__main__':\n    app.run(transport='stdio')\n\n```\n\n然后，我们到 Claude 桌面端上看看。\n\n![mcp002](.assets\u002Fmcp002.gif)\n\n这里要特别注意的是，目前 Claude 桌面端是没法读到资源装饰器设置 `greeting:\u002F\u002F{name}` 这种通配符的路径，未来将会被支持。但是，在我们的客户端代码中是可以当做资源模板来使用的，具体代码如下：\n\n```python\nimport asyncio\nfrom pydantic import AnyUrl\n\nfrom mcp.client.stdio import stdio_client\nfrom mcp import ClientSession, StdioServerParameters\n\nserver_params = StdioServerParameters(\n    command='uv',\n    args=['run', 'prompt_and_resources.py'],\n)\n\n\nasync def main():\n    async with stdio_client(server_params) as (stdio, write):\n        async with ClientSession(stdio, write) as session:\n            await session.initialize()\n\n            # 获取无通配符的资源列表\n            res = await session.list_resources()\n            print(res)\n\n            # 获取有通配符的资源列表(资源模板)\n            res = await session.list_resource_templates()\n            print(res)\n\n            # 读取资源，会匹配通配符\n            res = await session.read_resource(AnyUrl('greeting:\u002F\u002Fliming'))\n            print(res)\n\n            # 获取 Prompt 模板列表\n            res = await session.list_prompts()\n            print(res)\n\n            # 使用 Prompt 模板\n            res = await session.get_prompt(\n                '翻译专家', arguments={'target_language': '英语'})\n            print(res)\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n\n```\n\n\n\n### 生命周期\n\nMCP 生命周期分为3个阶段：\n\n- 初始化\n- 交互通信中\n- 服务被关闭\n\n因此，我们可以在这个三个阶段的开始和结束来做一些事情，比如创建数据库连接和关闭数据库连接、记录日志、记录工具使用信息等。\n\n下面我们将以网页搜索工具，把工具调用时的查询和查询到的结果存储到一个全局上下文中作为缓存为例，来看看生命周期如何使用。完整代码如下：\n\n```python\nimport httpx\nfrom dataclasses import dataclass\nfrom contextlib import asynccontextmanager\n\nfrom mcp.server import FastMCP\nfrom mcp.server.fastmcp import Context\n\n\n@dataclass\n# 初始化一个生命周期上下文对象\nclass AppContext:\n    # 里面有一个字段用于存储请求历史\n    histories: dict\n\n\n@asynccontextmanager\nasync def app_lifespan(server):\n    # 在 MCP 初始化时执行\n    histories = {}\n    try:\n        # 每次通信会把这个上下文通过参数传入工具\n        yield AppContext(histories=histories)\n    finally:\n        # 当 MCP 服务关闭时执行\n        print(histories)\n\n\napp = FastMCP(\n    'web-search', \n    # 设置生命周期监听函数\n    lifespan=app_lifespan\n)\n\n\n@app.tool()\n\n# 第一个参数会被传入上下文对象\nasync def web_search(ctx: Context, query: str) -> str:\n    \"\"\"\n    搜索互联网内容\n\n    Args:\n        query: 要搜索内容\n\n    Returns:\n        搜索结果的总结\n    \"\"\"\n    # 如果之前问过同样的问题，就直接返回缓存\n    histories = ctx.request_context.lifespan_context.histories\n    if query in histories：\n    \treturn histories[query]\n\n    async with httpx.AsyncClient() as client:\n        response = await client.post(\n            'https:\u002F\u002Fopen.bigmodel.cn\u002Fapi\u002Fpaas\u002Fv4\u002Ftools',\n            headers={'Authorization': 'YOUR API KEY'},\n            json={\n                'tool': 'web-search-pro',\n                'messages': [\n                    {'role': 'user', 'content': query}\n                ],\n                'stream': False\n            }\n        )\n\n        res_data = []\n        for choice in response.json()['choices']:\n            for message in choice['message']['tool_calls']:\n                search_results = message.get('search_result')\n                if not search_results:\n                    continue\n                for result in search_results:\n                    res_data.append(result['content'])\n\n        return_data = '\\n\\n\\n'.join(res_data)\n\n        # 将查询值和返回值存入到 histories 中\n        ctx.request_context.lifespan_context.histories[query] = return_data\n        return return_data\n\n\nif __name__ == \"__main__\":\n    app.run()\n\n```\n\n\n\n## 在 LangChain 中使用 MCP 服务器\n\n最近 LangChain 发布了一个新的开源项目 `langchain-mcp-adapters`，可以很方便的将 MCP 服务器集成到 LangChain 中。下面我们来看看如何使用它:\n\n```python\nfrom mcp import ClientSession, StdioServerParameters\nfrom mcp.client.stdio import stdio_client\n\nfrom langchain_mcp_adapters.tools import load_mcp_tools\nfrom langgraph.prebuilt import create_react_agent\n\nfrom langchain_openai import ChatOpenAI\nmodel = ChatOpenAI(model=\"gpt-4o\")\n\nserver_params = StdioServerParameters(\n    command='uv',\n    args=['run', 'web_search.py'],\n)\n\nasync with stdio_client(server_params) as (read, write):\n    async with ClientSession(read, write) as session:\n        await session.initialize()\n\n        # 获取工具列表\n        tools = await load_mcp_tools(session)\n\n        # 创建并使用 ReAct agent\n        agent = create_react_agent(model, tools)\n        agent_response = await agent.ainvoke({'messages': '杭州今天天气怎么样？'})\n```\n\n更详细的使用方法请参考：https:\u002F\u002Fgithub.com\u002Flangchain-ai\u002Flangchain-mcp-adapters\n\n\n\n## DeepSeek + cline + 自定义MCP = 图文大师\n\n最后，我们来使用 VsCode 的 cline 插件，来通过 DeepSeek 和我们自定义的一个图片生成的 mcp 服务器来构建一个图文大师的应用。废话不多说，我们直接开始。\n\n首先先来构建我们的图片生成的 mcp server，这里我们直接用 huggingface 上的 `FLUX.1-schnell` 模型，地址是：https:\u002F\u002Fhuggingface.co\u002Fspaces\u002Fblack-forest-labs\u002FFLUX.1-schnell 。这里我们不使用 `gradio_client` 库，而是会使用 `httpx` 手搓一个，因为使用 `gradio_client` 库可能会出现编码错误的bug。具体代码如下：\n\n```python\n# image_server.py\n\nimport json\nimport httpx\nfrom mcp.server import FastMCP\n\n\napp = FastMCP('image_server')\n\n\n@app.tool()\nasync def image_generation(image_prompt: str):\n    \"\"\"\n    生成图片\n    :param image_prompt: 图片描述，需要是英文\n    :return: 图片保存到的本地路径\n    \"\"\"\n    async with httpx.AsyncClient() as client:\n        data = {'data': [image_prompt, 0, True, 512, 512, 3]}\n\n        # 创建生成图片任务\n        response1 = await client.post(\n            'https:\u002F\u002Fblack-forest-labs-flux-1-schnell.hf.space\u002Fcall\u002Finfer',\n            json=data,\n            headers={\"Content-Type\": \"application\u002Fjson\"}\n        )\n\n        # 解析响应获取事件 ID\n        response_data = response1.json()\n        event_id = response_data.get('event_id')\n\n        if not event_id:\n            return '无法获取事件 ID'\n\n        # 通过流式的方式拿到返回数据\n        url = f'https:\u002F\u002Fblack-forest-labs-flux-1-schnell.hf.space\u002Fcall\u002Finfer\u002F{event_id}'\n        full_response = ''\n        async with client.stream('GET', url) as response2:\n            async for chunk in response2.aiter_text():\n                full_response += chunk\n\n        return json.loads(full_response.split('data: ')[-1])[0]['url']\n\nif __name__ == '__main__':\n    app.run(transport='stdio')\n\n```\n\n然后我们可以在虚拟环境下使用下面的命令打开 MCP Inspector 进行调试下我们的工具。\n\n```shell\nmcp dev image_server.py\n```\n\n![image-20250301231332749](.assets\u002Fimage-20250301231332749.png)\n\n接着我们在 VsCode 中安装 cline 插件，当安装完插件后，我们配置一下我们的 deepseek 的 api key。接着，我们点击右上角的 `MCP Server` 按钮打开 mcp server 列表。\n\n![image-20250301232248034](.assets\u002Fimage-20250301232248034.png)\n\n然后切换到 `Installed` Tab 点击 `Configure MCP Servers` 按钮来编辑自定义的 mcp 服务器。\n\n![image-20250301232417966](.assets\u002Fimage-20250301232417966.png)\n\n配置如下：\n\n```json\n{\n  \"mcpServers\": {\n    \"image_server\": {\n      \"command\": \"uv\",\n      \"args\": [\n        \"--directory\",\n        \"D:\u002Fprojects\u002Fmcp_getting_started\",\n        \"run\",\n        \"image_server.py\"\n      ],\n      \"env\": {},\n      \"disabled\": false,\n      \"autoApprove\": []\n    }\n  }\n}\n```\n\n我们保存后，这里的这个小点是绿色的就表示我们的服务器已连接，然后我们就可以开始使用啦。\n\n![image-20250301232809433](.assets\u002Fimage-20250301232809433.png)\n\n然后，我们就打开输入框，来输入我们的要写的文章的内容：\n\n![image-20250301233421292](.assets\u002Fimage-20250301233421292.png)\n\n我们可以看到，他正确的调用了我们的工具\n\n![image-20250301233726301](.assets\u002Fimage-20250301233726301.png)\n\n最后，就是可以看到生成的文章啦。\n\n![image-20250301234532249](.assets\u002Fimage-20250301234532249.png)\n\n\n\n## 借助 serverless 将 MCP 服务部署到云端\n\n上面我们讲的都是如何使用本地的 MCP 服务，但是有时我们希望直接把 MCP 服务部署到云端来直接调用，就省去了本地下载启动的烦恼了。此时，我们就需要来使用 MCP 的 SSE 的协议来实现了。\n\n此时，我们先来写 SSE 协议的 MCP 服务。实现起来很简单，只需要将我们最后的 `run` 命令中的 `transport` 参数设置为 `sse` 即可。下面还是以上面的网络搜索为例子，来实现一下 ，具体代码如下：\n\n```python\n# sse_server.py\n\nimport json\nfrom mcp.server import FastMCP\n\n\napp = FastMCP('sse_server')\n\n\n@app.tool()\nasync def web_search(query: str):\n    \"\"\"\n    搜索互联网内容\n    :param query: 要搜索内容\n    :return: 搜索结果的总结\n    \"\"\"\n    async with httpx.AsyncClient() as client:\n        response = await client.post(\n            'https:\u002F\u002Fopen.bigmodel.cn\u002Fapi\u002Fpaas\u002Fv4\u002Ftools',\n            headers={'Authorization': 'YOUR API KEY'},\n            json={\n                'tool': 'web-search-pro',\n                'messages': [\n                    {'role': 'user', 'content': query}\n                ],\n                'stream': False\n            }\n        )\n\n        res_data = []\n        for choice in response.json()['choices']:\n            for message in choice['message']['tool_calls']:\n                search_results = message.get('search_result')\n                if not search_results:\n                    continue\n                for result in search_results:\n                    res_data.append(result['content'])\n\n        return_data = '\\n\\n\\n'.join(res_data)\n\n        # 将查询值和返回值存入到 histories 中\n        ctx.request_context.lifespan_context.histories[query] = return_data\n        return return_data\n\nif __name__ == '__main__':\n    app.run(transport='sse')\n\n```\n\n# sse_web_search.py\nimport httpx\n\nfrom mcp.server import FastMCP\n\n\napp = FastMCP('web-search', port=9000)\n\n\n@app.tool()\nasync def web_search(query: str) -> str:\n    \"\"\"\n    搜索互联网内容\n\n    Args:\n        query: 要搜索内容\n\n    Returns:\n        搜索结果的总结\n    \"\"\"\n    async with httpx.AsyncClient() as client:\n        response = await client.post(\n            'https:\u002F\u002Fopen.bigmodel.cn\u002Fapi\u002Fpaas\u002Fv4\u002Ftools',\n            headers={'Authorization': 'YOUR API KEY'},\n            json={\n                'tool': 'web-search-pro',\n                'messages': [\n                    {'role': 'user', 'content': query}\n                ],\n                'stream': False\n            }\n        )\n\n        res_data = []\n        for choice in response.json()['choices']:\n            for message in choice['message']['tool_calls']:\n                search_results = message.get('search_result')\n                if not search_results:\n                    continue\n                for result in search_results:\n                    res_data.append(result['content'])\n\n        return '\\n\\n\\n'.join(res_data)\n\n\nif __name__ == \"__main__\":\n    app.run(transport='sse')\n\n```\n\n在 `FastMCP` 中，有几个可以设置 SSE 协议相关的参数：\n\n- host: 服务地址，默认为 `0.0.0.0`\n- port: 服务端口，默认为 8000。上述代码中，我设置为 `9000`\n- sse_path：sse 的路由，默认为 `\u002Fsse`\n\n此时，我们就可以直接写一个客户端的代码来进行测试了。具体代码如下：\n\n```python\nimport asyncio\nfrom mcp.client.sse import sse_client\nfrom mcp import ClientSession\n\n\nasync def main():\n    async with sse_client('http:\u002F\u002Flocalhost:9000\u002Fsse') as streams:\n        async with ClientSession(*streams) as session:\n            await session.initialize()\n\n            res = await session.call_tool('web_search', {'query': '杭州今天天气'})\n            print(res)\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n\n```\n\n我们可以看到，他正常工作了，并搜索到了内容：\n\n![image-20250406152518223](.assets\u002Fimage-20250406152518223.png)\n\n当然，我们也可以使用 `mcp dev sse_web_search.py` 的方式来测试。这里要注意的是，`Transport Type` 需要改成 `SSE`，然后下面填写我们的本地服务地址。\n\n![image-20250406153106098](.assets\u002Fimage-20250406153106098.png)\n\n当一切都测试没有问题后，我们就来将他通过 severless 的方式来部署到云端。这里我们选择的是阿里云的函数计算服务。首先我们先进入到阿里云的 `函数计算 FC 3.0` 的 `函数` 菜单，并点击 `创建函数` 来创建我们的服务。地址是：https:\u002F\u002Ffcnext.console.aliyun.com\u002Fcn-hangzhou\u002Ffunctions\n\n![image-20250406153655185](.assets\u002Fimage-20250406153655185.png)\n\n我们这里选择 `Web函数` ，运行环境我们选择 `Python 10`。代码上传方式这里可以根据大家需求来，因为我这里就一个 python 文件，所以我这里就直接选择`使用示例代码`了，这样我后面直接把我的代码覆盖进去了就行了。启动命令和监听端口我这里都保留为默认(**端口需要和代码中一致**)。\n\n环境变量大家可以将代码中用到的 apikey 可以设置为一个环境变量，这里我就不设置了。最后设置完成截图如下：\n\n![image-20250406154115438](.assets\u002Fimage-20250406154115438.png)\n\n在高级设置中，为了方便调试，我启动了日志功能。\n\n![image-20250406154228341](.assets\u002Fimage-20250406154228341.png)\n\n设置完成后，点创建即可。他就跳转到代码编辑部分，然后我们把之前的代码复制进去即可。\n\n![image-20250406154441634](.assets\u002Fimage-20250406154441634.png)\n\n完成后，我们来安装下依赖。我们点击右上角的`编辑层`。这里默认会有个默认的 flask 的层，因为开始的模板用的是 flask，这里我们就不需要了。我们删除他，再添加一个 mcp 的层。选择`添加官方公共层`，然后搜索 `mcp` 就能看到了一个 python 版的 MCP 层，里面包含了 MCP 所有用到的依赖。\n\n![image-20250406154753623](.assets\u002Fimage-20250406154753623.png)\n\n如果你还有其他第三方的，可以先搜索下看看公共层中是否有，没有就可以自行构建一个自定义的层。点击这里就可以，只需要提供一个 `requirements` 列表就可以了，这里就不赘述了。 \n\n![image-20250406154935751](.assets\u002Fimage-20250406154935751.png)\n\n当我们都设置完成后，点击右下角的部署即可。\n\n然后我们又回到了我们代码编辑的页面，此时，我们再点击左上角的部署代码。稍等一两秒就会提示代码部署成功。此时，我们的 MCP 服务就被部署到了云端。\n\n![image-20250406155135563](.assets\u002Fimage-20250406155135563.png)\n\n\n\n> 20250409 更新：不知道是不是官方看到了这篇文章，现在运行时可以直接选择 `MCP 运行时` 了，就不用再在层那里手动添加 `MCP 层` 了。\n>\n> ![image-20250409213302652](.assets\u002Fimage-20250409213302652.png)\n\n\n\n然后，我们切换到`配置`的`触发器`中，就可以看到我们用来访问的 URL 地址了。当然，你也可以绑定自己的域名。\n\n![image-20250406155353662](.assets\u002Fimage-20250406155353662.png)\n\n然后，我们就可以用我们上面的客户端代码进行测试了。\n\n```python\nimport asyncio\nfrom mcp.client.sse import sse_client\nfrom mcp import ClientSession\n\n\nasync def main():\n    async with sse_client('https:\u002F\u002Fmcp-test-whhergsbso.cn-hangzhou.fcapp.run\u002Fsse') as streams:\n        async with ClientSession(*streams) as session:\n            await session.initialize()\n\n            res = await session.call_tool('web_search', {'query': '杭州今天天气'})\n            print(res)\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n```\n\n如果我们发现在客户端有报错也不用慌，我们可以直接在日志中找到对应出错的请求点击`请求日志`查看报错来修复。\n\n![image-20250406155803071](.assets\u002Fimage-20250406155803071.png)\n\n到这里，我们的 MCP 服务就被部署到了云端，我们就可以在任何地方直接来使用它了。\n\n比如，在 `Cherry-Studio` 中，我们可以这样来设置：\n\n![image-20250406160152782](.assets\u002Fimage-20250406160152782.png)\n\n在 `Cline` 中：\n\n![image-20250406160709759](.assets\u002Fimage-20250406160709759.png)\n\n在 `Cursor` 中：\n\n![image-20250406161055717](.assets\u002Fimage-20250406161055717.png)\n\n```json\n{\n  \"mcpServers\": {\n    \"web-search\": {\n      \"url\": \"https:\u002F\u002Fmcp-test-whhergsbso.cn-hangzhou.fcapp.run\u002Fsse\"\n    }\n  }\n}\n```\n\n\n\n至此，整个 MCP 入门教程就到这里啦，后续有其他的再进行更新。相关代码会放到 github 仓库中：https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide","# MCP (Model Context Protocol) 快速上手指南\n\n本指南旨在帮助开发者快速理解并构建基于 MCP 协议的服务器与客户端，实现大语言模型与外部工具（如网络搜索、文件操作）的标准化连接。\n\n## 环境准备\n\n在开始之前，请确保您的开发环境满足以下要求：\n\n*   **操作系统**: Windows, macOS 或 Linux\n*   **Python**: 版本 3.11 或更高\n*   **Node.js**: 用于运行官方调试工具 `Inspector`\n*   **包管理工具**: 推荐使用 `uv` (高性能 Python 包管理器)\n*   **API Key**: \n    *   智谱 AI API Key (用于示例中的搜索功能): [获取地址](https:\u002F\u002Fbigmodel.cn\u002Fusercenter\u002Fproj-mgmt\u002Fapikeys)\n    *   DeepSeek\u002FOpenAI API Key (用于客户端调用)\n\n## 安装步骤\n\n### 1. 初始化项目与安装依赖\n\n使用 `uv` 创建项目目录、虚拟环境并安装必要的依赖库（包括 MCP SDK、HTTP 客户端和 OpenAI 兼容库）。\n\n```shell\n# 初始化项目\nuv init mcp_getting_started\ncd mcp_getting_started\n\n# 创建并激活虚拟环境\nuv venv\n# Windows:\n.venv\\Scripts\\activate.bat\n# macOS\u002FLinux:\nsource .venv\u002Fbin\u002Factivate\n\n# 安装依赖\n# mcp[cli] 包含服务端和命令行工具，httpx 用于网络请求，openai 用于客户端调用\nuv add \"mcp[cli]\" httpx openai python-dotenv\n```\n\n### 2. 验证安装\n\n安装完成后，可通过以下命令确认 `mcp` 命令行工具可用：\n\n```shell\nmcp --version\n```\n\n## 基本使用\n\n### 场景一：开发一个网络搜索 MCP 服务器\n\n我们将创建一个简单的服务器，暴露一个 `web_search` 工具，利用智谱 AI 接口进行联网搜索。\n\n**1. 创建服务器代码 (`web_search.py`)**\n\n在项目根目录下创建 `web_search.py`，填入以下代码。**注意替换 `Authorization` 头中的 API KEY。**\n\n```python\nimport httpx\nfrom mcp.server import FastMCP\n\n# 初始化 FastMCP 服务器\napp = FastMCP('web-search')\n\n@app.tool()\nasync def web_search(query: str) -> str:\n    \"\"\"\n    搜索互联网内容\n\n    Args:\n        query: 要搜索内容\n\n    Returns:\n        搜索结果的总结\n    \"\"\"\n    # 请替换为您自己的智谱 AI API KEY\n    api_key = \"换成你自己的 API KEY\" \n\n    async with httpx.AsyncClient() as client:\n        response = await client.post(\n            'https:\u002F\u002Fopen.bigmodel.cn\u002Fapi\u002Fpaas\u002Fv4\u002Ftools',\n            headers={'Authorization': f'Bearer {api_key}'},\n            json={\n                'tool': 'web-search-pro',\n                'messages': [\n                    {'role': 'user', 'content': query}\n                ],\n                'stream': False\n            }\n        )\n\n        res_data = []\n        data = response.json()\n        if 'choices' in data:\n            for choice in data['choices']:\n                for message in choice['message'].get('tool_calls', []):\n                    search_results = message.get('search_result')\n                    if not search_results:\n                        continue\n                    for result in search_results:\n                        res_data.append(result['content'])\n\n        return '\\n\\n\\n'.join(res_data)\n\nif __name__ == \"__main__\":\n    # 使用 stdio 传输层运行\n    app.run(transport='stdio')\n```\n\n**2. 调试服务器**\n\n使用官方提供的 `Inspector` 可视化工具进行调试，无需编写客户端代码即可测试工具功能。\n\n```shell\n# 方法 A: 使用 npx 运行 (推荐)\nnpx -y @modelcontextprotocol\u002Finspector uv run web_search.py\n\n# 方法 B: 使用 mcp dev 命令\nmcp dev web_search.py\n```\n\n运行成功后，终端会显示一个本地 URL（通常是 `http:\u002F\u002Flocalhost:5173`）。在浏览器打开该地址，点击左侧 **Connect**，然后切换到 **Tools** 标签页，点击 **List Tools** 即可看到 `web_search` 工具并进行测试调用。\n\n---\n\n### 场景二：开发调用 MCP 的 LLM 客户端\n\n本示例展示如何编写一个 Python 客户端，让 DeepSeek (或兼容 OpenAI 格式的模型) 自动调用上述开发的搜索工具。\n\n**1. 配置环境变量**\n\n创建 `.env` 文件，配置模型相关信息：\n\n```shell\nOPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxx\nOPENAI_BASE_URL=https:\u002F\u002Fapi.deepseek.com\nOPENAI_MODEL=deepseek-chat\n```\n\n**2. 创建客户端代码 (`client.py`)**\n\n```python\nimport json\nimport asyncio\nimport os\nfrom typing import Optional\nfrom contextlib import AsyncExitStack\n\nfrom openai import OpenAI\nfrom dotenv import load_dotenv\n\nfrom mcp import ClientSession, StdioServerParameters\nfrom mcp.client.stdio import stdio_client\n\nload_dotenv()\n\nclass MCPClient:\n    def __init__(self):\n        self.session: Optional[ClientSession] = None\n        self.exit_stack = AsyncExitStack()\n        # 初始化 OpenAI 兼容客户端\n        self.client = OpenAI(\n            api_key=os.getenv(\"OPENAI_API_KEY\"),\n            base_url=os.getenv(\"OPENAI_BASE_URL\")\n        )\n\n    async def connect_to_server(self):\n        \"\"\"连接到 MCP 服务器\"\"\"\n        server_params = StdioServerParameters(\n            command='uv',\n            args=['run', 'web_search.py'],\n            env=None\n        )\n\n        stdio_transport = await self.exit_stack.enter_async_context(\n            stdio_client(server_params))\n        stdio, write = stdio_transport\n        self.session = await self.exit_stack.enter_async_context(\n            ClientSession(stdio, write))\n\n        await self.session.initialize()\n\n    async def process_query(self, query: str) -> str:\n        \"\"\"处理用户查询，协调 LLM 与 MCP 工具\"\"\"\n        system_prompt = (\n            \"You are a helpful assistant with online search capabilities. \"\n            \"You MUST call the 'web_search' tool to search the internet before answering any question. \"\n            \"Do not answer from your own knowledge without searching first.\"\n        )\n        \n        messages = [\n            {\"role\": \"system\", \"content\": system_prompt},\n            {\"role\": \"user\", \"content\": query}\n        ]\n\n        # 1. 获取 MCP 服务器可用的工具列表\n        response = await self.session.list_tools()\n        available_tools = [{\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": tool.name,\n                \"description\": tool.description,\n                \"input_schema\": tool.inputSchema\n            }\n        } for tool in response.tools]\n\n        # 2. 请求 LLM (携带工具定义)\n        response = self.client.chat.completions.create(\n            model=os.getenv(\"OPENAI_MODEL\"),\n            messages=messages,\n            tools=available_tools\n        )\n\n        content = response.choices[0]\n        \n        # 3. 如果 LLM 决定调用工具\n        if content.finish_reason == \"tool_calls\":\n            tool_call = content.message.tool_calls[0]\n            tool_name = tool_call.function.name\n            tool_args = json.loads(tool_call.function.arguments)\n\n            print(f\"\\n[Calling tool: {tool_name} with args: {tool_args}]\")\n\n            # 执行 MCP 工具\n            result = await self.session.call_tool(tool_name, tool_args)\n            \n            # 将工具调用记录和结果加入对话历史\n            messages.append(content.message.model_dump())\n            messages.append({\n                \"role\": \"tool\",\n                \"content\": result.content[0].text,\n                \"tool_call_id\": tool_call.id,\n            })\n\n            # 4. 将工具结果再次发给 LLM 生成最终回答\n            final_response = self.client.chat.completions.create(\n                model=os.getenv(\"OPENAI_MODEL\"),\n                messages=messages,\n            )\n            return final_response.choices[0].message.content\n\n        return content.message.content\n\n    async def chat_loop(self):\n        while True:\n            try:\n                query = input(\"\\nQuery: \").strip()\n                if query.lower() == 'quit':\n                    break\n                response = await self.process_query(query)\n                print(\"\\n\" + response)\n            except Exception as e:\n                print(f\"Error: {e}\")\n\n    async def cleanup(self):\n        await self.exit_stack.aclose()\n\nasync def main():\n    client = MCPClient()\n    try:\n        await client.connect_to_server()\n        await client.chat_loop()\n    finally:\n        await client.cleanup()\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n**3. 运行客户端**\n\n确保 `web_search.py` 和 `client.py` 在同一目录下，且 `.env` 已配置好，然后运行：\n\n```shell\npython client.py\n```\n\n输入问题（例如：“今天杭州天气怎么样？”），客户端将自动调用 MCP 服务器进行搜索，并将结果整合后由大模型返回给您。","某 AI 应用开发者急需为智能助手集成实时网络搜索能力，以便回答用户关于最新科技新闻的提问。\n\n### 没有 MCP-Chinese-Getting-Started-Guide 时\n- **协议理解门槛高**：面对 Model Context Protocol (MCP) 复杂的 Resources、Tools、Transports 等概念，开发者需花费数天研读英文官方文档才能理清架构。\n- **环境配置繁琐**：手动搭建 Python 虚拟环境、管理 `mcp[cli]`、`httpx` 等依赖库时容易版本冲突，且缺乏标准化的项目初始化指引。\n- **调试过程黑盒化**：编写完服务端代码后，缺乏可视化工具验证接口连通性，只能靠打印日志盲目排查 stdio 传输层的连接错误。\n- **代码实现无参照**：不清楚如何用装饰器快速定义工具函数，也不懂如何将第三方 API（如智谱搜索）标准化封装为 MCP 工具，导致重复造轮子。\n\n### 使用 MCP-Chinese-Getting-Started-Guide 后\n- **极速上手核心概念**：指南通过\"USB-C 接口”的生动比喻和中文详解，让开发者在 1 小时内掌握 MCP 核心功能与 stdio 传输机制。\n- **标准化项目启动**：直接复用指南中的 `uv` 命令一键初始化项目并安装依赖，避免了环境配置陷阱，确保开发环境干净一致。\n- **可视化调试提效**：利用指南推荐的 `Inspector` 工具和具体命令，开发者可直观查看工具列表并实时测试搜索接口，秒级定位连接问题。\n- **开箱即用的代码模板**：参考指南中完整的 `web_search.py` 示例，直接复制并修改 API Key 即可实现具备摘要功能的搜索工具，大幅缩短开发周期。\n\nMCP-Chinese-Getting-Started-Guide 将原本需要数天的协议学习与试错成本压缩至小时级，让开发者能专注于业务逻辑而非底层协议对接。","https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002FliaokongVFX_MCP-Chinese-Getting-Started-Guide_bdf08737.png","liaokongVFX","了空","https:\u002F\u002Foss.gittoolsai.com\u002Favatars\u002FliaokongVFX_21b240af.jpg",null,"Hangzhou, China","https:\u002F\u002Fgithub.com\u002FliaokongVFX",3438,209,"2026-04-06T13:56:27","Windows, macOS, Linux","未说明",{"notes":84,"python":85,"dependencies":86},"本项目主要作为 MCP 服务器的开发指南，运行在标准 CPU 环境即可，无需 GPU。需安装 uv 来管理 Python 项目和虚拟环境。调试服务器时需安装 Node.js 以运行官方 Inspector 工具或 mcp dev 命令。代码示例中使用了智谱 (BigModel) 和 DeepSeek 的 API，需自行申请 API Key 并配置环境变量。传输层默认使用 stdio 协议。","3.11",[87,88,89,90,91,92],"mcp[cli]","httpx","openai","python-dotenv","uv","node.js (用于 Inspector 调试工具)",[13,14,15],[95,96,97,98,99],"ai","deepseek","mcp","modelcontextprotocol","mcp-server","2026-03-27T02:49:30.150509","2026-04-07T06:25:14.404852",[103,108,113,118,123,128,133,138],{"id":104,"question_zh":105,"answer_zh":106,"source_url":107},20997,"使用 MCP Inspector 调试时出现 Connection Error 怎么办？","可以尝试以下几种解决方法：\n1. 先采用无鉴权方式运行：`DANGEROUSLY_OMIT_AUTH=true mcp dev web_search.py`，随后填写 Proxy Session Token 并使用鉴权方式 `mcp dev web_search.py` 运行。\n2. 检查配置项，确保没有多余或错误的配置内容。\n3. 如果是 HTTP 431 错误，尝试删除配置文件中未使用的部分。","https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide\u002Fissues\u002F6",{"id":109,"question_zh":110,"answer_zh":111,"source_url":112},20998,"SSE 模式下如何传递参数（如 API Key、Token 等）？","目前 SSE 模式不像 stdio 模式那样直接支持 env 传参，但可以尝试以下方案：\n1. 将参数直接拼接到 URL 中，例如：`http:\u002F\u002Flocalhost:8000\u002Fsse?token=xxxxx&pid=xxxxx`，并在客户端配置中直接使用该 URL。\n2. 等待官方支持从 MCP 上下文中获取 headers，届时可通过自定义 headers 传递参数，功能将类似 stdio 的 env。\n3. 临时方案：通过 prompt 告知模型参数，或在 tool 描述中补充默认值（注意安全性）。","https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide\u002Fissues\u002F10",{"id":114,"question_zh":115,"answer_zh":116,"source_url":117},20999,"DeepSeek R1 等不支持 Function Calling 的模型如何使用 MCP？","对于不支持 Function Calling 的模型（如 DeepSeek R1），可以将 function 的 schema 作为提示词（prompt）的一部分直接传给 LLM，从而实现类似工具调用的效果。此外，如果是本地部署，可能需要设置特定启动参数（如 `--enable-auto-tool-choice` 和 `--tool-call-parser`），但需注意 R1 推理模型本身可能原生不支持该功能。","https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide\u002Fissues\u002F18",{"id":119,"question_zh":120,"answer_zh":121,"source_url":122},21000,"SSE 模式下如何实现自定义 Headers 进行鉴权？","目前官方客户端对 SSE 模式的鉴权支持尚不完善，但可以通过挂载到 FastAPI 自行实现。示例代码如下：\n```python\nfrom fastapi import FastAPI, HTTPException\nfrom mcp.server.fastmcp import FastMCP\nfrom mcp.server.sse import SseServerTransport\nfrom starlette.applications import Starlette\n\nmcp = FastMCP('Echo')\n\ndef create_sse_server(_mcp: FastMCP):\n    transport = SseServerTransport(mcp.settings.message_path)\n    async def handle_sse(request):\n        apikey = request.headers.get('apikey')\n        if apikey != '123456':\n            raise HTTPException(status_code=401, detail='Unauthorized')\n        async with transport.connect_sse(request.scope, request.receive, request._send) as streams:\n            await _mcp._mcp_server.run(streams[0], streams[1], _mcp._mcp_server.create_initialization_options())\n    return Starlette(routes=[Mount('\u002F', app=handle_sse)])\n```\n通过这种方式可以在 SSE 连接建立前验证 Header 中的 API Key。","https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide\u002Fissues\u002F9",{"id":124,"question_zh":125,"answer_zh":126,"source_url":127},21001,"如何在 Conda 环境中运行 MCP 项目而不是默认的 .venv？","可以手动创建并激活 Conda 环境，然后直接运行 Python 文件而不是依赖 `mcp dev` 自动创建虚拟环境。步骤如下：\n1. 创建环境：`conda create -n mcp_play python=3.11`\n2. 激活环境：`conda activate mcp_play`\n3. 安装依赖：`pip install \"mcp[cli]\" httpx openai`\n4. 运行项目：先激活环境，然后使用 `npx -y @modelcontextprotocol\u002Finspector python web_search.py` 代替默认的 `uv run` 命令。","https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide\u002Fissues\u002F13",{"id":129,"question_zh":130,"answer_zh":131,"source_url":132},21002,"教程中的代码运行报错，入口函数调用对象错误怎么办？","这是一个常见的代码笔误。如果代码中定义的是 `app = FastMCP(...)`，但在入口函数中写的是 `mcp.run(transport='stdio')`，会报错。应将其修改为 `app.run(transport='stdio')`，确保调用的对象名称与实例化时的变量名一致。","https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide\u002Fissues\u002F2",{"id":134,"question_zh":135,"answer_zh":136,"source_url":137},21003,"如何启用 Streamable HTTP 协议传输？","只需在初始化 FastMCP 应用后，将 `run` 方法的 `transport` 参数设置为 `'streamable-http'` 即可。示例代码：\n```python\napp = FastMCP('web-search')\napp.run(transport='streamable-http')\n```","https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide\u002Fissues\u002F24",{"id":139,"question_zh":140,"answer_zh":141,"source_url":142},21004,"调用外部 API 生成图片时出现超时或连接错误如何解决？","如果在调用外部 API（如 Hugging Face Spaces）时出现超时或连接错误，建议在 httpx 客户端中增加超时设置。可以在创建 `AsyncClient` 时添加 `timeout` 参数，例如：`async with httpx.AsyncClient(timeout=30.0) as client:`，以延长等待响应的时间。","https:\u002F\u002Fgithub.com\u002FliaokongVFX\u002FMCP-Chinese-Getting-Started-Guide\u002Fissues\u002F17",[]]