(Klikněte na obrázek výše pro zhlédnutí videa této lekce)
Tato lekce se zaměřuje na pokročilé nejlepší postupy pro vývoj, testování a nasazení MCP serverů a funkcí v produkčním prostředí. Jak ekosystémy MCP rostou na složitosti a významu, dodržování zavedených vzorců zajišťuje spolehlivost, udržovatelnost a interoperabilitu. Tato lekce shrnuje praktické zkušenosti získané z reálných implementací MCP, aby vás vedla při vytváření robustních a efektivních serverů s účinnými zdroji, výzvami a nástroji.
Na konci této lekce budete schopni:
- Aplikovat nejlepší postupy v oboru při návrhu MCP serverů a funkcí
- Vytvořit komplexní strategie testování MCP serverů
- Navrhnout efektivní a znovupoužitelné pracovní vzory pro složité aplikace MCP
- Implementovat správné zpracování chyb, logování a sledování v MCP serverech
- Optimalizovat implementace MCP pro výkon, bezpečnost a udržovatelnost
Než se ponoříme do konkrétních implementačních postupů, je důležité pochopit základní principy, které vedou efektivní vývoj MCP:
-
Standardizovaná komunikace: MCP využívá JSON-RPC 2.0 jako svůj základ, což poskytuje konzistentní formát pro požadavky, odpovědi a zpracování chyb napříč všemi implementacemi.
-
Uživatelsky orientovaný design: Vždy upřednostňujte souhlas, kontrolu a transparentnost uživatele ve vašich implementacích MCP.
-
Bezpečnost na prvním místě: Implementujte robustní bezpečnostní opatření včetně autentizace, autorizace, validace a omezení rychlosti.
-
Modulární architektura: Navrhujte MCP servery s modulárním přístupem, kde každý nástroj a zdroj má jasný a zaměřený účel.
-
Stavové připojení: Využijte schopnost MCP udržovat stav napříč více požadavky pro koherentnější a kontextově uvědomělé interakce.
Následující nejlepší postupy jsou odvozeny z oficiální dokumentace Model Context Protocol:
-
Souhlas a kontrola uživatele: Vždy vyžadujte explicitní souhlas uživatele před přístupem k datům nebo prováděním operací. Poskytněte jasnou kontrolu nad tím, jaká data jsou sdílena a jaké akce jsou autorizovány.
-
Ochrana soukromí dat: Zveřejňujte uživatelská data pouze s explicitním souhlasem a chraňte je vhodnými přístupovými kontrolami. Zabraňte neoprávněnému přenosu dat.
-
Bezpečnost nástrojů: Vyžadujte explicitní souhlas uživatele před spuštěním jakéhokoliv nástroje. Zajistěte, aby uživatelé rozuměli funkcionalitě každého nástroje, a prosazujte robustní bezpečnostní hranice.
-
Kontrola oprávnění nástrojů: Nakonfigurujte, které nástroje může model během relace používat, a zajistěte, že jsou přístupné pouze explicitně autorizované nástroje.
-
Autentizace: Vyžadujte správnou autentizaci před udělením přístupu k nástrojům, zdrojům nebo citlivým operacím pomocí API klíčů, OAuth tokenů nebo jiných bezpečných metod autentizace.
-
Validace parametrů: Prosazujte validaci pro všechny spuštěné nástroje, abyste zabránili chybným nebo škodlivým vstupům.
-
Omezení rychlosti: Implementujte omezení rychlosti, abyste zabránili zneužití a zajistili spravedlivé využití serverových zdrojů.
-
Vyjednávání schopností: Během nastavení připojení si vyměňte informace o podporovaných funkcích, verzích protokolu, dostupných nástrojích a zdrojích.
-
Návrh nástrojů: Vytvářejte zaměřené nástroje, které dělají jednu věc dobře, místo monolitických nástrojů, které řeší více problémů najednou.
-
Zpracování chyb: Implementujte standardizované chybové zprávy a kódy, které pomohou diagnostikovat problémy, elegantně zvládat selhání a poskytovat akční zpětnou vazbu.
-
Logování: Nakonfigurujte strukturované logy pro auditování, ladění a monitorování interakcí protokolu.
-
Sledování pokroku: U dlouhotrvajících operací hlaste aktualizace pokroku, aby bylo možné vytvořit responzivní uživatelská rozhraní.
-
Zrušení požadavků: Umožněte klientům zrušit probíhající požadavky, které již nejsou potřeba nebo trvají příliš dlouho.
Pro nejaktuálnější informace o nejlepších postupech MCP se podívejte na:
Každý nástroj MCP by měl mít jasný a zaměřený účel. Místo vytváření monolitických nástrojů, které se snaží řešit více problémů, vyvíjejte specializované nástroje, které vynikají v konkrétních úkolech.
// 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))
}
};
}
}Implementujte robustní zpracování chyb s informativními chybovými zprávami a vhodnými mechanismy obnovy.
# 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
passVždy důkladně validujte parametry, abyste zabránili chybným nebo škodlivým vstupům.
// 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. Výkonnostní základny: Udržujte výkonnostní benchmarky, abyste odhalili regresi 4. Bezpečnostní skeny: Automatizujte bezpečnostní testování jako součást pipeline
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.csprojOvěřte, že váš server správně implementuje specifikaci MCP.
- API koncové body: Testujte požadované koncové body (/resources, /tools, atd.)
- Formát požadavků/odpovědí: Ověřte soulad se schématem
- Chybové kódy: Ověřte správné status kódy pro různé scénáře
- Typy obsahu: Testujte zpracování různých typů obsahu
- Autentizační proces: Ověřte mechanismy autentizace v souladu se specifikací
[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
});
}- Testujte definice nástrojů samostatně: Ověřte schémata nezávisle na logice nástrojů
- Používejte parametrizované testy: Testujte nástroje s různými vstupy, včetně hraničních případů
- Kontrolujte chybové odpovědi: Ověřte správné zpracování všech možných chybových stavů
- Testujte logiku autorizace: Zajistěte správnou kontrolu přístupu pro různé uživatelské role
- Sledujte pokrytí testů: Usilujte o vysoké pokrytí kritického kódu
- Testujte streamované odpovědi: Ověřte správné zpracování streamovaného obsahu
- Simulujte problémy sítě: Testujte chování při špatných síťových podmínkách
- Testujte limity zdrojů: Ověřte chování při dosažení kvót nebo limitů rychlosti
- Automatizujte regresní testy: Vytvořte sadu, která se spouští při každé změně kódu
- Dokumentujte testovací případy: Udržujte jasnou dokumentaci testovacích scénářů
- Přílišná závislost na testování "šťastné cesty": Ujistěte se, že důkladně testujete chybové případy
- Ignorování výkonnostního testování: Identifikujte úzká místa dříve, než ovlivní produkci
- Testování pouze v izolaci: Kombinujte jednotkové, integrační a E2E testy
- Neúplné pokrytí API: Zajistěte testování všech koncových bodů a funkcí
- Nekonzistentní testovací prostředí: Používejte kontejnery pro zajištění konzistentního prostředí
Komplexní testovací strategie je klíčová pro vývoj spolehlivých a kvalitních MCP serverů. Implementací osvědčených postupů a tipů uvedených v tomto průvodci můžete zajistit, že vaše implementace MCP splňují nejvyšší standardy kvality, spolehlivosti a výkonu.
- Návrh nástrojů: Dodržujte princip jedné odpovědnosti, používejte injekci závislostí a navrhujte pro kompozici
- Návrh schémat: Vytvářejte jasná, dobře dokumentovaná schémata s vhodnými validačními omezeními
- Zpracování chyb: Implementujte elegantní zpracování chyb, strukturované chybové odpovědi a logiku opakování
- Výkon: Používejte caching, asynchronní zpracování a omezení zdrojů
- Bezpečnost: Aplikujte důkladnou validaci vstupů, kontroly autorizace a zpracování citlivých dat
- Testování: Vytvářejte komplexní jednotkové, integrační a end-to-end testy
- Vzory pracovních postupů: Aplikujte osvědčené vzory jako řetězce, dispečery a paralelní zpracování
Navrhněte MCP nástroj a pracovní postup pro systém zpracování dokumentů, který:
- Přijímá dokumenty v různých formátech (PDF, DOCX, TXT)
- Extrahuje text a klíčové informace z dokumentů
- Klasifikuje dokumenty podle typu a obsahu
- Generuje shrnutí každého dokumentu
Implementujte schémata nástrojů, zpracování chyb a pracovní postup, který nejlépe vyhovuje tomuto scénáři. Zvažte, jak byste testovali tuto implementaci.
- Připojte se ke komunitě MCP na Azure AI Foundry Discord Community, abyste byli informováni o nejnovějším vývoji
- Přispívejte do open-source MCP projektů
- Aplikujte principy MCP ve vlastních AI iniciativách vaší organizace
- Prozkoumejte specializované implementace MCP pro váš průmysl
- Zvažte absolvování pokročilých kurzů na specifická témata MCP, jako je multimodální integrace nebo integrace podnikových aplikací
- Experimentujte s vytvářením vlastních MCP nástrojů a pracovních postupů pomocí principů naučených prostřednictvím Hands on Lab
Další: Osvědčené postupy případové studie
Upozornění:
Tento dokument byl přeložen pomocí služby pro automatický překlad Co-op Translator. I když se snažíme o co největší přesnost, mějte prosím na paměti, že automatické překlady mohou obsahovat chyby nebo nepřesnosti. Původní dokument v jeho původním jazyce by měl být považován za závazný zdroj. Pro důležité informace doporučujeme profesionální lidský překlad. Neodpovídáme za žádná nedorozumění nebo nesprávné interpretace vyplývající z použití tohoto překladu.
