实战:用 Python 搭建 MCP 服务 —— 模型上下文协议(Model Context Protocol)应用指南

📌 实战:用 Python 搭建 MCP 服务 —— 模型上下文协议(Model Context Protocol)应用指南

标签:#MCP #AI工程化 #Python #LLM上下文管理 #Agent架构
在这里插入图片描述


🎯 引言:为什么需要 MCP?

在构建大语言模型(LLM)驱动的智能体(Agent)系统时,一个核心挑战是:如何在多轮对话、工具调用、多Agent协作中,高效、结构化地传递和管理上下文?

传统方式(如字符串拼接、JSON随意传递)往往导致:

  • 上下文结构混乱,难以调试
  • Agent 间协作信息丢失
  • 工具调用参数与模型输入耦合严重
  • 多轮对话状态难以追踪

为解决这些问题,业界提出了 MCP(Model Context Protocol)—— 模型上下文协议。它是一种标准化的上下文数据交换格式与通信协议,旨在统一 Agent、Tool、Memory、Planner 等模块之间的“语言”。

本文将手把手教你用 Python 实现一个简易但完整的 MCP 服务,让你的 LLM 应用从此告别“上下文泥潭”。


🧩 一、MCP 协议核心概念

MCP 定义了上下文数据的结构化表示,核心包含:

  1. Context Frame(上下文帧):一次交互的完整上下文单元,包含:

    • session_id:会话标识
    • turn_id:轮次编号
    • role:发送者角色(user / system / tool / agent)
    • content:内容(支持文本、结构化数据、工具调用等)
    • metadata:元信息(时间戳、来源、置信度等)
    • tools_called:本轮调用的工具列表
    • state:当前会话状态(如 waiting_tool, thinking, responding)
  2. MCP Service:负责接收、路由、转换、持久化上下文帧的服务层。

  3. MCP Client:Agent 或 Tool 通过 Client 发送/接收上下文帧。


🚀 二、Python 实战:搭建 MCP 服务

我们使用 FastAPI + Pydantic + Redis(可选)构建一个轻量级 MCP 服务。

📁 项目结构:

mcp_service/
├── models/
│   └── mcp.py          # MCP 数据模型
├── services/
│   └── context_manager.py  # 上下文管理逻辑
├── api/
│   └── routes.py       # API 路由
├── main.py             # 启动入口
└── requirements.txt

🔧 Step 1:定义 MCP 数据模型(models/mcp.py)

# models/mcp.py
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any
from enum import Enum
from datetime import datetimeclass RoleEnum(str, Enum):USER = "user"SYSTEM = "system"TOOL = "tool"AGENT = "agent"class ToolCall(BaseModel):tool_name: strarguments: Dict[str, Any]call_id: strclass ContextFrame(BaseModel):session_id: str = Field(..., description="会话唯一ID")turn_id: int = Field(..., description="对话轮次")role: RoleEnumcontent: strtools_called: List[ToolCall] = Field(default_factory=list)state: str = Field(default="responding")metadata: Dict[str, Any] = Field(default_factory=dict)timestamp: datetime = Field(default_factory=datetime.utcnow)class Config:json_encoders = {datetime: lambda v: v.isoformat()}

🧠 Step 2:实现上下文管理器(services/context_manager.py)

# services/context_manager.py
from typing import List, Dict
from .models.mcp import ContextFrame
import redis
import jsonclass ContextManager:def __init__(self, use_redis=False):self.sessions: Dict[str, List[ContextFrame]] = {}self.use_redis = use_redisif use_redis:self.redis_client = redis.Redis(host='localhost', port=6379, db=0)def add_frame(self, frame: ContextFrame):sid = frame.session_idif sid not in self.sessions:self.sessions[sid] = []self.sessions[sid].append(frame)if self.use_redis:key = f"mcp:session:{sid}"self.redis_client.rpush(key, frame.json())self.redis_client.expire(key, 3600)  # 1小时过期def get_session_frames(self, session_id: str) -> List[ContextFrame]:if self.use_redis:key = f"mcp:session:{session_id}"frames_data = self.redis_client.lrange(key, 0, -1)return [ContextFrame.parse_raw(f) for f in frames_data]else:return self.sessions.get(session_id, [])def get_latest_frame(self, session_id: str) -> Optional[ContextFrame]:frames = self.get_session_frames(session_id)return frames[-1] if frames else None

🌐 Step 3:构建 FastAPI 路由(api/routes.py)

# api/routes.py
from fastapi import APIRouter, HTTPException
from ..models.mcp import ContextFrame
from ..services.context_manager import ContextManagerrouter = APIRouter()
context_manager = ContextManager(use_redis=False)  # 可配置为 True@router.post("/mcp/push", summary="推送上下文帧")
async def push_context_frame(frame: ContextFrame):try:context_manager.add_frame(frame)return {"status": "success", "message": "Frame added"}except Exception as e:raise HTTPException(status_code=500, detail=str(e))@router.get("/mcp/session/{session_id}", summary="获取会话所有帧")
async def get_session_frames(session_id: str):frames = context_manager.get_session_frames(session_id)return {"session_id": session_id, "frames": frames}@router.get("/mcp/session/{session_id}/latest", summary="获取最新帧")
async def get_latest_frame(session_id: str):frame = context_manager.get_latest_frame(session_id)if not frame:raise HTTPException(status_code=404, detail="Session or frame not found")return frame

⚡ Step 4:启动服务(main.py)

# main.py
from fastapi import FastAPI
from api.routes import routerapp = FastAPI(title="MCP Service", version="0.1.0")app.include_router(router, prefix="/api/v1")@app.get("/")
def read_root():return {"message": "Welcome to MCP Service - Model Context Protocol"}if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)

📦 Step 5:安装依赖(requirements.txt)

fastapi
uvicorn
pydantic
redis  # 可选
python-dotenv  # 可选

启动服务:

pip install -r requirements.txt
python main.py

访问 http://localhost:8000/docs 查看自动生成的 API 文档!


🧪 三、客户端调用示例

你可以用任何 HTTP 客户端调用 MCP 服务。以下是 Python 示例:

# client_example.py
import requests
import json
from datetime import datetimeurl = "http://localhost:8000/api/v1/mcp/push"frame_data = {"session_id": "chat_123","turn_id": 1,"role": "user","content": "今天北京天气如何?","tools_called": [],"state": "waiting_tool","metadata": {"source": "web", "user_id": "u_001"}
}response = requests.post(url, json=frame_data)
print(response.json())# 获取最新帧
resp = requests.get("http://localhost:8000/api/v1/mcp/session/chat_123/latest")
print(resp.json())

🧠 四、在 Agent 系统中集成 MCP

假设你有一个天气查询 Agent,它可以这样使用 MCP:

class WeatherAgent:def __init__(self, mcp_url="http://localhost:8000/api/v1"):self.mcp_url = mcp_urldef process(self, user_input: str, session_id: str, turn_id: int):# 1. 接收用户输入 → 推送 MCP 帧self._push_frame(session_id, turn_id, "user", user_input)# 2. 决定调用工具tool_call = {"tool_name": "get_weather","arguments": {"location": "北京"},"call_id": "tc_001"}# 3. 推送 Agent 思考帧self._push_frame(session_id, turn_id + 1, "agent","正在调用天气API...",tools_called=[tool_call],state="calling_tool")# 4. 模拟工具返回weather_result = "北京,晴,25°C"self._push_frame(session_id, turn_id + 2, "tool",weather_result,metadata={"tool_name": "get_weather", "call_id": "tc_001"})# 5. Agent 生成最终回复final_reply = f"为您查询到:{weather_result}"self._push_frame(session_id, turn_id + 3, "agent",final_reply,state="responding")return final_replydef _push_frame(self, session_id, turn_id, role, content, **kwargs):data = {"session_id": session_id,"turn_id": turn_id,"role": role,"content": content,**kwargs}requests.post(f"{self.mcp_url}/mcp/push", json=data)

✅ 五、MCP 的进阶价值

  1. 可观测性:所有上下文帧可被记录、查询、分析,便于调试复杂 Agent 行为。
  2. 状态恢复:通过 session_id 可恢复中断的对话状态。
  3. 多Agent协作:不同 Agent 可订阅同一 session,实现上下文共享。
  4. 审计与合规:完整记录对话轨迹,满足企业合规要求。
  5. 插件化扩展:可轻松接入 Memory、Planner、Guardrail 等模块。

🔚 结语

MCP 不是银弹,但它为混乱的 LLM 上下文管理提供了一种标准化、可扩展、可观测的解决方案。通过本文的实战,你已掌握:

  • MCP 协议的核心结构
  • 用 Python + FastAPI 快速搭建 MCP 服务
  • 在 Agent 中集成 MCP 实现上下文流转

下一步,你可以:

  • 接入 Redis / PostgreSQL 实现持久化
  • 增加 WebSocket 支持实现实时推送
  • 开发 MCP 浏览器插件可视化上下文流
  • 与 LangChain / LlamaIndex 集成

🌟 让 MCP 成为你 AI 工程化架构的“中枢神经系统”,告别上下文混乱,拥抱结构化智能!


💬 评论区开放:你在项目中是如何管理 LLM 上下文的?是否遇到过上下文丢失或混乱的问题?欢迎分享!

#MCP #AI架构 #Python实战 #LLMOps #智能体开发

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/96484.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/96484.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

宋红康 JVM 笔记 Day16|垃圾回收相关概念

一、今日视频区间 P154-P168 二、一句话总结 System.gc()的理解;内存溢出与内存泄漏;Stop The World;垃圾回收的并行与并发;安全点与安全区域;再谈引用:强引用;再谈引用:软引用;再谈…

OpenCV 高阶 图像金字塔 用法解析及案例实现

目录 一、什么是图像金字塔? 二、图像金字塔的核心作用 三、图像金字塔的核心操作:上下采样 3.1 向下采样( pyrDown ):从高分辨率到低分辨率 1)原理与步骤 2)关键注意事项 3)…

【ARMv7】系统复位上电后的程序执行过程

引子:对于ARMv7-M系列SOC来说,上电后程序复位执行的过程相对来说比较简单,因为绝大部分芯片,都是XIP(eXecute In Place,就地执行)模式执行程序,不需要通过BooROM->PL(preloader)-…

神经网络的初始化:权重与偏置的数学策略

在深度学习中,神经网络的初始化是一个看似不起眼,却极其重要的环节。它就像是一场漫长旅程的起点,起点的选择是否恰当,往往决定了整个旅程的顺利程度。今天,就让我们一起深入探讨神经网络初始化的数学策略,…

第 16 篇:服务网格的未来 - Ambient Mesh, eBPF 与 Gateway API

系列文章:《Istio 服务网格详解》 第 16 篇:服务网格的未来 - Ambient Mesh, eBPF 与 Gateway API 本篇焦点: 反思当前主流 Sidecar 模式的挑战与权衡。 深入了解 Istio 官方的未来演进方向:Ambient Mesh (无边车模式)。 探讨革命性技术 eBPF 将如何从根本上重塑服务网格的…

摆动序列:如何让数组“上下起伏”地最长?

文章目录摘要描述题解答案题解代码分析代码解析示例测试及结果时间复杂度空间复杂度总结摘要 今天我们要聊的是 LeetCode 第 376 题 —— 摆动序列。 题目的意思其实很有意思:如果一个序列里的相邻差值能保持正负交替,就叫做“摆动”。比如 [1, 7, 4, 9…

玩转Docker | 使用Docker部署KissLists任务管理工具

玩转Docker | 使用Docker部署KissLists任务管理工具 前言 一、KissLists介绍 KissLists简介 KissLists核心特点 KissLists注意事项 二、系统要求 环境要求 环境检查 Docker版本检查 检查操作系统版本 三、部署KissLists服务 下载KissLists镜像 编辑部署文件 创建容器 检查容器状…

【滑动窗口】C++高效解决子数组问题

个人主页 : zxctscl 专栏 【C】、 【C语言】、 【Linux】、 【数据结构】、 【算法】 如有转载请先通知 文章目录前言1 209. 长度最小的子数组1.1 分析1.2 代码2 3. 无重复字符的最长子串2.1 分析2.2 代码3 1004. 最大连续1的个数 III3.1 分析3.2 代码4 1658. 将 x …

[rStar] 搜索代理(MCTS/束搜索)

第2章:搜索代理(MCTS/束搜索) 欢迎回到rStar 在前一章中,我们学习了求解协调器,它就像是解决数学问题的项目经理。 它组织整个过程,但本身并不进行"思考",而是将这项工作委托给其专家团队。 今天&#x…

Electron 核心模块速查表

为了更全面地覆盖常用 API,以下表格补充了更多实用方法和场景化示例,同时保持格式清晰易读。 一、主进程模块 模块名核心用途关键用法 示例注意事项app应用生命周期管理• 退出应用:app.quit()• 重启应用:app.relaunch() 后需…

Qt C++ 图形绘制完全指南:从基础到进阶实战

Qt C 图形绘制完全指南:从基础到进阶实战 前言 Qt框架提供了强大的2D图形绘制能力,通过QPainter类及其相关组件,开发者可以轻松实现各种复杂的图形绘制需求。本文将系统介绍Qt图形绘制的核心技术,并通过实例代码演示各种绘制技巧…

二分搜索边界问题

在使用二分搜索的时候&#xff0c;更新条件不总是相同&#xff0c;虽然说使用bS目的就是为了target&#xff0c;但也有如下几种情况&#xff1a;求第一个target的索引求第一个>target的索引求第一个>target的索引求最后一个target的索引求最后一个<target的索引求最后…

【springboot+vue3】博客论坛管理系统(源码+文档+调试+基础修改+答疑)

目录 一、整体目录&#xff1a; 项目包含源码、调试、修改教程、调试教程、讲解视频、开发文档&#xff08;项目摘要、前言、技术介绍、可行性分析、流程图、结构图、ER属性图、数据库表结构信息、功能介绍、测试致谢等约1万字&#xff09; 二、运行截图 三、代码部分&…

20250907_梳理异地备份每日自动巡检Python脚本逻辑流程+安装Python+PyCharm+配置自动运行

一、逻辑流程(autocheckbackup.py在做什么) 1.连接Linux服务器 用 paramiko 登录你配置的 Linux 服务器(10.1.3.15, 10.1.3.26),进入指定目录(如 /home, /backup/mes),递归列出文件。 采集到的信息:服务器IP、目录、数据库名称、文件名、大小、修改时间。 2.连接Wind…

terraform-state详解

一、Treeaform-state的作用 Terraform-state是指Terroform的状态&#xff0c;是terraform不可缺少的生命周期元素。本质上来讲&#xff0c;terraform状态是你的基础设施配置的元数据存储库&#xff0c;terraform会把它管理的资源状态保存在一个状态文件里。 默认情况下&#xf…

四、kubernetes 1.29 之 Pod 生命周期

一、概述当容器与 pause 容器共享网络&#xff08;Network&#xff09;、IPC&#xff08;进程间通信&#xff09;和 PID&#xff08;进程命名空间&#xff09;后&#xff0c;二者形成了一种紧密的 "共享命名空间" 关系&#xff0c;共同构成了 Kubernetes 中 "Po…

AI与环保:礼貌用语背后的能源挑战与解决方案

程序员的技术管理推荐阅读 窄化效应&#xff1a;程序员与管理者的隐形情绪陷阱 从“激励”到“保健”&#xff1a;80后与90后程序员&#xff0c;到底想要什么&#xff1f; 从“激励”到“保健”&#xff1a;80后与90后程序员&#xff0c;到底想要什么&#xff1f; 场景引入&…

OpenCV C++ 特征提取:从角点检测到对象识别

特征提取是计算机视觉的核心技术,通过识别图像中具有代表性的关键点及其描述信息,实现图像匹配、对象识别、姿态估计等高级任务。本章将系统讲解从基础的图像金字塔、角点检测,到复杂的 ORB 和 SIFT 特征提取与匹配,最终实现基于特征的对象检测完整流程。 一、图像金字塔 …

Codeforces Round 1049 (Div. 2) D题题解记录

大致题意&#xff1a;给定nnn个区间(li,ri)(l_i,r_i)(li​,ri​)。每次选取两个尚未被标记的区间(l1,r1)(l_1,r_1)(l1​,r1​)与(l2,r2)(l_2,r_2)(l2​,r2​)&#xff0c;使得他们均被标记&#xff0c;同时可以任选x∈[l1,r1]&#xff0c;y∈[l2,r2]x\in[l_1,r_1]&#xff0c;y…

《WINDOWS 环境下32位汇编语言程序设计》第15章 注册表和INI文件

15.1 注册表和INI文件简介在一个操作系统中&#xff0c;无论是操作系统本身还是运行于其中的大部分应用程序&#xff0c;都需要使用某种方式保存配置信息。在DOS系统中&#xff0c;配置信息往往是软件的开发者根据自己的喜好用各种途径加以保存的&#xff0c;比如在磁盘上面写一…