MCP în Python: Ghid Practic pentru Servere și Clienți AI

Ghid practic pentru Model Context Protocol (MCP) în Python: construiește servere cu FastMCP, creează clienți, integrează cu Claude Desktop și deployează în producție cu Streamable HTTP.

Ce este Model Context Protocol și de ce schimbă regulile jocului în 2026?

Dacă ai încercat vreodată să conectezi un model lingvistic mare (LLM) la o bază de date, la un API extern sau la fișierele de pe calculator, știi exact despre ce vorbesc. Fiecare integrare cere cod personalizat, fiecare provider AI vine cu propriul format, iar rezultatul final? Un haos de conectori care se strică la prima actualizare. Ei bine, exact aici intervine Model Context Protocol (MCP).

MCP a fost introdus de Anthropic în noiembrie 2024 și a fost adoptat rapid de OpenAI, Google DeepMind și Microsoft. În decembrie 2025, protocolul a fost donat către Agentic AI Foundation (AAIF) sub egida Linux Foundation — devenind un standard cu adevărat deschis. În 2026, MCP e de facto protocolul standard pentru integrarea instrumentelor AI.

Gândește-te la asta ca la USB-C, dar pentru software AI.

Fără MCP, situația arată cam așa: ai 5 aplicații AI și 10 instrumente externe. Fiecare aplicație are nevoie de un conector dedicat pentru fiecare instrument — asta înseamnă 50 de integrări personalizate (problema N×M). Cu MCP, fiecare instrument expune un singur server, iar fiecare aplicație implementează un singur client. Rezultat: 5 + 10 = 15 integrări (problema N+M). E o diferență enormă, sincer.

Arhitectura MCP: Host, Client și Server

Înainte de a scrie cod, merită să înțelegi cele trei componente ale arhitecturii MCP. Nu-i complicat, dar e important să ai imaginea de ansamblu.

MCP Host

Este aplicația principală cu care interacționează utilizatorul — de exemplu, Claude Desktop, un IDE cu suport AI (Cursor, VS Code) sau aplicația ta personalizată. Host-ul conține modelul LLM și orchestrează toată comunicarea.

MCP Client

Rulează în interiorul host-ului și se ocupă de comunicarea cu serverele MCP. Practic, clientul traduce cererile LLM-ului în mesaje MCP și convertește răspunsurile serverului în formatul pe care modelul îl înțelege. Un host poate avea mai mulți clienți conectați simultan la servere diferite.

MCP Server

Aici se întâmplă magia. Serverul e procesul extern care expune capabilități specifice — acces la baze de date, fișiere, API-uri, instrumente de calcul. Oferă trei tipuri de resurse:

  • Resources (Resurse): Date de tip read-only — similar cu endpoint-urile GET. Exemple: conținutul unui fișier, rezultatul unei interogări SQL, date de la un API.
  • Tools (Instrumente): Funcții pe care LLM-ul le poate apela pentru a executa acțiuni — similar cu endpoint-urile POST. Exemple: trimiterea unui email, scrierea într-o bază de date, executarea unui script.
  • Prompts (Șabloane): Template-uri reutilizabile care ghidează modelul în sarcini specifice. Exemple: „Analizează acest cod", „Rezumă acest document".

Comunicarea între componente se face prin JSON-RPC 2.0, iar transportul poate fi prin stdio (local) sau Streamable HTTP (în rețea).

Instalarea SDK-ului Python MCP

SDK-ul oficial Python implementează întreaga specificație MCP și include FastMCP — un framework de nivel înalt care simplifică enorm crearea serverelor. Hai să vedem cum instalezi tot ce-ți trebuie.

Opțiunea 1: Cu pip

python -m venv mcp-env
source mcp-env/bin/activate  # Linux/macOS
# sau: mcp-env\Scripts\activate  # Windows

pip install "mcp[cli]"

Opțiunea 2: Cu uv (recomandat pentru proiecte noi)

uv init mcp-proiect
cd mcp-proiect
uv add "mcp[cli]"

Verifică instalarea:

python -c "import mcp; print(mcp.__version__)"

Ai nevoie de Python 3.10+ și SDK-ul MCP versiunea 1.2.0 sau mai recentă. La momentul scrierii, versiunea curentă e 1.9.x.

Construirea primului tău server MCP

Acum vine partea interesantă. Hai să construim un server MCP practic — un instrument care permite unui LLM să interogheze o bază de date SQLite prin limbaj natural. Din experiența mea, acesta e unul dintre cele mai utile scenarii pe care le poți implementa.

Pas 1: Serverul de bază cu FastMCP

from mcp.server.fastmcp import FastMCP
import sqlite3
from pathlib import Path

# Inițializarea serverului MCP
mcp = FastMCP(
    "DatabaseAssistant",
    instructions="Asistent pentru interogarea bazelor de date SQLite."
)

# Calea către baza de date
DB_PATH = Path("./date_companie.db")


@mcp.resource("schema://tables")
def get_schema() -> str:
    """Returnează schema completă a bazei de date."""
    conn = sqlite3.connect(DB_PATH)
    cursor = conn.cursor()
    cursor.execute("""
        SELECT name, sql FROM sqlite_master
        WHERE type='table' AND name NOT LIKE 'sqlite_%'
    """)
    tables = cursor.fetchall()
    conn.close()

    schema_text = []
    for name, sql in tables:
        schema_text.append(f"-- Tabelul: {name}\n{sql}\n")
    return "\n".join(schema_text)


@mcp.tool()
def query_database(sql_query: str) -> str:
    """Execută o interogare SQL SELECT și returnează rezultatele.

    Args:
        sql_query: Interogarea SQL de executat (doar SELECT)
    """
    # Validare de securitate — doar SELECT
    normalized = sql_query.strip().upper()
    if not normalized.startswith("SELECT"):
        return "Eroare: Doar interogările SELECT sunt permise."

    blocked_keywords = ["DROP", "DELETE", "UPDATE", "INSERT", "ALTER", "CREATE"]
    for keyword in blocked_keywords:
        if keyword in normalized:
            return f"Eroare: Cuvântul cheie '{keyword}' nu este permis."

    try:
        conn = sqlite3.connect(DB_PATH)
        conn.row_factory = sqlite3.Row
        cursor = conn.cursor()
        cursor.execute(sql_query)
        rows = cursor.fetchmany(100)  # Limită de siguranță

        if not rows:
            return "Interogarea nu a returnat rezultate."

        # Formatare tabelară
        columns = rows[0].keys()
        header = " | ".join(columns)
        separator = "-+-".join("-" * len(col) for col in columns)
        data_rows = []
        for row in rows:
            data_rows.append(" | ".join(str(row[col]) for col in columns))

        result = f"{header}\n{separator}\n" + "\n".join(data_rows)
        return result

    except sqlite3.Error as e:
        return f"Eroare SQL: {e}"
    finally:
        conn.close()


@mcp.tool()
def list_tables() -> str:
    """Listează toate tabelele din baza de date."""
    conn = sqlite3.connect(DB_PATH)
    cursor = conn.cursor()
    cursor.execute("""
        SELECT name FROM sqlite_master
        WHERE type='table' AND name NOT LIKE 'sqlite_%'
        ORDER BY name
    """)
    tables = [row[0] for row in cursor.fetchall()]
    conn.close()
    return "\n".join(f"- {t}" for t in tables)


if __name__ == "__main__":
    mcp.run(transport="stdio")

Pas 2: Înțelegerea codului

Să vedem ce se întâmplă aici, pas cu pas:

  • FastMCP("DatabaseAssistant") — creează o instanță de server cu un nume descriptiv. Numele acesta apare în lista de servere disponibile din client.
  • @mcp.resource("schema://tables") — expune schema bazei de date ca resursă read-only. LLM-ul o citește automat pentru a înțelege structura datelor înainte de a genera interogări.
  • @mcp.tool() — decoratorul care transformă o funcție Python obișnuită într-un instrument MCP. Partea frumoasă: FastMCP generează automat schema JSON din type hints și docstring.
  • Validarea SQL — serverul acceptă exclusiv interogări SELECT, blocând orice operație de modificare. Asta e o măsură de securitate pe care n-ar trebui s-o sari niciodată.

Construirea unui client MCP în Python

Un server fără client nu prea face mare lucru, nu? Hai să construim un client care se conectează la serverul nostru, descoperă instrumentele disponibile și le folosește.

Client MCP cu transport stdio

import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client


async def main():
    # Configurare conexiune la server
    server_params = StdioServerParameters(
        command="python",
        args=["db_server.py"]
    )

    # Conectare la server
    async with stdio_client(server_params) as (read_stream, write_stream):
        async with ClientSession(read_stream, write_stream) as session:
            # Inițializare conexiune MCP
            await session.initialize()

            # Descoperire instrumente disponibile
            tools_result = await session.list_tools()
            print("Instrumente disponibile:")
            for tool in tools_result.tools:
                print(f"  - {tool.name}: {tool.description}")

            # Descoperire resurse
            resources_result = await session.list_resources()
            print("\nResurse disponibile:")
            for resource in resources_result.resources:
                print(f"  - {resource.uri}: {resource.name}")

            # Citire schema bazei de date
            schema = await session.read_resource("schema://tables")
            print(f"\nSchema:\n{schema.contents[0].text}")

            # Apel instrument — listare tabele
            tables = await session.call_tool("list_tables", arguments={})
            print(f"\nTabele:\n{tables.content[0].text}")

            # Apel instrument — interogare
            result = await session.call_tool(
                "query_database",
                arguments={"sql_query": "SELECT * FROM clienti LIMIT 5"}
            )
            print(f"\nRezultate:\n{result.content[0].text}")


asyncio.run(main())

Clientul parcurge exact fluxul standard MCP: conectare, inițializare, descoperire capabilități, apoi utilizare. Acesta e și modelul pe care aplicații precum Claude Desktop sau Cursor îl urmează în spatele scenei.

Serverul MCP cu Streamable HTTP pentru producție

Transportul stdio e perfect pentru dezvoltare locală, dar când vine vorba de producție ai nevoie de Streamable HTTP — transportul recomandat oficial în 2026. Spre deosebire de SSE (care e acum depreciat), Streamable HTTP oferă comunicare bidirecțională completă printr-un singur endpoint HTTP.

from mcp.server.fastmcp import FastMCP

mcp = FastMCP(
    "ProductionDB",
    host="0.0.0.0",
    port=8080,
    json_response=True,
    stateless_http=True
)


@mcp.tool()
def health_check() -> dict:
    """Verifică starea serverului MCP."""
    return {
        "status": "operational",
        "version": "1.0.0",
        "transport": "streamable-http"
    }


@mcp.tool()
def search_products(query: str, limit: int = 10) -> str:
    """Caută produse în catalog după nume sau descriere.

    Args:
        query: Termenul de căutare
        limit: Numărul maxim de rezultate (implicit 10)
    """
    import sqlite3
    conn = sqlite3.connect("catalog.db")
    cursor = conn.cursor()
    cursor.execute(
        "SELECT name, price, stock FROM products "
        "WHERE name LIKE ? OR description LIKE ? LIMIT ?",
        (f"%{query}%", f"%{query}%", limit)
    )
    rows = cursor.fetchall()
    conn.close()

    if not rows:
        return "Niciun produs găsit."
    lines = [f"- {name} | Preț: {price} RON | Stoc: {stock}"
             for name, price, stock in rows]
    return "\n".join(lines)


if __name__ == "__main__":
    mcp.run(transport="streamable-http")

Câteva aspecte de reținut pentru producție:

  • stateless_http=True — serverul nu păstrează stare între cereri, deci poți scala orizontal (mai multe instanțe în spatele unui load balancer).
  • json_response=True — răspunsurile sunt JSON pur, fără streaming SSE. Asta simplifică mult integrarea cu proxy-uri și API gateway-uri.
  • host="0.0.0.0" — serverul ascultă pe toate interfețele de rețea, nu doar pe localhost.

Integrarea cu Claude Desktop și alte aplicații AI

Unul dintre cele mai practice moduri de a folosi un server MCP e conectarea la Claude Desktop. Și procesul e surprinzător de simplu — serios, m-a uimit cât de puțin cod necesită.

Configurare Claude Desktop

Editează fișierul de configurare:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "database-assistant": {
      "command": "python",
      "args": ["/cale/completa/catre/db_server.py"]
    }
  }
}

După restart, Claude Desktop va detecta automat serverul tău și va afișa instrumentele disponibile. Poți apoi să întrebi direct: „Arată-mi primii 10 clienți după venituri din ultima lună" — iar Claude va genera interogarea SQL, o va executa prin serverul MCP și-ți va prezenta rezultatele formatate.

Integrarea cu OpenAI Agents SDK

OpenAI a adoptat oficial MCP în 2025, ceea ce e un semn clar că protocolul e aici pe termen lung. Poți conecta serverul tău MCP la agenți OpenAI astfel:

from agents import Agent
from agents.mcp import MCPServerStdio

# Definire server MCP
db_server = MCPServerStdio(
    command="python",
    args=["db_server.py"]
)

# Creare agent cu acces la instrumentele MCP
agent = Agent(
    name="Analist Date",
    instructions="Analizezi date din baza de date folosind SQL.",
    mcp_servers=[db_server]
)

# Agentul poate acum folosi toate instrumentele
# expuse de serverul tău MCP

Acesta e avantajul fundamental: construiești serverul o singură dată și funcționează cu Claude, ChatGPT, Cursor, sau orice altă aplicație compatibilă MCP. N-ai cum să nu apreciezi asta.

Securitate și bune practici

MCP oferă putere enormă agenților AI — iar cu putere mare vine și responsabilitate mare (da, știu, e un clișeu, dar e adevărat). Iată ce trebuie să ai în vedere.

1. Principiul privilegiului minim

Expune doar funcționalitățile strict necesare. Dacă agentul AI trebuie doar să citească date, nu expune instrumente de scriere. Simplu.

2. Protecție împotriva injecției de prompturi

Datele returnate de un server MCP sunt procesate de LLM, ceea ce creează un vector de atac destul de insidios. Un document malițios din baza de date ar putea conține instrucțiuni care manipulează comportamentul modelului. Sanitizează întotdeauna datele înainte de a le returna — nu-i un pas opțional.

3. Logging și audit

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[logging.FileHandler("mcp_audit.log")]
)

@mcp.tool()
def query_database(sql_query: str) -> str:
    """Execută o interogare SQL SELECT."""
    logging.info(f"Interogare primită: {sql_query}")
    # ... execuție
    logging.info(f"Rezultat: {len(rows)} rânduri returnate")
    return result

4. Atenție la transportul stdio

Un detaliu care te poate costa ore de debug: când folosești servere MCP bazate pe stdio, nu scrie niciodată în stdout cu print(). Asta va corupe mesajele JSON-RPC și va întrerupe comunicarea. Folosește print(..., file=sys.stderr) sau un sistem de logging dedicat.

Scenarii practice: De la prototip la producție

Teoria e bună, dar hai să vedem unde un server MCP chiar aduce valoare în viața reală.

Asistent intern pentru echipa de suport

Un server MCP conectat la baza de date de tickete și la documentația internă. Agentul poate răspunde la întrebări precum „Care sunt cele mai frecvente probleme raportate luna aceasta?" sau „Ce soluție am aplicat ultima dată pentru eroarea X?". Am văzut echipe care au redus timpul de răspuns cu 40% implementând ceva similar.

Dashboard conversațional pentru management

Un server MCP care expune KPI-uri din mai multe surse de date. Managerii pot întreba direct: „Cum arată vânzările din Q1 comparativ cu anul trecut?" — fără spreadsheet-uri, fără dashboard-uri separate. Doar o întrebare și un răspuns.

Automatizare DevOps

Combinarea mai multor servere MCP: GitHub (pentru PR-uri), Docker (pentru containere), baze de date (pentru monitorizare). Un agent AI poate primi comanda „Verifică dacă ultimul deploy a introdus regresii în performanță" și va interoga automat toate sistemele relevante.

Înlănțuirea serverelor MCP

Puterea reală a MCP apare când înlănțuiești mai multe servere. Un singur prompt poate declanșa: GitHub MCP → extrage PR-urile merged → Notion MCP → scrie rezumatul sprint-ului → Slack MCP → publică actualizarea în canal. Zero cod intermediar. E destul de impresionant când vezi totul funcționând.

Depanare și probleme frecvente

Oricât de bine ai scrie codul, vei întâlni probleme. Asta-i realitatea. Iată soluțiile pentru cele mai comune situații:

Serverul nu apare în Claude Desktop

  • Verifică sintaxa JSON în fișierul de configurare — o virgulă lipsă poate bloca totul (am pățit-o de mai multe ori)
  • Asigură-te că calea către script e absolută, nu relativă
  • Repornește Claude Desktop complet — minimizarea nu e suficientă
  • Verifică log-urile: ~/Library/Logs/Claude/mcp.log (macOS)

Eroare „Tool not found"

  • Verifică dacă funcția are decoratorul @mcp.tool()
  • Asigură-te că funcția are un docstring — FastMCP îl folosește ca descriere a instrumentului
  • Verifică type hints pe parametri — sunt obligatorii pentru generarea schemei

Timeout la interogări mari

  • Adaugă limite explicite în interogările SQL (LIMIT 100)
  • Implementează paginare pentru seturi mari de date
  • Folosește cursor.fetchmany() în loc de cursor.fetchall()

Întrebări frecvente (FAQ)

Care e diferența dintre MCP și apelurile de funcții (function calling)?

Function calling e un mecanism specific fiecărui provider AI — OpenAI, Anthropic și Google au implementări diferite. MCP, pe de altă parte, e un protocol standardizat care funcționează identic indiferent de provider. Construiești un server MCP o singură dată și îl conectezi la orice aplicație AI compatibilă. Pe scurt: function calling decide când se apelează un instrument, MCP standardizează cum.

Este MCP sigur pentru utilizare în producție?

Da, cu condiția să respecți bunele practici de securitate. MCP în sine e doar un protocol de comunicare — securitatea depinde de implementarea serverelor tale. Validează inputurile, aplică privilegiul minim, loghează apelurile și nu expune date sensibile fără autorizare. În 2026, numeroase companii folosesc MCP în producție, inclusiv prin platformele Cloudflare, Azure și Google Cloud.

Pot folosi MCP cu alte limbaje în afară de Python?

Absolut. Există SDK-uri oficiale pentru Python, TypeScript, C#, Go, Java și Rust (C# e menținut cu Microsoft, Go cu Google). Protocolul e agnostic față de limbaj — un server scris în Go poate fi consumat de un client Python fără nicio modificare.

Ce s-a întâmplat cu transportul SSE?

SSE (Server-Sent Events) a fost depreciat oficial în favoarea Streamable HTTP. Motivul: SSE suporta doar streaming unidirecțional (server → client), pe când Streamable HTTP oferă comunicare bidirecțională completă. Serverele SSE existente mai funcționează pentru compatibilitate, dar toate proiectele noi ar trebui să folosească Streamable HTTP sau stdio.

Câte servere MCP pot rula simultan?

Nu există o limită tehnică în protocol. Claude Desktop și Cursor suportă mai multe servere simultan. În practică, limita e dată de resursele sistemului și de fereastra de context a modelului — fiecare instrument descoperit ocupă spațiu în context. Recomandarea mea: grupează instrumentele conexe într-un singur server în loc să creezi servere separate pentru fiecare funcție.

Despre Autor Editorial Team

Our team of expert writers and editors.