diff --git a/content/docs/(get-started)/quick-start/server-developers.mdx b/content/docs/(get-started)/quick-start/server-developers.mdx index 4d993a0..f2db7e2 100644 --- a/content/docs/(get-started)/quick-start/server-developers.mdx +++ b/content/docs/(get-started)/quick-start/server-developers.mdx @@ -1,114 +1,1794 @@ ---- -title: 服务器开发者 -description: 快速开始 - 服务器开发者 ---- +# 针对服务端开发人员 -开始构建自己的服务器,以便在Claude中用于Desktop和其他客户端。 +> 开始构建自己的服务器,以便在Claude中用于Desktop和其他客户端 在本教程中,我们将构建一个简单的MCP天气服务器,并将其连接到主机Claude for Desktop。我们将从基本设置开始,然后进行更复杂的用例。 -## 我们将要干什么 +### 我们将要干什么 + 许多LLM(包括Claude)目前无法获取预报和恶劣天气警报。让我们用MCP解决这个问题。 -我们将构建一个server,该server提供两个工具:get-alerts and get-forecast。然后,我们将服务器连接到MCP主机(在本例中使用Claude for Desktop): +我们将构建一个server,该server提供两个工具: `get-alerts` and `get-forecast`。然后,我们将服务器连接到MCP主机(在本例中使用Claude for Desktop): + + ![ImaImageZoomge](/server-developers-01.png) + ![ImaImageZoomge](/server-developers-02.png) -服务可以连接任何客户端。为了简单起见,我们在这里选择了Claude作为桌面,但我们也有关于[建立自己的client](https://mcp.thinkinai.xyz/docs/quick-start/client-developers)建立自己的client的指南,以及[这里的其他客户列表](https://mcp.thinkinai.xyz/docs/example-clients)。 + + 服务可以连接任何客户端。为了简单起见,我们在这里选择了Claude作为桌面,但我们也有关于[建立自己的client](https://mcp.thinkinai.xyz/docs/quick-start/client-developers) 以及 [这里的其他客户列表](https://modelcontextprotocol.io/clients). + + + 由于服务器是本地运行的,MCP目前只支持桌面主机。远程主机正在积极开发中。 + -``` -为什么选择Claude for DeskTop而不是Claude.ai? -由于服务器是本地运行的,MCP目前只支持桌面主机。远程主机正在积极开发中。 -``` +### MCP核心概念 -## MCP核心概念 MCP服务器可以提供三种主要功能: - 1. **Resources**:客户端可以读取的类文件数据(如API响应或文件内容) - 2. **Tools**:LLM可以调用的函数(经用户批准) - 3. **Prompts**:帮助用户完成特定任务的预先编写的模板 + +1. **Resources**: 客户端可以读取的类文件数据(如API响应或文件内容) +2. **Tools**: LLM可以调用的函数(经用户批准) +3. **Prompts**: 帮助用户完成特定任务的预先编写的模板 + 本教程将主要关注工具。 -### 基于Java -这是一个基于Spring AI MCP自动配置和引导启动器的快速入门演示。要了解如何手动创建同步和异步MCP服务器,请参阅[Java SDK服务器](https://modelcontextprotocol.io/sdk/java/mcp-server)文档。 -让我们开始构建一个天气服务! - ● [你可以在这里找到我们将要构建的完整代码](https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-stdio-server)【MCP官方代码,支持查询美国天气情况】。 - ● [这里的代码也可以](https://github.com/weishuai8888/spring-ai-examples)【fork官方代码后调整了逻辑,支持查询全世界的天气情况】 -有关更多信息,请参阅[MCP Server Boot Starter](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html)参考文档。有关手动MCP服务器实现,请参阅[MCP Server Java SDK文档](https://modelcontextprotocol.io/sdk/java/mcp-server)。 +import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; + + + + 让我们开始构建我们的天气服务器吧!【你可以在这里找到我们将要构建的完整代码。](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/weather-server-python) -#### 系统要求 -~ - ● 已安装Java 17或更高版本 - ● [Spring Boot 3.3.x](https://docs.spring.io/spring-boot/installing.html) 或更高版本 + ### 预备知识 -#### 设置你的环境变量 -使用 [Spring Initizer](https://start.spring.io) 启动项目。 -你需要添加以下的依赖项: + 本快速入门假定您熟悉: -import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; + * Python + * LLMs like Claude - - - ```xml - - - org.springframework.ai - spring-ai-starter-mcp-server - - - - org.springframework - spring-web - - + ### 系统要求 + + * Python 3.10 or higher installed. + * You must use the Python MCP SDK 1.2.0 or higher. + + ### 设置你的环境 + + 首先,让我们安装uv并设置我们的Python项目和环境: + + + ```bash MacOS/Linux + curl -LsSf https://astral.sh/uv/install.sh | sh + ``` + + ```powershell Windows + powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" + ``` + + + 确保之后重新启动终端,以确保收到uv命令。 + + 现在,让我们创建并设置我们的项目: + + + ```bash MacOS/Linux + # Create a new directory for our project + uv init weather + cd weather + + # Create virtual environment and activate it + uv venv + source .venv/bin/activate + + # Install dependencies + uv add "mcp[cli]" httpx + + # Create our server file + touch weather.py + ``` + + ```powershell Windows + # Create a new directory for our project + uv init weather + cd weather + + # Create virtual environment and activate it + uv venv + .venv\Scripts\activate + + # Install dependencies + uv add mcp[cli] httpx + + # Create our server file + new-item weather.py + ``` + + + 现在,让我们开始构建您的服务器。 + + ## 构建您的服务 + + ### 导入包并设置实例 + + 将这些添加到您的顶部 `weather.py`: + + ```python + from typing import Any + import httpx + from mcp.server.fastmcp import FastMCP + + # Initialize FastMCP server + mcp = FastMCP("weather") + + # Constants + NWS_API_BASE = "https://api.weather.gov" + USER_AGENT = "weather-app/1.0" ``` - - - ```xml - dependencies { - implementation platform("org.springframework.ai:spring-ai-starter-mcp-server") - implementation platform("org.springframework:spring-web") + + FastMCP类使用Python类型提示和文档字符串自动生成工具定义,使创建和维护MCP工具变得容易。 + + ### 帮助函数 + + 接下来,让我们添加我们的助手函数,用于查询和格式化来自美国国家气象局API的数据: + + ```python + async def make_nws_request(url: str) -> dict[str, Any] | None: + """Make a request to the NWS API with proper error handling.""" + 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: + """Format an alert feature into a readable string.""" + 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')} + """ + ``` + + ### 执行工具 + + 工具执行处理程序负责实际执行每个工具的逻辑。让我们添加它: + + ```python + @mcp.tool() + async def get_alerts(state: str) -> str: + """Get weather alerts for a US state. + + Args: + state: Two-letter US state code (e.g. 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 "Unable to fetch alerts or no alerts found." + + if not data["features"]: + return "No active alerts for this state." + + 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: + """Get weather forecast for a location. + + Args: + latitude: Latitude of the location + longitude: Longitude of the location + """ + # First get the forecast grid endpoint + points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}" + points_data = await make_nws_request(points_url) + + if not points_data: + return "Unable to fetch forecast data for this location." + + # Get the forecast URL from the points response + forecast_url = points_data["properties"]["forecast"] + forecast_data = await make_nws_request(forecast_url) + + if not forecast_data: + return "Unable to fetch detailed forecast." + + # Format the periods into a readable forecast + periods = forecast_data["properties"]["periods"] + forecasts = [] + for period in periods[:5]: # Only show next 5 periods + 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) + ``` + + ### 运行服务 + + 最后,让我们初始化并运行服务器: + + ```python + if __name__ == "__main__": + # Initialize and run the server + mcp.run(transport='stdio') ``` + + 您的服务器已完成!运行`uv Run weather.py `以确认一切正常。 + + 现在让我们从现有的MCP主机Claude for Desktop测试您的服务器。 + + ## 使用Claude for Desktop测试您的服务器 + + + Claude for Desktop尚未在Linux上可用。Linux用户可以继续 [建立客户](https://mcp.thinkinai.xyz/docs/quick-start/client-developers) 构建连接到我们刚刚构建的服务器的MCP客户端的教程。 + + + 首先,确保您安装了Claude for Desktop。[您可以安装最新版本](https://claude.ai/download) 如果你已经有了Claude for Desktop, **确保它已更新到最新版本。** + + 我们需要为您想要使用的任何MCP服务器配置Claude for Desktop。为此,请在以下位置打开Claude for Desktop App配置`~/Library/Application Support/Claude/claude_desktop_config.json` 在文本编辑器中。如果文件不存在,请确保创建该文件。 + + 例如,如果你有[VS Code](https://code.visualstudio.com/) 安装: + + + + ```bash + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` + + + + ```powershell + code $env:AppData\Claude\claude_desktop_config.json + ``` + + + + 然后,您将在“mcpServers”键中添加服务器。如果至少有一台服务器配置正确,MCP UI元素将仅显示在Claude for Desktop中。 + 在这种情况下,我们将添加单个天气服务器,如下所示: + + + + ```json Python + { + "mcpServers": { + "weather": { + "command": "uv", + "args": [ + "--directory", + "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather", + "run", + "weather.py" + ] + } + } + } + ``` + + + + ```json Python + { + "mcpServers": { + "weather": { + "command": "uv", + "args": [ + "--directory", + "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather", + "run", + "weather.py" + ] + } + } + } + ``` + + + + + 您可能需要将完整路径放在 `uv` 可执行于 `command` 领域. 你可以通过运行来获得这个 `which uv` 在 MacOS/Linux or `where uv` 在 Windows. + + + + 确保传入服务器的绝对路径。 + + + 告诉 Claude for Desktop: + + 1. MCP服务器叫做 "weather" + 2. 通过运行来启动它 `uv --directory /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather run weather.py` + + 保存文件,然后重新启动 **Claude for Desktop**. - -然后通过设置应用程序属性来配置应用程序 - - - ```yaml - logging: - pattern: - console: - spring: - main: - banner-mode: off + + 让我们开始构建我们的天气服务器吧! [你可以在这里找到我们将要构建的完整代码。](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/weather-server-typescript) + + ### 预备知识 + + 本快速入门假定您熟悉: + + * TypeScript + * LLMs like Claude + + ### 系统要求 + + 对于TypeScript,请确保您安装了最新版本的Node。 + + ### 设置你的环境 + + 首先,让我们安装Node.js和npm,如果你还没有的话。您可以从以下网址下载它们 [nodejs.org](https://nodejs.org/). + 验证您的 Node.js 安装: + + ```bash + node --version + npm --version + ``` + + 对于本教程,您需要Node.js版本16或更高版本。 + 、 + 现在,让我们创建并设置我们的项目: + + + ```bash MacOS/Linux + # Create a new directory for our project + mkdir weather + cd weather + + # Initialize a new npm project + npm init -y + + # Install dependencies + npm install @modelcontextprotocol/sdk zod + npm install -D @types/node typescript + + # Create our files + mkdir src + touch src/index.ts + ``` + + ```powershell Windows + # Create a new directory for our project + md weather + cd weather + + # Initialize a new npm project + npm init -y + + # Install dependencies + npm install @modelcontextprotocol/sdk zod + npm install -D @types/node typescript + + # Create our files + md src + new-item src\index.ts + ``` + + + Update your package.json to add type: "module" and a build script: + + ```json package.json + { + "type": "module", + "bin": { + "weather": "./build/index.js" + }, + "scripts": { + "build": "tsc && chmod 755 build/index.js" + }, + "files": [ + "build" + ], + } ``` + + Create a `tsconfig.json` in the root of your project: + + ```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"] + } + ``` + + 现在,让我们开始构建您的服务器。 + + ## 构建您的服务 + + ### 导入包并设置实例 + + 将这些添加到您的顶部 `src/index.ts`: + + ```typescript + 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"; + + // Create server instance + const server = new McpServer({ + name: "weather", + version: "1.0.0", + capabilities: { + resources: {}, + tools: {}, + }, + }); + ``` + + ### 帮助函数 + + 接下来,让我们添加我们的助手函数,用于查询和格式化来自美国国家气象局API的数据: + + ```typescript + // Helper function for making NWS API requests + async function makeNWSRequest(url: string): Promise { + 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; + }; + } + + // Format alert data + 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[]; + }; + } + ``` + + ### 执行工具 + + 工具执行处理程序负责实际执行每个工具的逻辑。让我们添加它: + + ```typescript + // Register weather tools + server.tool( + "get-alerts", + "Get weather alerts for a state", + { + state: z.string().length(2).describe("Two-letter state code (e.g. CA, NY)"), + }, + async ({ state }) => { + const stateCode = state.toUpperCase(); + const alertsUrl = `${NWS_API_BASE}/alerts?area=${stateCode}`; + const alertsData = await makeNWSRequest(alertsUrl); + + if (!alertsData) { + return { + content: [ + { + type: "text", + text: "Failed to retrieve alerts data", + }, + ], + }; + } + + 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", + "Get weather forecast for a location", + { + latitude: z.number().min(-90).max(90).describe("Latitude of the location"), + longitude: z.number().min(-180).max(180).describe("Longitude of the location"), + }, + async ({ latitude, longitude }) => { + // Get grid point data + const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`; + const pointsData = await makeNWSRequest(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", + }, + ], + }; + } + + // Get forecast data + const forecastData = await makeNWSRequest(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", + }, + ], + }; + } + + // Format forecast 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, + }, + ], + }; + }, + ); + ``` + + ### 运行服务 + + 最后,实现运行服务器的主要功能: + + ```typescript + 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`来构建你的服务器!这是让服务器连接的一个非常重要的步骤。 + + 现在让我们从现有的MCP主机Claude for Desktop测试您的服务器。 + + ## 使用Claude for Desktop测试您的服务器 + + + Claude for Desktop尚未在Linux上可用。Linux用户可以继续 [构建一个客户端](https://mcp.thinkinai.xyz/docs/quick-start/client-developers) 构建连接到我们刚刚构建的服务器的MCP客户端的教程。 + + + 首先,确保您安装了Claude for Desktop。 [您可以安装最新版本在这里。](https://claude.ai/download) 如果你已经有了Claude for Desktop, **确保它已更新到最新版本。** + + 我们需要为您想要使用的任何MCP服务器配置Claude for Desktop。为此,请在以下位置打开Claude for Desktop App配置 `~/Library/Application Support/Claude/claude_desktop_config.json` 在文本编辑器中。如果文件不存在,请确保创建该文件。 + + 例如,如果你有安装 [VS Code](https://code.visualstudio.com/) : + + + + ```bash + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` + + + + ```powershell + code $env:AppData\Claude\claude_desktop_config.json + ``` + + + + 然后,您将在“mcpServers”键中添加服务器。如果至少有一台服务器配置正确,MCP UI元素将仅显示在Claude for Desktop中。 + + 在这种情况下,我们将添加单个天气服务器,如下所示: + + + + + ```json Node + { + "mcpServers": { + "weather": { + "command": "node", + "args": [ + "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js" + ] + } + } + } + ``` + + + + + + ```json Node + { + "mcpServers": { + "weather": { + "command": "node", + "args": [ + "C:\\PATH\\TO\\PARENT\\FOLDER\\weather\\build\\index.js" + ] + } + } + } + ``` + + + + + 告诉 Claude for Desktop: + + 1. 这个 MCP server 称为 "weather" + 2. 通过运行启动它 `node /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js` + + 保存文件,然后重新启动 **Claude for Desktop**. - + + + + 这是一个基于Spring AI MCP自动配置和引导启动器的快速入门演示。 + 要了解如何手动创建同步和异步MCP服务器,请参阅 [Java SDK Server](https://modelcontextprotocol.io/sdk/java/mcp-server) 说明. + + + 让我们开始构建我们的天气服务器吧! + [你可以在这里找到我们将要构建的完整代码。](https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-stdio-server) + + 有关更多信息,请参阅 [MCP Server Boot Starter](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html) 参考文件。 + 有关手动MCP服务器实施,请参阅 [MCP Server Java SDK documentation](https://modelcontextprotocol.io/sdk/java/mcp-server). + + ### 系统要求 + + * 安装Java 17 or 更高版本. + * [Spring Boot 3.3.x](https://docs.spring.io/spring-boot/installing.html) 或更高版本 + + ### 设置您的环境 + + 使用[Spring Initializer](https://start.spring.io/) 启动项目。 + + 您需要添加以下依赖项: + + + + ```xml + + + org.springframework.ai + spring-ai-starter-mcp-server + + + + org.springframework + spring-web + + + ``` + + + + ```groovy + dependencies { + implementation platform("org.springframework.ai:spring-ai-starter-mcp-server") + implementation platform("org.springframework:spring-web") + } + ``` + + + + 然后通过设置应用程序属性来配置应用程序: + + + ```bash application.properties + spring.main.bannerMode=off + logging.pattern.console= + ``` + + ```yaml application.yml + logging: + pattern: + console: + spring: + main: + banner-mode: off + ``` + + + 这个 [服务器配置属性](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html#_configuration_properties) 记录所有可用的属性。 + + 现在,让我们开始构建您的服务器。 + + ## 构建您的服务 + + ### 天气服务 + + 让我们实现一个 [WeatherService.java](https://github.com/spring-projects/spring-ai-examples/blob/main/model-context-protocol/weather/starter-stdio-server/src/main/java/org/springframework/ai/mcp/sample/server/WeatherService.java) REST客户端来查询来自美国国家气象局API的数据: + + ```java + @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` 在应用程序上下文中自动注册服务的注释。 + + Spring AI的“@Tool”注释,使创建和维护MCP工具变得容易。 + + 自动配置将自动向MCP服务器注册这些工具。 + + ### 创建你的Boot应用 + + ```java + @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(); + } + } + ``` + + 使用“MethodToolcallback Provider”实用程序将“@Tools”转换为MCP服务器使用的可操作回调。 + + ### 运行服务 + + 最后,让我们构建服务器: + + ```bash + ./mvnw clean install + ``` + + 这将在“target”文件夹中生成一个“mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar”文件。 + + 现在让我们从现有的MCP主机Claude for Desktop测试您的服务器。 + + ## 使用Claude for Desktop测试您的服务器 + + + Claude for Desktop尚未在Linux上可用。 + + + 首先,确保您安装了Claude for Desktop。 + [您可以在此处安装最新版本。](https://claude.ai/download) 如果您已经拥有Claude for Desktop,**请确保它已更新到最新版本** + + 我们需要为您想要使用的任何MCP服务器配置Claude for Desktop。 + 为此,请在文本编辑器中打开位于`~/Library/ApplicationSupport/Claude/Claude_Desktop_config.json`的Claude for Desktop App配置。 + 如果文件不存在,请确保创建该文件。 + + 例如,如果你已经安装[VS Code](https://code.visualstudio.com/): + + + + ```bash + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` + + + + ```powershell + code $env:AppData\Claude\claude_desktop_config.json + ``` + + + + 然后,您将在`mcpServers`键中添加服务器。 + 如果至少有一台服务器配置正确,MCP UI元素将仅显示在Claude for Desktop中。 + 在这种情况下,我们将添加单个天气服务器,如下所示: + + + + + ```json 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" + ] + } + } + } + ``` + + + + ```json 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" + ] + } + } + } + ``` + + + + + 确保传入服务器的绝对路径。 + + + 此消息告诉Claude for Desktop: + 1.有一个名为“我的天气服务器”的MCP服务器 + 2.通过运行`java-jar/AABSOLUTE/PATH/To/PARENT/FOLDER/mcp-weather-stdio-server--0.1-SNAPSHOT.jar来启动它` + 保存文件,然后重新启动**Claude for Desktop**。 + + ## 使用Java客户端测试您的服务器 + + ### 手动创建MCP客户端 + + 使用`McpClient`连接到服务器: + + + ```java + 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客户端引导启动器 + + 使用`spring-ai-starter-mcp-client`依赖项创建一个新的引导启动器应用程序: + + ```xml + + org.springframework.ai + spring-ai-starter-mcp-client + + ``` + + 并将`spring.ai.mcp.client.sdio.servers配置`属性设置为指向您的`claude_desktop_config.json`。 + 您可以重新使用现有的Anthropic Desktop配置: + ```properties - logging.pattern.console= - spring.main.banner-mode=off + spring.ai.mcp.client.stdio.servers-configuration=file:PATH/TO/claude_desktop_config.json + ``` + + 当您启动客户端应用程序时,自动配置将从claude\_desktop\_config.json自动创建MCP客户端。 + + 有关更多信息,请参阅[MCP客户端启动程序](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-client-docs.html)参考文件。 + + ##更多Java MCP服务器示例 + + 这个[starter-webflux-server](https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-webflux-server) 演示如何使用SSE传输创建MCP服务器。 + 它展示了如何使用Spring Boot的自动配置功能定义和注册MCP工具、资源和提示。 + + + + 让我们开始构建我们的天气服务器吧![你可以在这里找到我们将要构建的完整代码。](https://github.com/modelcontextprotocol/kotlin-sdk/tree/main/samples/weather-stdio-server) + + ### 必备知识 + + 本快速入门假定您熟悉: + + * Kotlin + * LLMs like Claude + + ### 系统要求 + + * Java 17 or higher installed. + + ### 设置你的环境 + + 首先,让我们安装`java`和`gradle`(如果还没有的话)。 + + 您可以从[Oracle JDK官方网站]下载`java`(https://www.oracle.com/java/technologies/downloads/). + + 验证您的“java”安装: + + ```bash + java --version + ``` + + 现在,让我们创建并设置您的项目: + + + ```bash MacOS/Linux + # Create a new directory for our project + mkdir weather + cd weather + + # Initialize a new kotlin project + gradle init + ``` + + ```powershell Windows + # Create a new directory for our project + md weather + cd weather + + # Initialize a new kotlin project + gradle init + ``` + + + 运行`gradle init`后,您将看到创建项目的选项。 + + 选择**Application**作为项目类型,**Kotlin**作为编程语言,**Java17**作为Java版本。 + + 或者,您可以使用[IntelliJ IDEA项目向导]创建Kotlin应用程序(https://kotlinlang.org/docs/jvm-get-started.html). + + + ```kotlin 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") + } + ``` + + ```groovy build.gradle + def mcpVersion = '0.3.0' + def slf4jVersion = '2.0.9' + def 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" + } + ``` + + + 此外,将以下插件添加到您的构建脚本中: + + + ```kotlin build.gradle.kts + plugins { + kotlin("plugin.serialization") version "your_version_of_kotlin" + id("com.github.johnrengelman.shadow") version "8.1.1" + } + ``` + + ```groovy build.gradle + plugins { + id 'org.jetbrains.kotlin.plugin.serialization' version 'your_version_of_kotlin' + id 'com.github.johnrengelman.shadow' version '8.1.1' + } + ``` + + + 现在,让我们开始构建您的服务器。 + + ## 构建您的服务器 + + ### 设置实例 + + 添加服务器初始化功能: + + ```kotlin + // Main function to run the MCP server + fun `run mcp server`() { + // Create the MCP Server instance with a basic implementation + val server = Server( + Implementation( + name = "weather", // Tool name is "weather" + version = "1.0.0" // Version of the implementation + ), + ServerOptions( + capabilities = ServerCapabilities(tools = ServerCapabilities.Tools(listChanged = true)) + ) + ) + + // Create a transport using standard IO for server communication + val transport = StdioServerTransport( + System.`in`.asInput(), + System.out.asSink().buffered() + ) + + runBlocking { + server.connect(transport) + val done = Job() + server.onClose { + done.complete() + } + done.join() + } + } + ``` + + ### 天气API助手函数 + + 接下来,让我们添加用于查询和转换来自美国国家气象局API的响应的函数和数据类: + + ```kotlin + // Extension function to fetch forecast information for given latitude and longitude + suspend fun HttpClient.getForecast(latitude: Double, longitude: Double): List { + val points = this.get("/points/$latitude,$longitude").body() + val forecast = this.get(points.properties.forecast).body() + return forecast.properties.periods.map { period -> + """ + ${period.name}: + Temperature: ${period.temperature} ${period.temperatureUnit} + Wind: ${period.windSpeed} ${period.windDirection} + Forecast: ${period.detailedForecast} + """.trimIndent() + } + } + + // Extension function to fetch weather alerts for a given state + suspend fun HttpClient.getAlerts(state: String): List { + val alerts = this.get("/alerts/active/area/$state").body() + 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() + } + } + + @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) + + @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 + ) { + @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?, + ) + } + ``` + + ### 执行工具 + + 工具执行处理程序负责实际执行每个工具的逻辑。让我们添加它: + + + ```kotlin + // Create an HTTP client with a default request configuration and 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) + } + // Install content negotiation plugin for JSON serialization/deserialization + install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } + } + + // Register a tool to fetch weather alerts by state + server.addTool( + name = "get_alerts", + description = """ + Get weather alerts for a US state. Input is Two-letter US state code (e.g. CA, NY) + """.trimIndent(), + inputSchema = Tool.Input( + properties = buildJsonObject { + putJsonObject("state") { + put("type", "string") + put("description", "Two-letter US state code (e.g. 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) }) + } + + // Register a tool to fetch weather forecast by latitude and longitude + server.addTool( + name = "get_forecast", + description = """ + Get weather forecast for a specific latitude/longitude + """.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) }) + } + ``` + + ### 运行服务 + + 最后,实现运行服务器的主要功能: + + ```kotlin + fun main() = `run mcp server`() + ``` + + 一定要跑`/gradlew build`来构建你的服务器。这是让服务器连接的一个非常重要的步骤。 + + 现在让我们从现有的MCP主机Claude for Desktop测试您的服务器。 + + ## 使用Claude for Desktop测试您的服务器 + + + Claude for Desktop尚未在Linux上可用。Linux用户可以继续学习[构建客户端](https://mcp.thinkinai.xyz/docs/quick-start/client-developers)教程,以构建一个连接到我们刚刚构建的服务器的MCP客户端。 + + + 首先,确保您安装了Claude for Desktop。[您可以安装最新版本在这里。](https://claude.ai/download)如果您已经拥有Claude for Desktop,**请确保它已更新到最新版本** + + 我们需要为您想要使用的任何MCP服务器配置Claude for Desktop。 + `~/Library/ApplicationSupport/Claude/Claude_Desktop_config.json`的Claude for Desktop App配置。 + + 如果文件不存在,请确保创建该文件。 + + 例如,如果你有[VS code](https://code.visualstudio.com/)已安装: + + + ```bash MacOS/Linux + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` + + ```powershell Windows + code $env:AppData\Claude\claude_desktop_config.json + ``` + + + 然后,您将在`mcpServers`键中添加服务器。 + + 如果至少有一台服务器配置正确,MCP UI元素将仅显示在Claude for Desktop中。 + + 在这种情况下,我们将添加单个天气服务器,如下所示: + + + ```json MacOS/Linux + { + "mcpServers": { + "weather": { + "command": "java", + "args": [ + "-jar", + "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/libs/weather-0.1.0-all.jar" + ] + } + } + } + ``` + + ```json Windows + { + "mcpServers": { + "weather": { + "command": "java", + "args": [ + "-jar", + "C:\\PATH\\TO\\PARENT\\FOLDER\\weather\\build\\libs\\weather-0.1.0-all.jar" + ] + } + } + } + ``` + + + 这将告诉Claude for Desktop: + + 1.有一个名为“天气”的MCP服务器 + + 2.通过运行`java-jar/AABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/libs/weather-0.1.0-all.jar启动它` + + 保存文件,然后重新启动**Claude for Desktop**。 + + + + 让我们开始构建我们的天气服务器吧![你可以在这里找到我们将要构建的完整代码。](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/QuickstartWeatherServer) + + ### 必备知识 + + 本快速入门假定您熟悉: + + * C# + * LLMs like Claude + * .NET 8 or higher + + ### 系统要求 + + * 安装[.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) 或更高版本。 + + ### 配置您的系统环境 + + 首先,让我们安装`dotnet`,如果你还没有的话。您可以从[微软官方.NET网站]下载`dotnet`(https://dotnet.microsoft.com/download/). 验证您的`dot`安装: + + ```bash + dotnet --version ``` + + 现在,让我们创建并设置您的项目: + + + ```bash MacOS/Linux + # Create a new directory for our project + mkdir weather + cd weather + # Initialize a new C# project + dotnet new console + ``` + + ```powershell Windows + # Create a new directory for our project + mkdir weather + cd weather + # Initialize a new C# project + dotnet new console + ``` + + + 运行`dotnetnew console `后,您将看到一个新的C#项目。 + + 您可以在您喜欢的IDE中打开项目,例如[Visual Studio](https://visualstudio.microsoft.com/)或[骑手](https://www.jetbrains.com/rider/). + + 或者,您可以使用[Visual Studio项目向导]创建C#应用程序(https://learn.microsoft.com/en-us/visualstudio/get-started/csharp/tutorial-console?view=vs-2022). + + 创建项目后,为模型上下文协议SDK添加NuGet包并托管: + + ```bash + # Add the Model Context Protocol SDK NuGet package + dotnet add package ModelContextProtocol --prerelease + # Add the .NET Hosting NuGet package + dotnet add package Microsoft.Extensions.Hosting + ``` + + 现在,让我们开始构建您的服务器。 + + ## 构建您的服务 + + 在项目中打开`Program.cs`文件,并用以下代码替换其内容: + + ```csharp + 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`。这可确保服务器不会向控制台写入任何其他消息。这仅对使用STDIO传输的服务器是必要的。 + + + 此代码设置了一个基本的控制台应用程序,该应用程序使用模型上下文协议SDK创建具有标准I/O传输的MCP服务器。 + + ### 天气API助手函数 + + 接下来,使用工具执行处理程序定义一个类,用于查询和转换来自美国国家气象局API的响应: + + ```csharp + 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 GetAlerts( + HttpClient client, + [Description("The US state to get alerts for.")] string state) + { + var jsonElement = await client.GetFromJsonAsync($"/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 GetForecast( + HttpClient client, + [Description("Latitude of the location.")] double latitude, + [Description("Longitude of the location.")] double longitude) + { + var jsonElement = await client.GetFromJsonAsync($"/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()} + """)); + } + } + ``` + + ### 运行服务 + + 最后,使用以下命令运行服务器: + + ```bash + dotnet run + ``` + + 这将启动服务器并监听标准输入/输出上的传入请求。 + + ## 使用Claude for Desktop测试您的服务器 + + + Claude for Desktop尚未在Linux上可用。Linux用户可以继续学习[构建客户端](https://mcp.thinkinai.xyz/docs/quick-start/client-developers)教程,以构建一个连接到我们刚刚构建的服务器的MCP客户端。 + + + 首先,确保您安装了Claude for Desktop。[您可以安装最新版本 在这里。](https://claude.ai/download)如果您已经拥有Claude for Desktop,**请确保它已更新到最新版本** + + 我们需要为您想要使用的任何MCP服务器配置Claude for Desktop。为此,请在文本编辑器中打开位于`~/Library/ApplicationSupport/Claude/Claude_Desktop_config.json`的Claude for Desktop App配置。如果文件不存在,请确保创建该文件。 + + 例如,如果你有[VS代码](https://code.visualstudio.com/)已安装: + + + + + ```bash + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` + + + + ```powershell + code $env:AppData\Claude\claude_desktop_config.json + ``` + + + + 然后,您将在`mcpServers`键中添加服务器。如果至少有一台服务器配置正确,MCP UI元素将仅显示在Claude for Desktop中。 + 在这种情况下,我们将添加单个天气服务器,如下所示: + + + + ```json + { + "mcpServers": { + "weather": { + "command": "dotnet", + "args": [ + "run", + "--project", + "/ABSOLUTE/PATH/TO/PROJECT", + "--no-build" + ] + } + } + } + ``` + + + + ```json + { + "mcpServers": { + "weather": { + "command": "dotnet", + "args": [ + "run", + "--project", + "C:\\ABSOLUTE\\PATH\\TO\\PROJECT", + "--no-build" + ] + } + } + } + ``` + + + + 这将告诉 Claude for Desktop: + + 1.有一个名为“天气”的MCP服务器 + + 2.通过运行`dotnetrun/AABSOLUTE/PATH/TO/PROJECT启动它` ,保存文件,然后重新启动**Claude for Desktop**。 -[服务器配置属性](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html#_configuration_properties)记录了所有可用的属性。现在,让我们开始构建您的服务器。 +### 使用命令进行测试 + +让我们确保Claude for Desktop正在使用我们在`天气`服务器中公开的两个工具。你可以通过寻找锤子来做到这一点 icon: + + +![ImaImageZoomge](/server-developers-03.png) + +点击锤子图标后,您应该看到列出了两个工具: + +![ImaImageZoomge](/server-developers-04.png) + +如果您的服务器没有被Claude for Desktop接收,请继续进行[故障排除]部分以获取调试提示。 + +如果锤子图标已显示,您现在可以在Claude for Desktop中运行以下命令来测试您的服务器: + +* 萨克拉门托的天气怎么样? + +* 德克萨斯州的活跃天气警报是什么? + + +![ImaImageZoomge](/server-developers-05.png) + + +![ImaImageZoomge](/server-developers-06.png) + + + 由于这是美国国家气象局,因此查询仅适用于美国各地。 + + +## hood下面发生了什么 + +当你问一个问题时: + +1.客户将您的问题发送给Claude + +2.Claude分析可用的工具并决定使用哪一个 + +3.客户端通过MCP服务器执行所选工具 + +4.结果被发送回克劳德 + +5.克劳德制定了一个自然语言反应 + +6.响应已显示给您! + + +## 故障排除 + + + + **从Claude获取桌面日志** + + 与MCP相关的Claude.app日志记录被写入`~/Library/Logs/Claude`中的日志文件: + + *`mcp.log`将包含有关mcp连接和连接失败的一般日志记录。 + + *名为`mcp-server SERVERNAME.log`的文件将包含来自指定服务器的错误(stderr)日志记录。 + + 您可以运行以下命令列出最近的日志,并跟踪任何新日志: + + ```bash + # Check Claude's logs for errors + tail -n 20 -f ~/Library/Logs/Claude/mcp*.log + ``` + + **服务器未在Claude中显示** + + 1.检查`claude_desktop_config.json `文件语法 + + 2.确保项目的路径是绝对的,而不是相对的 + + 3.完全重启Claude for Desktop + + **工具调用无声失败** + + 如果克劳德试图使用这些工具,但失败了: + + 1.检查克劳德的日志是否有错误 + + 2.验证您的服务器构建和运行没有错误 + + 3.尝试重新启动Claude for Desktop + + **这些都不起作用。我该怎么办** + + 请参阅我们的[调试指南](https://mcp.thinkinai.xyz/docs/tutorials/debugging),了解更好的调试工具和更详细的指导。 + + + + **错误:检索网格点数据失败** + + 这通常意味着: -# 构建你的服务器 -## 天气服务 + 1.坐标在美国境外 + 2.NWS API出现问题 + 3.你的价格有限 + 修复: + *验证您使用的是美国坐标 + *在请求之间添加一点延迟 + *查看NWS API状态页面 + **错误:\[STATE]没有活动警报** + 这不是一个错误,只是意味着该州目前没有天气警报。尝试不同的状态或在恶劣天气下检查。 + + + + 有关更高级的故障排除,请查看我们的[调试MCP]指南(https://mcp.thinkinai.xyz/docs/tutorials/debugging) + +## 下一步行动 + + + 了解如何构建可以连接到服务器的MCP客户端 + + + 查看我们的官方MCP服务器和实现库 + + + 了解如何有效地调试MCP服务器和集成 + + + 学习如何使用像Claude这样的LLM来加速您的MCP开发 + + diff --git a/content/docs/(get-started)/quick-start/server-developers.mdx.bak b/content/docs/(get-started)/quick-start/server-developers.mdx.bak new file mode 100644 index 0000000..2d9291d --- /dev/null +++ b/content/docs/(get-started)/quick-start/server-developers.mdx.bak @@ -0,0 +1,249 @@ +--- +title: 服务器开发者 +description: 快速开始 - 服务器开发者 +--- + +开始构建自己的服务器,以便在Claude中用于Desktop和其他客户端。 + +在本教程中,我们将构建一个简单的MCP天气服务器,并将其连接到主机Claude for Desktop。我们将从基本设置开始,然后进行更复杂的用例。 + +## 我们将要干什么 +许多LLM(包括Claude)目前无法获取预报和恶劣天气警报。让我们用MCP解决这个问题。 + +我们将构建一个server,该server提供两个工具:get-alerts and get-forecast。然后,我们将服务器连接到MCP主机(在本例中使用Claude for Desktop): +![ImaImageZoomge](/server-developers-01.png) +![ImaImageZoomge](/server-developers-02.png) + + +服务可以连接任何客户端。为了简单起见,我们在这里选择了Claude作为桌面,但我们也有关于[建立自己的client](https://mcp.thinkinai.xyz/docs/quick-start/client-developers)建立自己的client的指南,以及[这里的其他客户列表](https://mcp.thinkinai.xyz/docs/example-clients)。 + + +``` +为什么选择Claude for DeskTop而不是Claude.ai? +由于服务器是本地运行的,MCP目前只支持桌面主机。远程主机正在积极开发中。 +``` + +## MCP核心概念 +MCP服务器可以提供三种主要功能: + 1. **Resources**:客户端可以读取的类文件数据(如API响应或文件内容) + 2. **Tools**:LLM可以调用的函数(经用户批准) + 3. **Prompts**:帮助用户完成特定任务的预先编写的模板 +本教程将主要关注工具。 + +### 基于Java +这是一个基于Spring AI MCP自动配置和引导启动器的快速入门演示。要了解如何手动创建同步和异步MCP服务器,请参阅[Java SDK服务器](https://modelcontextprotocol.io/sdk/java/mcp-server)文档。 + +让我们开始构建一个天气服务![你可以在这里找到我们将要构建的完整代码](https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-stdio-server)【MCP官方代码,支持查询美国天气情况】。 +[这里的代码也可以](https://github.com/weishuai8888/spring-ai-examples)【fork官方代码后调整了逻辑,支持查询全世界的天气情况】 + +有关更多信息,请参阅[MCP Server Boot Starter](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html)参考文档。有关手动MCP服务器实现,请参阅[MCP Server Java SDK文档](https://modelcontextprotocol.io/sdk/java/mcp-server)。 + +#### 系统要求 +~ + ● 已安装Java 17或更高版本 + ● [Spring Boot 3.3.x](https://docs.spring.io/spring-boot/installing.html) 或更高版本 + +#### 设置你的环境变量 +使用 [Spring Initizer](https://start.spring.io) 启动项目。 + +你需要添加以下的依赖项: + +import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; + + + + ```xml + + + org.springframework.ai + spring-ai-starter-mcp-server + + + + org.springframework + spring-web + + + ``` + + + ```xml + dependencies { + implementation platform("org.springframework.ai:spring-ai-starter-mcp-server") + implementation platform("org.springframework:spring-web") + } + ``` + + + +然后通过设置应用程序属性来配置应用程序 + + + + ```yaml + logging: + pattern: + console: + spring: + main: + banner-mode: off + ``` + + + ```properties + logging.pattern.console= + spring.main.banner-mode=off + ``` + + + +[服务器配置属性](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html#_configuration_properties)记录了所有可用的属性。现在,让我们开始构建您的服务器。 + +# 构建你的服务器 +## 天气服务 +让我们实现一个WeatherService.java,它使用 REST 客户端从国家气象局 API 查询数据: +``` java +@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注释会自动在应用程序上下文中注册服务。Spring AI@Tool注释使创建和维护 MCP 工具变得容易。 + +自动配置将自动向 MCP 服务器注册这些工具。 + +## 创建启动应用程序 +``` java +@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程序将其转换@Tools为 MCP 服务器使用的可操作回调。 + +## 运行服务器 +最后,让我们构建服务器: +```bash +./mvnw clean install +``` +这将在文件夹mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar中生成一个文件target。 + +现在让我们从现有的 MCP 主机 Claude for Desktop 测试您的服务器。 + +# 使用 Claude for Desktop 测试你的服务器 +Claude for Desktop 尚未在 Linux 上提供。 + +首先,请确保您已安装 Claude for Desktop。 [您可以在此处安装最新版本](https://www.anthropic.com/app-unavailable-in-region)。如果您已经安装了 Claude for Desktop,[请确保它已更新到最新版本](https://www.anthropic.com/app-unavailable-in-region)。 + +我们需要为您想要使用的 MCP 服务器配置 Claude for Desktop。为此,请在~/Library/Application Support/Claude/claude_desktop_config.json文本编辑器中打开您的 Claude for Desktop App 配置。如果该文件不存在,请务必创建。 + +例如,如果你安装了VS Code: + + + + ```shell + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` + + + ```bash + code $env:AppData\Claude\claude_desktop_config.json + ``` + + + +然后您将在密钥中添加服务器mcpServers。只有至少一台服务器配置正确,MCP UI 元素才会显示在 Claude for Desktop 中。 + +在这种情况下,我们将像这样添加单个天气服务器: + + + ```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" + ] + } + } + } + ``` + + + ```bash + { + "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" + ] + } + } + } + ``` + + +确保传递了服务器的绝对路径。 + +这告诉 Claude for Desktop: + +1. 有一个名为“my-weather-server”的 MCP 服务器 +2. 通过运行来启动它java -jar /ABSOLUTE/PATH/TO/PARENT/FOLDER/mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar + +保存文件并重新启动Claude for Desktop。 + + + + + + + + diff --git a/content/docs/concepts/core-architecture.mdx b/content/docs/concepts/core-architecture.mdx new file mode 100644 index 0000000..af57329 --- /dev/null +++ b/content/docs/concepts/core-architecture.mdx @@ -0,0 +1,331 @@ +# 核心架构 + +> 了解 MCP 如何连接客户端、服务器和 LLM + +模型上下文协议 (MCP) 建立在灵活、可扩展的架构上,可实现 LLM 应用程序和集成之间的无缝通信。本文档涵盖核心架构组件和概念。 + +## 概述 + +MCP 遵循客户端-服务器架构,其中: + +* **主机** 是发起连接的 LLM 应用程序(如 Claude Desktop 或 IDE) +* **客户端**在主机应用程序内部与服务器保持 1:1 连接 +* **服务器** 向客户提供上下文、工具和提示 + + +![ImaImageZoomge](/core-architecture-01.png) + +## 核心组件 + +### 协议层 + +协议层处理消息框架、请求/响应链接和高级通信模式。 + +import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; + + + + ```typescript + class Protocol { + // Handle incoming requests + setRequestHandler(schema: T, handler: (request: T, extra: RequestHandlerExtra) => Promise): void + + // Handle incoming notifications + setNotificationHandler(schema: T, handler: (notification: T) => Promise): void + + // Send requests and await responses + request(request: Request, schema: T, options?: RequestOptions): Promise + + // Send one-way notifications + notification(notification: Notification): Promise + } + ``` + + + + ```python + class Session(BaseSession[RequestT, NotificationT, ResultT]): + async def send_request( + self, + request: RequestT, + result_type: type[Result] + ) -> Result: + """ + Send request and wait for response. Raises McpError if response contains error. + """ + # Request handling implementation + + async def send_notification( + self, + notification: NotificationT + ) -> None: + """Send one-way notification that doesn't expect response.""" + # Notification handling implementation + + async def _received_request( + self, + responder: RequestResponder[ReceiveRequestT, ResultT] + ) -> None: + """Handle incoming request from other side.""" + # Request handling implementation + + async def _received_notification( + self, + notification: ReceiveNotificationT + ) -> None: + """Handle incoming notification from other side.""" + # Notification handling implementation + ``` + + + +主要课程包括: + +- Protocol +- Client +- Server + +### 传输层 + +传输层处理客户端和服务器之间的实际通信。MCP 支持多种传输机制: + +1. **Stdio 传输** + * 使用标准输入/输出进行通信 + * 适合本地流程 + +2. **带有 SSE 传输的 HTTP** + * 使用服务器发送事件来发送服务器到客户端的消息 + * HTTP POST 用于客户端到服务器的消息 + +所有传输均使用 [JSON-RPC](https://www.jsonrpc.org/) 2.0 来交换消息。有关模型上下文协议消息格式的详细信息,请参阅 [规范](/specification/)。 + +### 消息类型 + +MCP 有以下主要类型的消息: + +1. **请求**期望对方做出回应: + ```typescript + interface Request { + method: string; + params?: { ... }; + } + ``` + +2. **结果**是对请求的成功响应: + ```typescript + interface Result { + [key: string]: unknown; + } + ``` + +3. **错误**表示请求失败: + ```typescript + interface Error { + code: number; + message: string; + data?: unknown; + } + ``` + +4. **通知**是单向消息,不期望得到响应: + ```typescript + interface Notification { + method: string; + params?: { ... }; + } + ``` + +## 连接生命周期 + +### 1. 初始化 + + +![ImaImageZoomge](/core-architecture-02.png) + +1. 客户端发送带有协议版本和功能的“初始化”请求 +2. 服务器以其协议版本和功能进行响应 +3. 客户端发送“已初始化”通知作为确认 +4. 开始正常消息交换 + +### 2. 消息交换 + +初始化后,支持以下模式: + +* **请求-响应**:客户端或服务器发送请求,对方响应 +* **通知**:任何一方发送单向消息 + +### 3. 终止 + +任何一方都可以终止连接: + +* 通过 `close()` 干净关闭 +* 传输断开 +* 错误情况 + +错误处理 + +MCP 定义了以下标准错误代码: + +```typescript +枚举错误代码 { + // 标准 JSON-RPC 错误代码 + 解析错误 = -32700, + 无效请求 = -32600, + 方法未找到 = -32601, + 无效参数 = -32602, + 内部错误 = -32603 +} +``` + +SDK 和应用程序可以定义自己的 -32000 以上的错误代码。 + +错误通过以下方式传播: + +* 对请求的错误响应 +* 传输中的错误事件 +* 协议级错误处理程序 + +## 实现示例 + +以下是实现 MCP 服务器的一个基本示例: + + + + ```typescript + import { Server } from "@modelcontextprotocol/sdk/server/index.js"; + import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; + + const server = new Server({ + name: "example-server", + version: "1.0.0" + }, { + capabilities: { + resources: {} + } + }); + + // Handle requests + server.setRequestHandler(ListResourcesRequestSchema, async () => { + return { + resources: [ + { + uri: "example://resource", + name: "Example Resource" + } + ] + }; + }); + + // Connect transport + const transport = new StdioServerTransport(); + await server.connect(transport); + ``` + + + + ```python + import asyncio + import mcp.types as types + from mcp.server import Server + from mcp.server.stdio import stdio_server + + app = Server("example-server") + + @app.list_resources() + async def list_resources() -> list[types.Resource]: + return [ + types.Resource( + uri="example://resource", + name="Example Resource" + ) + ] + + async def main(): + async with stdio_server() as streams: + await app.run( + streams[0], + streams[1], + app.create_initialization_options() + ) + + if __name__ == "__main__": + asyncio.run(main()) + ``` + + + +## 最佳实践 + +### 运输选择 + +1. **本地通信** + * 使用 stdio 传输本地进程 + * 高效同机通信 + * 简单的流程管理 + +2. **远程通信** + * 对于需要 HTTP 兼容性的场景使用 SSE + * 考虑包括身份验证和授权在内的安全影响 + +### 消息处理 + +1. **请求处理** + * 彻底验证输入 + * 使用类型安全模式 + * 妥善处理错误 + * 实现超时 + +2. **进度报告** + * 对长时间操作使用进度标记 + * 逐步报告进度 + * 包含已知的总体进度 + +3. **错误管理** + * 使用适当的错误代码 + * 包含有用的错误消息 + * 错误时清理资源 + +## 安全考虑 + +1. **运输安全** + * 使用 TLS 进行远程连接 + * 验证连接来源 + * 需要时实施身份验证 + +2. **消息验证** + * 验证所有传入消息 + * 净化输入 + * 检查消息大小限制 + * 验证 JSON-RPC 格式 + +3. **资源保护** + * 实施访问控制 + * 验证资源路径 + * 监控资源使用情况 + * 速率限制请求 + +4. **错误处理** + * 不要泄露敏感信息 + * 记录与安全相关的错误 + * 实施适当的清理 + * 处理 DoS 场景 + +## 调试和监控 + +1. **日志记录** + * 记录协议事件 + * 跟踪消息流 + * 监控性能 + * 记录错误 + +2. **诊断** + * 实施健康检查 + * 监控连接状态 + * 跟踪资源使用情况 + * 概况表现 + +3. **测试** + * 测试不同的传输方式 + * 验证错误处理 + * 检查边缘情况 + * 负载测试服务器 \ No newline at end of file diff --git a/public/core-architecture-01.png b/public/core-architecture-01.png new file mode 100644 index 0000000..5a2663c Binary files /dev/null and b/public/core-architecture-01.png differ diff --git a/public/core-architecture-02.png b/public/core-architecture-02.png new file mode 100644 index 0000000..f12fa2c Binary files /dev/null and b/public/core-architecture-02.png differ diff --git a/public/server-developers-03.png b/public/server-developers-03.png new file mode 100644 index 0000000..fb87d91 Binary files /dev/null and b/public/server-developers-03.png differ diff --git a/public/server-developers-04.png b/public/server-developers-04.png new file mode 100644 index 0000000..fb87d91 Binary files /dev/null and b/public/server-developers-04.png differ diff --git a/public/server-developers-05.png b/public/server-developers-05.png new file mode 100644 index 0000000..6d0b714 Binary files /dev/null and b/public/server-developers-05.png differ diff --git a/public/server-developers-06.png b/public/server-developers-06.png new file mode 100644 index 0000000..be14c3d Binary files /dev/null and b/public/server-developers-06.png differ