(Кликнете върху изображението по-горе, за да гледате видеото към този урок)
Този урок се фокусира върху напреднали най-добри практики за разработка, тестване и внедряване на MCP сървъри и функции в производствени среди. С нарастването на сложността и значимостта на MCP екосистемите, следването на утвърдени модели гарантира надеждност, поддръжка и съвместимост. Урокът обединява практически опит, натрупан от реални MCP реализации, за да ви насочи към създаването на стабилни, ефективни сървъри с подходящи ресурси, подсказки и инструменти.
До края на този урок ще можете да:
- Прилагате най-добрите практики в индустрията за проектиране на MCP сървъри и функции
- Създавате цялостни стратегии за тестване на MCP сървъри
- Проектирате ефективни, многократно използваеми модели на работни процеси за сложни MCP приложения
- Реализирате правилно управление на грешки, логване и наблюдаемост в MCP сървъри
- Оптимизирате MCP реализации за производителност, сигурност и поддръжка
Преди да се потопим в конкретни практики за реализация, е важно да разберем основните принципи, които ръководят ефективната разработка на MCP:
-
Стандартизирана комуникация: MCP използва JSON-RPC 2.0 като основа, осигурявайки последователен формат за заявки, отговори и управление на грешки във всички реализации.
-
Дизайн, ориентиран към потребителя: Винаги поставяйте на първо място съгласието, контрола и прозрачността на потребителя във вашите MCP реализации.
-
Сигурност на първо място: Реализирайте надеждни мерки за сигурност, включително удостоверяване, упълномощаване, валидиране и ограничаване на честотата.
-
Модулна архитектура: Проектирайте MCP сървъри с модулен подход, където всеки инструмент и ресурс има ясна и фокусирана цел.
-
Състояние на връзките: Използвайте способността на MCP да поддържа състояние през множество заявки за по-кохерентни и контекстуално осъзнати взаимодействия.
Следните най-добри практики са извлечени от официалната документация на Model Context Protocol:
-
Съгласие и контрол на потребителя: Винаги изисквайте изрично съгласие от потребителя преди достъп до данни или извършване на операции. Осигурете ясен контрол върху това какви данни се споделят и какви действия са упълномощени.
-
Поверителност на данните: Разкривайте потребителски данни само с изрично съгласие и ги защитавайте с подходящи контроли за достъп. Предпазвайте от неупълномощено предаване на данни.
-
Безопасност на инструментите: Изисквайте изрично съгласие от потребителя преди използването на който и да е инструмент. Осигурете разбиране за функционалността на всеки инструмент и наложете надеждни граници за сигурност.
-
Контрол на разрешенията за инструменти: Конфигурирайте кои инструменти са разрешени за използване от модел по време на сесия, като гарантирате, че само изрично упълномощени инструменти са достъпни.
-
Удостоверяване: Изисквайте правилно удостоверяване преди предоставяне на достъп до инструменти, ресурси или чувствителни операции, използвайки API ключове, OAuth токени или други сигурни методи.
-
Валидиране на параметри: Налагайте валидиране за всички извиквания на инструменти, за да предотвратите неправилно форматирани или злонамерени входни данни.
-
Ограничаване на честотата: Реализирайте ограничаване на честотата, за да предотвратите злоупотреба и да осигурите справедливо използване на ресурсите на сървъра.
-
Преговори за възможности: По време на настройката на връзката обменяйте информация за поддържаните функции, версии на протокола, налични инструменти и ресурси.
-
Дизайн на инструменти: Създавайте фокусирани инструменти, които изпълняват една задача добре, вместо монолитни инструменти, които се занимават с множество аспекти.
-
Управление на грешки: Реализирайте стандартизирани съобщения за грешки и кодове, за да помогнете при диагностициране на проблеми, да управлявате неуспехите грациозно и да предоставяте полезна обратна връзка.
-
Логване: Конфигурирайте структурирани логове за одит, отстраняване на грешки и наблюдение на взаимодействията с протокола.
-
Проследяване на напредъка: За дълготрайни операции предоставяйте актуализации за напредъка, за да позволите отзивчиви потребителски интерфейси.
-
Отмяна на заявки: Позволете на клиентите да отменят текущи заявки, които вече не са необходими или отнемат твърде много време.
За най-актуална информация относно най-добрите практики за MCP, вижте:
Всеки 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))
}
};
}
}Реализирайте надеждно управление на грешки с информативни съобщения за грешки и подходящи механизми за възстановяване.
# Python example with comprehensive error handling
class DataQueryTool:
def get_name(self):
return "dataQuery"
def get_description(self):
return "Queries data from specified database tables"
async def execute(self, parameters):
try:
# Parameter validation
if "query" not in parameters:
raise ToolParameterError("Missing required parameter: query")
query = parameters["query"]
# Security validation
if self._contains_unsafe_sql(query):
raise ToolSecurityError("Query contains potentially unsafe SQL")
try:
# Database operation with timeout
async with timeout(10): # 10 second timeout
result = await self._database.execute_query(query)
return ToolResponse(
content=[TextContent(json.dumps(result))]
)
except asyncio.TimeoutError:
raise ToolExecutionError("Database query timed out after 10 seconds")
except DatabaseConnectionError as e:
# Connection errors might be transient
self._log_error("Database connection error", e)
raise ToolExecutionError(f"Database connection error: {str(e)}")
except DatabaseQueryError as e:
# Query errors are likely client errors
self._log_error("Database query error", e)
raise ToolExecutionError(f"Invalid query: {str(e)}")
except ToolError:
# Let tool-specific errors pass through
raise
except Exception as e:
# Catch-all for unexpected errors
self._log_error("Unexpected error in DataQueryTool", e)
raise ToolExecutionError(f"An unexpected error occurred: {str(e)}")
def _contains_unsafe_sql(self, query):
# Implementation of SQL injection detection
pass
def _log_error(self, message, error):
# Implementation of error logging
passВинаги валидирайте параметрите старателно, за да предотвратите неправилно форматирани или злонамерени входни данни.
// JavaScript/TypeScript example with detailed parameter validation
class FileOperationTool {
getName() {
return "fileOperation";
}
getDescription() {
return "Performs file operations like read, write, and delete";
}
getDefinition() {
return {
name: this.getName(),
description: this.getDescription(),
parameters: {
operation: {
type: "string",
description: "Operation to perform",
enum: ["read", "write", "delete"]
},
path: {
type: "string",
description: "File path (must be within allowed directories)"
},
content: {
type: "string",
description: "Content to write (only for write operation)",
optional: true
}
},
required: ["operation", "path"]
};
}
async execute(parameters) {
// 1. Validate parameter presence
if (!parameters.operation) {
throw new ToolError("Missing required parameter: operation");
}
if (!parameters.path) {
throw new ToolError("Missing required parameter: path");
}
// 2. Validate parameter types
if (typeof parameters.operation !== "string") {
throw new ToolError("Parameter 'operation' must be a string");
}
if (typeof parameters.path !== "string") {
throw new ToolError("Parameter 'path' must be a string");
}
// 3. Validate parameter values
const validOperations = ["read", "write", "delete"];
if (!validOperations.includes(parameters.operation)) {
throw new ToolError(`Invalid operation. Must be one of: ${validOperations.join(", ")}`);
}
// 4. Validate content presence for write operation
if (parameters.operation === "write" && !parameters.content) {
throw new ToolError("Content parameter is required for write operation");
}
// 5. Path safety validation
if (!this.isPathWithinAllowedDirectories(parameters.path)) {
throw new ToolError("Access denied: path is outside of allowed directories");
}
// Implementation based on validated parameters
// ...
}
isPathWithinAllowedDirectories(path) {
// Implementation of path safety check
// ...
}
}// Java example with authentication and authorization
public class SecureDataAccessTool implements Tool {
private final AuthenticationService authService;
private final AuthorizationService authzService;
private final DataService dataService;
// Dependency injection
public SecureDataAccessTool(
AuthenticationService authService,
AuthorizationService authzService,
DataService dataService) {
this.authService = authService;
this.authzService = authzService;
this.dataService = dataService;
}
@Override
public String getName() {
return "secureDataAccess";
}
@Override
public ToolResponse execute(ToolRequest request) {
// 1. Extract authentication context
String authToken = request.getContext().getAuthToken();
// 2. Authenticate user
UserIdentity user;
try {
user = authService.validateToken(authToken);
} catch (AuthenticationException e) {
return ToolResponse.error("Authentication failed: " + e.getMessage());
}
// 3. Check authorization for the specific operation
String dataId = request.getParameters().get("dataId").getAsString();
String operation = request.getParameters().get("operation").getAsString();
boolean isAuthorized = authzService.isAuthorized(user, "data:" + dataId, operation);
if (!isAuthorized) {
return ToolResponse.error("Access denied: Insufficient permissions for this operation");
}
// 4. Proceed with authorized operation
try {
switch (operation) {
case "read":
Object data = dataService.getData(dataId, user.getId());
return ToolResponse.success(data);
case "update":
JsonNode newData = request.getParameters().get("newData");
dataService.updateData(dataId, newData, user.getId());
return ToolResponse.success("Data updated successfully");
default:
return ToolResponse.error("Unsupported operation: " + operation);
}
} catch (Exception e) {
return ToolResponse.error("Operation failed: " + e.getMessage());
}
}
}// C# rate limiting implementation
public class RateLimitingMiddleware
{
private readonly RequestDelegate _next;
private readonly IMemoryCache _cache;
private readonly ILogger<RateLimitingMiddleware> _logger;
// Configuration options
private readonly int _maxRequestsPerMinute;
public RateLimitingMiddleware(
RequestDelegate next,
IMemoryCache cache,
ILogger<RateLimitingMiddleware> logger,
IConfiguration config)
{
_next = next;
_cache = cache;
_logger = logger;
_maxRequestsPerMinute = config.GetValue<int>("RateLimit:MaxRequestsPerMinute", 60);
}
public async Task InvokeAsync(HttpContext context)
{
// 1. Get client identifier (API key or user ID)
string clientId = GetClientIdentifier(context);
// 2. Get rate limiting key for this minute
string cacheKey = $"rate_limit:{clientId}:{DateTime.UtcNow:yyyyMMddHHmm}";
// 3. Check current request count
if (!_cache.TryGetValue(cacheKey, out int requestCount))
{
requestCount = 0;
}
// 4. Enforce rate limit
if (requestCount >= _maxRequestsPerMinute)
{
_logger.LogWarning("Rate limit exceeded for client {ClientId}", clientId);
context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
context.Response.Headers.Add("Retry-After", "60");
await context.Response.WriteAsJsonAsync(new
{
error = "Rate limit exceeded",
message = "Too many requests. Please try again later.",
retryAfterSeconds = 60
});
return;
}
// 5. Increment request count
_cache.Set(cacheKey, requestCount + 1, TimeSpan.FromMinutes(2));
// 6. Add rate limit headers
context.Response.Headers.Add("X-RateLimit-Limit", _maxRequestsPerMinute.ToString());
context.Response.Headers.Add("X-RateLimit-Remaining", (_maxRequestsPerMinute - requestCount - 1).ToString());
// 7. Continue with the request
await _next(context);
}
private string GetClientIdentifier(HttpContext context)
{
// Implementation to extract API key or user ID
// ...
}
}...
3. Основни показатели за производителност: Поддържайте еталони за производителност, за да откривате регресии
4. Сканиране за сигурност: Автоматизирайте тестването за сигурност като част от процеса
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.
- API крайни точки: Тествайте задължителните крайни точки (/resources, /tools и др.)
- Формат на заявка/отговор: Проверете съответствието със схемата
- Кодове за грешки: Уверете се, че се използват правилните статус кодове за различни сценарии
- Типове съдържание: Тествайте обработката на различни типове съдържание
- Процес на удостоверяване: Проверете механизмите за удостоверяване, съответстващи на спецификацията
[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
});
}- Тествайте дефинициите на инструменти отделно: Проверете дефинициите на схемата независимо от логиката на инструментите
- Използвайте параметризирани тестове: Тествайте инструментите с разнообразни входни данни, включително гранични случаи
- Проверете отговорите при грешки: Уверете се, че се обработват правилно всички възможни условия за грешка
- Тествайте логиката за авторизация: Уверете се, че достъпът е правилно контролиран за различни роли на потребителите
- Следете покритието на тестовете: Стремете се към високо покритие на критичния код
- Тествайте стрийминг отговори: Проверете правилната обработка на стрийминг съдържание
- Симулирайте проблеми с мрежата: Тествайте поведението при лоши мрежови условия
- Тествайте ограниченията на ресурсите: Проверете поведението при достигане на квоти или лимити на скоростта
- Автоматизирайте регресионните тестове: Създайте комплект, който се изпълнява при всяка промяна в кода
- Документирайте тестовите случаи: Поддържайте ясна документация на тестовите сценарии
- Прекалено разчитане на тестове за "щастливия път": Уверете се, че тествате подробно случаите на грешки
- Пренебрегване на тестването за производителност: Идентифицирайте тесните места, преди да засегнат продукцията
- Тестване само в изолация: Комбинирайте модулни, интеграционни и крайни тестове
- Непълно покритие на API: Уверете се, че всички крайни точки и функции са тествани
- Несъответстващи тестови среди: Използвайте контейнери за осигуряване на последователни тестови среди
Цялостната стратегия за тестване е от съществено значение за разработването на надеждни и висококачествени MCP сървъри. Чрез прилагане на най-добрите практики и съвети, описани в това ръководство, можете да гарантирате, че вашите MCP реализации отговарят на най-високите стандарти за качество, надеждност и производителност.
- Дизайн на инструменти: Следвайте принципа за единична отговорност, използвайте инжектиране на зависимости и проектирайте за съвместимост
- Дизайн на схема: Създавайте ясни, добре документирани схеми с подходящи ограничения за валидиране
- Обработка на грешки: Прилагайте елегантна обработка на грешки, структурирани отговори за грешки и логика за повторение
- Производителност: Използвайте кеширане, асинхронна обработка и ограничаване на ресурсите
- Сигурност: Прилагайте задълбочена валидиране на входните данни, проверки за авторизация и обработка на чувствителни данни
- Тестване: Създавайте цялостни модулни, интеграционни и крайни тестове
- Модели на работни процеси: Прилагайте утвърдени модели като вериги, диспечери и паралелна обработка
Проектирайте MCP инструмент и работен процес за система за обработка на документи, която:
- Приема документи в различни формати (PDF, DOCX, TXT)
- Извлича текст и ключова информация от документите
- Класифицира документите по тип и съдържание
- Генерира резюме на всеки документ
Прилагайте схемите на инструмента, обработката на грешки и модел на работен процес, който най-добре отговаря на този сценарий. Помислете как бихте тествали тази реализация.
- Присъединете се към MCP общността в Azure AI Foundry Discord Community, за да сте в крак с последните разработки
- Допринасяйте за отворени MCP проекти
- Прилагайте MCP принципи във вашите собствени AI инициативи в организацията
- Изследвайте специализирани MCP реализации за вашата индустрия
- Помислете за участие в напреднали курсове по специфични MCP теми, като мултимодална интеграция или интеграция на корпоративни приложения
- Експериментирайте със създаването на собствени MCP инструменти и работни процеси, използвайки принципите, научени чрез Практическата лаборатория
Следва: Най-добри практики случаи от практиката
Отказ от отговорност:
Този документ е преведен с помощта на AI услуга за превод Co-op Translator. Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.
