9.3 MCP服务端开发

本节详细讲解如何从零开始构建MCP Server,包括基本开发步骤、完整的Python实现示例、关键概念说明以及错误处理方案。通过这些实现细节,你将掌握如何定义工具(Tool)、资源(Resource)和提示词(Prompt),选择合适的传输方式,并确保Server的稳定性。

9.3.1 开发MCP Server的基本步骤

创建一个完整的MCP Server需要:

  1. 定义Tools(可调用的函数)

  2. 定义Resources(可访问的数据)

  3. 定义Prompts(提示词模板)

  4. 实现处理器

  5. 选择传输方式并启动Server

9.3.2 完整的Python MCP Server实现

MCP Server的完整Python实现包括四个主要部分:工具定义、资源处理、不同传输方式的实现,以及主函数。我们将逐步展示这些部分,在每个部分之间加入设计说明。

第一部分:数据模型与工具定义

首先需要定义数据模型来表示工具、资源和提示词:

# mcp_server.py
# 一个完整的MCP Server示例,提供文件系统工具和资源访问

import json
import os
import subprocess
from typing import Dict, Any, List, Optional
from dataclasses import dataclass, asdict
from enum import Enum
import asyncio
import sys
from pathlib import Path

@dataclass
class MCPTool:
    """MCP工具定义"""
    name: str
    description: str
    inputSchema: Dict[str, Any]

@dataclass
class MCPResource:
    """MCP资源定义"""
    uri: str
    name: str
    description: str
    mimeType: str  # MIME 类型

@dataclass
class MCPPrompt:
    """MCP提示词定义"""
    name: str
    description: str
    arguments: List[Dict[str, Any]]

class MCPServerBase:
    """MCP Server基类"""

    def __init__(self):
        self.tools: Dict[str, MCPTool] = {}
        self.resources: Dict[str, MCPResource] = {}
        self.prompts: Dict[str, MCPPrompt] = {}
        self.tool_handlers: Dict[str, callable] = {}
        self.resource_readers: Dict[str, callable] = {}
        self.prompt_generators: Dict[str, callable] = {}

    def register_tool(
        self, name: str, description: str,
        input_schema: Dict[str, Any], handler: callable
    ) -> None:
        """注册工具"""
        self.tools[name] = MCPTool(name, description, input_schema)
        self.tool_handlers[name] = handler

    def register_resource(
        self, uri: str, name: str, description: str,
        mime_type: str, reader: callable
    ) -> None:
        """注册资源"""
        self.resources[uri] = MCPResource(uri, name, description, mime_type)
        self.resource_readers[uri] = reader

    def register_prompt(
        self, name: str, description: str,
        arguments: List[Dict[str, Any]], generator: callable
    ) -> None:
        """注册提示词"""
        self.prompts[name] = MCPPrompt(name, description, arguments)
        self.prompt_generators[name] = generator

设计说明:基类采用了“注册模式”(Registration Pattern),允许在运行时动态注册工具、资源和提示词。这提供了灵活性,使不同的Server实例可以有不同的功能集合。

第二部分:请求处理与JSON-RPC协议

MCP使用JSON-RPC 2.0协议进行通信。以下是请求处理的核心逻辑:

设计说明:请求处理是MCP通信的“中枢”。每个请求必须返回合法的JSON-RPC 2.0响应,包括id、result或error。这确保了客户端可以准确关联请求和响应,即使在异步场景中也不会混淆。

第三部分:资源与提示词处理

现在实现具体的处理方法,展示工具、资源和提示词的导出:

设计说明:这些处理方法遵循了一致的模式:列表方法返回所有已注册的对象、调用方法执行对应的处理器。注意 _handle_prompts_get 支持参数,使提示词能够根据不同的上下文动态生成。

第四部分:文件系统实现与传输

最后,我们实现一个具体的文件系统Server和两种传输方式。以下展示文件系统工具的实现:

设计说明:文件系统Server展示了三个关键的安全做法:(1) 限制可访问的根路径;(2) 验证路径不会逃出根目录;(3) 在执行操作前解析并验证所有路径。这是处理用户输入的敏感操作(如文件访问)的标准防御方式。上面代码使用 resolved.relative_to(self.root_path.resolve()) 进行边界检查是最佳实践——相比字符串前缀比较(resolved.startswith()),它能正确处理Windows路径、符号链接和相对路径。完整的五层递进式路径校验实践(长度检查、URL编码双解码、Unicode规范化、平台特定规范化、符号链接解析与边界检查)详见第 12.4 节。

第五部分:传输层实现

MCP支持多种传输方式。以下展示stdio和HTTP两种传输的简化实现:

stdio传输的优势:(1) 无需网络配置,完全通过管道通信;(2) 天然支持进程隔离;(3) 无序列化开销,只需逐行JSON处理。这使stdio成为本地工具集成的理想选择。

HTTP传输的优势:(1) 支持远程访问,跨机器通信;(2) GET流连接用于Server推送事件;(3) 标准的HTTP协议,易于在网络中部署和监控。

第六部分:启动函数

最后,主函数根据命令行参数选择传输方式:

Server开发的关键概念

1. 工具定义的JSON Schema

工具的inputSchema应该是完整的JSON Schema,包含:

  • type: 必须是“object”

  • properties: 参数定义

  • required: 必需参数列表

2. 资源URI规范

Resources应该有结构化的URI(统一资源标识符):

3. 提示词参数化

Prompts可以接受参数,在生成消息时使用这些参数:

错误处理

MCP Server应该正确处理和报告错误:

本小节小结

开发MCP Server的核心是:

  1. 定义Tool、Resource和Prompt的Schema

  2. 实现对应的处理器

  3. 选择合适的传输方式

  4. 正确处理错误和异常

关键要点:

  • JSON Schema应该准确且完整

  • 资源URI应该有明确的结构

  • 提示词应该支持参数化

  • 错误响应应该遵循JSON-RPC 2.0规范

下一节将讨论Harness如何在系统级别集成多个MCP Server。

最后更新于