https://modelcontextprotocol.io/quickstart/server
面向服务器开发者
开始构建你自己的服务器,以便在 Claude for Desktop 及其他客户端中使用。
在本教程中,我们将构建一个简单的 MCP 天气服务器,并将其连接到主机 Claude for Desktop。我们将从基础设置开始,然后逐步扩展到更复杂的用例。
我们将要构建什么
许多大语言模型(LLM)目前无法获取天气预报和严重天气警报。让我们用 MCP 来解决这个问题!
我们将构建一个服务器,暴露两个工具:get-alerts
和 get-forecast
。然后我们将把服务器连接到一个 MCP 主机(本例中为 Claude for Desktop):
为什么选择 Claude for Desktop 而不是 Claude.ai? 因为服务器是本地运行的,MCP 目前只支持桌面主机。远程主机正在积极开发中。注意: 服务器可以连接到任何客户端。这里我们选择 Claude for Desktop 只是为了简单起见,我们也有构建你自己的客户端的指南,以及其他客户端列表。
MCP 核心概念
MCP 服务器可以提供三种主要能力:
1. 资源(Resources):类似文件的数据,可以被客户端读取(如 API 响应或文件内容)
2. 工具(Tools):可以被 LLM 调用的函数(需用户批准)
3. 提示(Prompts):帮助用户完成特定任务的预设模板
本教程主要关注工具(Tools)。
Python
让我们开始构建天气服务器!完整代码可在此处查看。
前置知识
本快速入门假设你熟悉:
- Python
- 类似 Claude 的大语言模型
系统要求
- 已安装 Python 3.10 或更高版本
- 必须使用 Python MCP SDK 1.2.0 或更高版本
环境搭建
首先,安装 uv
并设置 Python 项目和环境:
# MacOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
安装完成后,重启终端以确保 uv
命令可用。
现在,创建并设置项目:
# MacOS/Linux
uv init weather
cd weather
# 创建虚拟环境并激活
uv venv
source .venv/bin/activate
# 安装依赖
uv add "mcp[cli]" httpx
# 创建服务器文件
touch weather.py
# Windows
uv init weather
cd weather
uv venv
.venv\Scripts\activate
uv add mcp[cli] httpx
new-item weather.py
构建你的服务器
导入包并初始化实例
在 weather.py
顶部添加如下内容:
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
# 初始化 FastMCP 服务器
mcp = FastMCP("weather")
# 常量
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
FastMCP 类利用 Python 类型提示和文档字符串自动生成工具定义,便于创建和维护 MCP 工具。
辅助函数
添加用于从美国国家气象局 API 查询和格式化数据的辅助函数:
async def make_nws_request(url: str) -> dict[str, Any] | None:
"""向 NWS API 发起请求并进行错误处理。"""
headers = {
"User-Agent": USER_AGENT,
"Accept": "application/geo+json"
}
async with httpx.AsyncClient() as client:
try:
response = await client.get(url, headers=headers, timeout=30.0)
response.raise_for_status()
return response.json()
except Exception:
return None
def format_alert(feature: dict) -> str:
"""将警报信息格式化为可读字符串。"""
props = feature["properties"]
return f"""
Event: {props.get('event', 'Unknown')}
Area: {props.get('areaDesc', 'Unknown')}
Severity: {props.get('severity', 'Unknown')}
Description: {props.get('description', 'No description available')}
Instructions: {props.get('instruction', 'No specific instructions provided')}
"""
工具执行实现
工具执行处理器负责实际执行每个工具的逻辑。添加如下内容:
@mcp.tool()
async def get_alerts(state: str) -> str:
"""获取美国某州的天气警报。
参数:
state: 两位美国州代码(如 CA, NY)
"""
url = f"{NWS_API_BASE}/alerts/active/area/{state}"
data = await make_nws_request(url)
if not data or "features" not in data:
return "无法获取警报或未找到警报。"
if not data["features"]:
return "该州暂无活跃警报。"
alerts = [format_alert(feature) for feature in data["features"]]
return "\n---\n".join(alerts)
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
"""获取某地的天气预报。
参数:
latitude: 地点纬度
longitude: 地点经度
"""
# 首先获取预报网格端点
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)
if not points_data:
return "无法获取该地点的预报数据。"
# 从 points 响应中获取预报 URL
forecast_url = points_data["properties"]["forecast"]
forecast_data = await make_nws_request(forecast_url)
if not forecast_data:
return "无法获取详细预报。"
# 格式化前 5 个时段的预报
periods = forecast_data["properties"]["periods"]
forecasts = []
for period in periods[:5]:
forecast = f"""
{period['name']}:
Temperature: {period['temperature']}°{period['temperatureUnit']}
Wind: {period['windSpeed']} {period['windDirection']}
Forecast: {period['detailedForecast']}
"""
forecasts.append(forecast)
return "\n---\n".join(forecasts)
运行服务器
最后,初始化并运行服务器:
if __name__ == "__main__":
# 初始化并运行服务器
mcp.run(transport='stdio')
你的服务器已完成!运行 uv run weather.py
以确认一切正常。
完整代码(补充)
weather.py
####################################
# 导入所需的库和模块
####################################
from typing import Any # 导入Any类型,用于类型提示
import httpx # 导入httpx库,这是一个现代的异步HTTP客户端
from mcp.server.fastmcp import FastMCP # 导入FastMCP,这是一个工具创建框架
####################################
# 初始化 FastMCP 服务器
####################################
# 创建一个名为"weather"的FastMCP实例,用于注册和管理工具
mcp = FastMCP("weather")
####################################
# 定义常量
####################################
# 美国国家气象局API的基础URL
NWS_API_BASE = "https://api.weather.gov"
# 用户代理字符串,告诉API服务器是谁在请求数据
USER_AGENT = "weather-app/1.0"
####################################
# 工具函数
####################################
async def make_nws_request(url: str) -> dict[str, Any] | None:
"""向美国国家气象局(NWS) API发起请求并进行错误处理。
参数:
url: 完整的API请求URL
返回:
成功时返回JSON响应转换成的字典,失败时返回None
"""
# 设置HTTP请求头
headers = {
"User-Agent": USER_AGENT, # 用户代理,标识应用程序
"Accept": "application/geo+json" # 指定接受的响应格式为地理JSON
}
# 创建一个异步HTTP客户端会话
async with httpx.AsyncClient() as client:
try:
# 发送GET请求,设置超时时间为30秒
response = await client.get(url, headers=headers, timeout=30.0)
# 如果状态码不是200系列,则抛出异常
response.raise_for_status()
# 将响应内容解析为JSON并返回
return response.json()
except Exception:
# 如果发生任何错误(网络错误、超时、解析错误等),返回None
return None
def format_alert(feature: dict) -> str:
"""将单个天气警报信息格式化为可读的字符串。
参数:
feature: API返回的单个警报特征字典
返回:
格式化后的警报信息字符串
"""
# 从feature中获取properties字典,这里包含了警报的详细信息
props = feature["properties"]
# 使用f-string格式化警报信息
# props.get('key', 'default')方法在key不存在时返回默认值
return f"""
Event: {props.get('event', 'Unknown')} # 警报事件类型,如暴风雨、洪水等
Area: {props.get('areaDesc', 'Unknown')} # 受影响的地理区域描述
Severity: {props.get('severity', 'Unknown')} # 严重程度,如Extreme、Severe等
Description: {props.get('description', 'No description available')} # 详细描述
Instructions: {props.get('instruction', 'No specific instructions provided')} # 官方建议和指导
"""
####################################
# API工具函数
####################################
@mcp.tool() # 使用装饰器将函数注册为FastMCP工具
async def get_alerts(state: str) -> str:
"""获取美国某州的活跃天气警报。
这个函数会连接到美国国家气象局API,获取指定州的所有活跃警报,
并将它们格式化为易读的文本。
参数:
state: 两位美国州代码(如 CA表示加利福尼亚州, NY表示纽约州)
返回:
格式化后的警报信息字符串,或者错误消息
"""
# 构建完整的API URL,用于获取特定州的活跃警报
url = f"{NWS_API_BASE}/alerts/active/area/{state}"
# 调用之前定义的函数发送API请求
data = await make_nws_request(url)
# 检查是否成功获取数据,以及数据格式是否正确
if not data or "features" not in data:
return "无法获取警报或未找到警报。"
# 检查是否有活跃警报
if not data["features"]:
return "该州暂无活跃警报。"
# 使用列表推导式将每个警报特征格式化为字符串
alerts = [format_alert(feature) for feature in data["features"]]
# 用分隔符连接所有警报字符串,并返回
return "\n---\n".join(alerts)
@mcp.tool() # 使用装饰器将函数注册为FastMCP工具
async def get_forecast(latitude: float, longitude: float) -> str:
"""获取指定地理坐标的天气预报。
这个函数会先查询坐标对应的预报网格端点,然后获取详细预报,
最后返回格式化的未来5个时段的天气预报。
参数:
latitude: 地点纬度坐标
longitude: 地点经度坐标
返回:
格式化后的天气预报信息,或者错误消息
"""
# 第一步:获取预报网格端点
# NWS API需要先查询坐标对应的元数据,才能获取预报URL
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)
# 检查是否成功获取元数据
if not points_data:
return "无法获取该地点的预报数据。"
# 第二步:从元数据响应中提取预报URL
# NWS API的响应中包含了用于获取预报的具体URL
forecast_url = points_data["properties"]["forecast"]
# 使用提取的URL获取详细预报
forecast_data = await make_nws_request(forecast_url)
# 检查是否成功获取预报
if not forecast_data:
return "无法获取详细预报。"
# 第三步:从预报数据中提取需要的信息并格式化
# 获取时段列表,每个时段代表一个预报周期(如今天白天、今晚等)
periods = forecast_data["properties"]["periods"]
# 创建一个空列表用于存储格式化后的预报
forecasts = []
# 只处理前5个时段的预报
for period in periods[:5]:
# 使用f-string格式化每个时段的预报
forecast = f"""
{period['name']}: # 时段名称,如"今天"、"今晚"、"星期二"等
Temperature: {period['temperature']}°{period['temperatureUnit']} # 温度和单位
Wind: {period['windSpeed']} {period['windDirection']} # 风速和风向
Forecast: {period['detailedForecast']} # 详细天气预报描述
"""
# 将格式化后的预报添加到列表中
forecasts.append(forecast)
# 用分隔符连接所有预报,并返回
return "\n---\n".join(forecasts)
####################################
# 主函数
####################################
if __name__ == "__main__":
# 当直接运行此脚本时,初始化并启动FastMCP服务器
# transport='stdio'表示使用标准输入/输出作为通信方式
mcp.run(transport='stdio')
代码功能解释
这是一个天气服务API封装,使用Python编写,主要功能如下:
1. 代码结构:
- 使用FastMCP框架创建了一个名为"weather"的服务
- 定义了两个主要工具函数:
get_alerts
和get_forecast
- 包含辅助函数用于HTTP请求和格式化输出
2. 主要功能:
- 连接美国国家气象局(NWS)的API获取天气数据
get_alerts
函数:获取美国特定州的天气警报信息get_forecast
函数:根据经纬度获取特定地点的天气预报
3. 技术特点:
- 使用异步HTTP请求(httpx库)提高性能
- 使用类型注解确保代码类型安全
- 包含错误处理机制,确保API请求失败时能优雅处理
总结
这个代码是一个简单的天气信息服务封装,可以集成到更大的应用程序中,为用户提供天气警报和预报功能。
使用 Claude for Desktop 测试服务器
注意: Claude for Desktop 目前不支持 Linux。Linux 用户可前往构建客户端教程,构建可连接到我们刚刚搭建的服务器的 MCP 客户端。
首先,确保你已安装 Claude for Desktop。点击此处下载安装最新版。 如果已安装,请确保已更新到最新版。
你需要为想要使用的 MCP 服务器配置 Claude for Desktop。打开 ~/Library/Application Support/Claude/claude_desktop_config.json
(MacOS/Linux)或 $env:AppData\Claude\claude_desktop_config.json
(Windows)进行编辑。如果文件不存在请新建。
例如,若你已安装 VS Code:
# MacOS/Linux
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
# Windows
code $env:AppData\Claude\claude_desktop_config.json
在 mcpServers
键下添加你的服务器。只有至少配置了一个服务器,Claude for Desktop 的 MCP UI 元素才会显示。
以天气服务器为例,配置如下:
(MacOS/Linux)
{
"mcpServers": {
"weather": {
"command": "uv",
"args": [
"--directory",
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
"run",
"weather.py"
]
}
}
}
(Windows)
{
"mcpServers": {
"weather": {
"command": "uv",
"args": [
"--directory",
"C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather",
"run",
"weather.py"
]
}
}
}
警告: 你可能需要在
command
字段中填写uv
可执行文件的完整路径。可通过which uv
(MacOS/Linux)或where uv
(Windows)获取。
注意: 路径必须为绝对路径。
这告诉 Claude for Desktop:
- 有一个名为 “weather” 的 MCP 服务器
- 通过运行
uv --directory /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather run weather.py
启动它
保存文件并重启 Claude for Desktop。
在Cursor里配置(补充)
配置参数
{
"mcpServers": {
"weather": {
"command": "uv",
"args": [
"--directory",
"C:\\Users\\Arnold\\Desktop\\test\\my-learning-test\\20250515_mcpTest_weather\\weather",
"run",
"weather.py"
]
}
}
}
注意:--directory
需要传入一个目录(weather
),uv会自动检测weather
目录中的虚拟环境,并且激活后,执行run
参数值指定的脚本(weather.py
)
检查mcp是否生效
按钮显示为绿色表示已生效。
提问和结果
注意:查看代码,该mcp会请求https://api.weather.gov/
,如果自己电脑无法访问这个链接,那么mcp也请求不到,请先排查自己电脑情况!
Parameters
{
"latitude": 34.0522,
"longitude": -118.2437
}
Result
Tonight:
Temperature: 57°F
Wind: 0 to 10 mph SSE
Forecast: Mostly clear, with a low around 57. South southeast wind 0 to 10 mph.
---
Tuesday:
Temperature: 87°F
Wind: 0 to 10 mph SSW
Forecast: Sunny, with a high near 87. South southwest wind 0 to 10 mph.
---
Tuesday Night:
Temperature: 65°F
Wind: 0 to 10 mph S
Forecast: Mostly clear, with a low around 65. South wind 0 to 10 mph.
---
Wednesday:
Temperature: 89°F
Wind: 0 to 10 mph S
Forecast: Sunny, with a high near 89. South wind 0 to 10 mph.
---
Wednesday Night:
Temperature: 65°F
Wind: 0 to 10 mph SSE
Forecast: Mostly clear, with a low around 65. South southeast wind 0 to 10 mph.
Node
让我们开始构建天气服务器!完整代码可在此处查看。
前置知识
本快速入门假设你熟悉:
- TypeScript
- 类似 Claude 的大语言模型
系统要求
- 请确保已安装最新版 Node
环境搭建
首先,安装 Node.js 和 npm(如未安装)。可从 nodejs.org 下载。验证安装:
node --version
npm --version
本教程需 Node.js 16 或更高版本。
创建并设置项目:
# MacOS/Linux
mkdir weather
cd weather
# 初始化 npm 项目
npm init -y
# 安装依赖
npm install @modelcontextprotocol/sdk zod
npm install -D @types/node typescript
# 创建文件
mkdir src
touch src/index.ts
# Windows
md weather
cd weather
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D @types/node typescript
md src
new-item src\index.ts
更新 package.json
,添加 type: “module” 和 build 脚本:
{
"type": "module",
"bin": {
"weather": "./build/index.js"
},
"scripts": {
"build": "tsc && chmod 755 build/index.js"
},
"files": [
"build"
]
}
在项目根目录创建 tsconfig.json
:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
构建你的服务器
导入包并初始化实例
在 src/index.ts
顶部添加如下内容:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// NWS API 基础地址
const NWS_API_BASE = "https://api.weather.gov";
// 用户代理字符串
const USER_AGENT = "weather-app/1.0";
// 创建 MCP 服务器实例,指定名称和版本
const server = new McpServer({
name: "weather",
version: "1.0.0",
capabilities: {
resources: {},
tools: {},
},
});
辅助函数
添加用于从美国国家气象局 API 查询和格式化数据的辅助函数:
// NWS API 请求辅助函数,返回泛型类型数据或 null
async function makeNWSRequest<T>(url: string): Promise<T | null> {
const headers = {
"User-Agent": USER_AGENT, // 设置用户代理
Accept: "application/geo+json", // 设置接受的内容类型
};
try {
// 发送 fetch 请求
const response = await fetch(url, { headers });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 返回 JSON 数据
return (await response.json()) as T;
} catch (error) {
// 捕获异常,输出错误日志
console.error("Error making NWS request:", error);
return null;
}
}
// 警报特征接口定义
interface AlertFeature {
properties: {
event?: string;
areaDesc?: string;
severity?: string;
status?: string;
headline?: string;
};
}
// 格式化警报数据为字符串
function formatAlert(feature: AlertFeature): string {
const props = feature.properties;
return [
`Event: ${props.event || "Unknown"}`,
`Area: ${props.areaDesc || "Unknown"}`,
`Severity: ${props.severity || "Unknown"}`,
`Status: ${props.status || "Unknown"}`,
`Headline: ${props.headline || "No headline"}`,
"---",
].join("\n");
}
// 预报时段接口定义
interface ForecastPeriod {
name?: string;
temperature?: number;
temperatureUnit?: string;
windSpeed?: string;
windDirection?: string;
shortForecast?: string;
}
// 警报响应接口
interface AlertsResponse {
features: AlertFeature[];
}
// 网格点响应接口
interface PointsResponse {
properties: {
forecast?: string;
};
}
// 预报响应接口
interface ForecastResponse {
properties: {
periods: ForecastPeriod[];
};
}
工具执行实现
注册天气工具:
// 注册天气警报工具
server.tool(
"get-alerts",
"获取某州的天气警报",
{
state: z.string().length(2).describe("两位州代码(如 CA, NY)"),
},
async ({ state }) => {
const stateCode = state.toUpperCase();
const alertsUrl = `${NWS_API_BASE}/alerts?area=${stateCode}`;
const alertsData = await makeNWSRequest<AlertsResponse>(alertsUrl);
if (!alertsData) {
return {
content: [
{
type: "text",
text: "获取警报数据失败",
},
],
};
}
const features = alertsData.features || [];
if (features.length === 0) {
return {
content: [
{
type: "text",
text: `该州暂无活跃警报:${stateCode}`,
},
],
};
}
// 格式化所有警报
const formattedAlerts = features.map(formatAlert);
const alertsText = `该州活跃警报:${stateCode}\n\n${formattedAlerts.join("\n")}`;
return {
content: [
{
type: "text",
text: alertsText,
},
],
};
},
);
// 注册天气预报工具
server.tool(
"get-forecast",
"获取某地的天气预报",
{
latitude: z.number().min(-90).max(90).describe("地点纬度"),
longitude: z.number().min(-180).max(180).describe("地点经度"),
},
async ({ latitude, longitude }) => {
// 获取网格点数据
const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`;
const pointsData = await makeNWSRequest<PointsResponse>(pointsUrl);
if (!pointsData) {
return {
content: [
{
type: "text",
text: `获取坐标网格点数据失败:${latitude}, ${longitude}。该地点可能不被 NWS API 支持(仅支持美国地区)。`,
},
],
};
}
const forecastUrl = pointsData.properties?.forecast;
if (!forecastUrl) {
return {
content: [
{
type: "text",
text: "无法从网格点数据获取预报 URL",
},
],
};
}
// 获取预报数据
const forecastData = await makeNWSRequest<ForecastResponse>(forecastUrl);
if (!forecastData) {
return {
content: [
{
type: "text",
text: "获取预报数据失败",
},
],
};
}
const periods = forecastData.properties?.periods || [];
if (periods.length === 0) {
return {
content: [
{
type: "text",
text: "无可用预报时段",
},
],
};
}
// 格式化所有预报时段
const formattedForecast = periods.map((period: ForecastPeriod) =>
[
`${period.name || "Unknown"}:`,
`Temperature: ${period.temperature || "Unknown"}°${period.temperatureUnit || "F"}`,
`Wind: ${period.windSpeed || "Unknown"} ${period.windDirection || ""}`,
`${period.shortForecast || "No forecast available"}`,
"---",
].join("\n"),
);
const forecastText = `该地预报:${latitude}, ${longitude}\n\n${formattedForecast.join("\n")}`;
return {
content: [
{
type: "text",
text: forecastText,
},
],
};
},
);
运行服务器
实现主函数运行服务器:
// 主函数,启动 MCP 服务器
async function main() {
const transport = new StdioServerTransport(); // 使用标准输入输出作为传输
await server.connect(transport); // 连接服务器
console.error("Weather MCP Server running on stdio");
}
main().catch((error) => {
// 捕获主函数异常并输出
console.error("Fatal error in main():", error);
process.exit(1);
});
务必运行 npm run build
构建服务器!
使用 Claude for Desktop 测试服务器
配置方式与 Python 版一致,只需将 command
改为 node
,args
填写绝对路径的 build/index.js
。
Java
注意: 这是基于 Spring AI MCP 自动配置和启动器的快速入门演示。手动创建同步和异步 MCP 服务器请参考 Java SDK Server 文档。
完整代码见 此处。
系统要求
- 已安装 Java 17 或更高版本
- Spring Boot 3.3.x 或更高
环境搭建
使用 Spring Initializer 启动项目。
添加如下依赖:
<!-- Maven -->
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
// Gradle
dependencies {
implementation platform("org.springframework.ai:spring-ai-starter-mcp-server")
implementation platform("org.springframework:spring-web")
}
配置 application.properties 或 application.yml:
spring.main.bannerMode=off
logging.pattern.console=
logging:
pattern:
console:
spring:
main:
banner-mode: off
构建你的服务器
WeatherService.java
实现 WeatherService:
@Service
public class WeatherService {
// REST 客户端,用于请求天气 API
private final RestClient restClient;
public WeatherService() {
// 初始化 REST 客户端,设置基础地址和请求头
this.restClient = RestClient.builder()
.baseUrl("https://api.weather.gov")
.defaultHeader("Accept", "application/geo+json")
.defaultHeader("User-Agent", "WeatherApiClient/1.0 (your@email.com)")
.build();
}
@Tool(description = "获取指定经纬度的天气预报")
public String getWeatherForecastByLocation(
double latitude, // 纬度
double longitude // 经度
) {
// 返回详细预报,包括温度、风速、风向、详细描述
// 这里应实现具体的 API 调用和数据格式化逻辑
return "";
}
@Tool(description = "获取美国某州的天气警报")
public String getAlerts(
@ToolParam(description = "两位美国州代码(如 CA, NY)") String state
) {
// 返回活跃警报,包括事件类型、影响区域、严重程度、描述、安全指引
// 这里应实现具体的 API 调用和数据格式化逻辑
return "";
}
// 其他方法...
}
创建 Boot 应用
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
// 启动 Spring Boot 应用
SpringApplication.run(McpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
// 注册 WeatherService 中的工具方法
return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}
}
运行服务器
./mvnw clean install
生成的 jar 文件在 target
目录下。
使用 Claude for Desktop 测试服务器
配置方式与 Python 版一致,只需将 command
改为 java
,args
填写绝对路径的 jar 文件。
Kotlin
完整代码见 此处。
前置知识
- Kotlin
- 类似 Claude 的大语言模型
系统要求
- 已安装 Java 17 或更高版本
环境搭建
安装 java
和 gradle
,可从 Oracle JDK 官网 下载。验证安装:
java --version
创建并设置项目:
# MacOS/Linux
mkdir weather
cd weather
gradle init
# Windows
md weather
cd weather
gradle init
选择 Application 类型,Kotlin 语言,Java 17 版本。
添加依赖:
// build.gradle.kts
val mcpVersion = "0.4.0"
val slf4jVersion = "2.0.9"
val ktorVersion = "3.1.1"
dependencies {
implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion")
implementation("org.slf4j:slf4j-nop:$slf4jVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
}
添加插件:
plugins {
kotlin("plugin.serialization") version "your_version_of_kotlin"
id("com.github.johnrengelman.shadow") version "8.1.1"
}
构建你的服务器
实例初始化
// 运行 MCP 服务器主函数
fun `run mcp server`() {
// 创建 MCP 服务器实例
val server = Server(
Implementation(
name = "weather", // 工具名
version = "1.0.0"
),
ServerOptions(
capabilities = ServerCapabilities(tools = ServerCapabilities.Tools(listChanged = true))
)
)
// 使用标准 IO 创建传输
val transport = StdioServerTransport(
System.`in`.asInput(),
System.out.asSink().buffered()
)
runBlocking {
server.connect(transport) // 连接服务器
val done = Job()
server.onClose {
done.complete() // 关闭时完成 job
}
done.join()
}
}
天气 API 辅助函数
// 获取指定经纬度天气预报
suspend fun HttpClient.getForecast(latitude: Double, longitude: Double): List<String> {
// 获取网格点信息
val points = this.get("/points/$latitude,$longitude").body<Points>()
// 获取预报信息
val forecast = this.get(points.properties.forecast).body<Forecast>()
// 格式化所有时段
return forecast.properties.periods.map { period ->
"""
${period.name}:
Temperature: ${period.temperature} ${period.temperatureUnit}
Wind: ${period.windSpeed} ${period.windDirection}
Forecast: ${period.detailedForecast}
""".trimIndent()
}
}
// 获取指定州天气警报
suspend fun HttpClient.getAlerts(state: String): List<String> {
// 获取警报信息
val alerts = this.get("/alerts/active/area/$state").body<Alert>()
// 格式化所有警报
return alerts.features.map { feature ->
"""
Event: ${feature.properties.event}
Area: ${feature.properties.areaDesc}
Severity: ${feature.properties.severity}
Description: ${feature.properties.description}
Instruction: ${feature.properties.instruction}
""".trimIndent()
}
}
工具执行实现
// 创建 HTTP 客户端
val httpClient = HttpClient {
defaultRequest {
url("https://api.weather.gov")
headers {
append("Accept", "application/geo+json")
append("User-Agent", "WeatherApiClient/1.0")
}
contentType(ContentType.Application.Json)
}
install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
}
// 注册获取警报工具
server.addTool(
name = "get_alerts",
description = "获取美国某州天气警报。输入为两位州代码(如 CA, NY)",
inputSchema = Tool.Input(
properties = buildJsonObject {
putJsonObject("state") {
put("type", "string")
put("description", "两位美国州代码(如 CA, NY)")
}
},
required = listOf("state")
)
) { request ->
val state = request.arguments["state"]?.jsonPrimitive?.content
if (state == null) {
return@addTool CallToolResult(
content = listOf(TextContent("必须提供 'state' 参数。"))
)
}
val alerts = httpClient.getAlerts(state)
CallToolResult(content = alerts.map { TextContent(it) })
}
// 注册获取天气预报工具
server.addTool(
name = "get_forecast",
description = "获取指定经纬度的天气预报",
inputSchema = Tool.Input(
properties = buildJsonObject {
putJsonObject("latitude") { put("type", "number") }
putJsonObject("longitude") { put("type", "number") }
},
required = listOf("latitude", "longitude")
)
) { request ->
val latitude = request.arguments["latitude"]?.jsonPrimitive?.doubleOrNull
val longitude = request.arguments["longitude"]?.jsonPrimitive?.doubleOrNull
if (latitude == null || longitude == null) {
return@addTool CallToolResult(
content = listOf(TextContent("必须提供 'latitude' 和 'longitude' 参数。"))
)
}
val forecast = httpClient.getForecast(latitude, longitude)
CallToolResult(content = forecast.map { TextContent(it) })
}
运行服务器
fun main() = `run mcp server`()
使用 Claude for Desktop 测试服务器
配置方式与 Python 版一致,只需将 command
改为 java
,args
填写绝对路径的 jar 文件。
C#
完整代码见 此处。
前置知识
- C#
- 类似 Claude 的大语言模型
- .NET 8 或更高
系统要求
- .NET 8 SDK 或更高
环境搭建
安装 dotnet
,验证:
dotnet --version
创建并设置项目:
# MacOS/Linux
mkdir weather
cd weather
dotnet new console
# Windows
mkdir weather
cd weather
dotnet new console
添加 NuGet 包:
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.Hosting
构建你的服务器
打开 Program.cs
,替换为:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol;
using System.Net.Http.Headers;
// 创建主机构建器,避免多余输出
var builder = Host.CreateEmptyApplicationBuilder(settings: null);
// 注册 MCP 服务器及传输方式
builder.Services.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
// 注册 HttpClient 单例,设置基础地址和用户代理
builder.Services.AddSingleton(_ =>
{
var client = new HttpClient() { BaseAddress = new Uri("https://api.weather.gov") };
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("weather-tool", "1.0"));
return client;
});
var app = builder.Build();
// 启动应用
await app.RunAsync();
注意: 创建
ApplicationHostBuilder
时请用CreateEmptyApplicationBuilder
,以避免服务器向控制台输出额外信息(仅 STDIO 传输时需要)。
天气 API 工具类
using ModelContextProtocol.Server;
using System.ComponentModel;
using System.Net.Http.Json;
using System.Text.Json;
namespace QuickstartWeatherServer.Tools;
// 标记为 MCP 工具类型
[McpServerToolType]
public static class WeatherTools
{
// 获取美国某州天气警报
[McpServerTool, Description("获取美国某州天气警报。")]
public static async Task<string> GetAlerts(
HttpClient client,
[Description("要获取警报的美国州。")] string state)
{
// 请求警报数据
var jsonElement = await client.GetFromJsonAsync<JsonElement>($"/alerts/active/area/{state}");
var alerts = jsonElement.GetProperty("features").EnumerateArray();
if (!alerts.Any())
{
return "该州暂无活跃警报。";
}
// 格式化所有警报
return string.Join("\n--\n", alerts.Select(alert =>
{
JsonElement properties = alert.GetProperty("properties");
return $"""
Event: {properties.GetProperty("event").GetString()}
Area: {properties.GetProperty("areaDesc").GetString()}
Severity: {properties.GetProperty("severity").GetString()}
Description: {properties.GetProperty("description").GetString()}
Instruction: {properties.GetProperty("instruction").GetString()}
""";
}));
}
// 获取某地的天气预报
[McpServerTool, Description("获取某地的天气预报。")]
public static async Task<string> GetForecast(
HttpClient client,
[Description("地点纬度。")] double latitude,
[Description("地点经度。")] double longitude)
{
// 请求网格点数据
var jsonElement = await client.GetFromJsonAsync<JsonElement>($"/points/{latitude},{longitude}");
var periods = jsonElement.GetProperty("properties").GetProperty("periods").EnumerateArray();
// 格式化所有时段
return string.Join("\n---\n", periods.Select(period => $"""
{period.GetProperty("name").GetString()}
Temperature: {period.GetProperty("temperature").GetInt32()}°F
Wind: {period.GetProperty("windSpeed").GetString()} {period.GetProperty("windDirection").GetString()}
Forecast: {period.GetProperty("detailedForecast").GetString()}
"""));
}
}
运行服务器
dotnet run
使用 Claude for Desktop 测试服务器
配置方式与 Python 版一致,只需将 command
改为 dotnet
,args
填写 run
及项目绝对路径。
命令行测试
确保 Claude for Desktop 能识别我们在 weather
服务器中暴露的两个工具。你可以通过查找锤子图标来确认:
点击锤子图标后,你应该能看到两个工具:
如果服务器未被 Claude for Desktop 识别,请前往故障排查部分获取调试建议。
如果锤子图标已出现,你可以在 Claude for Desktop 中运行以下命令测试服务器:
- What’s the weather in Sacramento?(萨克拉门托天气如何?)
- What are the active weather alerts in Texas?(德州当前有哪些天气警报?)
注意: 由于这是美国国家气象局服务,查询仅适用于美国地区。
底层原理
当你提问时:
1. 客户端将你的问题发送给 Claude(大模型服务器)
2. Claude 分析可用工具并决定使用哪一个(或多个)
3. 客户端通过 MCP 服务器执行选定工具
4. 结果返回给 Claude
5. Claude 生成自然语言回复
6. 回复展示给你!
故障排查
Claude for Desktop 集成问题获取 Claude for Desktop 日志
Claude.app 与 MCP 相关的日志写在 ~/Library/Logs/Claude
:
mcp.log
包含 MCP 连接及连接失败的通用日志mcp-server-SERVERNAME.log
文件包含指定服务器的错误(stderr)日志
你可以用如下命令查看最新日志并实时跟踪:
# 检查 Claude 日志中的错误
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
服务器未在 Claude 中显示
- 检查
claude_desktop_config.json
文件语法 - 确保项目路径为绝对路径
- 完全重启 Claude for Desktop
工具调用无响应
如果 Claude 尝试调用工具但失败:
- 检查 Claude 日志中的错误
- 确认服务器能正常构建和运行
- 尝试重启 Claude for Desktop
仍然无法解决?
请参考我们的调试指南获取更详细的调试工具和指导。
天气 API 问题错误:无法获取网格点数据
通常原因如下:
- 坐标不在美国境内
- NWS API 出现问题
- 被限流
解决方法:
- 确认使用的是美国坐标
- 在请求间添加短暂延迟
- 检查 NWS API 状态页
错误:该州暂无活跃警报
这不是错误,只是说明该州当前没有天气警报。可尝试其他州或在恶劣天气时再试。
更多高级故障排查,请参阅我们的调试 MCP 指南
后续步骤
主题 | 说明 |
---|---|
构建客户端 | 学习如何构建可连接到你服务器的 MCP 客户端 |
示例服务器 | 查看官方 MCP 服务器和实现的案例库 |
调试指南 | 学习如何高效调试 MCP 服务器和集成 |
用 LLM 构建 MCP | 学习如何利用 Claude 等 LLM 加速 MCP 开发 |