当前位置: 首页 > web >正文

MCP协议解析:如何通过Model Context Protocol 实现高效的AI客户端与服务端交互

言简意赅的讲解MCP解决的痛点

在人工智能和机器学习的世界中,如何有效地连接客户端和服务器进行数据交换一直是个挑战。MCP(Model Context Protocol)应运而生,解决了传统方式中的很多问题,尤其是在数据权限获取和工具调用方面。

从最初的怀疑到现在的认可,MCP已经成为了越来越多人开发AI相关服务时的首选协议。它为客户端与服务端之间的交互提供了一种简单而高效的方式。今天,就让我们一起来深入了解MCP协议,以及如何使用它开发一个简单的天气服务。本文将用Claude 和python来完成展示。

MCP成功调用展示

MCP协议简介

MCP的核心目标是使AI客户端能够在没有直接访问底层数据的情况下,通过协议获取所需的数据或调用远程服务。这些服务被分为两类:资源工具

  1. 资源(Resource):这些是可以读取的数据,类似于REST API中的端点。
  2. 工具(Tool):这些是可以调用的功能,类似于远程过程调用(RPC)。

在MCP框架下,资源和工具之间有明确的区分。具体来说,资源用于提供数据,而工具则用于执行特定的操作。

MCP协议的调用流程

当客户端需要获取某些数据或调用某个工具时,它会通过MCP协议与服务器进行交互。这个过程可以分为几个步骤:

资源的调用流程:
  1. 列出可用资源:客户端首先会调用 list_resources,查询服务器提供的所有资源。
  2. 读取特定资源:在了解可用资源后,客户端可以选择一个资源,并通过 read_resource 请求特定资源的数据。例如,客户端可能请求 weather://London/current,获取伦敦当前的天气信息。
  3. 返回数据:服务器会处理请求,调用相应的函数(例如 fetch_weather),并将处理后的数据返回给客户端。
工具的调用流程:
  1. 列出可用工具:客户端通过 list_tools 查询服务器上可用的工具。
  2. 调用工具:客户端可以选择一个工具并通过 call_tool 请求执行某个操作。例如,调用天气工具来获取某个城市的天气预报。

MCP的设计哲学使得每个功能都可以模块化、独立,且易于扩展。这也意味着在服务器端,每个函数仅负责执行单一任务,提高了代码的可维护性。

MCP协议的URI设计

MCP使用统一资源标识符(URI)来唯一标识资源,类似于REST API的URL。一个典型的URI格式可能是:

weather://London/current

  • weather:// 是协议或方案部分,表明这是一个天气相关的资源。
  • London 是主机部分,指定了查询的城市。
  • /current 是路径部分,表示我们想要获取该城市的当前天气数据。

这种设计方式使得资源的层次结构清晰明了,支持多种不同的数据请求。例如,weather://Paris/forecast 可能代表巴黎的天气预报,weather://NewYork/current 则代表纽约的当前天气。

为什么MCP协议如此受欢迎?

  1. 资源唯一标识:通过URI,客户端可以清晰地指向服务器上的特定资源,减少了资源冲突的可能。
  2. 清晰的层次结构:不同的资源可以根据URI的不同路径进行区分,例如,当前天气、天气预报等。
  3. 与现有标准兼容:MCP的设计理念与现代互联网的标准做法高度一致,特别是在API和RESTful服务的设计上。
  4. 模块化设计:每个功能都被封装成独立的模块,提升了代码的可维护性和可扩展性。

尽管MCP协议在理论上非常优越,但在实际应用中仍然面临一些挑战。比如,当前支持MCP协议的客户端不多,Claude是目前支持得较好的客户端之一。此外,MCP协议目前只能在本地端调用,无法直接指向远程服务器。

如何使用MCP协议开发自己的天气服务?

如果你也想基于MCP协议开发自己的服务,比如一个简单的天气查询工具,下面是一些基本的步骤和配置。

1. 安装Python环境

首先,你需要确保安装了Python 3.10及以上版本。可以通过以下命令来验证:

python --version  # Should be 3.10 or higher
2. 安装必要的依赖

使用Homebrew安装 uv,然后通过 uvx 创建一个新的MCP项目。

brew install uv
uv --version  # Should be 0.4.18 or higher

接着,创建项目:

uvx create-mcp-server --path weather_service
cd weather_service

安装必要的Python依赖:

uv add httpx python-dotenv
3. 设置API密钥

在项目根目录下创建一个 .env 文件,加入你的API密钥:

WARNING:
本文将使用 OpenWeatherMap API API KEY

OPENWEATHER_API_KEY=your-api-key-here
4. 开发服务

weather_service/src/weather_service/server.py 文件中设置基本的导入和服务器配置。具体代码可以根据需求定制。

import os
import json
import logging
from datetime import datetime, timedelta
from collections.abc import Sequence
from functools import lru_cache
from typing import Anyimport httpx
import asyncio
from dotenv import load_dotenv
from mcp.server import Server
from mcp.types import (Resource,Tool,TextContent,ImageContent,EmbeddedResource,LoggingLevel
)
from pydantic import AnyUrl# Load environment variables
load_dotenv()# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("weather-server")# API configuration
API_KEY = os.getenv("OPENWEATHER_API_KEY")
if not API_KEY:raise ValueError("OPENWEATHER_API_KEY environment variable required")API_BASE_URL = "http://api.openweathermap.org/data/2.5"
DEFAULT_CITY = "London"
CURRENT_WEATHER_ENDPOINT = "weather"
FORECAST_ENDPOINT = "forecast"# The rest of our server implementation will go here
# Create reusable params
http_params = {"appid": API_KEY,"units": "metric"
}async def fetch_weather(city: str) -> dict[str, Any]:async with httpx.AsyncClient() as client:response = await client.get(f"{API_BASE_URL}/weather",params={"q": city, **http_params})response.raise_for_status()data = response.json()return {"temperature": data["main"]["temp"],"conditions": data["weather"][0]["description"],"humidity": data["main"]["humidity"],"wind_speed": data["wind"]["speed"],"timestamp": datetime.now().isoformat()}app = Server("weather-server")
app = Server("weather-server")@app.list_resources()
async def list_resources() -> list[Resource]:"""List available weather resources."""uri = AnyUrl(f"weather://{DEFAULT_CITY}/current")return [Resource(uri=uri,name=f"Current weather in {DEFAULT_CITY}",mimeType="application/json",description="Real-time weather data")]@app.read_resource()
async def read_resource(uri: AnyUrl) -> str:"""Read current weather data for a city."""city = DEFAULT_CITYif str(uri).startswith("weather://") and str(uri).endswith("/current"):city = str(uri).split("/")[-2]else:raise ValueError(f"Unknown resource: {uri}")try:weather_data = await fetch_weather(city)return json.dumps(weather_data, indent=2)except httpx.HTTPError as e:raise RuntimeError(f"Weather API error: {str(e)}")app = Server("weather-server")# Resource implementation ...@app.list_tools()
async def list_tools() -> list[Tool]:"""List available weather tools."""return [Tool(name="get_forecast",description="Get weather forecast for a city",inputSchema={"type": "object","properties": {"city": {"type": "string","description": "City name"},"days": {"type": "number","description": "Number of days (1-5)","minimum": 1,"maximum": 5}},"required": ["city"]})]@app.call_tool()
async def call_tool(name: str, arguments: Any) -> Sequence[TextContent | ImageContent | EmbeddedResource]:"""Handle tool calls for weather forecasts."""if name != "get_forecast":raise ValueError(f"Unknown tool: {name}")if not isinstance(arguments, dict) or "city" not in arguments:raise ValueError("Invalid forecast arguments")city = arguments["city"]days = min(int(arguments.get("days", 3)), 5)try:async with httpx.AsyncClient() as client:response = await client.get(f"{API_BASE_URL}/{FORECAST_ENDPOINT}",params={"q": city,"cnt": days * 8,  # API returns 3-hour intervals**http_params,})response.raise_for_status()data = response.json()forecasts = []for i in range(0, len(data["list"]), 8):day_data = data["list"][i]forecasts.append({"date": day_data["dt_txt"].split()[0],"temperature": day_data["main"]["temp"],"conditions": day_data["weather"][0]["description"]})return [TextContent(type="text",text=json.dumps(forecasts, indent=2))]except httpx.HTTPError as e:logger.error(f"Weather API error: {str(e)}")raise RuntimeError(f"Weather API error: {str(e)}")
async def main():# Import here to avoid issues with event loopsfrom mcp.server.stdio import stdio_serverasync with stdio_server() as (read_stream, write_stream):await app.run(read_stream,write_stream,app.create_initialization_options())

weather_service/src/weather_service/__init__.py 文件中设置基本的导入和服务器配置。具体代码可以根据需求定制。

from . import server
import asynciodef main():"""Main entry point for the package."""asyncio.run(server.main())# Optionally expose other important items at package level
__all__ = ['main', 'server']
5. 配置Claude

将以下配置添加到 claude_desktop_config.json 文件中:

{"mcpServers": {"weather": {"command": "uv","args": ["--directory","path/to/your/project","run","weather-service"],"env": {"OPENWEATHER_API_KEY": "your-api-key"}}}
}

Claude客户端配置

Claude客户端配置

然后,重启Claude,确保你的天气服务已在应用中生效。

结语

MCP协议无疑为开发者提供了一个更高效、更简洁的方式来构建客户端与服务端之间的交互。虽然当前的支持还有待加强,但它在未来的发展潜力巨大,尤其是在AI与大数据应用的快速增长下,MCP协议无疑是一个值得关注的工具。


通过上述内容,你就已经基本理解了这个方法,基础用法我也都有展示。如果你能融会贯通,我相信你会很强

Best
Wenhao (楠博万)

http://www.xdnf.cn/news/15993.html

相关文章:

  • C++STL之stack和queue
  • Valgrind Memcheck 全解析教程:6个程序说明基础内存错误
  • SpringBoot的介绍和项目搭建
  • 基于有监督学习的主动攻击检测系统
  • Vision Transformer (ViT) 介绍
  • 以“融合进化 智领未来”之名,金仓Kingbase FlySync:国产数据库技术的突破与创新
  • Redis 概率型数据结构实战指南
  • C++ STL中迭代器学习笔记
  • Docker实践:使用Docker部署WhoDB开源轻量级数据库管理工具
  • AI大模型学习路线-全技术栈
  • HTML和CSS快速入门
  • Spring之AOP面向切面编程详解
  • 试用SAP BTP 06:AI服务-Data Attribute Recommendation
  • 数据结构-线性表顺序表示
  • 基于单片机无线防丢/儿童防丢报警器
  • RabbitMQ:解锁高效消息传递的密码[特殊字符]
  • playwright 最佳实践
  • 【web安全】SQL注入与认证绕过
  • MPLS-LDP
  • 小红书 MCP 服务器
  • ADC和DMA简述
  • 渗透笔记(XSS跨站脚本攻击)
  • Linux之dpkg--命令的用法
  • 软件测试-Bug
  • 41.FeignClient整合Sentinel
  • 【C++】C++入门
  • 氛围编码(Vice Coding)的工具选择方式
  • [CVPR]DVFL-Net:用于时空动作识别的轻量级蒸馏视频调焦网络
  • 华为开源自研AI框架昇思MindSpore应用案例:基于ERNIE模型实现对话情绪识别
  • Spring 事务和事务传播机制