西瓜视频手机版
81.45MB · 2025-11-06
要消费(使用)一个 MCP 服务器,你需要某种形式的客户端。例如,你可以使用 Claude Desktop 或 Visual Studio Code(VS Code) 这样的应用,它们能够消费 MCP 服务器、完成特性发现并加以使用。也有一些场景你会需要自己编写客户端——典型例子是把 AI 能力内建到你的应用中。想象一下,你有一个电商应用,想要一个 AI 增强的搜索:MCP 服务器可以是一个独立应用,而客户端则内置在电商应用里。
基于此,我们来探讨如何构建客户端,以及其中涉及到的内容。
在本章中,你将学会:
本章涵盖以下主题:
那么,构建客户端要做哪些事?从高层来看,需要:
理解了这些步骤后,接下来通过一个可跟打的练习来实现它。
在本练习中,你将构建一个连接到服务器并消费其特性的客户端。你将使用 SDK 来构建客户端并调用服务器。该客户端是一个命令行应用:允许你选择特性、提供参数,然后调用服务器并显示结果。
先编写用于与服务器建立连接的客户端代码:
from mcp import ClientSession, StdioServerParameters, types
from mcp.client.stdio import stdio_client
# Create server parameters for stdio connection
server_params = StdioServerParameters(
command="mcp", # Executable
args=["run", "server.py"], # Optional command line arguments
env=None, # Optional environment variables
)
async def run():
async with stdio_client(server_params) as (read, write):
async with ClientSession(
read, write
) as session:
# Initialize the connection
await session.initialize()
# list features
if __name__ == "__main__":
import asyncio
asyncio.run(run())
以上我们完成了:
StdioServerParameters 对象,指定如何启动服务器及可选命令行参数——因为服务器与客户端会同时运行,需要告诉客户端如何拉起服务器;run 函数,创建 ClientSession 并初始化与服务器的连接。稍后我们会在 run 中加入列举和调用特性的代码。现在为客户端增加列出服务器特性的代码。不同类型的特性略有差异,示例如下:
# List available resources
resources = await session.list_resources()
print("LISTING RESOURCES")
for resource in resources:
print("Resource: ", resource)
# List available tools
tools = await session.list_tools()
print("LISTING TOOLS")
for tool in tools.tools:
print("Tool: ", tool.name)
这里我们:
下面用一个已列出的工具作为示例进行调用。这里使用上一章创建的 add 工具。add 接收两个参数 a 与 b,返回它们的和。设想用户从列表里选中了某个工具,我们再提示用户输入参数并进行调用:
# Read information from the first tool
tool_name = tools.tools[0].name
print(f"Using tool: {tool_name}")
first_value = input("Enter first value: ")
second_value = input("Enter second value: ")
# Call a tool
print("CALL TOOL")
result = await session.call_tool(tool_name, arguments={
"a": first_value, "b": second_value})
print(result.content)
以上我们:
call_tool 调用该工具,并把参数以字典形式传入,随后打印返回结果。到这里,我们已经有了一个能连接服务器、列出特性并调用工具的客户端。不过它仍然偏“编程式”,用户体验不算友好。
在整合 LLM 之前,先看完整客户端代码:
from mcp import ClientSession, StdioServerParameters, types
from mcp.client.stdio import stdio_client
# Create server parameters for stdio connection
server_params = StdioServerParameters(
command="mcp", # Executable
args=["run", "server.py"], # Optional command line arguments
env=None, # Optional environment variables
)
async def run():
async with stdio_client(server_params) as (read, write):
async with ClientSession(
read, write
) as session:
# Initialize the connection
await session.initialize()
# List available resources
resources = await session.list_resources()
print("LISTING RESOURCES")
for resource in resources:
print("Resource: ", resource)
# List available tools
tools = await session.list_tools()
print("LISTING TOOLS")
for tool in tools.tools:
print("Tool: ", tool.name)
# Read information from the first tool
tool_name = tools.tools[0].name
print(f"Using tool: {tool_name}")
first_value = input("Enter first value: ")
second_value = input("Enter second value: ")
# Call a tool
print("CALL TOOL")
result = await session.call_tool(tool_name, arguments={"a": first_value, "b": second_value})
print(result.content)
# Read a resource
print("READING RESOURCE")
content, mime_type = await session.read_resource("greeting://hello")
if __name__ == "__main__":
import asyncio
asyncio.run(run())
如果你跟着代码一起敲,现在就应当有一个可用客户端:它能连接到服务器并消费其特性。章节末尾的作业会给你更多练习机会。
接下来我们通过整合 LLM来改进客户端,你将看到这如何带来更佳的用户体验,并帮助抽象服务器使用的复杂度。
到目前为止,你已经看到了如何用 STDIO 和 SSE 构建并测试客户端。不过你可能也注意到,这种方式相当“编程味”而且对用户不够友好——也就是说,每次想用某个能力,都需要知道特性的精确名称及其参数。这正是 LLM 派上用场的地方:把 LLM 引入客户端后,你可以把“知道细节”的负担抽象掉,把注意力放在“完成任务”上。下面看看它如何工作。
如果不用 LLM,应用的构建与流程通常是:
这种方式比较僵硬,要求用户必须明确知道并从列表中显式选择某个特性。那么,有没有更好的方式?
为了解决客户端“僵硬”的问题,设想用户并不了解这些特性——他们只通过自然语言提示进行交互。此时应用的流程变为:
这种方式在用户体验上差异巨大:用户不必再了解或手动选择特性,只需输入自然语言请求,剩下的由 LLM 来“决定”。
接下来看看实操方式。
目前有很多 AI 提供商可供调用 LLM。本书使用 GitHub Models(免费),只要你有 GitHub 账号即可。要使用 GitHub Models,你需要在 GitHub Codespaces 中启动项目,或配置一个具有相应权限的 个人访问令牌(PAT) 。之所以需要令牌,是因为你要调用 API,令牌会作为 Bearer Token 用于身份验证。若使用本地模型(如 Ollama),则不需要令牌。虽然可以在源码里直接写令牌,但出于安全,建议放在环境变量中。
如果你从未使用过 AI,需要知道的核心概念是:向模型发送一个提示(prompt) ,得到一个回复(response) 。提示是描述你希望 LLM 执行何事的自然语言文本;回复也是自然语言文本,包含对该提示的答案。
把 LLM 与 MCP 结合使用的思路是:让 LLM 指示在给定提示下应调用哪个函数。例如,当提示是 “Add 1 and 2” 时,如果 MCP 服务器中存在名为 add 且参数为 a、b 的函数,那么 LLM 应该给出“调用 add(a=1, b=2)”的指示。
下面是调用代码示例。我们将调用一个 GitHub Models 的模型,所以请确保你有 GitHub 账号并创建了具有正确权限的 PAT,或在 Codespaces 中运行。
需要明确以下内容:
gpt-4o)add 工具)当然,你也可以只发 prompt、收一段文本答案;但这里我们希望 LLM 指出要调用的函数及其参数,所以会把函数定义也一并发给模型。函数定义是一个 JSON 对象,描述函数名、说明与参数(参数按 JSON Schema 定义)。
# json description of functions
functions = [
{
"type": "function",
"function": {
"name": "add",
"description": "Add two numbers",
"type": "function",
"parameters": {
"type": "object",
"properties": {
"a": {
"type": "number",
"description": "The first number to add"
},
"b": {
"type": "number",
"description": "The second number to add"
}
},
"required": ["a", "b"]
}
}
}
]
# get token from environment variable
token = os.environ["GITHUB_TOKEN"]
# the endpoint for GitHub Models
endpoint = "https://models.github.ai/inference"
# the model to use
model_name = "gpt-4o"
# creation of chat client
client = OpenAI(
base_url=endpoint,
api_key=token
)
print("CALLING LLM")
response = client.chat.completions.create(
messages=[
{
"role": "system",
"content": "You are a helpful assistant.",
},
{
"role": "user",
"content": prompt,
},
],
model=model_name,
tools = functions,
# Optional parameters
temperature=1.,
max_tokens=1000,
top_p=1.
)
response_message = response.choices[0].message
print("LLM RESPONSE: ", response_message)
另外,除了 prompt,你还可以给 LLM 传一些配置项,如 temperature、max_tokens、top_p 等。这里不展开说明(可参考 OpenAI 文档);简单来说,它们会影响回复的随机性/创造性以及所谓“上下文窗口”的大小。
下面看看如何把 LLM 集成到客户端中。目标是显著提升用户体验,并抽象掉使用服务器的复杂性。为此我们需要完成这些步骤:
开干!
如果你是在消费他人的 MCP 服务器,这可能是你做的第一件事。在继续之前,确保已安装 MCP SDK。
第一步与之前相同:列出服务器可用特性。通过列出工具实现:
tools = await session.list_tools()
print("LISTING TOOLS")
for tool in tools.tools:
print("Tool: ", tool.name)
下一步很关键:把特性列表转换为 LLM 能理解的格式。这样我们才能让 LLM 决定应该用哪个特性。转换代码如下:
先添加转换函数:
def to_llm_tool(tool):
tool_schema = {
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"type": "function",
"parameters": {
"type": "object",
"properties": tool.inputSchema["properties"]
}
}
}
return tool_schema
该函数接收一个 tool,并将其转换成 LLM 可理解的格式。
调用转换过程:
functions = []
for tool in tools.tools:
print("Tool: ", tool.name)
print("Tool", tool.inputSchema["properties"])
functions.append(to_llm_tool(tool))
可以看到,我们遍历工具列表,对每个工具调用转换函数。
现在一切就绪,接下来要管理用户输入并向 LLM 发送补全请求。LLM 的响应会告诉我们要调用的函数名及其参数(这里的函数即服务器上的某个特性)。
我们需要做两件事:
调用 LLM:
def call_llm(prompt, functions):
token = os.environ["GITHUB_TOKEN"]
endpoint = "https://models.github.ai/inference"
model_name = "gpt-4o"
client = OpenAI(
base_url=endpoint,
api_key=token,
)
print("CALLING LLM")
response = client.complete(
messages=[
{
"role": "system",
"content": "You are a helpful assistant.",
},
{
"role": "user",
"content": prompt,
},
],
model=model_name,
tools = functions,
# Optional parameters
temperature=1.,
max_tokens=1000,
top_p=1.
)
response_message = response.choices[0].message
print("LLM RESPONSE: ", response_message)
functions_to_call = []
上述我们:
prompt 与 functions,并返回 LLM 响应;complete 方法调用 LLM,传入提示与函数集合,并打印响应。检查 LLM 是否返回函数调用:
if response_message.tool_calls:
for tool_call in response_message.tool_calls:
print("TOOL: ", tool_call)
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
functions_to_call.append({ "name": name, "args": args })
return functions_to_call
这里我们:
response_message.tool_calls 判断是否存在函数调用;tool_calls,打印工具名与参数;此时我们拿到了 LLM 的响应(可能包含函数调用)。下一步就是按需调用 MCP 服务器:
functions_to_call = call_llm(prompt, functions)
# call suggested functions
for f in functions_to_call:
result = await session.call_tool(f["name"], arguments=f["args"])
print("TOOLS result: ", result.content)
我们做了:
call_tool),并打印结果。很不错吧?用户现在可以直接用自然语言与服务器交互了,你也因此极大简化了用户的心智负担。
本章展示了如何构建能够连接 MCP 服务器并消费其特性的客户端。我们先从能列出并调用特性的简单客户端开始,然后通过集成 LLM显著提升体验:用户以自然语言表达意图,LLM 负责决定调用哪个特性及其参数,从而隐藏底层复杂度。
下一章,我们将介绍如何在 VS Code 与 Claude Desktop 中消费 MCP 服务器。