CAI Technology
Menu ☰
iris · · 13 min citire

MCP server design patterns: cum proiectezi un Model Context Protocol robust

Tools naming, response format, error handling, idempotency și rate limiting — pattern-uri concrete pentru un MCP server folosit în producție de agenți AI.

CAI Technology · Ultima revizuire: 30.04.2026
MCP server design patterns: cum proiectezi un Model Context Protocol robust

MCP server design patterns: cum proiectezi un Model Context Protocol pe care un agent AI îl folosește corect

Model Context Protocol (MCP) a devenit, în mai puțin de un an, lingua franca pentru cum un agent AI primește acces la tools — baze de date, API-uri, file systems, sisteme proprietare. Spec-ul este simplu, implementarea este aparent banală, dar diferența între un MCP server pe care un agent îl folosește bine și unul pe care îl folosește prost este enormă. În acest articol prezentăm pattern-urile pe care le aplicăm intern, distilate din erorile pe care le-am făcut și din observarea zilnică a modului în care un model lingvistic interpretează schema unui tool.

TL;DR

Ce înseamnă „un MCP server bun”

Un MCP server expune un set de tools către un agent AI. Calitatea serverului se măsoară în trei dimensiuni: rata de apel corect (agentul cheamă tool-ul potrivit pentru ce vrea utilizatorul), rata de utilizare a răspunsului (agentul folosește efectiv informația returnată), rata de recuperare la eroare (agentul își dă seama când ceva a eșuat și ce să facă).

Aceste trei rate nu se măsoară în testare unitară. Se măsoară prin observarea logurilor de conversație ale agentului în producție. Pattern-urile descrise mai jos sunt distilate din astfel de observații, nu din specificații teoretice.

Pattern 1: Tool naming — verb-object cu context

Numele tool-ului este prima informație pe care modelul lingvistic o vede. Selecția tool-ului se face pe baza descrierii și a numelui, în această ordine. Un nume bun reduce dependența de descriere.

Anti-pattern. get_data, query, execute, run. Modelul nu poate distinge între două tools cu nume generice; va alege la întâmplare bazat pe ordine sau pe lungimea descrierii.

Pattern. Verb-obiect cu context concret. Exemple bune: search_invoices_by_supplier, get_server_status, create_dns_record, list_failed_backups_last_24h. Numele este suficient de specific încât modelul nu trebuie să citească descrierea pentru a-și da seama când se aplică.

Recomandare practică: un MCP server ar trebui să aibă maxim 15-20 de tools. Peste această cifră, modelul începe să confunde tools similare. Dacă aveți 40 de tools, fie le grupați în mai multe servere MCP, fie reproiectați tools mai generale cu parametri.

Pattern 2: Schema parametrilor — strict, dar cu defaults

Un tool MCP are o schema JSON pentru parametri. Pentru un model lingvistic, schema este și documentație și constraint. Două reguli:

Folosiți enums acolo unde valorile sunt limitate. priority: "low" | "medium" | "high" este infinit mai bun decât priority: string. Modelul nu va inventa o valoare în afara enum-ului. Dacă lăsați string liber, va apărea „critical” sau „urgent” sau „p0” — toate valide pentru un om, toate invalide pentru codul vostru.

Defaults explicite. Orice parametru cu valoare implicită rezonabilă să aibă default în schema. Modelul va omite parametrul, ceea ce este corect. Fără defaults, modelul va inventa valori sau va eșua silent.

Exemplu de schema bună pentru search_invoices:

{
  "supplier_name": { "type": "string", "description": "Numele furnizorului. Acceptă potriviri parțiale case-insensitive." },
  "date_from": { "type": "string", "format": "date", "description": "Data minimă (inclusivă). Format ISO 8601: 2026-01-01." },
  "date_to": { "type": "string", "format": "date", "default": "today" },
  "status": { "type": "string", "enum": ["paid", "pending", "overdue", "cancelled"], "default": "pending" },
  "limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
}

Observați: descrierea spune nu doar ce face parametrul, ci cum se comportă (potriviri parțiale case-insensitive). Modelul va folosi aceste detalii.

Pattern 3: Response format — structură + narativ

Un model lingvistic citește răspunsul tool-ului și apoi îl folosește să răspundă utilizatorului sau să decidă următorul pas. Răspunsul trebuie să satisfacă ambele scopuri.

Anti-pattern. Returnați un blob JSON imens. Modelul îl va parsa, dar va consuma mulți tokens, va pierde context și va putea omite informații importante.

Anti-pattern opus. Returnați doar un text narativ. Modelul nu poate accesa structura, nu poate filtra, nu poate cita.

Pattern. Returnați un text scurt care rezumă, urmat de date structurate cu maxim 50-100 rânduri relevante. Includeți metadata despre paginare:

Found 47 invoices matching "ACME". Showing 20 most recent.

Results:
- INV-2026-0451 | ACME Corp | 2026-04-28 | 12,450.00 EUR | pending
- INV-2026-0438 | ACME Industries | 2026-04-25 | 3,200.00 EUR | paid
- ...

Pagination: page 1 of 3. Use offset=20 for next page.
Filters applied: supplier_name="ACME", status=any, date_from=null.

Modelul citește rezumatul, accesează datele, și știe cum să continue dacă e nevoie.

Pattern 4: Idempotency — fără surprize la retry

Un agent AI va apela accidental același tool de două ori. Cauze: timeout urmat de retry, ambiguitate în plan, model care „uită” că tocmai a făcut acțiunea. Pentru tools read-only (search, get, list), idempotency este gratuită. Pentru tools de scriere, este obligatorie.

Pattern. Adăugați un parametru idempotency_key opțional. Dacă agentul îl trimite și un apel cu aceeași cheie a fost executat în ultimele N minute, returnați rezultatul anterior fără a re-executa acțiunea. Acest pattern este standard în plățile Stripe și este transferabil 1:1.

create_dns_record(domain, type, value, idempotency_key="iris-plan-9472-step-3")

A doua oară când agentul apelează cu aceeași cheie, primește același răspuns fără efecte secundare.

Pentru tools care nu pot suporta idempotency (de exemplu, „send email”), faceți acest fapt explicit în descriere: „NOT idempotent. Each call sends a new email.” Modelul va înțelege și va folosi tool-ul cu mai multă atenție.

Pattern 5: Error handling — erori acționabile

O eroare returnată unui agent AI nu este pentru un dezvoltator care va citi un stack trace. Este pentru un model lingvistic care va decide ce să facă în continuare.

Anti-pattern. {"error": "permission denied"}. Modelul nu știe ce permisiune lipsește, nu știe dacă să retry, nu știe ce să comunice utilizatorului.

Pattern. Eroare structurată cu cod, mesaj uman, și sugestie de recuperare:

{
  "error": {
    "code": "INSUFFICIENT_SCOPE",
    "message": "Tool 'create_dns_record' requires scope 'dns:write', but the current session has scopes ['dns:read', 'inventory:read'].",
    "recovery": "Ask the user to re-authenticate with the missing scope, or use 'list_dns_records' to inspect existing entries."
  }
}

Modelul va citi recovery și fie va comunica utilizatorului ce trebuie făcut, fie va apela alt tool.

Codurile de eroare ar trebui să fie un enum mic și stabil: INSUFFICIENT_SCOPE, RATE_LIMITED, RESOURCE_NOT_FOUND, INVALID_PARAMETER, UPSTREAM_UNAVAILABLE, CONFLICT. Modelul învață cu timpul să răspundă diferit la fiecare cod.

Pattern 6: Rate limiting — protecția împotriva loop-urilor

Un agent AI care intră într-un loop poate consuma rapid resurse. Văzut în producție: un agent care, blocat de o eroare ambiguă, a apelat același tool de 200 de ori în 3 minute, fiecare apel hitând o bază de date externă cu cost per query.

Pattern. Rate limiting per sesiune agent, nu per IP sau per user. O sesiune agent este scopul corect: un agent are un task, are un buget de invocări pentru acel task. La depășire, răspundeți cu eroare RATE_LIMITED și un câmp retry_after.

Bugetul tipic pentru o sesiune: 50-100 invocări per tool per oră. Pentru tools costisitoare (apel LLM extern, query DB lentă), 10-20. Modelul va vedea eroarea, va înțelege că trebuie să schimbe abordarea, și nu va spamăi.

Bonus: includeți în răspunsul fiecărui tool un câmp quota_remaining opțional. Modelul va folosi această informație pentru a-și prioritiza acțiunile.

Pattern 7: Versioning și backward compatibility

Un MCP server care evoluează va întâlni problema clasică: clienți (agenți) care folosesc o schemă veche.

Pattern. Niciodată nu schimbați semantica unui tool existent fără un nume nou. Dacă search_invoices se schimbă pentru a accepta un parametru nou, este OK (cu default). Dacă se schimbă astfel încât parametrii vechi se interpretează diferit, creați search_invoices_v2 și păstrați search_invoices deprecated cu warning în răspuns.

Modelele lingvistice își bazează comportamentul pe descrierile pe care le-au „învățat”. Schimbarea silent rupe agenți deja în producție.

Pattern 8: Telemetry și debugging

Pentru fiecare apel de tool, înregistrați: timestamp, tool name, parametri, durata, rezultat (succes / cod eroare), session ID al agentului, identificator de plan (dacă agentul lucrează cu propose-then-act).

Aceste loguri sunt singura sursă de adevăr pentru a îmbunătăți serverul. Veți observa:

Dashboard minimal: rata de apel per tool, rata de eroare per tool și cod, durata p50/p95 per tool, sesiuni cu mai mult de 30 invocări (loop suspect).

Trei greșeli pe care le-am făcut și le-am corectat

Greșeală 1: tool-ul execute_query cu SQL string ca parametru. Modelul a generat queries care funcționau pe modelul lui dar nu pe baza noastră (sintaxa subtilă). Soluție: tools specializate (get_user_by_email, count_orders_in_range) cu schema strictă.

Greșeală 2: tool-ul update_config care accepta orice cheie din config. Modelul a actualizat câmpuri pe care nu trebuia să le atingă. Soluție: tools dedicate per câmp critic (update_dns_ttl, update_email_recipient) cu validare pe valoarea acceptată.

Greșeală 3: descrieri care assumau că modelul cunoaște contextul nostru intern. „Update the alert” — care alert? Soluție: descrieri auto-suficiente, cu termeni explicați în context.

Concluzie

Un MCP server bun nu este un wrapper subțire peste API-ul intern. Este o suprafață proiectată specific pentru consum de către un model lingvistic, cu naming, schema, response format și error handling adaptate pentru cum un model decide. Investiția în design plătește în calitatea agentului care îl folosește — și în nopți liniștite în care nu rulați logs încercând să înțelegeți de ce un agent a apelat un tool greșit de 50 de ori.

Articole conexe

Surse externe

Următorul pas

Dacă echipa dvs. construiește sau evaluează un MCP server pentru un agent AI și doriți o opinie tehnică pe schema și response format, vă oferim o consultație de 30 de minute fără cost.

Începem cu o conversație de 30 de minute.

Audit AI-readiness gratuit pentru companii peste 50 angajați. Răspundem în 24 de ore.