【动手学MCP从0到1】2.5 MCP中的Context日志输出、进度汇报和服务端调用客户端的大模型项目实现步骤详解

MCP中的Context

  • 1. Context
  • 2. 日志输出
    • 2.1 服务端
    • 2.2 客户端
      • 2.2.1 客户端代码调试
      • 2.2.2 客户端全部代码
  • 3. 进度汇报
    • 3.1 服务端
    • 3.2 客户端
      • 3.2.1 客户端代码调试
      • 3.2.2 客户端全部代码
  • 4. 模型调用
    • 4.1 服务端
    • 4.2 客户端
      • 4.2.1 客户端代码调试
      • 4.2.2 客户端全部代码

1. Context

Context对象给MCP Server提供了更多获取客户端信息、以及和客户端进行交互的接口。通过Context对象,能够获取到以以下对象:

  • 当前请求的id:request_id
  • 客户端id:client_id
  • 服务端的session对象

能够在单次请求中额外发送以下数据给客户端:

  • 日志输出:可以发送服务端的日志给客户端
  • 进度汇报:在处理一些耗时操作时,可以在处理过程中发送处理进度给客户端
  • 模型调用:当服务端需要客户端的大模型能力时,可以调用客户端的大模型能力

通过Context,可以实现更加复杂的需求。但是注意,Context对象只能在Tool中使用,不能在 ResourcePrompt 中使用。使用方式也非常简单,只需要在函数上添加一个 Context 类型的参数即可。

项目文件架构:新建一个Context_mcp文件夹,下面再分别创建三个子文件夹为Log_output、Load_report和Model_call。然后再三个子文件夹中都分别创建两个py文件,命名为server.py和client.py

2. 日志输出

如果服务端代码在执行过程中想要将执行过程的日志发送给客户端,那么借助 Context 非常方便的实现。

2.1 服务端

在Log_output文件夹下的server.py文件中,添加 Context 信息,添加的方式是接着tool工具,然后再函数中添加 Context 参数,参数的具体名称不限制,往往采用ctx或者content进行命名.

比如定义日志输出的函数为log_tool,参数分别为两个:filesctx。前者是指定文件列表,用于存放文件路径的容器,第二个就是Context对应的参数。

"""
-------------------------------------------------------------------------------
@Project : MCP projects
File    : client.py
Time    : 2025-06-05 17:20
author  : musen
Email   : xianl828@163.com
-------------------------------------------------------------------------------
"""
from mcp.server.fastmcp import FastMCP,Context# app =  FastMCP
mcp: FastMCP = FastMCP()#两种书写方式,都可以
# @app.tool()
@mcp.tool()
async def log_tool(files:list[str],ctx:Context):'''处理文件的接口:param files: 文件列表:param ctx: 上下文对象,无需客户端传递:return: 处理结果'''for index,file in enumerate(files):await asyncio.sleep(1)# ctx.log()await ctx.info(f"正在处理第{index+1}个文件")return "所有文件处理完成"if __name__ == '__main__':mcp.run(transport='sse')

服务端的代码中,通过log函数进行信息的传递,其属于最基础的操作,点击函数进入技术文档可以知道如果使用log进行消息传递,需要指定levelmessage两个重要参数,其中level包含具体的信息类型。

在这里插入图片描述

通过往下滑, 可以发现文档中已经有关于会话信息操作的封装函数,如下(刚好对应上面level参数中指定的4类,比如这里服务端的代码使用ctx.info()函数进行消息传递)

在这里插入图片描述

2.2 客户端

2.2.1 客户端代码调试

在Log_output文件夹下的client.py文件中,先进行架构的搭建

import asyncio
from mcp.client.sse import sse_client
from mcp import ClientSessionasync def run():async with sse_client("http://127.0.0.1:8000/sse") as (read_stream, write_stream):async with ClientSession(read_stream, write_stream) as session:await session.initialize()# 获取所有工具tools = (await session.list_tools()).toolsprint(tools)if __name__ == "__main__":asyncio.run(run())

执行结果如下:(以上代码是之前项目中都使用的框架,熟悉的操作,就看是不是结果可以正常输出)
在这里插入图片描述

目前tool工具对象中只有一个数据,因为进行演示demo,这里可以直接进行列表对象的索引(以往的项目都是遍历循环)

tool = tools[0]
response = await session.call_tool(name=tool.name,arguments={"files":["a.txt","b.txt"]})
print(response)

代码执行输出结果如下:(输出结果中出现“所有文件处理完毕”,说明客户端中成功调用了服务端中的日志输出工具)

在这里插入图片描述

现在可以成功调用工具,我们的目标是获取客户端发过来的日志信息,需要结合这个session中的logging_callback参数。

通过点击加logging_callback参数j,进入技术文档,可以发现,这参数需要制定一个回调函数。

在这里插入图片描述

进一步,点击一个回调函数,查看具体说明,主要查看这个函数需要传入的参数类别,如下

在这里插入图片描述

因此,在client.py中定一个回调函数,命名为logging_handle,然后这个函数指定的对应就是上面红框中的数据对象,然后再把这个回调函数赋值给logging_callback参数,如下

from mcp.types import LoggingMessageNotificationParamsasync def logging_handle(params: LoggingMessageNotificationParams):print(params)async def run():async with sse_client("http://127.0.0.1:8000/sse") as (read_stream, write_stream):async with ClientSession(read_stream,write_stream,logging_callback=logging_handle) as session:await session.initialize()....

执行代码,输出结果如下:(此时,我们就可以成功将服务端的信息在客户端打印出来了,即获取到客户端的日志信息)

在这里插入图片描述

由此,就可以根据自己的需求完成一些日志方面的操作,比如将日志信息保存本地,进行日志操作之间的交互(增加用户体验)等。

2.2.2 客户端全部代码

给出client.py文件的全部代码,方便学习理解

import asyncio
from mcp.client.sse import sse_client
from mcp import ClientSession
from mcp.types import LoggingMessageNotificationParamsasync def logging_handle(params: LoggingMessageNotificationParams):print(params)async def run():async with sse_client("http://127.0.0.1:8000/sse") as (read_stream, write_stream):async with ClientSession(read_stream,write_stream,logging_callback=logging_handle) as session:await session.initialize()# 获取所有工具tools = (await session.list_tools()).tools# print(tools)tool = tools[0]response = await session.call_tool(name=tool.name,arguments={"files":["a.txt","b.txt"]})print(response)if __name__ == "__main__":asyncio.run(run())

3. 进度汇报

当服务端的代码在处理一些耗时操作时,可以向客户端实时反馈执行的进度。可以通过report_process()函数进行设置

3.1 服务端

在Load_report文件夹下的server.py文件中,输入代码如下

import asyncio
from mcp.server.fastmcp import FastMCP,Context
from mcp.types import RequestParamsmcp: FastMCP = FastMCP()@mcp.tool()
async def load_task(files:list[str],ctx: Context):'''处理多个文件进行进度汇报:param files: 多个文件路径:param ctx: 上下文对象,无需客户端传递:return: 处理结果'''for index,file in enumerate(files):await asyncio.sleep(1)ctx.request_context.meta = RequestParams.Meta(progressToken=ctx.request_id)await ctx.report_progress(index,len(files))return "处理完毕"if __name__ == '__main__':mcp.run(transport="sse")

代码中,关于调用ctx.report_progress()函数前,需要添加一行代码ctx.request_context.meta = RequestParams.Meta(),具体原因可以通过查看ctx.report_progress()函数技术文档,如下(这个函数中有一个progress_token参数,如果这个参数的赋值为None,这调用函数直接返回为空,相当于函数不会发送信息。而要求函数发送信息,则要求progress_token参数不为None,进一步找到前面的request_context.meta参数,这个值不能为None,然后就返回request_context.meta.progressToken对象)

在这里插入图片描述

进一步点击request_context.meta.progressToken,查看它的技术文档,可以发现这个参数下还是一个函数嵌套。

在这里插入图片描述

进一步点击函数,进入下一次技术文档,如下(可知这个进度的Token需要指定一个字符串或者整形的数据,避免None值出现)

在这里插入图片描述

因此,为了在执行ctx.report_progress()函数前,添加一行代码ctx.request_context.meta = RequestParams.Meta(),而且里面的这个参数设置需要时唯一的,这里采用的就是每一个请求的id。 做开发项目,最有趣的过程就是在进行探索的过程,这个过程自己过一遍后,收获感会很充实。

3.2 客户端

3.2.1 客户端代码调试

在Load_report文件夹下的client.py文件中,与前面的日志输出类似,这里也有一个message_handler参数进行回调。同样,进入该参数的技术文档,查看详细说明,如下

在这里插入图片描述

进一步点击进去,查看详细说明,如下(该函数回调只有一个参数,就是message,可接受三种数据类型)

在这里插入图片描述

因此在定义回调函数时候,指定传入的参数类型设置,可以直接把技术文档中的说明复制过来,如下


from mcp.types import LoggingMessageNotificationParams,ServerNotification,ClientResult,ServerRequest
from mcp.client.session import RequestResponder
async def message_handler(message: RequestResponder[ServerRequest, ClientResult]| ServerNotification| Exception):print("*"*30)print(message)print("*" * 30)async def run():async with sse_client("http://127.0.0.1:8000/sse") as (read_stream, write_stream):async with ClientSession(read_stream,write_stream,logging_callback=logging_handle,message_handler=message_handler  #这个参数设置) as session:await session.initialize()

执行代码输出结果如下:(可以输出加载信息)

在这里插入图片描述

3.2.2 客户端全部代码

import asyncio
from mcp.client.sse import sse_client
from mcp import ClientSession
from mcp.types import LoggingMessageNotificationParams,ServerNotification,ClientResult,ServerRequest
from mcp.client.session import RequestResponderasync def message_handler(message: RequestResponder[ServerRequest, ClientResult]| ServerNotification| Exception):print("*"*30)print(message)print("*" * 30)async def run():async with sse_client("http://127.0.0.1:8000/sse") as (read_stream, write_stream):async with ClientSession(read_stream,write_stream,logging_callback=logging_handle,message_handler=message_handler) as session:await session.initialize()# 获取所有工具tools = (await session.list_tools()).tools# print(tools)tool = tools[0]response = await session.call_tool(name=tool.name,arguments={"files":["a.txt","b.txt"]})print(response)if __name__ == "__main__":asyncio.run(run())

4. 模型调用

有时候服务端可能需要调用大模型的能力,那么可以使用MCP提供的sampling功能来实现。(注意是服务端调用大模型能力,之前项目都是客户端调用大模型来执行服务端中的Tool)

4.1 服务端

在Mdel_call文件夹下的server.py文件中,具体实现就是调用Context对象下面的session.create_message()函数

"""import asyncio
from mcp.server.fastmcp import FastMCP,Context
from mcp.types import SamplingMessage, TextContentmcp: FastMCP = FastMCP()@mcp.tool()
async def sampling_tool(ctx:Context):# 直接发送一个Sampling的消息response = await ctx.session.create_message(max_token=2048,messages=[SamplingMessage(role="user",content=TextContent(type="text",text="请帮我按照主题“2025年高考”为主题写两篇诗词"))])print(response)return "采样成功"if __name__ == '__main__':mcp.run(transport="sse")

关于函数中具体参数的配置,也可以参考本博客中前2个案例,都是通过技术文档中的说明,逐步进行完善,然后填写完整,这个过程不是要背具体的方式,而是要学会具体的方法。比如这里对于大模型能力的应用,知道借助上下文Context对象下面的session.create_message()函数创建就可以了,然后就是具体的参数设置直接借助说明文档快速完成。

4.2 客户端

4.2.1 客户端代码调试

在Mdel_call文件夹下的client.py文件中,客服端中对应的就是sampling_callback参数完成

点击该参数,进入技术文档,如下

在这里插入图片描述

然后进入下一级的技术文档,如下(此时就出现了具体的参数和数据类型了)

在这里插入图片描述

在客户端,新定一个函数为sampling_handler(),然后把上图中的下面介绍两个参数及对应的数据类型的代码全部复制到创建的函数中。还需要注意,技术文档中回调函数需要返回CreateMessageResult数据类型。我们可以先手动创建一个类数据对象,如下

from mcp.types import CreateMessageRequestParams, TextContent, CreateMessageResult
from mcp.client.session import RequestContext
async def sampling_handler(context: RequestContext["ClientSession", Any],params: CreateMessageRequestParams,):print(f"context: {context}")print(f"params: {params}")return CreateMessageResult(role= "assistant",  # 大模型返回的信息,此时的角色对应的是assistantcontent=TextContent(type="text", text="手动输出的结果"),model="deepseek-chat")

将回调函数复制给对应的参数后,执行客户端代码,结果输出如下(检查客户端输出没有报错,然后服务端出现自己手动创建的CreateMessageResult数据,即证明代码可以跑通)

客户端:
在这里插入图片描述

服务端:

在这里插入图片描述

那么接下来,就是换用加入大模型,让其进行返回结果到服务端,如下(关于message变量的赋值,可以参考上图客户端输出结果进行获取,由此而进一步获得对应的rolecontent内容)

async def sampling_handler(context: RequestContext["ClientSession", Any],params: CreateMessageRequestParams,):print(f"context: {context}")print(f"params: {params}")deepseek = OpenAI(api_key="sk-5d307e0xxxxx5a6ce4575ff9",base_url="https://api.deepseek.com",)message = params.messages[0]   messages = [{"role": message.role,"content": message.content.text}]response = deepseek.chat.completions.create(messages=messages,model="deepseek-chat")print(response)return CreateMessageResult(role= "assistant",content=TextContent(type="text", text="手动输出的结果"),model="deepseek-chat")

这里执行代码,输出如下(此时,只是测试代码是否正常运行,输出的结果是在客户端的输出窗口)

在这里插入图片描述

最后,就是把return中返回的结果使用response变量中的数据进行替换(根据上图的输出结果,逐步进行获取里面content内容),即可,代码如下

print(response)
text = response.choices[0].message.content
return CreateMessageResult(role= "assistant",content=TextContent(type="text", text=text),model="deepseek-chat"

重新执行客户端的代码,输出结果如下
在这里插入图片描述

当客户端的代码运行完毕后,此时,点击服务端的输出窗口,检查是否可以获取到客户端调用大模型输出的结果,如下(完美获取想要的结果)

在这里插入图片描述

4.2.2 客户端全部代码

在强调一遍,这个项目是服务端获取客户端调用大模型返回的结果,这里是功能实现,具体后续的扩展开发就任凭需求了。

"""
-------------------------------------------------------------------------------
@Project : MCP projects
File    : client.py
Time    : 2025-06-05 17:20
author  : musen
Email   : xianl828@163.com
-------------------------------------------------------------------------------
"""import asyncio
from typing import Any
from mcp.client.sse import sse_client
from mcp import ClientSession
from mcp.types import CreateMessageRequestParams, TextContent, CreateMessageResult
from mcp.client.session import RequestContext
from openai import OpenAIasync def sampling_handler(context: RequestContext["ClientSession", Any],params: CreateMessageRequestParams,):print(f"context: {context}")print(f"params: {params}")deepseek = OpenAI(api_key="sk-5d307e0a45xxxxx4575ff9",base_url="https://api.deepseek.com",)message = params.messages[0]messages = [{"role": message.role,"content": message.content.text}]response = deepseek.chat.completions.create(messages=messages,model="deepseek-chat")print(response)text = response.choices[0].message.contentreturn CreateMessageResult(role= "assistant",content=TextContent(type="text", text=text),model="deepseek-chat")async def run():async with sse_client("http://127.0.0.1:8000/sse") as (read_stream, write_stream):async with ClientSession(read_stream,write_stream,sampling_callback=sampling_handler) as session:await session.initialize()# 获取所有工具tools = (await session.list_tools()).tools# print(tools)tool = tools[0]response = await session.call_tool(name=tool.name)  #服务端的tool中函数没有参数arguments,这里就不需要指定print(response)if __name__ == "__main__":asyncio.run(run())

至此,关于 MCP中的Context的日志输出、进度汇报和服务端调用客户端的大模型输出结果的三个项目的实战详细梳理就完结了,撒花✿✿ヽ(°▽°)ノ✿。

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

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

相关文章

QT自定义资源管理器

使用qt 和 C实现。还在优化中 项目地址:GitHub - Linda1226/FileResourceManager: 自定义资源管理器 有问题可以交流

[华为eNSP] OSPF综合实验

目录 配置流程 画出拓扑图、标注重要接口IP 配置客户端IP 配置服务端IP 配置服务器服务 配置路由器基本信息:名称和接口IP 配置路由器ospf协议 测试结果 通过配置OSPF路由协议,实现跨多路由器的网络互通,并验证终端设备的访问能力。 …

如何把本地服务器变成公网服务器?内网ip网址转换到外网连接访问

​ 内网IP只能在本地内部网络连接访问,当本地搭建服务器部署好相关网站或应用后,在局域网内可以通过内网IP访问,但在外网是无法直接访问异地内网IP端口应用的,只有公网IP和域名才能实现互联网上的访问。那么需要如何把本地服务器变…

Linux-文件管理及归档压缩

1.根下的目录作用说明: /:Linux系统中所有的文件都在根下/bin:(二进制命令目录)存放常用的用户命令/boot:系统启动时的引导文件(内核的引导配置文件,grub配置文件,内核配置文件) 例…

从零开始的python学习(七)P95+P96+P97+P98+P99+P100+P101

本文章记录观看B站python教程学习笔记和实践感悟,视频链接:【花了2万多买的Python教程全套,现在分享给大家,入门到精通(Python全栈开发教程)】 https://www.bilibili.com/video/BV1wD4y1o7AS/?p6&share_sourcecopy_web&v…

Linux 查找特定字符详细讲解

CentOS 7 中使用 grep 查找特定字符详细笔记​ 一、grep 命令概述​ grep 全称为 Global Regular Expression Print,即全局正则表达式打印,是 CentOS 7 系统中用于文本搜索的核心工具。它基于正则表达式或固定字符串,在文件、标准输入流中进…

uniappx插件nutpi-idcard 开发与使用指南(适配鸿蒙)

uniappx插件nutpi-idcard 开发与使用指南(适配鸿蒙) 前言 nutpi-idcard 是一个基于 UTS (uni-app TypeScript Syntax) 开发的 uni-app 插件适配鸿蒙,主要用于解析身份证号码,提取其中的关键信息,如地区、出生日期、性…

Grafana-ECharts应用讲解(玫瑰图示例)

工具: MySQL 数据库 MySQL Workbench 数据库管理工具(方便编辑数据) Grafana v11.5.2 Business Charts 6.6(原 Echarts插件) 安装 安装 MySQL社区版安装 MySQL Workbench安装 Grafana在 Grafana 插件中搜索 Business Charts 进行安装以上安装步骤网上教程很多,自行搜…

React状态管理Context API + useReducer

在 React 中,Context API useReducer 是一种轻量级的状态管理方案,适合中小型应用或需要跨组件共享复杂状态的场景。它避免了 Redux 的繁琐配置,同时提供了清晰的状态更新逻辑。 1. 基本使用步骤 (1) 定义 Reducer 类似于 Redux 的 reduce…

3 个优质的终端 GitHub 开源工具

1、Oh My Zsh Oh My Zsh 是一个帮助你管理和美化 zsh 终端的开源工具。它让你的终端更炫酷、更高效。安装后,你可以快速使用各种插件和主题,比如常见的 git 命令简化、支持多种编程语言工具等,每次打开终端都会有惊喜。无论你是开发者还是普…

无人机巡检智能边缘计算终端技术方案‌‌——基于EFISH-SCB-RK3588工控机/SAIL-RK3588核心板的国产化替代方案‌

一、方案核心价值‌ ‌实时AI处理‌:6TOPS NPU实现无人机影像的实时缺陷检测(延迟<50ms)‌全国产化‌:芯片、操作系统、算法工具链100%自主可控‌极端环境适配‌:-40℃~85℃稳定运行,IP65防护等…

SpringAI 1.0.0 正式版——利用Redis存储会话(ChatMemory)

官方文档:Chat Memory :: Spring AI Reference 1. 引言 SpringAI 1.0.0 改动了很多地方,本文根据官方的InMemoryChatMemoryRepository实现了自定义的RedisChatMemoryRepository,并使用MessageWindowChatMemory创建ChatMemory 2. 实现 2.1.…

RFC8489-STUN

0. 学习参考 RFC5389 中文翻译 中文RFC RFC文档 RFC翻译 RFC中文版 RFC 5389:NAT 的会话遍历实用程序 (STUN) --- RFC 5389: Session Traversal Utilities for NAT (STUN) 1. RFC 3489的演变 自 RFC 3489 发布以来的经验发现,…

开始在本地部署自己的 Gitea 服务器

0.简介 在软件开发和团队协作中,代码管理是至关重要的环节。笔者一直使用gitblit管理自己的仓库。然鹅,这个软件已经很久没有更新了。经过多方考察,发现Gitea 是一款轻量级的开源代码托管平台,具有易于部署、资源占用少、功能丰富…

Xsens-AAA工作室品质,为动画师准备

每一帧都讲述着一个故事,当动作真实呈现时,故事便鲜活起来。我们打造并改进了 Xsens Animate,助力专业人士突破数字动画的界限。 通过升级后的 Xsens Animate,您可以获得女性和男性解剖模型以及更精确的运动引擎,从一…

嵌入(Embedding)技术的实现原理与应用场景解析

嵌入(Embedding)技术的实现原理与应用场景解析 引言:从One-Hot到语义空间 在自然语言处理的演进历程中,嵌入技术(Embedding)的诞生标志着一个重要转折点——它让离散的符号表示突破了维度诅咒&#xff0c…

金仓数据库征文-金仓KES数据同步优化实践:逻辑解码与增量同步

目录 一.同步场景与方案选型 二.什么是KES 三.同步环境配置 1.前置条件验证 2.逻辑解码配置 四.同步实施与问题排查 1.结构映射规则 2.增量数据捕获 3.数据一致性校验 五.性能调优实践 1.同步线程优化 2.批量提交优化 3.资源监控指标 六.典型场景解决方案 1.双向…

开源语义分割工具箱mmsegmentation基于Lovedata数据集训练模型

开源语义分割工具箱mmsegmentation安装环境 文章目录 1、下载数据集2、整理数据集3、下载预训练模型4、测试5、训练模型参考官方数据处理步骤 https://github.com/open-mmlab/mmsegmentation/blob/main/docs/zh_cn/user_guides/2_dataset_prepare.md#loveda 数据集类别标签:…

Python概率统计可视化——概率分布、假设检验与分子运动模型

Python概率统计可视化——概率分布、假设检验与分子运动模型 前言 概率统计作为描述不确定性和随机现象的数学工具,广泛应用于物理学、生物学、经济学等领域。然而,抽象的概率分布和统计推断过程往往难以直观理解。可视化技术通过将概率密度、假设检验逻…

NLP学习路线图(二十二): 循环神经网络(RNN)

在自然语言处理(NLP)的广阔天地中,序列数据是绝对的核心——无论是流淌的文本、连续的语音还是跳跃的时间序列,都蕴含着前后紧密关联的信息。传统神经网络如同面对一幅打散的拼图,无法理解词语间的顺序关系&#xff0c…