MCP
1、基础知识
协议介绍:modelcontextprotocol.io/docs/gettin...
MCP Host
:集成并使用一个或多个MCP Client
的AI应用MCP Client
:MCP Host
中的一个组件,可以与MCP Server
建立通信MCP Server
:提供特定能力的工具集合,AI应用可以通过标准协议访问MCP Server
提供的各种能力。
调用流程:
MCP Host
将用户的"自然语言"请求发送给MCP Client
MCP Client
传递用户的请求组装成 Prompt 并调用LLM
,然后获取需要执行的"指令(如,执行的工具+参数)"MCP Client
根据指令请求MCP Server
,并得到工具的执行结果MCP Client
得到结果后传递给 LLM,生成自然语言响应
MCP 协议支持两种主要的通信机制:基于标准输入输出(STDIO)
的本地通信 和基于 HTTP
的远程通信 (2025 年 3 月 26 日,MCP协议更新:用 Streamable HTTP
替代原先的 HTTP + SSE
)。
这两种机制都使用 JSON-RPC 2.0 格式进行消息传输,确保了通信的标准化和可扩展性。
- 本地通信 :通过
STDIO
传输数据,适用于在同一台机器上运行的客户端和服务器之间的通信。 - 远程通信 :利用
HTTP
,实现跨网络的实时数据传输,适用于需要访问远程资源或分布式部署的场景。
2、实战
2.1 写在前面:调试工具 MCP Inspector
MCP Inspector
是一个用于测试和调试 MCP Server 的交互式开发工具。
启动: mcp dev hello_mcp_server.py
2.2 以 Streamable HTTP
为例
MCP Server
python
# mcp_server.py
from mcp.server.fastmcp import FastMCP
# Initialize FastMCP server
mcp = FastMCP(name="mcp-server-demo"
, host="0.0.0.0"
, port=2222)
@mcp.tool(name="get_weather", description="获取指定城市的天气信息")
async def get_weather(city: str) -> str:
weather_data = {
"北京": "北京:晴,25°C",
"深圳": "深圳:多云,27°C"
}
return weather_data.get(city, f"{city}:无法获取天气信息")
@mcp.tool(name="suggest_activity", description="根据天气推荐适合的活动")
async def suggest_activity(condition: str) -> str:
if "晴" in condition:
return "天气晴朗,推荐去户外散步或运动。"
elif "多云" in condition:
return "天气多云,适合逛公园或咖啡馆。"
elif "雨" in condition:
return "天气下雨,建议在家阅读或看电影。"
else:
return "建议进行室内活动。"
# Static resource
@mcp.resource("config://version")
def get_version():
return "2.0.1"
# Dynamic resource template
@mcp.resource("users://{user_id}/profile")
def get_profile(user_id: int):
# Fetch profile for user_id...
return {"name": f"User {user_id}", "status": "active"}
def main():
print("启动 MCP Server(Streamable HTTP): http://localhost:2222/mcp")
mcp.run(transport="streamable-http")
if __name__ == "__main__":
print("[begin]run mcp server")
main()
print("[end]run mcp server")
MCP Client
python
# mcp_client.py
import asyncio
import os
from contextlib import AsyncExitStack
from datetime import timedelta
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
# [可选]避免因网络导致访问不通
os.environ["NO_PROXY"] = "localhost,127.0.0.1"
class WeatherMCPClient:
def __init__(self, server_url="http://localhost:2222/mcp"):
self.server_url = server_url
self._sse_context = None
self._session = None
self.exit_stack = AsyncExitStack()
async def __aenter__(self):
print(f"[begin]connect to {self.server_url}")
# 创建 transport
stream_transport = await self.exit_stack.enter_async_context(streamablehttp_client(
url=self.server_url,
timeout=timedelta(seconds=60),
))
# 创建 session
read_stream, write_stream, get_session_id = stream_transport
self._session = await self.exit_stack.enter_async_context(ClientSession(read_stream, write_stream))
# 初始化 connection
await self._session.initialize()
# 打印session_id
session_id = get_session_id()
if session_id:
print(f"Session ID: {session_id}")
print(f"[end]connect to {self.server_url}")
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.exit_stack:
print("\n清理资源并断开连接")
await self.exit_stack.aclose()
async def list_tools(self):
return await self._session.list_tools()
async def list_resources(self):
return await self._session.list_resources()
async def call_tool(self, name, arguments):
return await self._session.call_tool(name, arguments)
async def main():
async with WeatherMCPClient() as client:
print("连接 MCP Server")
print("\n可用工具:")
tools = await client.list_tools()
print(tools)
print("\n可用资源:")
resources = await client.list_resources()
print(resources)
print("\n调用工具(city=北京)...")
result = await client.call_tool("get_weather", {"city": "北京"})
print("\n工具返回:")
for item in result.content:
print(" -", item.text)
if __name__ == "__main__":
print("[begin]run mcp client")
asyncio.run(main())
print("[end]run mcp client")
实战
-
启动
MCP Server
python mcp_server.py
-
【可选】启动
MCP Inspector
,测试和调试MCP Server
。
- 启动
mcp dev mcp_server.py
- 配置Connect
Transport Type: Streamable HTTP
URL: http://localhost:2222/mcp
- 点击"Connect/Disconnect"
- 启动
MCP Client
python mcp_client.py
2.3 以 STDIO
为例
MCP Server
python
# mcp_server.py
from mcp.server.fastmcp import FastMCP
# Initialize FastMCP server
mcp = FastMCP(name="mcp-server-demo")
@mcp.tool(name="get_weather", description="获取指定城市的天气信息")
async def get_weather(city: str) -> str:
# 同上,略...
@mcp.tool(name="suggest_activity", description="根据天气推荐适合的活动")
async def suggest_activity(condition: str) -> str:
# 同上,略...
# Static resource
@mcp.resource("config://version")
def get_version():
# 同上,略...
# Dynamic resource template
@mcp.resource("users://{user_id}/profile")
def get_profile(user_id: int):
# 同上,略...
def main():
# 以标准 I/O 方式运行 MCP 服务器
print("启动 MCP Server(stdio)")
mcp.run(transport='stdio')
if __name__ == "__main__":
main()
MCP Client
python
# mcp_client.py
import asyncio
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
class WeatherMCPClient:
def __init__(self, server_url="mcp_server.py"):
# 略...
async def __aenter__(self):
print(f"[begin]connect to {self.server_url}")
# 准备参数
server_params = StdioServerParameters(
# 用于启动 MCP Server 的配置
command='python', args=[self.server_url], env=None
)
# 创建 transport
stdio_transport = await self.exit_stack.enter_async_context(
stdio_client(server_params)
)
# 创建 session
read_stream, write_stream = stdio_transport
self._session = await self.exit_stack.enter_async_context(ClientSession(read_stream, write_stream))
# 初始化 connection
await self._session.initialize()
print(f"[end]connect to {self.server_url}")
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.exit_stack:
print("\n清理资源并断开连接")
await self.exit_stack.aclose()
async def list_tools(self):
# 略...
async def list_resources(self):
# 略...
async def call_tool(self, name, arguments):
# 略...
async def main():
async with WeatherMCPClient() as client:
# 略...
if __name__ == "__main__":
print("[begin]run mcp client")
asyncio.run(main())
print("[end]run mcp client")
实战
启动 MCP Client
python mcp_client.py
MCP Client
启动后,会去拉起MCP Server
,所以无需手动启动MCP Server