第1篇:MCP核心概念与组件实战
MCP核心概念
MCP是什么?
Model Context Protocol (MCP) 是一套让AI应用能够调用本地工具的标准协议。简单来说,MCP就是让应用程序能够调用本地工具的一套标准协议。有了MCP就像给AI装了一双"手",让它不只是能"说",还能"做"。
MCP的两种部署模式
MCP支持两种主要的部署模式:本地模式和远程模式,它们各有特点和适用场景。
本地模式
定义:MCP服务器与客户端在同一台机器上运行,通过stdio(标准输入输出)进行通信。
特点:
- 部署简单:无需网络配置,适合个人开发和学习
- 低延迟:本地通信,响应速度快
- 安全性高:数据不经过网络传输,减少安全风险
远程模式
定义:MCP服务器部署在远程服务器上,通过网络协议(HTTP/HTTPS、WebSocket、TCP等)与客户端通信。
特点:
- 可扩展性强:支持多用户并发访问
- 集中管理:便于维护和更新
- 资源共享:多个客户端可共享同一服务
在本教程中,我们将使用本地模式进行开发和学习,这更适合初学者快速上手。
MCP三大组件
MCP(Model Context Protocol)是一个开放协议,它定义了AI模型与外部工具之间的交互标准。一个完整的MCP应用由三大核心组件构成:
1. 工具组件(Tools)
工具组件是MCP的"行动力",它允许AI模型执行具体的功能操作。
核心功能:
- 执行特定任务(如查询食谱、计算营养等)
- 接收参数并返回结构化结果
- 处理错误和异常情况
示例:
- 食谱查询工具:根据食材和偏好推荐食谱
- 食材推荐工具:基于季节和地区推荐合适食材
2. 资源组件(Resources)
资源组件是MCP的"记忆体",负责数据的存储和状态管理。
核心功能:
- 存储和管理应用数据
- 提供数据访问接口
- 维护数据一致性
示例:
- 食谱数据库:存储各类食谱信息
- 用户偏好:记录用户的食谱偏好和限制
3. 提示词组件(Prompts)
提示词组件是MCP的"表达力",负责优化AI与用户的交互体验。
核心功能:
- 设计和优化提示词模板
- 管理多轮对话上下文
- 提供个性化交互体验
示例:
- 食谱查询提示词:引导用户提供更详细的查询条件
- 个性化推荐提示词:基于用户历史提供定制化食谱推荐
实战:构建智能食谱助手
本教程将带你用这三大组件构建一个简单但功能完整的智能食谱助手,让你在30分钟内掌握MCP的核心概念和实战技能。
环境准备
在开始实践之前,我们需要先准备好开发环境。
1. 创建虚拟环境
首先,打开终端(Windows用户可以使用Anaconda Prompt),然后执行以下命令创建一个新的conda环境:
conda create -n mcp-env python=3.10
激活刚刚创建的环境:
conda activate mcp-env
2. 安装FastMCP
FastMCP是MCP协议的Python实现,它简化了MCP应用的开发过程。在激活的环境中执行以下命令进行安装:
conda install -c conda-forge fastmcp
3. 安装MCP Inspector
MCP Inspector是一个用于调试和测试MCP应用的工具,可以帮助我们更好地理解MCP的工作原理:
npm install -g @modelcontextprotocol/inspector
如果你没有安装npm,可以从nodejs官网下载并安装Node.js,它会自动包含npm。
4. 创建项目结构
创建一个新的项目目录,并在其中创建主应用文件:
mkdir recipe-assistant
cd recipe-assistant
touch recipe_app.py
基础框架
让我们先创建一个基础框架,包含三大组件的基本结构:
# recipe_app.py
from fastmcp import FastMCP
from typing import List, Dict, Optional# 创建MCP应用实例
mcp = FastMCP("RecipeAssistant")# 这里将添加三大组件的实现...if __name__ == "__main__":mcp.run()
三大组件实现
我们将分别实现三大组件:资源组件(数据访问),工具组件(功能执行),提示词组件(文本生成)
1. 资源组件 - 食谱数据存储
资源组件就像是AI的"记忆体",负责数据的存储和管理。我们先创建一个简单的内存数据存储:
# 食谱数据(内存存储)
RECIPES_DATABASE = [{"id": "recipe_001","name": "宫保鸡丁","cuisine": "川菜","description": "经典川菜,麻辣鲜香","ingredients": [{"name": "鸡胸肉", "quantity": "300g"},{"name": "花生米", "quantity": "50g"},{"name": "干辣椒", "quantity": "10个"},{"name": "花椒", "quantity": "1茶匙"},{"name": "葱", "quantity": "2根"},{"name": "姜", "quantity": "3片"},{"name": "蒜", "quantity": "3瓣"}],"steps": ["鸡胸肉切丁,用料酒、生抽、淀粉腌制15分钟","热锅凉油,放入花椒和干辣椒爆香","加入鸡丁翻炒至变色","加入葱姜蒜继续翻炒","加入调好的宫保汁炒匀","最后加入花生米翻炒均匀即可"],"difficulty": "中等"},{"id": "recipe_002","name": "番茄炒蛋","cuisine": "家常菜","description": "简单易做的家常菜","ingredients": [{"name": "番茄", "quantity": "2个"},{"name": "鸡蛋", "quantity": "3个"},{"name": "葱", "quantity": "适量"},{"name": "盐", "quantity": "适量"},{"name": "糖", "quantity": "少许"}],"steps": ["番茄切块,鸡蛋打散","热锅倒油,倒入鸡蛋炒熟盛出","锅中再倒少许油,放入番茄翻炒","番茄出汁后加入盐和糖调味","倒入炒好的鸡蛋翻炒均匀","撒上葱花即可"],"difficulty": "简单"}
]# 用户偏好数据
USER_PREFERENCES = {"user_001": {"favorite_cuisines": ["川菜"],"dietary_restrictions": ["又麻又辣"],"cooking_skill": "初级"},"user_002": {"favorite_cuisines": ["鲁菜"],"dietary_restrictions": ["少点辣"],"cooking_skill": "高级"},"user_003": {"favorite_cuisines": ["家常菜"],"dietary_restrictions": ["健康好吃"],"cooking_skill": "初级"}
}def _get_all_recipes() -> Dict:"""获取所有食谱数据"""return {"success": True,"recipes": RECIPES_DATABASE,"count": len(RECIPES_DATABASE)}def _get_user_preferences(user_id: str) -> Dict:"""获取用户偏好数据"""if user_id in USER_PREFERENCES:return {"success": True,"preferences": USER_PREFERENCES[user_id]}return {"success": False,"error": f"未找到ID为{user_id}的用户"}def _get_recipe_by_id(recipe_id: str) -> Dict:"""根据ID获取特定食谱"""for recipe in RECIPES_DATABASE:if recipe["id"] == recipe_id:return {"success": True,"recipe": recipe}return {"success": False,"error": f"未找到ID为{recipe_id}的食谱"}# 资源组件
@mcp.resource("recipes://all")
def get_all_recipes() -> Dict:return _get_all_recipes()@mcp.resource("recipes://{recipe_id}")
def get_recipe_by_id(recipe_id: str) -> Dict:return _get_recipe_by_id(recipe_id)@mcp.resource("users://{user_id}/preferences")
def get_user_preferences(user_id: str) -> Dict:return _get_user_preferences(user_id)
2. 工具组件 - 食谱查询与推荐
工具组件就像是AI的"行动力",负责执行具体的操作和计算。我们实现两个工具:食谱查询和个性化推荐。
# 工具组件
@mcp.tool()
def search_recipes_by_ingredient(ingredient: str) -> Dict:all_recipes_result = _get_all_recipes()if not all_recipes_result["success"]:return {"success": False,"error": "获取食谱数据失败"}# 查找包含指定食材的食谱matched_recipes = []for recipe in all_recipes_result["recipes"]:for ing in recipe["ingredients"]:if ingredient in ing["name"]:matched_recipes.append(recipe)breakreturn {"success": True,"recipes": matched_recipes,"count": len(matched_recipes)}@mcp.tool()
def recommend_recipes(user_id: str, available_ingredients: List[str] = None) -> Dict:user_prefs_result = _get_user_preferences(user_id)all_recipes_result = _get_all_recipes()if not user_prefs_result["success"]:return {"success": False,"error": f"获取用户偏好失败: {user_prefs_result.get('error', '')}"}user_prefs = user_prefs_result["preferences"]# 根据用户偏好筛选食谱favorite_cuisines = user_prefs.get("favorite_cuisines", [])recommended_recipes = []for recipe in all_recipes_result["recipes"]:# 根据菜系筛选if recipe["cuisine"] in favorite_cuisines:# 如果提供了可用食材,检查是否有匹配的食材if available_ingredients:has_ingredient = Falsefor ing in recipe["ingredients"]:if any(avail_ing in ing["name"] for avail_ing in available_ingredients):has_ingredient = Truebreakif has_ingredient:recommended_recipes.append(recipe)else:recommended_recipes.append(recipe)return {"success": True,"recipes": recommended_recipes,"count": len(recommended_recipes)}
3. 提示词组件 - 用户交互界面
提示词组件就像是AI的"语言能力",负责生成自然、友好的回复。我们实现两个提示词模板:食谱查询和食谱详情。
@mcp.prompt()
def generate_recipe_search_response(ingredient: str) -> str:"""生成食谱查询的回复"""all_recipes_result = _get_all_recipes()if not all_recipes_result["success"]:return f"抱歉,查询食谱时出现了问题:获取食谱数据失败"# 查找包含指定食材的食谱matched_recipes = []for recipe in all_recipes_result["recipes"]:for ing in recipe["ingredients"]:if ingredient in ing["name"]:matched_recipes.append(recipe)breakif not matched_recipes:return f"抱歉,我没有找到包含{ingredient}的食谱。请尝试其他食材。"# 生成回复文本response = f"我找到了{len(matched_recipes)}个包含{ingredient}的食谱:\n\n"for i, recipe in enumerate(matched_recipes, 1):response += f"{i}. {recipe['name']} - {recipe['description']}\n"response += f" 难度:{recipe['difficulty']}\n"response += f" 主要食材:{', '.join(ing['name'] for ing in recipe['ingredients'][:3])}\n\n"response += f"想了解某个食谱的详细做法,请告诉我食谱的编号。"return response@mcp.prompt()
def generate_recipe_details(recipe_id: str) -> str:"""生成食谱详细信息的回复Args:recipe_id: 食谱IDReturns:格式化的食谱详情"""recipe_result = _get_recipe_by_id(recipe_id)if not recipe_result["success"]:return f"抱歉,查询食谱时出现了问题:{recipe_result['error']}"recipe = recipe_result["recipe"]# 生成详细食谱信息response = f"# {recipe['name']}\n\n"response += f"{recipe['description']}\n\n"response += "## 食材准备\n\n"for ing in recipe["ingredients"]:response += f"- {ing['name']}: {ing['quantity']}\n"response += "\n## 烹饪步骤\n\n"for i, step in enumerate(recipe["steps"], 1):response += f"{i}. {step}\n"response += f"\n难度:{recipe['difficulty']}\n"response += f"菜系:{recipe['cuisine']}\n"return response
完整应用
现在我们已经实现了三大组件,让我们把它们组合起来,形成一个完整的应用:
# 在文件末尾添加测试代码
if __name__ == "__main__":# 初始化MCPmcp.run()
运行与测试
保存上面的代码到recipe_app.py
文件中,然后在终端中运行:
python recipe_app.py
你将看到测试输出,然后MCP服务会启动。你可以使用MCP Inspector来交互式地测试这个应用:
npx @modelcontextprotocol/inspector stdio python recipe_app.py
在Inspector界面中,你可以:
-
调用资源组件:
recipes://all
或recipes://recipe_001
-
调用工具组件:
search_recipes_by_ingredient
(参数:{"ingredient": "鸡蛋"}
)
-
调用提示词组件:
generate_recipe_search_response
(参数:{"ingredient": "鸡胸肉"}
)
总结
通过这个简单的食谱助手项目,我们学习了MCP的三大组件,下一篇我们要解决数据持久化的问题,将食谱助手从内存存储升级到SQLite数据库。