在本教程中,我们将构建一个简单的 MCP 天气服务器,并将其连接到一个 host,即 Claude for Desktop。我们将从基本设置开始,然后逐步介绍更复杂的用例。Documentation Index
Fetch the complete documentation index at: https://mcp-docs.cn/llms.txt
Use this file to discover all available pages before exploring further.
我们将构建什么
许多 LLM 目前不具备获取天气预报和恶劣天气警报的能力。让我们使用 MCP 来解决这个问题! 我们将构建一个服务器,它暴露两个 tools:get-alerts 和 get-forecast。然后,我们将服务器连接到一个 MCP host(在本例中是 Claude for Desktop):


服务器可以连接到任何 client。我们在这里选择 Claude for Desktop 是为了简单起见,但我们也有关于构建你自己的 client 的指南以及其他 clients 列表。
为什么选择 Claude for Desktop 而不是 Claude.ai?
为什么选择 Claude for Desktop 而不是 Claude.ai?
因为服务器是本地运行的,所以 MCP 目前仅支持 desktop hosts。远程 hosts 正在积极开发中。
核心 MCP 概念
MCP 服务器可以提供三种主要类型的能力:- Resources: 可以被 clients 读取的类文件数据(如 API 响应或文件内容)
- Tools: 可以被 LLM 调用的函数(需要用户批准)
- Prompts: 预先编写的模板,帮助用户完成特定任务
- Python
- Node
- Java
- Kotlin
- C#
让我们开始构建我们的天气服务器!你可以在这里找到我们将构建的完整代码。之后请务必重启你的终端,以确保 现在让我们深入构建你的服务器。FastMCP class 使用 Python type hints 和 docstrings 来自动生成 tool definitions,从而轻松创建和维护 MCP tools。你的 server 已经完成!运行 首先,确保你已安装 Claude for Desktop。你可以在这里安装最新版本。 如果你已经安装了 Claude for Desktop,请确保它已更新到最新版本。我们需要为你想要使用的任何 MCP servers 配置 Claude for Desktop。为此,请在文本编辑器中打开 然后,你将在 这告诉 Claude for Desktop:
前提知识
本快速入门假设你熟悉:- Python
- LLMs,如 Claude
系统要求
- 已安装 Python 3.10 或更高版本。
- 你必须使用 Python MCP SDK 1.2.0 或更高版本。
设置你的环境
首先,让我们安装uv 并设置我们的 Python 项目和环境:curl -LsSf https://astral.sh/uv/install.sh | sh
uv 命令被识别。现在,让我们创建并设置我们的项目:# 为我们的项目创建一个新 directory
uv init weather
cd weather
# 创建 virtual environment 并激活它
uv venv
source .venv/bin/activate
# 安装 dependencies
uv add "mcp[cli]" httpx
# 创建我们的 server file
touch weather.py
构建你的服务器
导入 packages 并设置 instance
将这些添加到你的weather.py 文件的顶部:from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
# 初始化 FastMCP server
mcp = FastMCP("weather")
# Constants
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
Helper functions
接下来,让我们添加 helper functions,用于查询和格式化来自 National Weather Service 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:
"""将警报 feature 格式化为可读的字符串。"""
props = feature["properties"]
return f"""
事件: {props.get('event', 'Unknown')}
区域: {props.get('areaDesc', 'Unknown')}
严重性: {props.get('severity', 'Unknown')}
描述: {props.get('description', 'No description available')}
指示: {props.get('instruction', 'No specific instructions provided')}
"""
实现 tool execution
Tool execution handler 负责实际执行每个 tool 的逻辑。让我们添加它:@mcp.tool()
async def get_alerts(state: str) -> str:
"""获取美国州的天气警报。
Args:
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:
"""获取某个位置的天气预报。
Args:
latitude: 位置的纬度
longitude: 位置的经度
"""
# 首先获取预报网格 endpoint
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)
if not points_data:
return "无法获取此位置的预报数据。"
# 从 points response 中获取预报 URL
forecast_url = points_data["properties"]["forecast"]
forecast_data = await make_nws_request(forecast_url)
if not forecast_data:
return "无法获取详细预报。"
# 将 periods 格式化为可读的预报
periods = forecast_data["properties"]["periods"]
forecasts = []
for period in periods[:5]: # 仅显示接下来的 5 个 periods
forecast = f"""
{period['name']}:
温度: {period['temperature']}°{period['temperatureUnit']}
风: {period['windSpeed']} {period['windDirection']}
预报: {period['detailedForecast']}
"""
forecasts.append(forecast)
return "\n---\n".join(forecasts)
运行 server
最后,让我们初始化并运行 server:if __name__ == "__main__":
# 初始化并运行 server
mcp.run(transport='stdio')
uv run weather.py 以确认一切正常。现在让我们从现有的 MCP host,Claude for Desktop 测试你的 server。使用 Claude for Desktop 测试你的 server
Claude for Desktop 尚不适用于 Linux。Linux 用户可以继续阅读 构建 client 教程,以构建一个连接到我们刚刚构建的 server 的 MCP client。
~/Library/Application Support/Claude/claude_desktop_config.json 中的 Claude for Desktop App configuration。如果该 file 不存在,请确保创建它。例如,如果你安装了 VS Code:- MacOS/Linux
- Windows
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
code $env:AppData\Claude\claude_desktop_config.json
mcpServers key 中添加你的 servers。只有正确配置了至少一个 server,MCP UI 元素才会显示在 Claude for Desktop 中。在本例中,我们将添加我们的单个天气服务器,如下所示:- MacOS/Linux
- Windows
Python
{
"mcpServers": {
"weather": {
"command": "uv",
"args": [
"--directory",
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
"run",
"weather.py"
]
}
}
}
Python
{
"mcpServers": {
"weather": {
"command": "uv",
"args": [
"--directory",
"C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather",
"run",
"weather.py"
]
}
}
}
你可能需要在
command 字段中放入 uv executable 的完整 path。你可以在 MacOS/Linux 上运行 which uv 或在 Windows 上运行 where uv 来获取它。确保你传入的是你的 server 的绝对 path。
- 有一个名为 “weather” 的 MCP server
- 通过运行
uv --directory /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather run weather.py来启动它
让我们开始构建我们的天气服务器!你可以在这里找到我们将构建的完整代码。在本教程中,你需要 Node.js 16 或更高版本。现在,让我们创建并设置我们的项目:更新你的 package.json 以添加 type: “module” 和一个 build script:在你的项目的根目录下创建一个 现在让我们深入构建你的服务器。确保运行 首先,确保你已安装 Claude for Desktop。你可以在这里安装最新版本。 如果你已经安装了 Claude for Desktop,请确保它已更新到最新版本。我们需要为你想要使用的任何 MCP servers 配置 Claude for Desktop。为此,请在文本编辑器中打开 然后,你将在 这告诉 Claude for Desktop:
前提知识
本快速入门假设你熟悉:- TypeScript
- LLMs,如 Claude
系统要求
对于 TypeScript,请确保你已安装最新版本的 Node。设置你的环境
首先,如果你还没有安装 Node.js 和 npm,请安装它们。你可以从 nodejs.org 下载它们。 验证你的 Node.js 安装:node --version
npm --version
# 为我们的项目创建一个新 directory
mkdir weather
cd weather
# 初始化一个新的 npm 项目
npm init -y
# 安装 dependencies
npm install @modelcontextprotocol/sdk zod
npm install -D @types/node typescript
# 创建我们的 files
mkdir src
touch src/index.ts
package.json
{
"type": "module",
"bin": {
"weather": "./build/index.js"
},
"scripts": {
"build": "tsc && chmod 755 build/index.js"
},
"files": [
"build"
],
}
tsconfig.json: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"]
}
构建你的服务器
导入 packages 并设置 instance
将这些添加到你的src/index.ts 文件的顶部:import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const NWS_API_BASE = "https://api.weather.gov";
const USER_AGENT = "weather-app/1.0";
// 创建 server instance
const server = new McpServer({
name: "weather",
version: "1.0.0",
capabilities: {
resources: {},
tools: {},
},
});
Helper functions
接下来,让我们添加 helper functions,用于查询和格式化来自 National Weather Service API 的数据:// Helper function 用于发送 NWS API 请求
async function makeNWSRequest<T>(url: string): Promise<T | null> {
const headers = {
"User-Agent": USER_AGENT,
Accept: "application/geo+json",
};
try {
const response = await fetch(url, { headers });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
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[];
};
}
实现 tool execution
Tool execution handler 负责实际执行每个 tool 的逻辑。让我们添加它:// 注册天气 tools
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: `No active alerts for ${stateCode}`,
},
],
};
}
const formattedAlerts = features.map(formatAlert);
const alertsText = `Active alerts for ${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: `Failed to retrieve grid point data for coordinates: ${latitude}, ${longitude}. This location may not be supported by the NWS API (only US locations are supported).`,
},
],
};
}
const forecastUrl = pointsData.properties?.forecast;
if (!forecastUrl) {
return {
content: [
{
type: "text",
text: "Failed to get forecast URL from grid point data",
},
],
};
}
// 获取预报数据
const forecastData = await makeNWSRequest<ForecastResponse>(forecastUrl);
if (!forecastData) {
return {
content: [
{
type: "text",
text: "Failed to retrieve forecast data",
},
],
};
}
const periods = forecastData.properties?.periods || [];
if (periods.length === 0) {
return {
content: [
{
type: "text",
text: "No forecast periods available",
},
],
};
}
// 格式化预报 periods
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 = `Forecast for ${latitude}, ${longitude}:\n\n${formattedForecast.join("\n")}`;
return {
content: [
{
type: "text",
text: forecastText,
},
],
};
},
);
运行 server
最后,实现 main function 以运行 server: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 以 build 你的 server!这是让你的 server 连接起来非常重要的一步。现在让我们从现有的 MCP host,Claude for Desktop 测试你的 server。使用 Claude for Desktop 测试你的 server
Claude for Desktop 尚不适用于 Linux。Linux 用户可以继续阅读 构建 client 教程,以构建一个连接到我们刚刚构建的 server 的 MCP client。
~/Library/Application Support/Claude/claude_desktop_config.json 中的 Claude for Desktop App configuration。如果该 file 不存在,请确保创建它。例如,如果你安装了 VS Code:- MacOS/Linux
- Windows
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
code $env:AppData\Claude\claude_desktop_config.json
mcpServers key 中添加你的 servers。只有正确配置了至少一个 server,MCP UI 元素才会显示在 Claude for Desktop 中。在本例中,我们将添加我们的单个天气服务器,如下所示:- MacOS/Linux
- Windows
{
"mcpServers": {
"weather": {
"command": "node",
"args": [
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js"
]
}
}
}
{
"mcpServers": {
"weather": {
"command": "node",
"args": [
"C:\\PATH\\TO\\PARENT\\FOLDER\\weather\\build\\index.js"
]
}
}
}
- 有一个名为 “weather” 的 MCP server
- 通过运行
node /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js来启动它
这是一个基于 Spring AI MCP auto-configuration 和 boot starters 的快速入门演示。
要学习如何手动创建 sync 和 async MCP Servers,请查阅 Java SDK Server 文档。
系统要求
- 已安装 Java 17 或更高版本。
- Spring Boot 3.3.x 或更高版本
设置你的环境
使用 Spring Initializer 来 bootstrap 项目。你需要添加以下 dependencies:- Maven
- Gradle
<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>
dependencies {
implementation platform("org.springframework.ai:spring-ai-starter-mcp-server")
implementation platform("org.springframework:spring-web")
}
spring.main.bannerMode=off
logging.pattern.console=
构建你的服务器
Weather Service
让我们实现一个 WeatherService.java,它使用 REST client 来查询来自 National Weather Service API 的数据:@Service
public class WeatherService {
private final RestClient restClient;
public WeatherService() {
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 = "Get weather forecast for a specific latitude/longitude")
public String getWeatherForecastByLocation(
double latitude, // Latitude coordinate
double longitude // Longitude coordinate
) {
// Returns detailed forecast including:
// - Temperature and unit
// - Wind speed and direction
// - Detailed forecast description
}
@Tool(description = "Get weather alerts for a US state")
public String getAlerts(
@ToolParam(description = "Two-letter US state code (e.g. CA, NY)" String state
) {
// Returns active alerts including:
// - Event type
// - Affected area
// - Severity
// - Description
// - Safety instructions
}
// ......
}
@Service annotation 将在你的 application context 中自动注册该 service。
Spring AI @Tool annotation,使其易于创建和维护 MCP tools。auto-configuration 将自动向 MCP server 注册这些 tools。创建你的 Boot Application
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}
}
MethodToolCallbackProvider utils 将 @Tools 转换为 MCP server 使用的可操作 callbacks。运行 server
最后,让我们 build server:./mvnw clean install
target 文件夹中生成一个 mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar file。现在让我们从现有的 MCP host,Claude for Desktop 测试你的 server。使用 Claude for Desktop 测试你的 server
Claude for Desktop 尚不适用于 Linux。
~/Library/Application Support/Claude/claude_desktop_config.json 中的 Claude for Desktop App configuration。
如果该 file 不存在,请确保创建它。例如,如果你安装了 VS Code:- MacOS/Linux
- Windows
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
code $env:AppData\Claude\claude_desktop_config.json
mcpServers key 中添加你的 servers。
只有正确配置了至少一个 server,MCP UI 元素才会显示在 Claude for Desktop 中。在本例中,我们将添加我们的单个天气服务器,如下所示:- MacOS/Linux
- Windows
java
{
"mcpServers": {
"spring-ai-mcp-weather": {
"command": "java",
"args": [
"-Dspring.ai.mcp.server.stdio=true",
"-jar",
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar"
]
}
}
}
java
{
"mcpServers": {
"spring-ai-mcp-weather": {
"command": "java",
"args": [
"-Dspring.ai.mcp.server.transport=STDIO",
"-jar",
"C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather\\mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar"
]
}
}
}
确保你传入的是你的 server 的绝对 path。
- 有一个名为 “my-weather-server” 的 MCP server
- 通过运行
java -jar /ABSOLUTE/PATH/TO/PARENT/FOLDER/mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar来启动它
使用 Java client 测试你的 server
手动创建 MCP Client
使用McpClient 连接到 server:var stdioParams = ServerParameters.builder("java")
.args("-jar", "/ABSOLUTE/PATH/TO/PARENT/FOLDER/mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar")
.build();
var stdioTransport = new StdioClientTransport(stdioParams);
var mcpClient = McpClient.sync(stdioTransport).build();
mcpClient.initialize();
ListToolsResult toolsList = mcpClient.listTools();
CallToolResult weather = mcpClient.callTool(
new CallToolRequest("getWeatherForecastByLocation",
Map.of("latitude", "47.6062", "longitude", "-122.3321")));
CallToolResult alert = mcpClient.callTool(
new CallToolRequest("getAlerts", Map.of("state", "NY")));
mcpClient.closeGracefully();
使用 MCP Client Boot Starter
使用spring-ai-starter-mcp-client dependency 创建一个新的 boot starter application:<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
spring.ai.mcp.client.stdio.servers-configuration property 以指向你的 claude_desktop_config.json。
你可以重用现有的 Anthropic Desktop configuration:spring.ai.mcp.client.stdio.servers-configuration=file:PATH/TO/claude_desktop_config.json
更多 Java MCP Server 示例
starter-webflux-server 演示了如何使用 SSE transport 创建 MCP server。 它展示了如何使用 Spring Boot 的 auto-configuration 功能来定义和注册 MCP Tools、Resources 和 Prompts。让我们开始构建我们的天气服务器!你可以在这里找到我们将构建的完整代码。现在,让我们创建并设置你的项目:运行 此外,将以下 plugins 添加到你的 build script:现在让我们深入构建你的服务器。确保运行 首先,确保你已安装 Claude for Desktop。你可以在这里安装最新版本。 如果你已经安装了 Claude for Desktop,请确保它已更新到最新版本。我们需要为你想要使用的任何 MCP servers 配置 Claude for Desktop。
为此,请在文本编辑器中打开 然后,你将在 这告诉 Claude for Desktop:
前提知识
本快速入门假设你熟悉:- Kotlin
- LLMs,如 Claude
系统要求
- 已安装 Java 17 或更高版本。
设置你的环境
首先,如果你还没有安装java 和 gradle,请安装它们。
你可以从 官方 Oracle JDK 网站 下载 java。
验证你的 java 安装:java --version
# 为我们的项目创建一个新 directory
mkdir weather
cd weather
# 初始化一个新的 kotlin 项目
gradle init
gradle init 后,你将看到用于创建你的项目的选项。
选择 Application 作为项目类型,Kotlin 作为编程语言,Java 17 作为 Java 版本。或者,你可以使用 IntelliJ IDEA 项目向导 创建一个 Kotlin application。创建项目后,添加以下 dependencies: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"
}
构建你的服务器
设置 instance
添加一个 server 初始化 function:// Main function 以运行 MCP server
fun `run mcp server`() {
// 创建具有基本实现的 MCP Server instance
val server = Server(
Implementation(
name = "weather", // Tool name 是 "weather"
version = "1.0.0" // Implementation 的 version
),
ServerOptions(
capabilities = ServerCapabilities(tools = ServerCapabilities.Tools(listChanged = true))
)
)
// 创建一个使用 standard IO 的 transport,用于 server 通信
val transport = StdioServerTransport(
System.`in`.asInput(),
System.out.asSink().buffered()
)
runBlocking {
server.connect(transport)
val done = Job()
server.onClose {
done.complete()
}
done.join()
}
}
Weather API helper functions
接下来,让我们添加 functions 和 data classes,用于查询和转换来自 National Weather Service API 的响应:// Extension function 以获取给定纬度和经度的预报信息
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}:
温度: ${period.temperature} ${period.temperatureUnit}
风: ${period.windSpeed} ${period.windDirection}
预报: ${period.detailedForecast}
""".trimIndent()
}
}
// Extension function 以获取给定州的 weather alerts
suspend fun HttpClient.getAlerts(state: String): List<String> {
val alerts = this.get("/alerts/active/area/$state").body<Alert>()
return alerts.features.map { feature ->
"""
事件: ${feature.properties.event}
区域: ${feature.properties.areaDesc}
严重性: ${feature.properties.severity}
描述: ${feature.properties.description}
指示: ${feature.properties.instruction}
""".trimIndent()
}
}
@Serializable
data class Points(
val properties: Properties
) {
@Serializable
data class Properties(val forecast: String)
}
@Serializable
data class Forecast(
val properties: Properties
) {
@Serializable
data class Properties(val periods: List<Period>)
@Serializable
data class Period(
val number: Int, val name: String, val startTime: String, val endTime: String,
val isDaytime: Boolean, val temperature: Int, val temperatureUnit: String,
val temperatureTrend: String, val probabilityOfPrecipitation: JsonObject,
val windSpeed: String, val windDirection: String,
val shortForecast: String, val detailedForecast: String,
)
}
@Serializable
data class Alert(
val features: List<Feature>
) {
@Serializable
data class Feature(
val properties: Properties
)
@Serializable
data class Properties(
val event: String, val areaDesc: String, val severity: String,
val description: String, val instruction: String?,
)
}
实现 tool execution
Tool execution handler 负责实际执行每个 tool 的逻辑。让我们添加它:// 创建一个 HTTP client,它具有 default request configuration 和 JSON content negotiation
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)
}
// 安装 content negotiation plugin,用于 JSON serialization/deserialization
install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
}
// 注册一个 tool,以按州获取 weather alerts
server.addTool(
name = "get_alerts",
description = """
获取美国州的天气警报。输入是两个字母的美国州代码(例如 CA、NY)
""".trimIndent(),
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("The 'state' parameter is required."))
)
}
val alerts = httpClient.getAlerts(state)
CallToolResult(content = alerts.map { TextContent(it) })
}
// 注册一个 tool,以按纬度和经度获取天气预报
server.addTool(
name = "get_forecast",
description = """
获取特定纬度/经度的天气预报
""".trimIndent(),
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("The 'latitude' and 'longitude' parameters are required."))
)
}
val forecast = httpClient.getForecast(latitude, longitude)
CallToolResult(content = forecast.map { TextContent(it) })
}
运行 server
最后,实现 main function 以运行 server:fun main() = `run mcp server`()
./gradlew build 以 build 你的 server。这是让你的 server 连接起来非常重要的一步。现在让我们从现有的 MCP host,Claude for Desktop 测试你的 server。使用 Claude for Desktop 测试你的 server
Claude for Desktop 尚不适用于 Linux。Linux 用户可以继续阅读 构建 client 教程,以构建一个连接到我们刚刚构建的 server 的 MCP client。
~/Library/Application Support/Claude/claude_desktop_config.json 中的 Claude for Desktop App configuration。
如果该 file 不存在,请确保创建它。例如,如果你安装了 VS Code:code ~/Library/Application\ Support/Claude/claude_desktop_config.json
mcpServers key 中添加你的 servers。
只有正确配置了至少一个 server,MCP UI 元素才会显示在 Claude for Desktop 中。在本例中,我们将添加我们的单个天气服务器,如下所示:{
"mcpServers": {
"weather": {
"command": "java",
"args": [
"-jar",
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/libs/weather-0.1.0-all.jar"
]
}
}
}
- 有一个名为 “weather” 的 MCP server
- 通过运行
java -jar /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/libs/weather-0.1.0-all.jar来启动它
让我们开始构建我们的天气服务器!你可以在这里找到我们将构建的完整代码。现在,让我们创建并设置你的项目:运行 现在让我们深入构建你的服务器。此代码设置了一个基本的控制台 application,它使用 Model Context Protocol SDK 创建一个具有 standard I/O transport 的 MCP server。这将启动 server 并侦听 standard input/output 上的传入请求。首先,确保你已安装 Claude for Desktop。你可以在这里安装最新版本。 如果你已经安装了 Claude for Desktop,请确保它已更新到最新版本。
我们需要为你想要使用的任何 MCP servers 配置 Claude for Desktop。为此,请在文本编辑器中打开 然后,你将在 这告诉 Claude for Desktop:
前提知识
本快速入门假设你熟悉:- C#
- LLMs,如 Claude
- .NET 8 或更高版本
系统要求
- 已安装 .NET 8 SDK 或更高版本。
设置你的环境
首先,如果你还没有安装dotnet,请安装它。你可以从 官方 Microsoft .NET 网站 下载 dotnet。验证你的 dotnet 安装:dotnet --version
# 为我们的项目创建一个新 directory
mkdir weather
cd weather
# 初始化一个新的 C# 项目
dotnet new console
dotnet new console 后,你将看到一个新的 C# 项目。
你可以在你最喜欢的 IDE 中打开该项目,例如 Visual Studio 或 Rider。
或者,你可以使用 Visual Studio 项目向导 创建一个 C# application。
创建项目后,为 Model Context Protocol SDK 和 hosting 添加 NuGet package:# 添加 Model Context Protocol SDK NuGet package
dotnet add package ModelContextProtocol --prerelease
# 添加 .NET Hosting NuGet package
dotnet add package Microsoft.Extensions.Hosting
构建你的服务器
打开你项目中的Program.cs file,并将内容替换为以下代码:using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol;
using System.Net.Http.Headers;
var builder = Host.CreateEmptyApplicationBuilder(settings: null);
builder.Services.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
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 而不是 CreateDefaultBuilder。这确保了 server 不会向控制台写入任何其他消息。这仅对于使用 STDIO transport 的 servers 是必要的。Weather API helper functions
接下来,定义一个 class,其中包含用于查询和转换来自 National Weather Service API 的响应的 tool execution handlers:using ModelContextProtocol.Server;
using System.ComponentModel;
using System.Net.Http.Json;
using System.Text.Json;
namespace QuickstartWeatherServer.Tools;
[McpServerToolType]
public static class WeatherTools
{
[McpServerTool, Description("Get weather alerts for a US state.")]
public static async Task<string> GetAlerts(
HttpClient client,
[Description("The US state to get alerts for.")] string state)
{
var jsonElement = await client.GetFromJsonAsync<JsonElement>($"/alerts/active/area/{state}");
var alerts = jsonElement.GetProperty("features").EnumerateArray();
if (!alerts.Any())
{
return "No active alerts for this state.";
}
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("Get weather forecast for a location.")]
public static async Task<string> GetForecast(
HttpClient client,
[Description("Latitude of the location.")] double latitude,
[Description("Longitude of the location.")] 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()}
"""));
}
}
运行 server
最后,使用以下 command 运行 server:dotnet run
使用 Claude for Desktop 测试你的 server
Claude for Desktop 尚不适用于 Linux。Linux 用户可以继续阅读 构建 client 教程,以构建一个连接到我们刚刚构建的 server 的 MCP client。
~/Library/Application Support/Claude/claude_desktop_config.json 中的 Claude for Desktop App configuration。如果该 file 不存在,请确保创建它。
例如,如果你安装了 VS Code:- MacOS/Linux
- Windows
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
code $env:AppData\Claude\claude_desktop_config.json
mcpServers key 中添加你的 servers。只有正确配置了至少一个 server,MCP UI 元素才会显示在 Claude for Desktop 中。
在本例中,我们将添加我们的单个天气服务器,如下所示:- MacOS/Linux
- Windows
{
"mcpServers": {
"weather": {
"command": "dotnet",
"args": [
"run",
"--project",
"/ABSOLUTE/PATH/TO/PROJECT",
"--no-build"
]
}
}
}
{
"mcpServers": {
"weather": {
"command": "dotnet",
"args": [
"run",
"--project",
"C:\\ABSOLUTE\\PATH\\TO\\PROJECT",
"--no-build"
]
}
}
}
- 有一个名为 “weather” 的 MCP server
- 通过运行
dotnet run /ABSOLUTE/PATH/TO/PROJECT来启动它 保存 file,并重新启动 Claude for Desktop。
使用 commands 测试
让我们确保 Claude for Desktop 正在接收我们在weather server 中暴露的两个 tools。你可以通过查找锤子 

- Sacramento 的天气怎么样?
- Texas 有哪些活跃的天气警报?


由于这是美国国家气象局,因此查询仅适用于美国的位置。
底层发生了什么
当你提出问题时:- client 将你的问题发送给 Claude
- Claude 分析可用的 tools 并决定使用哪些 tool
- client 通过 MCP server 执行选择的 tool
- 结果被发回给 Claude
- Claude 制定自然语言响应
- 响应显示给你!
故障排除
Claude for Desktop 集成问题
Claude for Desktop 集成问题
从 Claude for Desktop 获取 logs与 MCP 相关的 Claude.app 日志被写入 Server 未在 Claude 中显示
~/Library/Logs/Claude 中的 log files:mcp.log将包含关于 MCP 连接和连接失败的通用 logging。- 名为
mcp-server-SERVERNAME.log的 Files 将包含来自命名 server 的错误 (stderr) logging。
# 检查 Claude 的 logs 中是否有错误
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
- 检查你的
claude_desktop_config.jsonfile 语法 - 确保你的项目的 path 是绝对路径而不是相对路径
- 完整重启 Claude for Desktop
- 检查 Claude 的 logs 中是否有错误
- 验证你的 server build 和运行是否没有错误
- 尝试重启 Claude for Desktop
Weather API 问题
Weather API 问题
错误:Failed to retrieve grid point data(未能检索网格点数据)这通常意味着:
- 坐标在美国境外
- NWS API 存在问题
- 你正受到速率限制
- 验证你是否正在使用美国坐标
- 在请求之间添加少量延迟
- 检查 NWS API 状态页面
有关更高级的故障排除,请查看我们的 MCP Debug 指南
后续步骤
构建 client
了解如何构建你自己的 MCP client,它可以连接到你的 server
示例 servers
查看我们的官方 MCP servers 和 implementations 画廊
Debug 指南
学习如何有效地 debug MCP servers 和 integrations
使用 LLMs 构建 MCP
了解如何使用 LLMs(如 Claude)来加速你的 MCP 开发