在上一章里,你已经掌握了基础:你构建了第一个 MCP 服务器、定义了工具,并学会用本地(STDIO)与远程(HTTP)两种传输方式与之通信。你还结识了你的新挚友——MCP Inspector。
现在,是时候把你的成果接入真实世界了。一个独立运行的 MCP 服务器很强大,但当它**连接到大型语言模型(LLM)**时,它的真正潜力才会被解锁。本章将让你的自定义 Python 代码变成当今最先进 AI 的一项新技能。
我们将把你的本地开发机与 Anthropic、Google、OpenAI 的云端服务连接起来。你会学到让每个平台识别你的 MCP 服务器所需的具体客户端代码,等同于给它们加上“新超能力”。
在本章结束时,你将能够:
- 使用 ngrok 将本地 MCP 服务器暴露到互联网。
- 将你的服务器集成到 Anthropic 的 Claude、Google 的 Gemini,以及 OpenAI 的 ChatGPT。
- 理解 有状态(stateful) 与 无状态(stateless) MCP 服务器的关键区别及其重要性。
让我们给你的 AI 装上工具。
创建一个“通用工具”
在集成任何平台之前,我们需要先准备一个可供它们调用的工具。我们将创建一个简单但非常实用的 MCP 服务器。
LLM 在执行精确、确定性任务(比如统计字符串字符数)方面并不擅长——这正是工具发挥作用的理想场景。我们将构建一个 count_characters 服务器,供三个 LLM 共同使用。
为保持简洁,我们继续使用 fastmcp。新建文件 mcp_server_count_characters.py。
图 27. mcp_server_count_characters.py
from fastmcp import FastMCP
# 1. 使用描述性名称初始化 MCP 服务器。
mcp = FastMCP("Characters Counting MCP Server")
# 2. 定义工具。LLM 不擅长,但 Python 很擅长!
@mcp.tool
def count_characters(string: str) -> int:
"""Counts the number of characters in the given string."""
return len(string)
# 3. 让脚本通过 HTTP 运行。
if __name__ == "__main__":
mcp.run(transport="http", port=9000)
这个服务器与第 2 章的远程服务器几乎相同:它只定义了一个工具 count_characters,并以 HTTP 服务的形式运行在 9000 端口。
现在运行该服务器。接下来整章都要保持这个终端窗口开启。
图 28. 运行 MCP 服务器
> python .\mcp_server_count_characters.py
你会看到熟悉的启动横幅,表明服务器正在 http://127.0.0.1:9000 上运行。
从 localhost 到全世界:使用 ngrok
问题来了:你的服务器跑在 localhost 上,这是一个只有你的电脑能识别的私有地址。而 Anthropic、Google、OpenAI 的 LLM 都在云端数据中心,它们无法直接访问你的机器。
我们需要给它们一个公网地址。在开发阶段,最简便的方法是使用 ngrok。它会从互联网上的公共 URL 建立一条安全隧道,直通你本地机器上的某个端口。
首先,从官方下载并安装 ngrok:ngrok.com/downloads。
安装后,你需要把它与自己的账号连接,以获得一个固定域名(非常推荐用于开发)。在你的 ngrok 控制台按步骤完成设置:dashboard.ngrok.com/get-started…。
配置完成后,打开一个新的终端窗口(第一扇窗口里的 MCP 服务器要继续保持运行)。启动 ngrok,把你的 9000 端口暴露到公网。
图 29. 运行 ngrok
> ngrok http http://127.0.0.1:9000 --domain=your-ngrok-endpoint
注:将
your-ngrok-endpoint替换成你在 ngrok 控制台中配置的静态域名。如果你没有静态域名,也可以省略--domain参数,ngrok 会在每次启动时分配一个随机临时 URL。但要注意:每次启动都得在客户端代码里更新这个 URL。
ngrok 会显示一块含有连接信息的屏幕。其中最重要的是公网的 Forwarding URL,类似:
https://unique-name-1234.ngrok-free.app
这就是你的公共 MCP 服务器地址。发往这个 URL 的任何请求都会被安全地转发到你本地 9000 端口上运行的 Python 脚本。
集成 Anthropic(Claude)
先来接入 Anthropic 的 Claude。我们写一个 Python 脚本,让 Claude 使用我们的新工具。
首先你需要一个 API Key,可在开发者控制台获取:console.anthropic.com/。
然后在虚拟环境中安装 Anthropic 的 Python 库:
> uv add anthropic
新建文件 anthropic_interact_with_mcp.py。
图 30. anthropic_interact_with_mcp.py
import os
import anthropic
# 1. 从环境变量获取公共 MCP 服务器 URL。
url = os.environ.get('MCP_SERVER_URL')
input_string = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
"""
prompt = f"Count how many characters are in this string: {input_string}"
# 2. 初始化 Anthropic 客户端。
client = anthropic.Anthropic()
# 3. 发起 API 调用,并传入 MCP 服务器信息。
response = client.beta.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=2000,
messages=[{"role": "user", "content": prompt}],
mcp_servers=[
{
"type": "url",
"url": url,
"name": "characters-counting-server",
}
],
extra_headers={
"anthropic-beta": "mcp-client-2025-04-04"
}
)
# 4. 打印结构化响应。
print(response.content)
代码解读
-
服务器 URL:我们从环境变量中读取 MCP 服务器的公网地址。这是良好实践,可将敏感或可变信息移出源码。
-
客户端初始化:
anthropic库的常规初始化。它会自动从ANTHROPIC_API_KEY环境变量中读取 API Key。 -
API 调用的关键点:
mcp_servers:告诉 Claude 你有哪些 MCP 服务器可用。我们提供服务器的url和一个name,Claude 会以此引用工具。extra_headers:MCP 集成目前在 Anthropic 侧仍处于 beta,需要该特殊 Header 才能启用。
-
打印响应:API 返回的并非简单字符串,而是一个结构化对象,包含完整对话过程(包括工具调用)。
运行 Claude 客户端
运行脚本前,需要设置两个环境变量。Windows(PowerShell)与 Bash(Linux/macOS)命令略有差异。
设置 Anthropic API Key:
# Bash
$ export ANTHROPIC_API_KEY="your-api-key"
# PowerShell
> $Env:ANTHROPIC_API_KEY = "your-api-key"
设置公共 MCP 服务器 URL(**注意在 ngrok URL 末尾加上 /mcp/!**这是 fastmcp 暴露的端点):
# Bash
$ export MCP_SERVER_URL="https://your-ngrok-endpoint/mcp/"
# PowerShell
> $Env:MCP_SERVER_URL = "https://your-ngrok-endpoint/mcp/"
现在运行脚本:
图 31. 运行 anthropic_interact_with_mcp.py
> python .\anthropic_interact_with_mcp.py
[BetaTextBlock(citations=None, text="I'll count the characters in that string for you.", type='text'), BetaMCPToolUseBlock(id='mcptoolu_01LJTmnPaMf6GTg5UFKAmHFX', input={'string': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'}, name='count_characters', server_name='characters-counting-server', type='mcp_tool_use'), BetaMCPToolResultBlock(content=[BetaTextBlock(citations=None, text='123', type='text')], is_error=False, tool_use_id='mcptoolu_01LJTmnPaMf6GTg5UFKAmHFX', type='mcp_tool_result'), BetaTextBlock(citations=None, text='The string "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." contains **123 characters**.', type='text')]
仔细看输出,它是一系列对话轮次的列表:
- 先是一个 BetaTextBlock,Claude 确认了你的请求;
- 接着是关键的 BetaMCPToolUseBlock,表明 Claude 调用了你在
characters-counting-server上的count_characters工具; - 然后是 BetaMCPToolResultBlock,其中包含你的本地服务器返回的结果(
123); - 最后是另一个 BetaTextBlock,Claude 用该结果生成自然语言答案。
你已经成功给 Claude 增添了一项新技能!
集成 Google(Gemini)
接下来把同一个服务器接入 Google 的 Gemini。流程类似:获取 API Key、编写客户端脚本并运行。
在 Google AI Studio 获取 Gemini API Key:aistudio.google.com/app/apikey
安装 Google Generative AI 库:
> uv add google-genai
创建文件 google_gemini_interact_with_mcp.py。
图 32. google_gemini_interact_with_mcp.py
import os
import asyncio
from fastmcp import Client
from google import genai
# 1. 获取公共 MCP 服务器 URL。
url = os.environ.get('MCP_SERVER_URL')
input_string = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
"""
prompt = f"Count how many characters are in this string: {input_string}"
# 2. 创建 Gemini 客户端对象。
gemini_client = genai.Client()
# 3. 创建一个 MCP 客户端实例。
mcp_client = Client(url)
async def main():
# 4. 连接 MCP 客户端。
async with mcp_client:
# 5. 发起 API 调用,并把 MCP 会话作为工具传入。
response = await gemini_client.aio.models.generate_content(
model="gemini-2.0-flash",
contents=f"Count how many characters are in this string: {input_string}",
config=genai.types.GenerateContentConfig(
temperature=0,
tools=[mcp_client.session],
),
)
print(response)
if __name__ == "__main__":
asyncio.run(main())
代码解读
- 服务器 URL:依旧从环境变量中读取 ngrok 公网地址。
- 初始化 Gemini 客户端:通过
genai.Client()创建。 - 创建 MCP 客户端:与 Anthropic 示例不同,这里我们使用 fastmcp 的
Client,指向服务器的公网 URL。 - 连接 MCP 客户端:
async with mcp_client:建立与服务器的会话连接。 - API 调用的关键点:
tools=[mcp_client.session]。这里把活动的 MCP 连接会话对象作为工具传给 Gemini 的 SDK;SDK 能识别它,并据此发现并调用你服务器上的工具。
运行 Gemini 客户端
设置 Gemini API Key:
# Bash
$ export GEMINI_API_KEY="your-api-key"
# PowerShell
> $Env:GEMINI_API_KEY = "your-api-key"
MCP_SERVER_URL 环境变量在之前示例中已设置。现在运行脚本:
图 33. 运行 google_gemini_interact_with_mcp.py
> python .\google_gemini_interact_with_mcp.py
输出会比较长,但滚动查看时你会看到两个非常重要的片段:
模型侧的函数调用(function_call):
...
Content(
parts=[
Part(
function_call=FunctionCall(
args={
'string': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
},
name='count_characters'
)
),
],
role='model'
)
...
这表明 Gemini 已决定调用你的 count_characters 工具。
工具侧的函数响应(function_response):
...
Content(
parts=[
Part(
function_response=FunctionResponse(
name='count_characters',
response={
'result': CallToolResult(
content=[
<... Max depth ...>,
],
isError=False,
structuredContent={
<... Max depth ...>: <... Max depth ...>
}
)
}
)
),
],
role='user'
)] parsed=None
这表示来自你 MCP 服务器 的响应已经被传回到对话中。最后,模型会使用这些信息给出最终答案。
与 OpenAI(ChatGPT)的集成
最后,让我们把最受欢迎的平台 —— OpenAI 的 ChatGPT —— 接上。正如你所猜的那样,这个流程从获取 API Key 与编写客户端脚本开始。
在 OpenAI 平台获取你的密钥:platform.openai.com/api-keys。
然后安装库:
> uv add openai
创建文件 openai_interact_with_mcp.py。
图 34. openai_interact_with_mcp.py
import os
from openai import OpenAI
client = OpenAI()
url = os.environ.get('MCP_SERVER_URL')
input_string = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
"""
prompt = f"Count how many characters are in this string: {input_string}"
resp = client.responses.create(
model="gpt-4.1",
tools=[
{
"type": "mcp",
"server_label": "characters_counting_server",
"server_url": url,
"require_approval": "never",
},
],
input=prompt,
)
print(resp)
这个脚本看起来很直接:它定义了一个 type: "mcp" 的工具,并提供了服务器的 URL。
但是,如果你用当前的服务器直接运行它,LLM 会调用失败。为什么?
这就引出了 MCP 服务器设计中的一个关键概念。
有状态 vs. 无状态:两类服务器的故事
到目前为止,你运行的 MCP 服务器是有状态(stateful)的。
有状态服务器就像一通持续的电话:当客户端连接时,服务器会建立一个持久会话并跟踪这个特定客户端的状态。后续来自同一客户端的请求会被路由到同一会话中,使得持续对话的通信快速而高效。这是 fastmcp 的默认行为。
而**无状态(stateless)**服务器更像是一连串独立的短信:每个请求都被当作全新的交互。服务器为该请求创建一个全新的传输通道,处理、响应,然后立刻“忘记”一切 —— 没有会话,也没有持久连接。
OpenAI 的客户端实现要求使用无状态的 MCP 服务器。
它不会维护我们默认服务器所期望的会话状态。要让 OpenAI 客户端正常工作,我们需要以无状态模式重启服务器。
先停止正在运行的服务器(Ctrl+C)。
现在创建一个新的服务器文件 mcp_server_count_characters_stateless.py。改动非常小,却至关重要。
图 35. mcp_server_count_characters_stateless.py(fastmcp 版本)
from fastmcp import FastMCP
# 唯一的变化是加上 stateless_http=True
mcp = FastMCP("Characters Counting MCP Server", stateless_http=True)
@mcp.tool
def count_characters(string: str) -> int:
"""Counts the number of characters in the given string."""
return len(string)
if __name__ == "__main__":
mcp.run(transport="http", port=9000)
唯一的区别就是在 FastMCP 构造函数里传入 stateless_http=True。这个开关告诉服务器:把每个请求都当作独立事务处理,不跟踪会话。
在用 mcp 库吗?
如果你使用更底层的 mcp 库,改法也相同。
图 36. mcp_server_count_characters_stateless.py(mcp 版本)
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My MCP Server", host="127.0.0.1", port=9000, stateless_http=True)
@mcp.tool()
def count_characters(string: str) -> int:
return len(string)
if __name__ == "__main__":
mcp.run(transport="streamable-http")
现在运行新的无状态服务器。你的 ngrok 隧道会自动重新连接到它。
图 37. 运行无状态 MCP 服务器
> python .\mcp_server_count_characters_stateless.py
运行 OpenAI 客户端
在正确类型的服务器已经运行后,执行 OpenAI 脚本。先设置你的 API Key:
# Bash
$ export OPENAI_API_KEY="your-api-key"
# PowerShell
> $Env:OPENAI_API_KEY = "your-api-key"
现在运行客户端:
图 38. 运行 OpenAI 客户端代码
> python .\openai_interact_with_mcp.py
这一次就能正常工作了!输出中会包含一个 McpCall 对象,清楚显示对你的 MCP 工具的调用,例如:
...
McpCall(
id='mcp_68723aba473c819082c3861b7253292e024b1966e79d99f0',
arguments='{"string":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."}',
name='count_characters',
server_label='characters_counting_server',
type='mcp_call',
error=None,
output='123',
approval_request_id=None
)
在随后的对话流程(由 OpenAI 库为你处理)中,你会看到工具结果被用于生成最终答案:
“你提供的字符串包含 123 个字符。”
关键要点(Key Takeaways)
本章是一次重大飞跃:你已经从本地试验,迈向让自定义的 Python 代码与全球最强大的 AI 模型对接。
- 公网可达至关重要:要让云端 LLM 使用你的工具,MCP 服务器必须有公网 URL;在开发阶段,ngrok 是极佳的解决方案。
- 平台特定的集成方式:不同 LLM 平台告知 MCP 服务器的方式略有差异——从 Anthropic 的
mcp_servers参数、到向 Gemini 传入mcp_client.session,再到 OpenAI 的tools对象。 - 有状态 vs. 无状态:这是关键的架构决策。有状态服务器是默认选项,适合能维持会话的客户端;无状态服务器则适用于把每次请求都当作一次性事务的客户端。选对模式是成功集成的关键。
你现在已经名副其实地成为一名 AI“工具匠” :几乎可以为任何任务编写一个 Python 函数,并把它作为 Claude、Gemini 或 ChatGPT 的新能力对外提供。
在下一章,我们将解锁更丰富、更动态的交互层次,探索把一个简单工具升级为具备上下文感知的 LLM 合作伙伴所需的核心组件。你将学会如何:
- 通过 Resources 与 Prompts 提供上下文;
- 在长时任务中报告进度并发送日志消息;
- 使用 Elicitation 将单向命令转变为双向对话。
你已经学会给 AI 一件工具;接下来,你将学会给它整间工具工坊。