0Pricing
AI Engineering Academy · 课时

构建您的第一个 MCP 服务器

使用 Python MCP SDK 创建一个公开资源、工具和提示的服务器,然后将其连接到 Claude Desktop,完整查看其运行效果。

构建您的第一个 MCP 服务器 是 CoddyKit 上的免费 AI Engineering Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Engineering Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Engineering Academy 课程共包含 4 节课。

MCP 服务器项目设置

在 Python 中构建 MCP 服务器需要 mcp SDK 和 Python 环境。您将创建一个服务器,使 Claude Desktop 或任何 MCP 客户端都能通过 stdio 连接。首先安装软件包并创建服务器文件。

# Create a project directory
# mkdir my_mcp_server && cd my_mcp_server

# Create a virtual environment
# python -m venv venv && source venv/bin/activate

# Install the MCP SDK
# pip install mcp httpx

# Project structure:
# my_mcp_server/
#   server.py          <- Your MCP server
#   requirements.txt
#   README.md

创建服务器对象

从 mcp 软件包导入内容,并使用服务器名称创建一个 Server 实例。客户端会在 MCP 服务器列表中显示该名称,因此请选择一个具有描述性的名称。服务器对象是注册所有工具、资源和提示的入口。

# server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
import asyncio
import httpx

# Create the server — name is shown in Claude Desktop
app = Server('weather-server')

# --- Tool registrations go here ---

# Entry point
if __name__ == '__main__':
    asyncio.run(stdio_server(app))

使用 @app.list_tools() 注册工具

@app.list_tools() 装饰器会注册一个处理程序,当客户端发出请求时,该处理程序会返回可用工具列表。每个工具都由名称、描述和 inputSchema 定义,后者是描述其参数的 JSON 架构。客户端会将此列表发送给 LLM,使其了解可以调用哪些工具。

@app.list_tools()
async def list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name='get_weather',
            description='Get current weather for a city. Use when the user asks about weather in a specific location.',
            inputSchema={
                'type': 'object',
                'properties': {
                    'city': {
                        'type': 'string',
                        'description': 'City name, e.g. London or New York'
                    },
                    'units': {
                        'type': 'string',
                        'enum': ['metric', 'imperial'],
                        'description': 'Temperature units. Default is metric.'
                    }
                },
                'required': ['city']
            }
        ),
        types.Tool(
            name='list_cities',
            description='Return a list of major cities the user can query weather for.',
            inputSchema={'type': 'object', 'properties': {}, 'required': []}
        )
    ]

实现工具执行

@app.call_tool() 装饰器负责处理工具执行。当客户端调用工具时,此处理器会接收工具名称和参数。执行相应的逻辑,并返回一个包含结果字符串的 TextContent 对象列表。

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
    if name == 'list_cities':
        cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney']
        return [types.TextContent(type='text', text=', '.join(cities))]

    if name == 'get_weather':
        city = arguments['city']
        units = arguments.get('units', 'metric')
        unit_symbol = 'C' if units == 'metric' else 'F'

        # Call real weather API (stub here)
        async with httpx.AsyncClient() as client:
            # Replace with actual API call
            result = f'{city}: 18{chr(176)}{unit_symbol}, partly cloudy, humidity 65%'

        return [types.TextContent(type='text', text=result)]

    raise ValueError(f'Unknown tool: {name}')

公开资源

资源是模型可以读取的静态或动态数据——您可以把它们理解为文件。定义资源 URI 并实现资源读取器。资源会作为客户端中的条目出现,供 AI 引用,适用于配置、文档或经常访问的数据快照。

@app.list_resources()
async def list_resources() -> list[types.Resource]:
    return [
        types.Resource(
            uri='weather://supported-cities',
            name='Supported Cities List',
            description='Complete list of cities available in this weather server.',
            mimeType='text/plain'
        )
    ]

@app.read_resource()
async def read_resource(uri: str) -> str:
    if uri == 'weather://supported-cities':
        cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney', 'Dubai', 'Singapore']
        return '\n'.join(cities)
    raise ValueError(f'Unknown resource URI: {uri}')

注册提示模板

提示是可重复使用的消息模板,客户端会将其作为斜杠命令或快捷操作呈现给用户。它们接受参数,并返回一个消息列表,作为对话的初始上下文。提示非常适合封装复杂指令,让用户通过一条命令即可触发。

@app.list_prompts()
async def list_prompts() -> list[types.Prompt]:
    return [
        types.Prompt(
            name='weather-report',
            description='Generate a formatted weather report for a city.',
            arguments=[
                types.PromptArgument(name='city', description='City name', required=True)
            ]
        )
    ]

@app.get_prompt()
async def get_prompt(name: str, arguments: dict) -> types.GetPromptResult:
    if name == 'weather-report':
        city = arguments.get('city', 'London')
        return types.GetPromptResult(
            description=f'Weather report for {city}',
            messages=[
                types.PromptMessage(
                    role='user',
                    content=types.TextContent(
                        type='text',
                        text=f'Use the get_weather tool to look up {city} and give me a detailed weather report including what clothing I should wear.'
                    )
                )
            ]
        )
    raise ValueError(f'Unknown prompt: {name}')

连接到 Claude Desktop

要将您的 MCP 服务器与 Claude Desktop 搭配使用,请将其添加到 Claude Desktop 配置文件中。在 macOS 上,该文件位于 ~/Library/Application Support/Claude/claude_desktop_config.json。指定启动服务器的命令,以及服务器所需的环境变量。

# ~/Library/Application Support/Claude/claude_desktop_config.json
# Add this JSON configuration:

# {
#   "mcpServers": {
#     "weather-server": {
#       "command": "/path/to/venv/bin/python",
#       "args": ["/path/to/my_mcp_server/server.py"],
#       "env": {
#         "WEATHER_API_KEY": "your_api_key_here"
#       }
#     }
#   }
# }

# After saving, restart Claude Desktop.
# Your server's tools will appear in Claude's tool list.

使用 MCP CLI 测试您的服务器

在连接到 Claude Desktop 之前,请使用 MCP 检查器或 CLI 工具测试您的服务器。mcp dev 命令会启动服务器,并打开一个基于浏览器的检查器,您可以在其中手动调用工具并查看原始协议消息,从而轻松调试问题。

# Install the MCP development tools
# pip install 'mcp[cli]'

# Start the inspector with your server
# mcp dev server.py

# The inspector opens at http://localhost:5173
# You can:
# - See all registered tools and their schemas
# - Call tools with custom arguments
# - Browse available resources
# - Test prompt templates
# - View the full JSON-RPC message exchange

MCP 服务器中的错误处理

MCP 服务器绝不能因输入错误而崩溃。请将所有工具执行过程包装在 try/except 中,并将错误消息作为 TextContent 返回,而不是抛出异常。对于致命错误(例如配置问题或缺少 API 密钥),请在启动时记录日志,并在服务器进入主循环前抛出异常。

@app.call_tool()
async def call_tool_safe(name: str, arguments: dict) -> list[types.TextContent]:
    try:
        if name == 'get_weather':
            city = arguments.get('city')
            if not city:
                return [types.TextContent(type='text', text='Error: city argument is required.')]
            result = await fetch_weather(city, arguments.get('units', 'metric'))
            return [types.TextContent(type='text', text=result)]
        raise ValueError(f'Unknown tool: {name}')
    except httpx.TimeoutException:
        return [types.TextContent(type='text', text='Error: Weather API timed out. Try again.')]
    except Exception as e:
        return [types.TextContent(type='text', text=f'Error: {str(e)}')]

MCP 服务器的日志记录

由于 MCP 服务器通过标准输入输出与客户端通信,打印语句会破坏协议。请始终使用 stderr 进行日志记录——它会写入独立的数据流,不会干扰 MCP 消息交换。请配置 Python 的日志模块,将日志写入 stderr。

import logging
import sys

# Configure logging to stderr (NOT stdout — that's the MCP channel)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    stream=sys.stderr
)
logger = logging.getLogger('weather-server')

# In your tool handler:
# logger.info(f'Getting weather for {city}')
# logger.error(f'API call failed: {e}')

# Claude Desktop captures stderr to a log file:
# ~/Library/Logs/Claude/mcp-server-weather-server.log

打包您的 MCP 服务器

您可以使用 pyproject.toml 打包 MCP 服务器并发布到 PyPI,也可以将其作为 Docker 容器分发给团队。请使用环境变量保存所有机密信息——例如 API 密钥和数据库 URL——使服务器配置与代码分离。请在清晰易懂的 README 中记录所需的环境变量和示例配置。

快速检查

测试您对使用 Python 构建 MCP 服务器的理解。

课程回顾

在本课中,您学到了:MCP 服务器通过 @app.list_tools() 和 @app.call_tool() 装饰器公开工具;资源和提示通过可读取的数据与可重复使用的模板扩展服务器功能;以及日志记录必须使用 stderr,以避免破坏标准输入输出上的 MCP 协议通道。接下来,我们将把 MCP 服务器连接到数据库,并通过分页公开动态资源。

常见问题解答

「构建您的第一个 MCP 服务器」课时是免费的吗?

是的 — 「构建您的第一个 MCP 服务器」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Engineering Academy 课程的其余内容,请升级到 CoddyKit PRO。 AI Engineering Academy 课程共包含 4 节课。

「构建您的第一个 MCP 服务器」这节课中我会学到什么?

使用 Python MCP SDK 创建一个公开资源、工具和提示的服务器,然后将其连接到 Claude Desktop,完整查看其运行效果。 你通过在浏览器中直接运行的动手代码来练习 AI Engineering Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 AI Engineering Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 AI Engineering Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「构建您的第一个 MCP 服务器」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 AI Engineering Academy 课中编写并运行代码吗?

能。每节 AI Engineering Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 什么是 MCP,以及它为何重要
  2. 构建您的第一个 MCP 服务器
  3. 通过 MCP 暴露数据库资源
  4. MCP 安全与身份验证
← 返回 AI Engineering Academy