MCP 开发最佳实践

AIGC开发者必看:MCP模型上下文协议最佳实践 随着AIGC(人工智能生成内容)技术的爆发式发展,多模型协同架构成为构建复杂智能系统的主流范式。Model Context Protocol(MCP)作为模型间上下文交互的核心规范,定义了模型输入输出的标准化格式、上下文数据的序列化协议以及跨模型协作的交互流程。本文聚焦MCP协议的工程实现细节,涵盖从基础概念到复杂场景的最佳实践,帮助开发者解决多模型协同中的上下文管理难题。本文采用"原理解析→算法实现→实战应用→生态建设"的递进式结构,通过以下核心模块展开:MCP协议采用三层架构设计,实现关注点分离与跨平台 阅读详情

概述

本课程重点介绍在生产环境中开发、测试和部署 MCP 服务器及功能的高级最佳实践。随着 MCP 生态系统的复杂性和重要性日益增长,遵循既定模式可确保可靠性、可维护性和互操作性。本课程整合了从实际 MCP 实施中获得的实践经验,旨在指导您使用有效的资源、提示和工具创建强大高效的服务器。

学习目标

学完本课后,您将能够:

  • 在 MCP 服务器和功能设计中应用行业最佳实践
  • 为 MCP 服务器创建全面的测试策略
  • 为复杂的 MCP 应用程序设计高效、可重复使用的工作流模式
  • 在 MCP 服务器中实施适当的错误处理、日志记录和可观察性
  • 优化 MCP 实施以提高性能、安全性和可维护性
  • MCP 工具开发最佳实践

    建筑原则

    1.单一职责原则

    每个 MCP 功能都应该有清晰、专注的目标。与其创建试图解决多个问题的单一工具,不如开发擅长特定任务的专用工具。

    好的例子:

    // A focused tool that does one thing well
    public class WeatherForecastTool : ITool
    {
        private readonly IWeatherService _weatherService;
        
        public WeatherForecastTool(IWeatherService weatherService)
        {
            _weatherService = weatherService;
        }
        
        public string Name => "weatherForecast";
        public string Description => "Gets weather forecast for a specific location";
        
        public ToolDefinition GetDefinition()
        {
            return new ToolDefinition
            {
                Name = Name,
                Description = Description,
                Parameters = new Dictionary<string, ParameterDefinition>
                {
                    ["location"] = new ParameterDefinition
                    {
                        Type = ParameterType.String,
                        Description = "City or location name"
                    },
                    ["days"] = new ParameterDefinition
                    {
                        Type = ParameterType.Integer,
                        Description = "Number of forecast days",
                        Default = 3
                    }
                },
                Required = new[] { "location" }
            };
        }
          public async Task<ToolResponse> ExecuteAsync(IDictionary<string, object> parameters)
        {
            var location = parameters["location"].ToString();
            var days = parameters.ContainsKey("days") 
                ? Convert.ToInt32(parameters["days"]) 
                : 3;
                
            var forecast = await _weatherService.GetForecastAsync(location, days);
            
            return new ToolResponse
            {
                Content = new List<ContentItem>
                {
                    new TextContent(JsonSerializer.Serialize(forecast))
                }
            };
        }
    }

    糟糕的例子:

    // A tool trying to do too many things
    public class WeatherToolSuite : ITool
    {
        public string Name => "weather";
        public string Description => "Weather-related functionality";
        
        public ToolDefinition GetDefinition()
        {
            return new ToolDefinition
            {
                Name = Name,
                Description = Description,
                Parameters = new Dictionary<string, ParameterDefinition>
                {
                    ["action"] = new ParameterDefinition
                    {
                        Type = ParameterType.String,
                        Description = "Weather action to perform",
                        Enum = new[] { "forecast", "history", "alerts", "radar" }
                    },
                    ["location"] = new ParameterDefinition
                    {
                        Type = ParameterType.String,
                        Description = "City or location name"
                    },
                    // Many more properties for different actions...
                },
                required = new[] { "action", "location" }
            };
        }
        
        public async Task<ToolResponse> ExecuteAsync(ToolRequest request)
        {
            // Complex conditional logic to handle different actions
            var action = request.Parameters.GetProperty("action").GetString();
            var location = request.Parameters.GetProperty("location").GetString();
            
            switch (action)
            {
                case "forecast":
                    // Forecast logic
                    break;
                case "history":
                    // Historical data logic
                    break;
                // More cases...
                default:
                    throw new ToolExecutionException($"Unknown action: {action}");
            }
            
            // Result processing
            // ...
        }
    }
    2.依赖注入和可测试性

    设计工具通过构造函数注入来接收其依赖项,使其可测试和可配置:

    // Java example with dependency injection
    public class CurrencyConversionTool implements Tool {
        private final ExchangeRateService exchangeService;
        private final CacheService cacheService;
        private final Logger logger;
        
        // Dependencies injected through constructor
        public CurrencyConversionTool(
                ExchangeRateService exchangeService,
                CacheService cacheService,
                Logger logger) {
            this.exchangeService = exchangeService;
            this.cacheService = cacheService;
            this.logger = logger;
        }
        
        // Tool implementation
        // ...
    }
    3. 可组合工具

    设计可组合在一起的工具以创建更复杂的工作流程:

    # Python example showing composable tools
    class DataFetchTool(Tool):
        def get_name(self):
            return "dataFetch"
        
        # Implementation...
    
    class DataAnalysisTool(Tool):
        def get_name(self):
            return "dataAnalysis"
        
        # This tool can use results from the dataFetch tool
        async def execute_async(self, request):
            # Implementation...
            pass
    
    class DataVisualizationTool(Tool):
        def get_name(self):
            return "dataVisualize"
        
        # This tool can use results from the dataAnalysis tool
        async def execute_async(self, request):
            # Implementation...
            pass
    
    # These tools can be used independently or as part of a workflow

    架构设计最佳实践

    模式是模型和工具之间的契约。精心设计的模式可以提高工具的可用性。

    1. 清晰的参数描述

    始终包含每个参数的描述信息:
     

    public object GetSchema()
    {
        return new {
            type = "object",
            properties = new {
                query = new { 
                    type = "string", 
                    description = "Search query text. Use precise keywords for better results." 
                },
                filters = new {
                    type = "object",
                    description = "Optional filters to narrow down search results",
                    properties = new {
                        dateRange = new { 
                            type = "string", 
                            description = "Date range in format YYYY-MM-DD:YYYY-MM-DD" 
                        },
                        category = new { 
                            type = "string", 
                            description = "Category name to filter by" 
                        }
                    }
                },
                limit = new { 
                    type = "integer", 
                    description = "Maximum number of results to return (1-50)",
                    default = 10
                }
            },
            required = new[] { "query" }
        };
    }
    2. 验证约束

    包括验证约束以防止无效输入:

    Map<String, Object> getSchema() {
        Map<String, Object> schema = new HashMap<>();
        schema.put("type", "object");
        
        Map<String, Object> properties = new HashMap<>();
        
        // Email property with format validation
        Map<String, Object> email = new HashMap<>();
        email.put("type", "string");
        email.put("format", "email");
        email.put("description", "User email address");
        
        // Age property with numeric constraints
        Map<String, Object> age = new HashMap<>();
        age.put("type", "integer");
        age.put("minimum", 13);
        age.put("maximum", 120);
        age.put("description", "User age in years");
        
        // Enumerated property
        Map<String, Object> subscription = new HashMap<>();
        subscription.put("type", "string");
        subscription.put("enum", Arrays.asList("free", "basic", "premium"));
        subscription.put("default", "free");
        subscription.put("description", "Subscription tier");
        
        properties.put("email", email);
        properties.put("age", age);
        properties.put("subscription", subscription);
        
        schema.put("properties", properties);
        schema.put("required", Arrays.asList("email"));
        
        return schema;
    }
    3.一致的回报结构

    保持响应结构的一致性,以便模型更容易解释结果:

    
    
    async def execute_async(self, request):
        try:
            # Process request
            results = await self._search_database(request.parameters["query"])
            
            # Always return a consistent structure
            return ToolResponse(
                result={
                    "matches": [self._format_item(item) for item in results],
                    "totalCount": len(results),
                    "queryTime": calculation_time_ms,
                    "status": "success"
                }
            )
        except Exception as e:
            return ToolResponse(
                result={
                    "matches": [],
                    "totalCount": 0,
                    "queryTime": 0,
                    "status": "error",
                    "error": str(e)
                }
            )
        
    def _format_item(self, item):
        """Ensures each item has a consistent structure"""
        return {
            "id": item.id,
            "title": item.title,
            "summary": item.summary[:100] + "..." if len(item.summary) > 100 else item.summary,
            "url": item.url,
            "relevance": item.score
        }

    错误处理

    强大的错误处理对于 MCP 工具保持可靠性至关重要。

    1. 优雅的错误处理

    处理适当级别的错误并提供信息消息:

    public async Task<ToolResponse> ExecuteAsync(ToolRequest request)
    {
        try
        {
            string fileId = request.Parameters.GetProperty("fileId").GetString();
            
            try
            {
                var fileData = await _fileService.GetFileAsync(fileId);
                return new ToolResponse { 
                    Result = JsonSerializer.SerializeToElement(fileData) 
                };
            }
            catch (FileNotFoundException)
            {
                throw new ToolExecutionException($"File not found: {fileId}");
            }
            catch (UnauthorizedAccessException)
            {
                throw new ToolExecutionException("You don't have permission to access this file");
            }
            catch (Exception ex) when (ex is IOException || ex is TimeoutException)
            {
                _logger.LogError(ex, "Error accessing file {FileId}", fileId);
                throw new ToolExecutionException("Error accessing file: The service is temporarily unavailable");
            }
        }
        catch (JsonException)
        {
            throw new ToolExecutionException("Invalid file ID format");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unexpected error in FileAccessTool");
            throw new ToolExecutionException("An unexpected error occurred");
        }
    }
    2. 结构化错误响应

    尽可能返回结构化的错误信息:

    @Override
    public ToolResponse execute(ToolRequest request) {
        try {
            // Implementation
        } catch (Exception ex) {
            Map<String, Object> errorResult = new HashMap<>();
            
            errorResult.put("success", false);
            
            if (ex instanceof ValidationException) {
                ValidationException validationEx = (ValidationException) ex;
                
                errorResult.put("errorType", "validation");
                errorResult.put("errorMessage", validationEx.getMessage());
                errorResult.put("validationErrors", validationEx.getErrors());
                
                return new ToolResponse.Builder()
                    .setResult(errorResult)
                    .build();
            }
            
            // Re-throw other exceptions as ToolExecutionException
            throw new ToolExecutionException("Tool execution failed: " + ex.getMessage(), ex);
        }
    }
    3.重试逻辑

    针对暂时性故障实施适当的重试逻辑:

    async def execute_async(self, request):
        max_retries = 3
        retry_count = 0
        base_delay = 1  # seconds
        
        while retry_count < max_retries:
            try:
                # Call external API
                return await self._call_api(request.parameters)
            except TransientError as e:
                retry_count += 1
                if retry_count >= max_retries:
                    raise ToolExecutionException(f"Operation failed after {max_retries} attempts: {str(e)}")
                    
                # Exponential backoff
                delay = base_delay * (2 ** (retry_count - 1))
                logging.warning(f"Transient error, retrying in {delay}s: {str(e)}")
                await asyncio.sleep(delay)
            except Exception as e:
                # Non-transient error, don't retry
                raise ToolExecutionException(f"Operation failed: {str(e)}")

    性能优化

    1.缓存

    对昂贵的操作实施缓存:

    public class CachedDataTool : IMcpTool
    {
        private readonly IDatabase _database;
        private readonly IMemoryCache _cache;
        
        public CachedDataTool(IDatabase database, IMemoryCache cache)
        {
            _database = database;
            _cache = cache;
        }
        
        public async Task<ToolResponse> ExecuteAsync(ToolRequest request)
        {
            var query = request.Parameters.GetProperty("query").GetString();
            
            // Create cache key based on parameters
            var cacheKey = $"data_query_{ComputeHash(query)}";
            
            // Try to get from cache first
            if (_cache.TryGetValue(cacheKey, out var cachedResult))
            {
                return new ToolResponse { Result = cachedResult };
            }
            
            // Cache miss - perform actual query
            var result = await _database.QueryAsync(query);
            
            // Store in cache with expiration
            var cacheOptions = new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(TimeSpan.FromMinutes(15));
                
            _cache.Set(cacheKey, JsonSerializer.SerializeToElement(result), cacheOptions);
            
            return new ToolResponse { Result = JsonSerializer.SerializeToElement(result) };
        }
        
        private string ComputeHash(string input)
        {
            // Implementation to generate stable hash for cache key
        }
    }
    2.异步处理

    对 I/O 密集型操作使用异步编程模式:

    public class AsyncDocumentProcessingTool implements Tool {
        private final DocumentService documentService;
        private final ExecutorService executorService;
        
        @Override
        public ToolResponse execute(ToolRequest request) {
            String documentId = request.getParameters().get("documentId").asText();
            
            // For long-running operations, return a processing ID immediately
            String processId = UUID.randomUUID().toString();
            
            // Start async processing
            CompletableFuture.runAsync(() -> {
                try {
                    // Perform long-running operation
                    documentService.processDocument(documentId);
                    
                    // Update status (would typically be stored in a database)
                    processStatusRepository.updateStatus(processId, "completed");
                } catch (Exception ex) {
                    processStatusRepository.updateStatus(processId, "failed", ex.getMessage());
                }
            }, executorService);
            
            // Return immediate response with process ID
            Map<String, Object> result = new HashMap<>();
            result.put("processId", processId);
            result.put("status", "processing");
            result.put("estimatedCompletionTime", ZonedDateTime.now().plusMinutes(5));
            
            return new ToolResponse.Builder().setResult(result).build();
        }
        
        // Companion status check tool
        public class ProcessStatusTool implements Tool {
            @Override
            public ToolResponse execute(ToolRequest request) {
                String processId = request.getParameters().get("processId").asText();
                ProcessStatus status = processStatusRepository.getStatus(processId);
                
                return new ToolResponse.Builder().setResult(status).build();
            }
        }
    }
    3. 资源限制

    实施资源限制以防止过载:

    class ThrottledApiTool(Tool):
        def __init__(self):
            self.rate_limiter = TokenBucketRateLimiter(
                tokens_per_second=5,  # Allow 5 requests per second
                bucket_size=10        # Allow bursts up to 10 requests
            )
        
        async def execute_async(self, request):
            # Check if we can proceed or need to wait
            delay = self.rate_limiter.get_delay_time()
            
            if delay > 0:
                if delay > 2.0:  # If wait is too long
                    raise ToolExecutionException(
                        f"Rate limit exceeded. Please try again in {delay:.1f} seconds."
                    )
                else:
                    # Wait for the appropriate delay time
                    await asyncio.sleep(delay)
            
            # Consume a token and proceed with the request
            self.rate_limiter.consume()
            
            # Call API
            result = await self._call_api(request.parameters)
            return ToolResponse(result=result)
    
    class TokenBucketRateLimiter:
        def __init__(self, tokens_per_second, bucket_size):
            self.tokens_per_second = tokens_per_second
            self.bucket_size = bucket_size
            self.tokens = bucket_size
            self.last_refill = time.time()
            self.lock = asyncio.Lock()
        
        async def get_delay_time(self):
            async with self.lock:
                self._refill()
                if self.tokens >= 1:
                    return 0
                
                # Calculate time until next token available
                return (1 - self.tokens) / self.tokens_per_second
        
        async def consume(self):
            async with self.lock:
                self._refill()
                self.tokens -= 1
        
        def _refill(self):
            now = time.time()
            elapsed = now - self.last_refill
            
            # Add new tokens based on elapsed time
            new_tokens = elapsed * self.tokens_per_second
            self.tokens = min(self.bucket_size, self.tokens + new_tokens)
            self.last_refill = now

    安全最佳实践

    1. 输入验证

    始终彻底验证输入参数:

    public async Task<ToolResponse> ExecuteAsync(ToolRequest request)
    {
        // Validate parameters exist
        if (!request.Parameters.TryGetProperty("query", out var queryProp))
        {
            throw new ToolExecutionException("Missing required parameter: query");
        }
        
        // Validate correct type
        if (queryProp.ValueKind != JsonValueKind.String)
        {
            throw new ToolExecutionException("Query parameter must be a string");
        }
        
        var query = queryProp.GetString();
        
        // Validate string content
        if (string.IsNullOrWhiteSpace(query))
        {
            throw new ToolExecutionException("Query parameter cannot be empty");
        }
        
        if (query.Length > 500)
        {
            throw new ToolExecutionException("Query parameter exceeds maximum length of 500 characters");
        }
        
        // Check for SQL injection attacks if applicable
        if (ContainsSqlInjection(query))
        {
            throw new ToolExecutionException("Invalid query: contains potentially unsafe SQL");
        }
        
        // Proceed with execution
        // ...
    }
    2.授权检查

    实施适当的授权检查:

    @Override
    public ToolResponse execute(ToolRequest request) {
        // Get user context from request
        UserContext user = request.getContext().getUserContext();
        
        // Check if user has required permissions
        if (!authorizationService.hasPermission(user, "documents:read")) {
            throw new ToolExecutionException("User does not have permission to access documents");
        }
        
        // For specific resources, check access to that resource
        String documentId = request.getParameters().get("documentId").asText();
        if (!documentService.canUserAccess(user.getId(), documentId)) {
            throw new ToolExecutionException("Access denied to the requested document");
        }
        
        // Proceed with tool execution
        // ...
    }
    3.敏感数据处理

    谨慎处理敏感数据:

    class SecureDataTool(Tool):
        def get_schema(self):
            return {
                "type": "object",
                "properties": {
                    "userId": {"type": "string"},
                    "includeSensitiveData": {"type": "boolean", "default": False}
                },
                "required": ["userId"]
            }
        
        async def execute_async(self, request):
            user_id = request.parameters["userId"]
            include_sensitive = request.parameters.get("includeSensitiveData", False)
            
            # Get user data
            user_data = await self.user_service.get_user_data(user_id)
            
            # Filter sensitive fields unless explicitly requested AND authorized
            if not include_sensitive or not self._is_authorized_for_sensitive_data(request):
                user_data = self._redact_sensitive_fields(user_data)
            
            return ToolResponse(result=user_data)
        
        def _is_authorized_for_sensitive_data(self, request):
            # Check authorization level in request context
            auth_level = request.context.get("authorizationLevel")
            return auth_level == "admin"
        
        def _redact_sensitive_fields(self, user_data):
            # Create a copy to avoid modifying the original
            redacted = user_data.copy()
            
            # Redact specific sensitive fields
            sensitive_fields = ["ssn", "creditCardNumber", "password"]
            for field in sensitive_fields:
                if field in redacted:
                    redacted[field] = "REDACTED"
            
            # Redact nested sensitive data
            if "financialInfo" in redacted:
                redacted["financialInfo"] = {"available": True, "accessRestricted": True}
            
            return redacted

    MCP 工具的测试最佳实践

    全面的测试确保 MCP 工具正常运行、处理边缘情况并与系统的其余部分正确集成。

    单元测试

    1. 单独测试每个工具

    为每个工具的功能创建重点测试:

    [Fact]
    public async Task WeatherTool_ValidLocation_ReturnsCorrectForecast()
    {
        // Arrange
        var mockWeatherService = new Mock<IWeatherService>();
        mockWeatherService
            .Setup(s => s.GetForecastAsync("Seattle", 3))
            .ReturnsAsync(new WeatherForecast(/* test data */));
        
        var tool = new WeatherForecastTool(mockWeatherService.Object);
        
        var request = new ToolRequest(
            toolName: "weatherForecast",
            parameters: JsonSerializer.SerializeToElement(new { 
                location = "Seattle", 
                days = 3 
            })
        );
        
        // Act
        var response = await tool.ExecuteAsync(request);
        
        // Assert
        Assert.NotNull(response);
        var result = JsonSerializer.Deserialize<WeatherForecast>(response.Result);
        Assert.Equal("Seattle", result.Location);
        Assert.Equal(3, result.DailyForecasts.Count);
    }
    
    [Fact]
    public async Task WeatherTool_InvalidLocation_ThrowsToolExecutionException()
    {
        // Arrange
        var mockWeatherService = new Mock<IWeatherService>();
        mockWeatherService
            .Setup(s => s.GetForecastAsync("InvalidLocation", It.IsAny<int>()))
            .ThrowsAsync(new LocationNotFoundException("Location not found"));
        
        var tool = new WeatherForecastTool(mockWeatherService.Object);
        
        var request = new ToolRequest(
            toolName: "weatherForecast",
            parameters: JsonSerializer.SerializeToElement(new { 
                location = "InvalidLocation", 
                days = 3 
            })
        );
        
        // Act & Assert
        var exception = await Assert.ThrowsAsync<ToolExecutionException>(
            () => tool.ExecuteAsync(request)
        );
        
        Assert.Contains("Location not found", exception.Message);
    }
    2. 模式验证测试

    测试模式是否有效并正确执行约束:

    @Test
    public void testSchemaValidation() {
        // Create tool instance
        SearchTool searchTool = new SearchTool();
        
        // Get schema
        Object schema = searchTool.getSchema();
        
        // Convert schema to JSON for validation
        String schemaJson = objectMapper.writeValueAsString(schema);
        
        // Validate schema is valid JSONSchema
        JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
        JsonSchema jsonSchema = factory.getJsonSchema(schemaJson);
        
        // Test valid parameters
        JsonNode validParams = objectMapper.createObjectNode()
            .put("query", "test query")
            .put("limit", 5);
            
        ProcessingReport validReport = jsonSchema.validate(validParams);
        assertTrue(validReport.isSuccess());
        
        // Test missing required parameter
        JsonNode missingRequired = objectMapper.createObjectNode()
            .put("limit", 5);
            
        ProcessingReport missingReport = jsonSchema.validate(missingRequired);
        assertFalse(missingReport.isSuccess());
        
        // Test invalid parameter type
        JsonNode invalidType = objectMapper.createObjectNode()
            .put("query", "test")
            .put("limit", "not-a-number");
            
        ProcessingReport invalidReport = jsonSchema.validate(invalidType);
        assertFalse(invalidReport.isSuccess());
    }
    3. 错误处理测试

    针对错误情况创建特定的测试:

    @pytest.mark.asyncio
    async def test_api_tool_handles_timeout():
        # Arrange
        tool = ApiTool(timeout=0.1)  # Very short timeout
        
        # Mock a request that will time out
        with aioresponses() as mocked:
            mocked.get(
                "https://api.example.com/data",
                callback=lambda *args, **kwargs: asyncio.sleep(0.5)  # Longer than timeout
            )
            
            request = ToolRequest(
                tool_name="apiTool",
                parameters={"url": "https://api.example.com/data"}
            )
            
            # Act & Assert
            with pytest.raises(ToolExecutionException) as exc_info:
                await tool.execute_async(request)
            
            # Verify exception message
            assert "timed out" in str(exc_info.value).lower()
    
    @pytest.mark.asyncio
    async def test_api_tool_handles_rate_limiting():
        # Arrange
        tool = ApiTool()
        
        # Mock a rate-limited response
        with aioresponses() as mocked:
            mocked.get(
                "https://api.example.com/data",
                status=429,
                headers={"Retry-After": "2"},
                body=json.dumps({"error": "Rate limit exceeded"})
            )
            
            request = ToolRequest(
                tool_name="apiTool",
                parameters={"url": "https://api.example.com/data"}
            )
            
            # Act & Assert
            with pytest.raises(ToolExecutionException) as exc_info:
                await tool.execute_async(request)
            
            # Verify exception contains rate limit information
            error_msg = str(exc_info.value).lower()
            assert "rate limit" in error_msg
            assert "try again" in error_msg

    集成测试

    1. 工具链测试

    测试工具按预期组合协同工作:

    [Fact]
    public async Task DataProcessingWorkflow_CompletesSuccessfully()
    {
        // Arrange
        var dataFetchTool = new DataFetchTool(mockDataService.Object);
        var analysisTools = new DataAnalysisTool(mockAnalysisService.Object);
        var visualizationTool = new DataVisualizationTool(mockVisualizationService.Object);
        
        var toolRegistry = new ToolRegistry();
        toolRegistry.RegisterTool(dataFetchTool);
        toolRegistry.RegisterTool(analysisTools);
        toolRegistry.RegisterTool(visualizationTool);
        
        var workflowExecutor = new WorkflowExecutor(toolRegistry);
        
        // Act
        var result = await workflowExecutor.ExecuteWorkflowAsync(new[] {
            new ToolCall("dataFetch", new { source = "sales2023" }),
            new ToolCall("dataAnalysis", ctx => new { 
                data = ctx.GetResult("dataFetch"),
                analysis = "trend" 
            }),
            new ToolCall("dataVisualize", ctx => new {
                analysisResult = ctx.GetResult("dataAnalysis"),
                type = "line-chart"
            })
        });
        
        // Assert
        Assert.NotNull(result);
        Assert.True(result.Success);
        Assert.NotNull(result.GetResult("dataVisualize"));
        Assert.Contains("chartUrl", result.GetResult("dataVisualize").ToString());
    }
    2. MCP 服务器测试

    使用完整工具注册和执行来测试 MCP 服务器:

    @SpringBootTest
    @AutoConfigureMockMvc
    public class McpServerIntegrationTest {
        
        @Autowired
        private MockMvc mockMvc;
        
        @Autowired
        private ObjectMapper objectMapper;
        
        @Test
        public void testToolDiscovery() throws Exception {
            // Test the discovery endpoint
            mockMvc.perform(get("/mcp/tools"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.tools").isArray())
                .andExpect(jsonPath("$.tools[*].name").value(hasItems(
                    "weatherForecast", "calculator", "documentSearch"
                )));
        }
        
        @Test
        public void testToolExecution() throws Exception {
            // Create tool request
            Map<String, Object> request = new HashMap<>();
            request.put("toolName", "calculator");
            
            Map<String, Object> parameters = new HashMap<>();
            parameters.put("operation", "add");
            parameters.put("a", 5);
            parameters.put("b", 7);
            request.put("parameters", parameters);
            
            // Send request and verify response
            mockMvc.perform(post("/mcp/execute")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(request)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.result.value").value(12));
        }
        
        @Test
        public void testToolValidation() throws Exception {
            // Create invalid tool request
            Map<String, Object> request = new HashMap<>();
            request.put("toolName", "calculator");
            
            Map<String, Object> parameters = new HashMap<>();
            parameters.put("operation", "divide");
            parameters.put("a", 10);
            // Missing parameter "b"
            request.put("parameters", parameters);
            
            // Send request and verify error response
            mockMvc.perform(post("/mcp/execute")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(request)))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.error").exists());
        }
    }
    3.端到端测试

    测试从模型提示到工具执行的完整工作流程:

    @pytest.mark.asyncio
    async def test_model_interaction_with_tool():
        # Arrange - Set up MCP client and mock model
        mcp_client = McpClient(server_url="http://localhost:5000")
        
        # Mock model responses
        mock_model = MockLanguageModel([
            MockResponse(
                "What's the weather in Seattle?",
                tool_calls=[{
                    "tool_name": "weatherForecast",
                    "parameters": {"location": "Seattle", "days": 3}
                }]
            ),
            MockResponse(
                "Here's the weather forecast for Seattle:\n- Today: 65°F, Partly Cloudy\n- Tomorrow: 68°F, Sunny\n- Day after: 62°F, Rain",
                tool_calls=[]
            )
        ])
        
        # Mock weather tool response
        with aioresponses() as mocked:
            mocked.post(
                "http://localhost:5000/mcp/execute",
                payload={
                    "result": {
                        "location": "Seattle",
                        "forecast": [
                            {"date": "2023-06-01", "temperature": 65, "conditions": "Partly Cloudy"},
                            {"date": "2023-06-02", "temperature": 68, "conditions": "Sunny"},
                            {"date": "2023-06-03", "temperature": 62, "conditions": "Rain"}
                        ]
                    }
                }
            )
            
            # Act
            response = await mcp_client.send_prompt(
                "What's the weather in Seattle?",
                model=mock_model,
                allowed_tools=["weatherForecast"]
            )
            
            # Assert
            assert "Seattle" in response.generated_text
            assert "65" in response.generated_text
            assert "Sunny" in response.generated_text
            assert "Rain" in response.generated_text
            assert len(response.tool_calls) == 1
            assert response.tool_calls[0].tool_name == "weatherForecast"

    性能测试

    1.负载测试

    测试您的 MCP 服务器可以处理多少个并发请求:

    [Fact]
    public async Task McpServer_HandlesHighConcurrency()
    {
        // Arrange
        var server = new McpServer(
            name: "TestServer",
            version: "1.0",
            maxConcurrentRequests: 100
        );
        
        server.RegisterTool(new FastExecutingTool());
        await server.StartAsync();
        
        var client = new McpClient("http://localhost:5000");
        
        // Act
        var tasks = new List<Task<McpResponse>>();
        for (int i = 0; i < 1000; i++)
        {
            tasks.Add(client.ExecuteToolAsync("fastTool", new { iteration = i }));
        }
        
        var results = await Task.WhenAll(tasks);
        
        // Assert
        Assert.Equal(1000, results.Length);
        Assert.All(results, r => Assert.NotNull(r));
    }
    2.压力测试

    在极端负载下测试系统:

    @Test
    public void testServerUnderStress() {
        int maxUsers = 1000;
        int rampUpTimeSeconds = 60;
        int testDurationSeconds = 300;
        
        // Set up JMeter for stress testing
        StandardJMeterEngine jmeter = new StandardJMeterEngine();
        
        // Configure JMeter test plan
        HashTree testPlanTree = new HashTree();
        
        // Create test plan, thread group, samplers, etc.
        TestPlan testPlan = new TestPlan("MCP Server Stress Test");
        testPlanTree.add(testPlan);
        
        ThreadGroup threadGroup = new ThreadGroup();
        threadGroup.setNumThreads(maxUsers);
        threadGroup.setRampUp(rampUpTimeSeconds);
        threadGroup.setScheduler(true);
        threadGroup.setDuration(testDurationSeconds);
        
        testPlanTree.add(threadGroup);
        
        // Add HTTP sampler for tool execution
        HTTPSampler toolExecutionSampler = new HTTPSampler();
        toolExecutionSampler.setDomain("localhost");
        toolExecutionSampler.setPort(5000);
        toolExecutionSampler.setPath("/mcp/execute");
        toolExecutionSampler.setMethod("POST");
        toolExecutionSampler.addArgument("toolName", "calculator");
        toolExecutionSampler.addArgument("parameters", "{\"operation\":\"add\",\"a\":5,\"b\":7}");
        
        threadGroup.add(toolExecutionSampler);
        
        // Add listeners
        SummaryReport summaryReport = new SummaryReport();
        threadGroup.add(summaryReport);
        
        // Run test
        jmeter.configure(testPlanTree);
        jmeter.run();
        
        // Validate results
        assertEquals(0, summaryReport.getErrorCount());
        assertTrue(summaryReport.getAverage() < 200); // Average response time < 200ms
        assertTrue(summaryReport.getPercentile(90.0) < 500); // 90th percentile < 500ms
    }
    3. 监控和分析

    设置监控以进行长期性能分析:

    # Configure monitoring for an MCP server
    def configure_monitoring(server):
        # Set up Prometheus metrics
        prometheus_metrics = {
            "request_count": Counter("mcp_requests_total", "Total MCP requests"),
            "request_latency": Histogram(
                "mcp_request_duration_seconds", 
                "Request duration in seconds",
                buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 2.5, 5.0, 10.0]
            ),
            "tool_execution_count": Counter(
                "mcp_tool_executions_total", 
                "Tool execution count",
                labelnames=["tool_name"]
            ),
            "tool_execution_latency": Histogram(
                "mcp_tool_duration_seconds", 
                "Tool execution duration in seconds",
                labelnames=["tool_name"],
                buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 2.5, 5.0, 10.0]
            ),
            "tool_errors": Counter(
                "mcp_tool_errors_total",
                "Tool execution errors",
                labelnames=["tool_name", "error_type"]
            )
        }
        
        # Add middleware for timing and recording metrics
        server.add_middleware(PrometheusMiddleware(prometheus_metrics))
        
        # Expose metrics endpoint
        @server.router.get("/metrics")
        async def metrics():
            return generate_latest()
        
        return server

    MCP 工作流设计模式

    精心设计的 MCP 工作流程能够提升效率、可靠性和可维护性。以下是一些值得遵循的关键模式:

    1. 工具链模式

    按顺序连接多个工具,每个工具的输出将成为下一个工具的输入:

    # Python Chain of Tools implementation
    class ChainWorkflow:
        def __init__(self, tools_chain):
            self.tools_chain = tools_chain  # List of tool names to execute in sequence
        
        async def execute(self, mcp_client, initial_input):
            current_result = initial_input
            all_results = {"input": initial_input}
            
            for tool_name in self.tools_chain:
                # Execute each tool in the chain, passing previous result
                response = await mcp_client.execute_tool(tool_name, current_result)
                
                # Store result and use as input for next tool
                all_results[tool_name] = response.result
                current_result = response.result
            
            return {
                "final_result": current_result,
                "all_results": all_results
            }
    
    # Example usage
    data_processing_chain = ChainWorkflow([
        "dataFetch",
        "dataCleaner",
        "dataAnalyzer",
        "dataVisualizer"
    ])
    
    result = await data_processing_chain.execute(
        mcp_client,
        {"source": "sales_database", "table": "transactions"}
    )

    2. 调度程序模式

    使用根据输入调度到专门工具的中央工具:

    public class ContentDispatcherTool : IMcpTool
    {
        private readonly IMcpClient _mcpClient;
        
        public ContentDispatcherTool(IMcpClient mcpClient)
        {
            _mcpClient = mcpClient;
        }
        
        public string Name => "contentProcessor";
        public string Description => "Processes content of various types";
        
        public object GetSchema()
        {
            return new {
                type = "object",
                properties = new {
                    content = new { type = "string" },
                    contentType = new { 
                        type = "string",
                        enum = new[] { "text", "html", "markdown", "csv", "code" }
                    },
                    operation = new { 
                        type = "string",
                        enum = new[] { "summarize", "analyze", "extract", "convert" }
                    }
                },
                required = new[] { "content", "contentType", "operation" }
            };
        }
        
        public async Task<ToolResponse> ExecuteAsync(ToolRequest request)
        {
            var content = request.Parameters.GetProperty("content").GetString();
            var contentType = request.Parameters.GetProperty("contentType").GetString();
            var operation = request.Parameters.GetProperty("operation").GetString();
            
            // Determine which specialized tool to use
            string targetTool = DetermineTargetTool(contentType, operation);
            
            // Forward to the specialized tool
            var specializedResponse = await _mcpClient.ExecuteToolAsync(
                targetTool,
                new { content, options = GetOptionsForTool(targetTool, operation) }
            );
            
            return new ToolResponse { Result = specializedResponse.Result };
        }
        
        private string DetermineTargetTool(string contentType, string operation)
        {
            return (contentType, operation) switch
            {
                ("text", "summarize") => "textSummarizer",
                ("text", "analyze") => "textAnalyzer",
                ("html", _) => "htmlProcessor",
                ("markdown", _) => "markdownProcessor",
                ("csv", _) => "csvProcessor",
                ("code", _) => "codeAnalyzer",
                _ => throw new ToolExecutionException($"No tool available for {contentType}/{operation}")
            };
        }
        
        private object GetOptionsForTool(string toolName, string operation)
        {
            // Return appropriate options for each specialized tool
            return toolName switch
            {
                "textSummarizer" => new { length = "medium" },
                "htmlProcessor" => new { cleanUp = true, operation },
                // Options for other tools...
                _ => new { }
            };
        }
    }

    3.并行处理模式

    同时执行多个工具以提高效率:

    public class ParallelDataProcessingWorkflow {
        private final McpClient mcpClient;
        
        public ParallelDataProcessingWorkflow(McpClient mcpClient) {
            this.mcpClient = mcpClient;
        }
        
        public WorkflowResult execute(String datasetId) {
            // Step 1: Fetch dataset metadata (synchronous)
            ToolResponse metadataResponse = mcpClient.executeTool("datasetMetadata", 
                Map.of("datasetId", datasetId));
            
            // Step 2: Launch multiple analyses in parallel
            CompletableFuture<ToolResponse> statisticalAnalysis = CompletableFuture.supplyAsync(() ->
                mcpClient.executeTool("statisticalAnalysis", Map.of(
                    "datasetId", datasetId,
                    "type", "comprehensive"
                ))
            );
            
            CompletableFuture<ToolResponse> correlationAnalysis = CompletableFuture.supplyAsync(() ->
                mcpClient.executeTool("correlationAnalysis", Map.of(
                    "datasetId", datasetId,
                    "method", "pearson"
                ))
            );
            
            CompletableFuture<ToolResponse> outlierDetection = CompletableFuture.supplyAsync(() ->
                mcpClient.executeTool("outlierDetection", Map.of(
                    "datasetId", datasetId,
                    "sensitivity", "medium"
                ))
            );
            
            // Wait for all parallel tasks to complete
            CompletableFuture<Void> allAnalyses = CompletableFuture.allOf(
                statisticalAnalysis, correlationAnalysis, outlierDetection
            );
            
            allAnalyses.join();  // Wait for completion
            
            // Step 3: Combine results
            Map<String, Object> combinedResults = new HashMap<>();
            combinedResults.put("metadata", metadataResponse.getResult());
            combinedResults.put("statistics", statisticalAnalysis.join().getResult());
            combinedResults.put("correlations", correlationAnalysis.join().getResult());
            combinedResults.put("outliers", outlierDetection.join().getResult());
            
            // Step 4: Generate summary report
            ToolResponse summaryResponse = mcpClient.executeTool("reportGenerator", 
                Map.of("analysisResults", combinedResults));
            
            // Return complete workflow result
            WorkflowResult result = new WorkflowResult();
            result.setDatasetId(datasetId);
            result.setAnalysisResults(combinedResults);
            result.setSummaryReport(summaryResponse.getResult());
            
            return result;
        }
    }

    4.错误恢复模式

    针对工具故障实施优雅的回退:

    class ResilientWorkflow:
        def __init__(self, mcp_client):
            self.client = mcp_client
        
        async def execute_with_fallback(self, primary_tool, fallback_tool, parameters):
            try:
                # Try primary tool first
                response = await self.client.execute_tool(primary_tool, parameters)
                return {
                    "result": response.result,
                    "source": "primary",
                    "tool": primary_tool
                }
            except ToolExecutionException as e:
                # Log the failure
                logging.warning(f"Primary tool '{primary_tool}' failed: {str(e)}")
                
                # Fall back to secondary tool
                try:
                    # Might need to transform parameters for fallback tool
                    fallback_params = self._adapt_parameters(parameters, primary_tool, fallback_tool)
                    
                    response = await self.client.execute_tool(fallback_tool, fallback_params)
                    return {
                        "result": response.result,
                        "source": "fallback",
                        "tool": fallback_tool,
                        "primaryError": str(e)
                    }
                except ToolExecutionException as fallback_error:
                    # Both tools failed
                    logging.error(f"Both primary and fallback tools failed. Fallback error: {str(fallback_error)}")
                    raise WorkflowExecutionException(
                        f"Workflow failed: primary error: {str(e)}; fallback error: {str(fallback_error)}"
                    )
        
        def _adapt_parameters(self, params, from_tool, to_tool):
            """Adapt parameters between different tools if needed"""
            # This implementation would depend on the specific tools
            # For this example, we'll just return the original parameters
            return params
    
    # Example usage
    async def get_weather(workflow, location):
        return await workflow.execute_with_fallback(
            "premiumWeatherService",  # Primary (paid) weather API
            "basicWeatherService",    # Fallback (free) weather API
            {"location": location}
        )

    5. 工作流组合模式

    通过组合更简单的工作流程来构建复杂的工作流程:

    public class CompositeWorkflow : IWorkflow
    {
        private readonly List<IWorkflow> _workflows;
        
        public CompositeWorkflow(IEnumerable<IWorkflow> workflows)
        {
            _workflows = new List<IWorkflow>(workflows);
        }
        
        public async Task<WorkflowResult> ExecuteAsync(WorkflowContext context)
        {
            var results = new Dictionary<string, object>();
            
            foreach (var workflow in _workflows)
            {
                var workflowResult = await workflow.ExecuteAsync(context);
                
                // Store each workflow's result
                results[workflow.Name] = workflowResult;
                
                // Update context with the result for the next workflow
                context = context.WithResult(workflow.Name, workflowResult);
            }
            
            return new WorkflowResult(results);
        }
        
        public string Name => "CompositeWorkflow";
        public string Description => "Executes multiple workflows in sequence";
    }
    
    // Example usage
    var documentWorkflow = new CompositeWorkflow(new IWorkflow[] {
        new DocumentFetchWorkflow(),
        new DocumentProcessingWorkflow(),
        new InsightGenerationWorkflow(),
        new ReportGenerationWorkflow()
    });
    
    var result = await documentWorkflow.ExecuteAsync(new WorkflowContext {
        Parameters = new { documentId = "12345" }
    });

    测试 MCP 服务器:最佳实践和重要提示

    概述

    测试是开发可靠、高质量的 MCP 服务器的关键环节。本指南提供了全面的最佳实践和技巧,帮助您在整个开发生命周期内测试 MCP 服务器,涵盖单元测试、集成测试以及端到端验证等各个环节。

    为什么测试对于 MCP 服务器如此重要

    MCP 服务器是 AI 模型和客户端应用程序之间的关键中间件。全面的测试可确保:

  • 生产环境中的可靠性
  • 准确处理请求和响应
  • 正确实施 MCP 规范
  • 抵御故障和极端情况的能力
  • 各种负载下性能一致
  • MCP 服务器的单元测试

    单元测试(基础)

    单元测试单独验证 MCP 服务器的各个组件。

    测试什么
  • 资源处理程序:独立测试每个资源处理程序的逻辑
  • 工具实现:通过各种输入验证工具行为
  • 提示模板:确保提示模板正确呈现
  • 模式验证:测试参数验证逻辑
  • 错误处理:验证无效输入的错误响应
  • 单元测试的最佳实践
    // Example unit test for a calculator tool in C#
    [Fact]
    public async Task CalculatorTool_Add_ReturnsCorrectSum()
    {
        // Arrange
        var calculator = new CalculatorTool();
        var parameters = new Dictionary<string, object>
        {
            ["operation"] = "add",
            ["a"] = 5,
            ["b"] = 7
        };
        
        // Act
        var response = await calculator.ExecuteAsync(parameters);
        var result = JsonSerializer.Deserialize<CalculationResult>(response.Content[0].ToString());
        
        // Assert
        Assert.Equal(12, result.Value);
    }
    # Example unit test for a calculator tool in Python
    def test_calculator_tool_add():
        # Arrange
        calculator = CalculatorTool()
        parameters = {
            "operation": "add",
            "a": 5,
            "b": 7
        }
        
        # Act
        response = calculator.execute(parameters)
        result = json.loads(response.content[0].text)
        
        # Assert
        assert result["value"] == 12

    集成测试(中间层)

    集成测试验证 MCP 服务器组件之间的交互。

    测试什么
  • 服务器初始化:使用各种配置测试服务器启动
  • 路由注册:验证所有端点是否正确注册
  • 请求处理:测试完整的请求-响应周期
  • 错误传播:确保跨组件正确处理错误
  • 身份验证和授权:测试安全机制
  • 集成测试的最佳实践
    // Example integration test for MCP server in C#
    [Fact]
    public async Task Server_ProcessToolRequest_ReturnsValidResponse()
    {
        // Arrange
        var server = new McpServer();
        server.RegisterTool(new CalculatorTool());
        await server.StartAsync();
        
        var request = new McpRequest
        {
            Tool = "calculator",
            Parameters = new Dictionary<string, object>
            {
                ["operation"] = "multiply",
                ["a"] = 6,
                ["b"] = 7
            }
        };
        
        // Act
        var response = await server.ProcessRequestAsync(request);
        
        // Assert
        Assert.NotNull(response);
        Assert.Equal(McpStatusCodes.Success, response.StatusCode);
        // Additional assertions for response content
        
        // Cleanup
        await server.StopAsync();
    }

    端到端测试(顶层)

    端到端测试验证从客户端到服务器的完整系统行为。

    测试什么
  • 客户端-服务器通信:测试完整的请求-响应周期
  • 真实客户端 SDK:使用实际客户端实现进行测试
  • 负载下的性能:验证多个并发请求的行为
  • 错误恢复:测试系统从故障中恢复
  • 长时间运行的操作:验证流式操作和长时间操作的处理
  • E2E 测试的最佳实践
    // Example E2E test with a client in TypeScript
    describe('MCP Server E2E Tests', () => {
      let client: McpClient;
      
      beforeAll(async () => {
        // Start server in test environment
        await startTestServer();
        client = new McpClient('http://localhost:5000');
      });
      
      afterAll(async () => {
        await stopTestServer();
      });
      
      test('Client can invoke calculator tool and get correct result', async () => {
        // Act
        const response = await client.invokeToolAsync('calculator', {
          operation: 'divide',
          a: 20,
          b: 4
        });
        
        // Assert
        expect(response.statusCode).toBe(200);
        expect(response.content[0].text).toContain('5');
      });
    });

    MCP 测试的模拟策略

    模拟对于测试期间隔离组件至关重要。

    要模拟的组件

  • 外部 AI 模型:用于可预测测试的模拟模型响应
  • 外部服务:模拟 API 依赖项(数据库、第三方服务)
  • 身份验证服务:模拟身份提供者
  • 资源提供者:模拟昂贵的资源处理程序
  • 示例:模拟 AI 模型响应

    // C# example with Moq
    var mockModel = new Mock<ILanguageModel>();
    mockModel
        .Setup(m => m.GenerateResponseAsync(
            It.IsAny<string>(),
            It.IsAny<McpRequestContext>()))
        .ReturnsAsync(new ModelResponse { 
            Text = "Mocked model response",
            FinishReason = FinishReason.Completed
        });
    
    var server = new McpServer(modelClient: mockModel.Object);
    # Python example with unittest.mock
    @patch('mcp_server.models.OpenAIModel')
    def test_with_mock_model(mock_model):
        # Configure mock
        mock_model.return_value.generate_response.return_value = {
            "text": "Mocked model response",
            "finish_reason": "completed"
        }
        
        # Use mock in test
        server = McpServer(model_client=mock_model)
        # Continue with test

    性能测试

    性能测试对于生产 MCP 服务器至关重要。

    测量什么

  • 延迟:请求的响应时间
  • 吞吐量:每秒处理的请求数
  • 资源利用率:CPU、内存、网络使用情况
  • 并发处理:并行请求下的行为
  • 扩展特性:随着负载增加的性能
  • 性能测试工具

    k6:开源负载测试工具
  • JMeter:全面的性能测试
  • Locust:基于 Python 的负载测试
  • Azure 负载测试:基于云的性能测试

示例:使用 k6 进行基本负载测试

// k6 script for load testing MCP server
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 10,  // 10 virtual users
  duration: '30s',
};

export default function () {
  const payload = JSON.stringify({
    tool: 'calculator',
    parameters: {
      operation: 'add',
      a: Math.floor(Math.random() * 100),
      b: Math.floor(Math.random() * 100)
    }
  });

  const params = {
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer test-token'
    },
  };

  const res = http.post('http://localhost:5000/api/tools/invoke', payload, params);
  
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });
  
  sleep(1);
}

MCP 服务器的测试自动化

自动化测试可确保一致的质量和更快的反馈循环。

CI/CD 集成

  1. 对拉取请求运行单元测试:确保代码更改不会破坏现有功能
  2. 暂存集成测试:在预生产环境中运行集成测试
  3. 性能基准:维护性能基准以捕捉回归
  4. 安全扫描:将安全测试自动化作为管道的一部分

CI 管道示例 (GitHub Actions)

name: MCP Server Tests

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Set up Runtime
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: '8.0.x'
    
    - name: Restore dependencies
      run: dotnet restore
    
    - name: Build
      run: dotnet build --no-restore
    
    - name: Unit Tests
      run: dotnet test --no-build --filter Category=Unit
    
    - name: Integration Tests
      run: dotnet test --no-build --filter Category=Integration
      
    - name: Performance Tests
      run: dotnet run --project tests/PerformanceTests/PerformanceTests.csproj

符合 MCP 规范的测试

验证您的服务器是否正确实现了 MCP 规范。

关键合规领域

  1. API 端点:测试所需端点(/resources、/tools 等)
  2. 请求/响应格式:验证架构合规性
  3. 错误代码:验证各种场景的正确状态代码
  4. 内容类型:测试处理不同的内容类型
  5. 身份验证流程:验证符合规范的身份验证机制

合规性测试套件

[Fact]
public async Task Server_ResourceEndpoint_ReturnsCorrectSchema()
{
    // Arrange
    var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token");
    
    // Act
    var response = await client.GetAsync("http://localhost:5000/api/resources");
    var content = await response.Content.ReadAsStringAsync();
    var resources = JsonSerializer.Deserialize<ResourceList>(content);
    
    // Assert
    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    Assert.NotNull(resources);
    Assert.All(resources.Resources, resource => 
    {
        Assert.NotNull(resource.Id);
        Assert.NotNull(resource.Type);
        // Additional schema validation
    });
}

有效 MCP 服务器测试的十大技巧

  1. 单独测试工具定义:独立于工具逻辑验证架构定义
  2. 使用参数化测试:使用各种输入(包括边缘情况)的测试工具
  3. 检查错误响应:验证所有可能的错误情况是否得到正确的错误处理
  4. 测试授权逻辑:确保对不同用户角色进行适当的访问控制
  5. 监控测试覆盖率:目标是实现关键路径代码的高覆盖率
  6. 测试流媒体响应:验证流媒体内容的正确处理
  7. 模拟网络问题:测试在较差的网络条件下的行为
  8. 测试资源限制:验证达到配额或速率限制时的行为
  9. 自动化回归测试:构建一个在每次代码更改时运行的套件
  10. 记录测试用例:维护测试场景的清晰文档

常见的测试陷阱

  • 过度依赖快乐路径测试:确保彻底测试错误情况
  • 忽视性能测试:在瓶颈影响生产之前识别它们
  • 仅进行隔离测试:结合单元、集成和 E2E 测试
  • API 覆盖不完整:确保所有端点和功能都经过测试
  • 不一致的测试环境:使用容器来确保一致的测试环境

结论

全面的测试策略对于开发可靠、高质量的 MCP 服务器至关重要。通过实施本指南中概述的最佳实践和技巧,您可以确保您的 MCP 实现满足最高的质量、可靠性和性能标准。

关键要点

  1. 工具设计:遵循单一责任原则,使用依赖注入,并进行可组合性设计
  2. 模式设计:创建清晰、有据可查的模式,并设置适当的验证约束
  3. 错误处理:实现优雅的错误处理、结构化的错误响应和重试逻辑
  4. 性能:使用缓存、异步处理和资源限制
  5. 安全性:应用全面的输入验证、授权检查和敏感数据处理
  6. 测试:创建全面的单元、集成和端到端测试
  7. 工作流模式:应用已建立的模式,如链、调度程序和并行处理

锻炼

为文档处理系统设计一个 MCP 工具和工作流程:

  1. 接受多种格式的文档(PDF、DOCX、TXT)
  2. 从文档中提取文本和关键信息
  3. 按类型和内容对文档进行分类
  4. 生成每个文档的摘要

实现最适合此场景的工具架构、错误处理和工作流模式。思考如何测试此实现。


后续步骤

恭喜您完成 MCP 课程!继续您的旅程:

  1. 加入 MCP 社区,了解最新动态
  2. 为开源 MCP 项目做出贡献
  3. 在您自己组织的 AI 计划中应用 MCP 原则
  4. 探索适合您行业的专门 MCP 实施。
  5. 考虑参加特定 MCP 主题的高级课程,例如多模式集成或企业应用程序集成。
  6. 尝试使用本课程中学到的原理来构建您自己的 MCP 工具和工作流程。

      对ai感兴趣的可以留言大家一起沟通,ai的浪潮还在持续!

MCP开发实战快速开始 本文介绍了如何快速开始MCP开发,包括搭建开发环境、创建MCP服务器以及构建AI原生应用。首先,通过安装Python和MCP SDK,配置开发环境,并验证其正确性。接着,详细讲解了如何创建一个简单的文件服务器,使用MCP CLI快速生成服务器项目,并通过测试客户端验证服务器功能。最后,展示了如何利用MCP服务器和大语言模型构建一个能够访问和分析文件的智能助手。通过本文的实战示例,读者可以快速上手MCP开发,并应用于AI原生应用的构建。 阅读详情

相关推荐

数字取证双雄:Passware Kit Forensic 与 ElcomSoft 在实战场景下的能力边界解析

本文深入解析了Passware Kit Forensic与ElcomSoft在数字取证实战中的能力边界,涵盖Windows、macOS、iOS和Android系统的密码恢复与数据提取技术。通过真实案例对比两款工具在硬件加速、分布式计算及云取证等场景下的表现差异,为取证专家提供关键决策参考。特别探讨了密码恢复软件在加密设备突破中的核心作用。

weixin_30342209的博客 399

通过 MCP 实现 AI Agent 目前有哪些最佳实践?

通过(Model Context Protocol)实现AI Agent的最佳实践,目前已在工具集成、框架设计、开发模式等方面形成了多种创新方案。

2401_84204413的博客 2597

手把手教你用示波器搞定PCIe 5.0 REFCLK时钟信号测量(附完整参数解读)

本文详细介绍了如何使用示波器进行PCIe 5.0 REFCLK时钟信号测量,包括示波器与探头的选择、关键参数的测量方法及解读,以及实战中的常见问题与解决方案。通过手把手教学和完整参数解读,帮助硬件工程师高效完成时钟信号测量,确保系统稳定性。

weixin_28704565的博客 359

大模型应用之:MCP最佳实践

MCP 建立了一个通用的「即插即用」协议,允许任何兼容 MCP 的客户端与任何兼容 MCP 的服务器协同工作,大大降低了集成复杂性和开发时间。正如 USB 创建了一个通用接口,允许任何 USB 设备连接到任何 USB 端口——消除了对特定设备连接器的需求——MCP 创建了一种标准化的方式,使 AI 应用程序能够连接各种数据源和工具。然而,有理由保持乐观。MCP 生态系统似乎已经克服了最初的采用障碍,越来越多的代理(MCP 客户端)出现,每周开发的 MCP 服务器数量也在增加,用于解决特定领域的任务。

Diligence is the mother of success. 905

手把手教你新建一个winform项目(史上最全)

本文介绍如何利用visual studio新建一个winform程序

qq_34059233的博客 4万+

【MATLAB例程】基于TDOA定位(两步最小二乘)的三维轨迹定位和UKF滤波,TDOA的锚点可以自适应,附完整代码

该代码实现了一种**融合TDOA(到达时间差)定位与无迹卡尔曼滤波(UKF)**的动态目标轨迹跟踪方案,适用于三维空间中的移动目标定位与滤波。TDOA定位:通过两步加权最小二乘法(TS-WLS)解算目标位置。UKF滤波:对定位结果进行非线性滤波,抑制噪声并提升轨迹平滑性。多维度误差分析:对比滤波前后误差,评估算法性能。

985工科博士毕业,专攻定位、导航和滤波等算法研究。从业10年,主要使用MATLAB 675

MCP 打造 AI Agent 的 5 大最佳实践

通过 MCP 构建 AI Agent,需要同时关注:清晰任务边界与能力模块合理工具链与多渠道接入成熟设计模式保证扩展与可控状态与上下文管理科学化安全、审计、回滚机制齐备高并发下采用队列、异步与容器化策略当你把 MCP 当成 Agent 的“大脑中枢”,AI Agent 就能从概念走向企业级落地,实现真正的业务增效。

霍格沃兹测试学院的博客 860

华为OD新系统机试真题 - 获取大写字母瓷砖拼出独特图案数量

华为OD机试新系统真题【获取大写字母瓷砖拼出独特图案数量】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD新系统四月二十九号机试真题【获取大写字母瓷砖拼出独特图案数量】

qq_45776114的博客 1218

MCP Server技术拆解:揭秘AI大模型的万能接口架构

MCP服务器为AI能力的统一管理和调用提供了标准化的解决方案。通过本文的学习,相信你已经掌握了MCP服务器的基本概念、创建方法和使用技巧。在实际应用中,记得根据具体需求进行适当的调整和优化。作者简介:本文作者专注于AI服务架构设计和开发,有丰富的实战经验。

qq_27492797的博客 5444

Transformer实战(32)——Transformer模型压缩

在本节中,我们学习了如何在硬件资源受限的情况下减轻运行大模型的负担。我们介绍了如何通过蒸馏、剪枝和量化从训练好的模型中提取高效模型。预训练轻量级的通用语言模型(如 DistilBERT) 可以在各种任务上进行微调,并表现出与非蒸馏模型相当的性能。随着数据量的不断增加,我们希望模型能够更快地运行,在这方面,高效 Transformer 起着至关重要的作用。

盼小辉丶的博客 2567

2024 年高教社杯全国大学生数学建模竞赛 E 题 交通流量管控 详细思路+matlab代码+python代码+论文范例

随着城市化进程的加快、机动车的快速普及,以及人们活动范围的不断扩大,城市道。景区周边道路上既有本地居民出行,也有过境车辆,化配置,在保证车辆通行的前提下,使得两条主路上的车流平均速度最大。对五一黄金周期间的数据进行分析,判定寻找停车位的巡游车辆,并估算假。相位通过的每辆车的信息,包括拍摄地点、行驶方向、拍摄时间和车牌号。济发展和百姓幸福感的一个“痛点”,是相关部门的棘手难题之一。纬中路交叉口,根据车流量的差异,可将一天分成若干个时段,估。是小镇中两条主路的情况:纬中路,从环西路到环东路,约。

YOLO 3310

MCP(模型上下文协议)Server开发实践:使用LLM构建MCP的最佳实践

MCP(模型上下文协议)是一种用于管理大语言模型(LLM)上下文的技术协议,旨在解决模型复杂性和规模增加带来的上下文管理问题。MCP通过标准化存储、传输和处理上下文信息,提升模型推理效率,支持多模态应用,并降低开发成本。文章详细介绍了MCP的核心特点、价值、开发实践及最佳实践,包括如何构建MCP Server、选择合适的LLM、集成MCP协议以及优化性能。MCP作为LLM应用中的重要技术,正被广泛应用于提升业务效率和竞争力。未来,MCP技术有望在更多场景中发挥重要作用。

分享平时的学习心得和笔记 872

时间序列预测 | Python实现ARIMAX和VARMAX时间序列数据预测

时间序列预测 | Python实现ARIMAX和VARMAX时间序列数据预测 目录时间序列预测 | Python实现ARIMAX和VARMAX时间序列数据预测基本介绍ARIMAXVARVARMAVARMAXSESHWES参考资料 基本介绍 本文介绍ARIMAX、VAR、VARMA和VARMAX基本概念及其Python程序设计。 ARIMAX 具有外源回归量的自回归整合移动平均值(ARIMAX)是 ARIMA 模型的扩展,其还包括外生变量的建模。 外生变量也称为协变量,可以被认为是并行输入序列,其在与

前程算法屋的博客 6688

【SpringAI】第六弹:深入解析 MCP 上下文协议、开发和部署 MCP 服务、MCP 安全问题与最佳实践

深入解析 MCP 上下文协议、开发和部署 MCP 服务、MCP 安全问题与最佳实践

OuO-1 的博客 2624

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

革命性AWS MCP Servers:让AWS最佳实践无缝融入你的开发流程

AWS MCP Servers 是一套专业化的 MCP 服务器套件,能够将 AWS 最佳实践直接带入你的开发工作流。无论你是新手开发者还是经验丰富的工程师,这套工具都能帮助你更高效、更规范地使用 AWS 服务,显著提升开发效率与项目质量。 ## 为什么选择 AWS MCP Servers? 在当今云原生开发的浪潮中,如何将 AWS 最佳实践无缝融入日常开发流程,是许多团队面临的共同挑战。AWS

gitblog_00156的博客 760

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Context7 MCP Server技术博客:每周更新的开发技巧与最佳实践

在日常开发中,你是否经常遇到LLM生成的代码示例过时、API不存在或者无法适配特定库版本的问题?Context7 MCP Server(Model Context Protocol Server,模型上下文协议服务器)正是为解决这些痛点而生。本文将分享如何利用Context7获取实时更新的代码文档、自定义项目解析规则,以及最新的开发技巧与最佳实践,帮助开发者提升编码效率。 ## Context7...

gitblog_01111的博客 422

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【强烈收藏】AI应用开发进阶:企业级架构设计与MCP最佳实践

本文全面介绍了企业级AI应用构建的最佳实践,重点阐述了基于MCP范式的AI应用架构,包括AI Agent与LLM的双引擎模式、MCP服务的核心作用、两种构建路径(全新开发与存量改造),以及AI Agent的核心组件、推理模式和运行时挑战。同时详细讲解了如何通过阿里云AI网关、函数计算FC、MSE Nacos等产品实现高效、安全、可观测的AI应用,为企业数字化转型提供技术支持。

m0_59162559的博客 887

MCP协议最佳实践:mcp-for-beginners开发规范

Model Context Protocol(MCP,模型上下文协议)是用于标准化AI模型与客户端应用交互的开放式协议框架。本文基于[mcp-for-beginners](https://gitcode.com/GitHub_Trending/mc/mcp-for-beginners)项目,从架构设计、安全防护、工具开发、测试部署四个维度,系统梳理MCP应用开发的核心规范与实施指南,帮助开发者构建...

gitblog_01085的博客 1310

云原生时代下MCP开发最佳实践,掌握这6大技术点让你少走3年弯路

掌握MCP云原生应用开发6大关键技术,解决部署复杂、运维困难等核心痛点,涵盖微服务架构、容器化部署、持续交付等场景。提升开发效率与系统稳定性,助力企业快速落地云原生转型,值得收藏。

Instrulink的博客 959

MCP 工具开发最佳实践(结合 weather-mcp)

文章摘要: 《MCP 工具开发最佳实践》总结了开发 MCP Server 时的工程经验。文章从 14 个方面给出建议:1) 根据场景选择 stdio 或 HTTP 模式;2) 鉴权设计需支持降级和缓存;3) 配置管理采用三层优先级;4) 工具描述要详细明确;5) 尽早进行参数校验;6) 合理处理错误;7) 配置 HTTP 客户端;8) 日志只输出到 stderr;9) 考虑性能与缓存;10) 协议版本协商;11) 编写本地测试;12) 打包分发;13) 给客户端的建议;14) 避免常见反模式。文章以 wea

qq_33988736的博客 365

Spec Workflow MCP多语言支持详解:全球化开发团队的最佳实践

Spec Workflow MCP作为一款Model Context Protocol (MCP)服务器,为AI辅助软件开发提供结构化规范驱动的工作流工具,其强大的多语言支持功能让全球化开发团队能够无缝协作。本文将详细介绍Spec Workflow MCP的多语言支持特性、配置方法及最佳实践,帮助团队高效实现国际化开发。 ## 多语言支持核心架构 Spec Workflow MCP的国际化架构

gitblog_00097的博客 441

MCP(模型上下文协议)Server 开发实践:使用 LLM 构建 MCP 的最佳实践

摘要: MCP(模型上下文协议)是一种规范模型间交互的协议,通过共享上下文空间实现协同工作,提升协作效率与泛化能力。其核心价值包括:优化复杂任务处理(如医疗诊断)、增强模型适应性(如多语言文本分类)以及构建多样化模型生态。开发MCP需明确协作模型、设计上下文结构、实现接口并监控运行状态。借助LLM构建MCP Server时,可发挥其知识整合、动态更新和协调仲裁优势,例如智能写作辅助系统的上下文初始化或资源分配决策。MCP为模型协作提供了高效框架,未来应用前景广阔。

分享平时的学习心得和笔记 1084

Microsoft MCP最佳实践:企业级AI应用开发的架构设计与代码规范

Microsoft MCP(Model Context Protocol)是企业级AI应用开发的核心协议,通过标准化AI应用与数据源的连接方式,为大型语言模型提供统一的上下文接入方案。本文将深入探讨Microsoft MCP的最佳实践,涵盖架构设计原则、代码规范标准以及企业级部署策略。 ## 🌟 MCP架构设计核心原则 ### 客户端-服务器分离架构 Microsoft MCP采用清晰的客户

gitblog_00490的博客 688

避开这5个坑!TypeScript开发MCP服务的最佳实践

本文针对TypeScript开发MCP服务时常见的五个陷阱,提供了专业的最佳实践指南。重点涵盖如何通过自定义类型声明补全官方SDK的类型缺失,使用Zod进行运行时参数校验,实现分层的异步错误处理机制,建立类型安全的配置管理系统,以及优化性能的缓存与并发控制策略。这些实践能帮助开发者构建健壮、可维护的MCP服务,特别是与飞书文档等企业级API集成时。

wind6的博客 1057

MCP工具开发最佳实践与规范

本文系统阐述2026年MCP工具开发最佳实践,涵盖协议演进、设计原则与Java SDK规范。强调工具命名语义化、描述完整化、Schema严格化,倡导高内聚、低耦合的复合与工作流工具设计,强化幂等性、安全可控与结果可控。针对大返回值、版本兼容等痛点,提出分页、URI引用、能力协商等解决方案。推荐“Agent→MCP Adapter→服务”架构,实现跨模型兼容与生态复用,助力构建高效、可靠、可维护的AI工具链。

BADAO_LIUMANG_QIZHI的博客 467
上一篇: MCP - 网络搜索
下一篇: AI应用开发篇--作为一个个人开发者我怎末来运用ai实现自己的能力成长,能挣的一桶金
goodfornothing-s
博客等级 码龄8年 105粉丝 36原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值