服務器開發

在本教程中,我們將構建一個簡單的MCP天氣服務器並將其連接到宿主應用程序Claude桌面版。我們將從基本設置開始,然後逐步進行到更復雜的用例。

我們將構建什麼

許多大語言模型(包括Claude)目前沒有獲取天氣預報和嚴重天氣警報的能力。讓我們使用MCP來解決這個問題!

我們將構建一個服務器,提供兩個工具:get-alertsget-forecast。然後我們將服務器連接到MCP宿主應用程序(在本例中是Claude桌面版):

服務器可以連接到任何客戶端。我們在這裡選擇Claude桌面版是為了簡化,但我們也提供構建自己的客戶端的指南,以及其他客戶端列表

為什麼是Claude桌面版而不是Claude.ai?

因為服務器是本地運行的,MCP目前只支持桌面宿主。遠程宿主正在積極開發中。

服務器可以連接到任何客戶端。我們在這裡選擇Claude桌面版是為了簡單起見,但我們也有關於構建自己的客戶端的指南以及其他客戶端列表
為什麼是Claude桌面版而不是Claude.ai?
因為服務器是本地運行的,MCP目前只支持桌面宿主應用程序。遠程宿主應用程序正在積極開發中。

MCP核心概念

MCP服務器可以提供三種主要類型的功能:

  1. 資源:類似文件的數據,可以被客戶端讀取(如API響應或文件內容)
  2. 工具:可以被大語言模型調用的函數(需要用戶批准)
  3. 提示:預先編寫的模板,幫助用戶完成特定任務

本教程將主要關注工具。

讓我們開始構建我們的天氣服務器!您可以在這裡找到我們將要構建的完整代碼。

前提知識

本快速入門假設您熟悉:

  • Python
  • 像Claude這樣的大語言模型

系統要求

  • 安裝Python 3.10或更高版本。
  • 您必須使用Python MCP SDK 1.2.0或更高版本。

設置您的環境

首先,讓我們安裝uv並設置我們的Python項目和環境:

安裝 uv
curl -LsSf https://astral.sh/uv/install.sh | sh
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

安裝後請確保重啟您的終端,以確保uv命令被正確識別。

現在,讓我們創建並設置我們的項目:

創建項目
# 為我們的項目創建一個新目錄
uv init weather
cd weather

# 創建虛擬環境並激活它
uv venv
source .venv/bin/activate

# 安裝依賴
uv add "mcp[cli]" httpx

# 創建我們的服務器文件
touch weather.py
# 創建我們的服務器文件
touch 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發出GET請求,處理錯誤並返回JSON響應"""
    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:
    """獲取指定州的天氣警報(使用兩字母州代碼如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: 位置的經度
    """
    # 首先獲取預報網格端點
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)

    if not points_data:
        return "無法為此位置獲取預報數據。"

    # 從點響應中獲取預報URL
    forecast_url = points_data["properties"]["forecast"]
    forecast_data = await make_nws_request(forecast_url)

    if not forecast_data:
        return "無法獲取詳細預報。"

    # 將時段格式化為可讀預報
    periods = forecast_data["properties"]["periods"]
    forecasts = []
    for period in periods[:5]:  # 只顯示接下來的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以確認一切正常工作。

現在讓我們使用現有的MCP宿主應用程序Claude桌面版測試您的服務器。

使用Claude桌面版測試您的服務器

Claude桌面版尚未在Linux上可用。Linux用戶可以通過構建客戶端教程創建自定義客戶端。

首先,確保您已安裝Claude桌面版。您可以在這裡安裝最新版本。 如果您已經安裝了Claude桌面版,請確保它已更新到最新版本。

我們需要為您想要使用的任何MCP服務器配置Claude桌面版。為此,請在文本編輯器中打開您的Claude桌面版應用程序配置文件,位於~/Library/Application Support/Claude/claude_desktop_config.json。如果該文件不存在,請創建它。

例如,如果您已安裝VS Code

打開配置文件
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
code %APPDATA%\Claude\claude_desktop_config.json

然後您將在mcpServers鍵中添加您的服務器。只有在至少一個服務器正確配置的情況下,MCP UI元素才會在Claude桌面版中顯示。

在這種情況下,我們將添加我們的單個天氣服務器,如下所示:

配置Claude桌面版
{
    "mcpServers": {
        "weather": {
            "command": "uv",
            "args": [
                "--directory",
                "ABSOLUTE_PATH_PLACEHOLDER",
                "run",
                "weather.py"
            ]
        }
    }
}
{
    "mcpServers": {
        "weather": {
            "command": "uv",
            "args": [
                "--directory",
                "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather",
                "run",
                "weather.py"
            ]
        }
    }
}
您可能需要在command字段中放入uv可執行文件的完整路徑。您可以通過在MacOS/Linux上運行which uv或在Windows上運行where uv來獲取此路徑。
確保您傳入服務器的絕對路徑。

這告訴Claude桌面版:

  1. 有一個名為"weather"的MCP服務器
  2. 使用uv命令運行該服務器
  3. 在指定目錄中運行服務器
  4. 使用weather.py作為入口點

現在,重啟Claude桌面版。您應該會在工具欄中看到一個新的MCP圖標。點擊它,您將看到我們的天氣工具!

下一步

恭喜!您已經成功構建了您的第一個MCP服務器。這只是開始 - 您可以:

讓我們開始構建我們的天氣服務器!您可以在這裡找到我們將要構建的完整代碼。

前提知識

本快速入門假設您熟悉:

  • Node.js
  • 像Claude這樣的大語言模型

系統要求

  • 安裝Node.js 18或更高版本
  • 您必須使用Node.js MCP SDK 1.2.0或更高版本

設置您的環境

首先,讓我們創建一個新的Node.js項目:

創建項目
# 創建一個新目錄
mkdir weather
cd weather

# 初始化項目
npm init -y

# 安裝依賴
npm install @mcp/node node-fetch

# 創建我們的服務器文件
touch weather.js

現在讓我們開始構建您的服務器。

構建您的服務器

導入包並設置實例

將這些添加到您的weather.js頂部:

const { MCPServer } = require('@mcp/node');
const fetch = require('node-fetch');

// 初始化MCP服務器
const mcp = new MCPServer('weather');

// 常量
const NWS_API_BASE = 'https://api.weather.gov';
const USER_AGENT = 'weather-app/1.0';

輔助函數

接下來,讓我們添加我們的輔助函數,用於查詢和格式化來自國家氣象服務API的數據:

async function makeNWSRequest(url) {
    const headers = {
        'User-Agent': USER_AGENT,
        'Accept': 'application/geo+json'
    };
    try {
        const response = await fetch(url, { headers });
        if (!response.ok) throw new Error('API request failed');
        return await response.json();
    } catch (error) {
        return null;
    }
}

function formatAlert(feature) {
    const props = feature.properties;
    return `
Event: ${props.event || 'Unknown'}
Area: ${props.areaDesc || 'Unknown'}
Severity: ${props.severity || 'Unknown'}
Description: ${props.description || 'No description available'}
Instructions: ${props.instruction || 'No specific instructions provided'}
`;
}

實現工具執行

工具執行處理程序負責實際執行每個工具的邏輯。讓我們添加它:

// 獲取警報工具
mcp.tool({
    name: 'get-alerts',
    description: '獲取美國州的天氣警報',
    parameters: {
        state: {
            type: 'string',
            description: '兩字母美國州代碼(例如CA、NY)'
        }
    },
    async execute({ state }) {
        const url = `${NWS_API_BASE}/alerts/active/area/${state}`;
        const data = await makeNWSRequest(url);

        if (!data || !data.features) {
            return '無法獲取警報或未找到警報。';
        }

        if (data.features.length === 0) {
            return '該州沒有活動警報。';
        }

        const alerts = data.features.map(formatAlert);
        return alerts.join('\n---\n');
    }
});

// 獲取預報工具
mcp.tool({
    name: 'get-forecast',
    description: '獲取位置的天氣預報',
    parameters: {
        latitude: {
            type: 'number',
            description: '位置的緯度'
        },
        longitude: {
            type: 'number',
            description: '位置的經度'
        }
    },
    async execute({ latitude, longitude }) {
        // 首先獲取預報網格端點
        const pointsUrl = `${NWS_API_BASE}/points/${latitude},${longitude}`;
        const pointsData = await makeNWSRequest(pointsUrl);

        if (!pointsData) {
            return '無法為此位置獲取預報數據。';
        }

        // 從點響應中獲取預報URL
        const forecastUrl = pointsData.properties.forecast;
        const forecastData = await makeNWSRequest(forecastUrl);

        if (!forecastData) {
            return '無法獲取詳細預報。';
        }

        // 將時段格式化為可讀預報
        const periods = forecastData.properties.periods;
        const forecasts = periods.slice(0, 5).map(period => `
${period.name}:
Temperature: ${period.temperature}°${period.temperatureUnit}
Wind: ${period.windSpeed} ${period.windDirection}
Forecast: ${period.detailedForecast}
`);

        return forecasts.join('\n---\n');
    }
});

運行服務器

最後,讓我們初始化並運行服務器:

// 初始化並運行服務器
mcp.start();

您的服務器已完成!運行node weather.js以確認一切正常工作。

現在讓我們使用現有的MCP宿主應用程序Claude桌面版測試您的服務器。

使用Claude桌面版測試您的服務器

Claude桌面版尚未在Linux上可用。Linux用戶可以繼續學習構建客戶端教程,以構建連接到我們剛剛構建的服務器的MCP客戶端。

首先,確保您已安裝Claude桌面版。您可以在這裡安裝最新版本。 如果您已經安裝了Claude桌面版,請確保它已更新到最新版本。

我們需要為您想要使用的任何MCP服務器配置Claude桌面版。為此,請在文本編輯器中打開您的Claude桌面版應用程序配置文件,位於~/Library/Application Support/Claude/claude_desktop_config.json。如果該文件不存在,請創建它。

例如,如果您已安裝VS Code

打開配置文件
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
code $env:AppData\Claude\claude_desktop_config.json

然後您將在mcpServers鍵中添加您的服務器。只有在至少一個服務器正確配置的情況下,MCP UI元素才會在Claude桌面版中顯示。

在這種情況下,我們將添加我們的單個天氣服務器,如下所示:

配置Claude桌面版
{
    "mcpServers": {
        "weather": {
            "command": "node",
            "args": [
                "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/weather.js"
            ]
        }
    }
}
{
    "mcpServers": {
        "weather": {
            "command": "node",
            "args": [
                "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather\\weather.js"
            ]
        }
    }
}
確保您傳入服務器的絕對路徑。

這告訴Claude桌面版:

  1. 有一個名為"weather"的MCP服務器
  2. 使用node命令運行該服務器
  3. 使用weather.js作為入口點

現在,重啟Claude桌面版。您應該會在工具欄中看到一個新的MCP圖標。點擊它,您將看到我們的天氣工具!

下一步

恭喜!您已經成功構建了您的第一個MCP服務器。這只是開始 - 您可以:

讓我們開始構建我們的天氣服務器!您可以在這裡找到我們將要構建的完整代碼。

前提知識

本快速入門假設您熟悉:

  • Java
  • 像Claude這樣的大語言模型

系統要求

  • 安裝Java 17或更高版本
  • 您必須使用Java MCP SDK 1.2.0或更高版本

設置您的環境

首先,讓我們創建一個新的Maven項目。在您喜歡的IDE中創建一個新的Maven項目,或者使用以下命令:

創建項目
mvn archetype:generate \
    -DgroupId=com.example \
    -DartifactId=weather \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false

然後,將以下依賴項添加到您的pom.xml文件中:

<dependencies>
    <dependency>
        <groupId>dev.mcp</groupId>
        <artifactId>mcp-java</artifactId>
        <version>1.2.0</version>
    </dependency>
</dependencies>

現在讓我們開始構建您的服務器。

構建您的服務器

創建主類

src/main/java/com/example目錄中創建一個新的Weather.java文件:

package com.example;

import dev.mcp.MCPServer;
import dev.mcp.Tool;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;

public class Weather {
    private static final String NWS_API_BASE = "https://api.weather.gov";
    private static final String USER_AGENT = "weather-app/1.0";
    private static final HttpClient client = HttpClient.newHttpClient();
    private static final ObjectMapper mapper = new ObjectMapper();

    public static void main(String[] args) {
        MCPServer mcp = new MCPServer("weather");
        setupTools(mcp);
        mcp.start();
    }

    private static void setupTools(MCPServer mcp) {
        // 工具設置代碼將在這裡
    }
}

輔助方法

Weather類中添加以下輔助方法:

private static Map<String, Object> makeNWSRequest(String url) {
    try {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header("User-Agent", USER_AGENT)
            .header("Accept", "application/geo+json")
            .build();

        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() != 200) {
            return null;
        }

        return mapper.readValue(response.body(), Map.class);
    } catch (Exception e) {
        return null;
    }
}

private static String formatAlert(Map<String, Object> feature) {
    Map<String, Object> props = (Map<String, Object>) feature.get("properties");
    return String.format("""
        Event: %s
        Area: %s
        Severity: %s
        Description: %s
        Instructions: %s
        """,
        props.getOrDefault("event", "Unknown"),
        props.getOrDefault("areaDesc", "Unknown"),
        props.getOrDefault("severity", "Unknown"),
        props.getOrDefault("description", "No description available"),
        props.getOrDefault("instruction", "No specific instructions provided"));
}

實現工具執行

現在讓我們在setupTools方法中實現我們的工具:

private static void setupTools(MCPServer mcp) {
    // 獲取警報工具
    mcp.tool(new Tool.Builder()
        .name("get-alerts")
        .description("獲取美國州的天氣警報")
        .parameter("state", "string", "兩字母美國州代碼(例如CA、NY)")
        .execute(params -> {
            String state = params.get("state").toString();
            String url = String.format("%s/alerts/active/area/%s", NWS_API_BASE, state);
            Map<String, Object> data = makeNWSRequest(url);

            if (data == null || !data.containsKey("features")) {
                return "無法獲取警報或未找到警報。";
            }

            List<Map<String, Object>> features = (List<Map<String, Object>>) data.get("features");
            if (features.isEmpty()) {
                return "該州沒有活動警報。";
            }

            return features.stream()
                .map(Weather::formatAlert)
                .collect(Collectors.joining("\n---\n"));
        })
        .build());

    // 獲取預報工具
    mcp.tool(new Tool.Builder()
        .name("get-forecast")
        .description("獲取位置的天氣預報")
        .parameter("latitude", "number", "位置的緯度")
        .parameter("longitude", "number", "位置的經度")
        .execute(params -> {
            double latitude = Double.parseDouble(params.get("latitude").toString());
            double longitude = Double.parseDouble(params.get("longitude").toString());

            // 首先獲取預報網格端點
            String pointsUrl = String.format("%s/points/%f,%f", NWS_API_BASE, latitude, longitude);
            Map<String, Object> pointsData = makeNWSRequest(pointsUrl);

            if (pointsData == null) {
                return "無法為此位置獲取預報數據。";
            }

            // 從點響應中獲取預報URL
            Map<String, Object> properties = (Map<String, Object>) pointsData.get("properties");
            String forecastUrl = properties.get("forecast").toString();
            Map<String, Object> forecastData = makeNWSRequest(forecastUrl);

            if (forecastData == null) {
                return "無法獲取詳細預報。";
            }

            // 將時段格式化為可讀預報
            Map<String, Object> forecastProps = (Map<String, Object>) forecastData.get("properties");
            List<Map<String, Object>> periods = (List<Map<String, Object>>) forecastProps.get("periods");
            
            return periods.stream()
                .limit(5)
                .map(period -> String.format("""
                    %s:
                    Temperature: %s°%s
                    Wind: %s %s
                    Forecast: %s
                    """,
                    period.get("name"),
                    period.get("temperature"),
                    period.get("temperatureUnit"),
                    period.get("windSpeed"),
                    period.get("windDirection"),
                    period.get("detailedForecast")))
                .collect(Collectors.joining("\n---\n"));
        })
        .build());
}

您的服務器已完成!運行mvn compile exec:java -Dexec.mainClass="com.example.Weather"以確認一切正常工作。

現在讓我們使用現有的MCP宿主應用程序Claude桌面版測試您的服務器。

使用Claude桌面版測試您的服務器

Claude桌面版尚未在Linux上可用。Linux用戶可以繼續學習構建客戶端教程,以構建連接到我們剛剛構建的服務器的MCP客戶端。

首先,確保您已安裝Claude桌面版。您可以在這裡安裝最新版本。 如果您已經安裝了Claude桌面版,請確保它已更新到最新版本。

我們需要為您想要使用的任何MCP服務器配置Claude桌面版。為此,請在文本編輯器中打開您的Claude桌面版應用程序配置文件,位於~/Library/Application Support/Claude/claude_desktop_config.json。如果該文件不存在,請創建它。

例如,如果您已安裝VS Code

打開配置文件
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
code $env:AppData\Claude\claude_desktop_config.json

然後您將在mcpServers鍵中添加您的服務器。只有在至少一個服務器正確配置的情況下,MCP UI元素才會在Claude桌面版中顯示。

在這種情況下,我們將添加我們的單個天氣服務器,如下所示:

配置Claude桌面版
{
    "mcpServers": {
        "weather": {
            "command": "mvn",
            "args": [
                "compile",
                "exec:java",
                "-Dexec.mainClass=com.example.Weather",
                "-f",
                "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/pom.xml"
            ]
        }
    }
}
{
    "mcpServers": {
        "weather": {
            "command": "mvn",
            "args": [
                "compile",
                "exec:java",
                "-Dexec.mainClass=com.example.Weather",
                "-f",
                "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather\\pom.xml"
            ]
        }
    }
}
確保您傳入服務器的絕對路徑。

這告訴Claude桌面版:

  1. 有一個名為"weather"的MCP服務器
  2. 使用mvn命令運行該服務器
  3. 在指定目錄中運行服務器
  4. 使用Weather類作為入口點

現在,重啟Claude桌面版。您應該會在工具欄中看到一個新的MCP圖標。點擊它,您將看到我們的天氣工具!

下一步

恭喜!您已經成功構建了您的第一個MCP服務器。這只是開始 - 您可以:

測試命令

讓我們確認Claude桌面端能正確識別天氣服務中的兩個工具。注意查看錘子圖標

MCP工具圖標

點擊錘子圖標後應該看到兩個工具:

如果工具未顯示,請參考故障排除章節。

成功顯示工具後,可以在Claude桌面端測試以下命令:

  • 薩克拉門託的天氣如何?
  • 德克薩斯州有哪些活躍的天氣警報?
該服務使用美國國家氣象局數據,僅支持美國地區查詢。

底層工作原理

問題處理流程:

  1. 客戶端將問題發送給Claude
  2. Claude分析可用工具並選擇使用
  3. 通過MCP服務器執行工具
  4. 返回結果給Claude
  5. Claude生成自然語言響應
  6. 向用戶展示最終結果

故障排除

Claude桌面端集成問題

獲取桌面端日誌

日誌文件位於~/Library/Logs/Claude

  • mcp.log 包含MCP連接日誌
  • mcp-server-SERVERNAME.log 包含指定服務器的錯誤日誌

實時查看日誌命令:

tail -n 20 -f ~/Library/Logs/Claude/mcp*.log

服務器未顯示

  1. 檢查claude_desktop_config.json文件語法
  2. 確認項目使用絕對路徑
  3. 完全重啟Claude桌面端

工具調用靜默失敗

  1. 檢查Claude日誌
  2. 驗證服務器能否正常啟動
  3. 嘗試重啟Claude桌面端
天氣API問題

錯誤:無法獲取網格點數據 可能原因:

  1. 座標不在美國境內
  2. NWS API服務異常
  3. 觸發速率限制

解決方案:

  • 確認使用美國座標
  • 增加請求間隔時間
  • 查看NWS API狀態頁
📚
完整調試指南請參考《MCP調試手冊》

後續步驟