
人工智能MCP 服务MCP Clients【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址https://gitcode.com/gh_mirrors/pythonsd/python-sdk点击查看免费下载Client是 python-sdk 提供给 Python 程序与 MCP 服务器对话的高层入口一个对象、一个生命周期、一组类型化的async方法。本文以仓库文档 docs/client/index.md 为骨架带你从零搭建第一个 Streamable HTTP 客户端并逐一讲解工具调用、资源读取、提示词渲染、补全、分页与测试内使用同时结合 src/mcp/client/client.py 的源码佐证每个结论。读完你将能独立编写与任意 MCP 服务器交互的客户端程序。第一个客户端生命周期即一切客户端需要一个服务器来对话。本文所有代码片段连接的是同一个 Bookshop 服务器源码见 docs_src/client/tutorial001.py请保存为server.py并通过 HTTP 保持运行# 内容见 docs_src/client/tutorial001.py from pydantic import BaseModel from mcp.server import MCPServer from mcp.server.mcpserver.exceptions import ToolError from mcp.types import Completion, CompletionArgument, CompletionContext, PromptReference, ResourceTemplateReference mcp MCPServer(Bookshop, instructionsSearch the catalog before recommending a book.) GENRES [fiction, non-fiction, poetry] class Book(BaseModel): title: str author: str year: int mcp.tool(titleSearch the catalog) def search_books(query: str, limit: int 10) - str: Search the catalog by title or author. return fFound 3 books matching {query!r} (showing up to {limit}). mcp.tool() def lookup_book(title: str) - Book: Look up a book by its exact title. if title ! Dune: raise ToolError(fNo book titled {title!r} in the catalog.) return Book(titleDune, authorFrank Herbert, year1965) mcp.resource(catalog://genres) def genres() - list[str]: The genres the catalog is organised by. return GENRES mcp.resource(catalog://genres/{genre}) def books_in_genre(genre: str) - str: Every title we stock in one genre. return f3 books filed under {genre}. mcp.prompt(titleRecommend a book) def recommend(genre: str) - str: Ask for a recommendation in a genre. return fRecommend one {genre} book from the catalog and say why. mcp.completion() async def complete_genre( ref: PromptReference | ResourceTemplateReference, argument: CompletionArgument, context: CompletionContext | None, ) - Completion | None: return Completion(values[genre for genre in GENRES if genre.startswith(argument.value)])uv run mcp run server.py --transport streamable-http启动后服务器运行在http://localhost:8000/mcp。客户端是独立程序保存为client.py在第二个终端执行python client.py# 内容见 docs_src/client/tutorial001_client.py import anyio from mcp import Client async def main() - None: async with Client(http://localhost:8000/mcp) as client: print(client.server_info) print(client.server_capabilities) print(client.protocol_version) print(client.instructions) if __name__ __main__: anyio.run(main)三个关键点Client(http://localhost:8000/mcp)传入的是URL 字符串因此通过 Streamable HTTP 连接到你刚启动的服务器。async with就是生命周期进入时连接并完成协议协商退出时断开。没有connect()/close()配对方法块结束后Client不可复用。进入块之后连接信息已经以普通属性的形式就绪无需额外等待。从源码看Client的类文档src/mcp/client/client.py明确支持四类连接对象内部通过_Connector按类型分派到不同的传输构建路径。可以传给Client的四种东西Client只接收一个位置参数根据其类型决定传输方式传入对象传输方式适用场景URL 字符串如Client(http://localhost:8000/mcp)Streamable HTTP部署在服务器后面的正式连接StdioServerParameters以子进程方式启动命令通过其 stdin/stdout 对话本地进程通信任意传输对象Transport直接使用只要满足async with ... as (read, write)协议例如用你自己的 HTTP 客户端包装的streamable_http_client(url, http_client...)MCPServer或低层Server实例进程内连接无子进程、无端口测试场景docs/get-started/testing.md 正是基于此构建本文其余内容对四种方式完全一致。关于请求头、子进程参数、超时以及Transport协议本身的细节见 docs/client/transports.md。连接后客户端上的属性进入块的那一刻四个只读属性即被填充client.server_info服务器的身份标识。对于 2026 世代但不报告身份信息的服务器返回Nonepython-sdk 的服务器默认会报告。本例中server_info.name为Bookshopserver_info.version为服务器报告的值。client.server_capabilities服务器具备的能力tools、resources、prompts、completions等。服务器不具备的能力值为None。client.protocol_version双方协商一致的协议版本本例为2026-07-28。client.instructions服务器的instructions字符串未设置则为None。你从未手动选择过协议版本默认情况下Client会探测服务器对旧世代服务器回退到传统握手流程因此同一个客户端可以适配任何世代的服务器。需要手动控制时参见 docs/protocol-versions.md。源码中对应的是Client.mode参数auto/legacy/ 协议版本字符串默认auto见 src/mcp/client/client.py。提示client.session是底层ClientSessionsrc/mcp/client/session.py作为低层逃生通道存在本文内容用不到它。列出工具list_tools()# 内容见 docs_src/client/tutorial002.py import anyio from mcp import Client async def main() - None: async with Client(http://localhost:8000/mcp) as client: result await client.list_tools() for tool in result.tools: print(tool.name) print(tool.title) print(tool.description) print(tool.input_schema) if __name__ __main__: anyio.run(main)list_tools()返回ListToolsResult工具位于.tools中。每个工具都是主机host会直接交给模型的完整定义。第一个工具如下tool.name # search_books tool.title # Search the catalog tool.description # Search the catalog by title or author.而tool.input_schema是服务器从函数类型注解推导出的 JSON Schema{ type: object, properties: { query: {title: Query, type: string}, limit: {default: 10, title: Limit, type: integer} }, required: [query], title: search_booksArguments }这张 Schema 既包含 UI 渲染参数表单所需的全部信息也包含模型生成合法参数所需的全部信息。第二个工具lookup_book注册时未带title因此其tool.title为None。提示title是可选的向人类展示工具的 UI 需要自行抉择——有title用title没有就用name。from mcp.shared.metadata_utils import get_display_name恰好实现了这一逻辑并同时支持工具、资源、资源模板与提示词四种对象。调用工具call_tool()返回三样东西call_tool(name, arguments)执行工具并返回CallToolResult# 内容见 docs_src/client/tutorial003.py import anyio from mcp import Client from mcp.types import TextContent async def main() - None: async with Client(http://localhost:8000/mcp) as client: result await client.call_tool(lookup_book, {title: Dune}) for block in result.content: if isinstance(block, TextContent): print(block.text) print(result.structured_content) print(result.is_error) if __name__ __main__: anyio.run(main)服务器的lookup_book返回一个 PydanticBook。客户端看到的是result.content # [TextContent(typetext, text{\n title: Dune,\n author: Frank Herbert,\n year: 1965\n})] result.structured_content # {title: Dune, author: Frank Herbert, year: 1965} result.is_error # False一个返回值三样可读的东西各有各的消费方。从 src/mcp/client/client.py 的签名看call_tool还支持read_timeout_seconds、progress_callback、input_responses、request_state、meta等高级参数用于超时控制、进度回调和多轮输入请求InputRequiredResult的自动重试。content给模型读的content是内容块content block的list内容块是联合类型TextContent、ImageContent、AudioContent、ResourceLink或EmbeddedResource一个工具可以返回多个不同类型的块。这就是为什么main在触碰block.text之前先用isinstance(block, TextContent)收窄类型。注意isinstance之外没有.text——类型检查器不会允许因为ImageContent有的是.data而不是.text。这个联合类型如实反映了工具允许发送给你的内容你的代码也应如此。structured_content给应用代码读的structured_content是工具返回值对应的 JSON与工具声明的output_schema一致。无需字符串解析、无需猜测。当两者同时存在时它们是有意地重复说同一件事content给模型structured_content给代码。结构化那一半从何而来、如何控制见 docs/servers/structured-output.md。is_error工具是否失败抛出异常的工具不会在客户端抛出异常而是作为一个is_errorTrue的普通结果返回。验证向lookup_book查询Solaris目录中不存在的书名函数会抛出ToolError但调用仍然正常返回result.is_error # True result.content # [TextContent(typetext, textError executing tool lookup_book: No book titled Solaris in the catalog.)] result.structured_content # NoneToolError的消息落进了content那里模型可以读到并重试。这是有意设计工具错误是对话的一部分而不是崩溃。如果工具以其他异常崩溃content里只会写Error executing tool lookup_book。在信任structured_content之前务必先检查is_error。警告is_errorTrue覆盖的范围不止你自己写的raise。请求一个服务器上根本不存在的工具call_tool(does_not_exist, {})也不会抛出任何异常你会得到相同形状的结果——is_errorTruecontent中包含Unknown tool: does_not_exist。Client的方法只会在服务器以 JSON-RPC错误而非结果应答时才抛出MCPError。服务器何时返回哪种见 docs/servers/handling-errors.md。资源两种列举 一种读取资源的动词成对出现列举有两种读取有一种。# 内容见 docs_src/client/tutorial004.py import anyio from mcp import Client from mcp.types import TextResourceContents async def main() - None: async with Client(http://localhost:8000/mcp) as client: listed await client.list_resources() print([resource.uri for resource in listed.resources]) templates await client.list_resource_templates() print([template.uri_template for template in templates.resource_templates]) result await client.read_resource(catalog://genres/poetry) for contents in result.contents: if isinstance(contents, TextResourceContents): print(contents.text) if __name__ __main__: anyio.run(main)list_resources()返回具体资源即 URI 固定的那些。本例为[catalog://genres]。list_resource_templates()返回参数化资源。本例为[catalog://genres/{genre}]。两者是两个独立列表因为模板在填入值之前不可读。read_resource(uri)接收普通的strURI对两者都适用传入catalog://genres/poetry服务器会把它匹配到模板上。read_resource返回contents是TextResourceContents或BlobResourceContents的列表。思路与工具内容一致先用isinstance收窄再读.text或.blob。客户端还可以在资源变更时收到通知。在 2025 世代的连接上是subscribe_resource(uri)/unsubscribe_resource(uri)——这对方法MCPServer并未实现因此在 2026-07-28 协议上这些动词已不存在请求会得到-32601Method not found。2026 年的替代方案是subscriptions/listen流MCPServer确实提供它此时server_capabilities.resources.subscribe为True用client.listen(...)消费它的方法见 docs/client/subscriptions.md。提示词list_prompts()与get_prompt()# 内容见 docs_src/client/tutorial005.py import anyio from mcp import Client async def main() - None: async with Client(http://localhost:8000/mcp) as client: listed await client.list_prompts() print(listed.prompts) result await client.get_prompt(recommend, {genre: poetry}) for message in result.messages: print(message.role, message.content) if __name__ __main__: anyio.run(main)list_prompts()告诉你服务器提供什么、每个提示词需要什么参数prompt.name # recommend prompt.title # Recommend a book prompt.arguments # [PromptArgument(namegenre, requiredTrue)]get_prompt(name, arguments)负责渲染。参数 dict 是str - str类型提示词参数永远是字符串。结果是messages即PromptMessage列表每个都有role和content块message.role # user message.content # TextContent(typetext, textRecommend one poetry book from the catalog and say why.)主机把这些消息原样交给模型这个功能到此为止。补全complete()带补全处理器的服务器可以在用户输入时为提示词参数和资源模板参数提供自动补全。# 内容见 docs_src/client/tutorial006.py import anyio from mcp import Client from mcp.types import PromptReference async def main() - None: async with Client(http://localhost:8000/mcp) as client: result await client.complete( refPromptReference(typeref/prompt, namerecommend), argument{name: genre, value: p}, ) print(result.completion.values) if __name__ __main__: anyio.run(main)ref指明你在补全哪个提示词或模板PromptReference或ResourceTemplateReference。argument是{name: ..., value: ...}参数名加上用户目前已输入的内容。答案在result.completion.values中。输入p服务器返回[poetry]。服务器端的实现以及处理器如何利用其他已填充参数缩小建议范围见 docs/servers/completions.md。分页cursor与next_cursor所有list_*方法都接受cursor关键字参数所有结果都带有next_cursor。当next_cursor为None时说明已取完。# 内容见 docs_src/client/tutorial007.py import anyio from mcp import Client from mcp.types import Tool async def list_all_tools(client: Client) - list[Tool]: tools: list[Tool] [] cursor: str | None None while True: page await client.list_tools(cursorcursor) tools.extend(page.tools) if page.next_cursor is None: return tools cursor page.next_cursor async def main() - None: async with Client(http://localhost:8000/mcp) as client: tools await list_all_tools(client) print([tool.name for tool in tools]) if __name__ __main__: anyio.run(main)list_all_tools对任何服务器都是正确的MCPServer一次性返回全部内容next_cursor为None循环只执行一次——这正是大多数代码从不写这个循环的原因。真正分页的服务器以及游标遵循的规则见 docs/advanced/pagination.md。测试中使用进程内连接本文所有的client.py都是通过 HTTP 访问server.py的。测试时跳过网络直接把服务器对象交给Clientfrom server import mcp然后Client(mcp)。无需进程、无需端口上面所有方法的行为完全一致。为此准备的构造参数只有一个Client(mcp, raise_exceptionsTrue)。它只在进程内连接时生效其作用与围绕它构建的整套测试模式见 docs/get-started/testing.md。该参数在 src/mcp/client/client.py 中有明确定义与文档。总结Client(x)对 URL 字符串走 Streamable HTTP对StdioServerParameters启动子进程对传输对象直接进入测试时接收服务器对象本身。async with即完整生命周期。块内server_capabilities与protocol_version已填充服务器提供时server_info与instructions亦然。list_tools()给出每个工具的name、title、description和input_schema。call_tool()返回给模型的content、给代码的structured_content以及is_error。抛出异常的工具返回的是结果不是异常。content是块类型的联合读取前用isinstance收窄。list_resources/list_resource_templates/read_resource、list_prompts/get_prompt、complete构成完整的动词集合。所有list_*都接受cursor循环直到next_cursor为None。服务器能反过来向客户端请求什么、以及你如何响应见 docs/client/callbacks.md。赞分享人工智能MCP 服务MCP Clients【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址https://gitcode.com/gh_mirrors/pythonsd/python-sdk点击查看免费下载相关推荐MCP Python SDK 客户端回调Client Callbacks完全指南用 Callback 应答服务端发起的请求MCP Python SDK 客户端回调Client Callbacks完全指南用 Callback 应答服务端发起的请求 导读 在 Model Cont人工智能MCP 服务MCP ClientsMCP Python SDK 客户端回调Client Callbacks完全指南应答服务器的反向请求MCP Python SDK 客户端回调Client Callbacks完全指南应答服务器的反向请求 导读 在 Model Context Protoco人工智能MCP 服务MCP ClientsMCP 客户端回调Client Callbacks实战指南用 Client(...) 应答服务端发起的请求MCP 客户端回调Client Callbacks实战指南用 Client ... 应答服务端发起的请求 在 MCPModel Context Prot人工智能MCP 服务MCP Clients上一篇从入门到实战2025深度学习全栈教程基于dl_tutorials项目下一篇Heimdallr配置完全指南从环境变量到多渠道通知分组创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考