Edu.Romania MCP Online

Documentație API pentru Conectarea Modelelor AI

Ai nimerit direct în camera motoarelor. Ceea ce vezi aici este interfața de programare (API) a unui Model Context Protocol (MCP) – un agent inteligent dedicat sistemului de educație din România.

Noi, la AI.Parte, credem cu tărie în libertatea informației și în puterea colaborării. De aceea am construit acest MCP. Este, din câte știm, primul MCP server cu informații deschise din România, creat pentru a aduce claritate acolo unde domnește confuzia.

Suntem cu toții o comunitate de "makers" pentru binele comun.
Haide să facem lucruri cool împreună!
Două metode de integrare: Recomandăm Metoda 1 (Nativă MCP) pentru simplitate și eficiență. Metoda 2 (Tool calling standard) este o alternativă flexibilă pentru orice alt scenariu.

Termeni de Utilizare


Metoda 1: Integrare Nativă MCP (Recomandată)

Aceasta este metoda modernă și cea mai eficientă. În loc ca aplicația ta să orchestreze apelurile, delegi această responsabilitate direct către modelul AI. Pur și simplu îi spui lui Claude adresa serverului nostru, iar el se va conecta, va descoperi uneltele și le va folosi automat pentru a-ți răspunde.

Date de Conectare

URL Server MCP: https://aiparte.ro/scoli/server_scoli/mcp
Nume Server (sugerat): aiparte_edu_romania
Autentificare: Nu este necesară.

Exemple de Apel în Aplicația Dvs.

Observă cât de simplu este. Nu există logică de orchestrare, doar specifici adresa serverului nostru în cererea către API-ul modelului AI.

import anthropic

client = anthropic.Anthropic(api_key="CHEIA_TA_API_ANTHROPIC")

mcp_server = {
    "type": "url",
    "url": "https://aiparte.ro/scoli/server_scoli/mcp",
    "name": "aiparte_edu_romania"
}

message = client.messages.create(
    model="claude-3-5-sonnet-20240620",
    max_tokens=4096,
    messages=[{"role": "user", "content": "Ce licee de top sunt în Cluj-Napoca?"}],
    mcp_servers=[mcp_server]
)
print(message.content)
curl https://api.anthropic.com/v1/messages \
  --header "x-api-key: $ANTHROPIC_API_KEY" \
  --header "anthropic-version: 2023-06-01" \
  --header "anthropic-beta: mcp-client-2025-04-04" \
  --header "content-type: application/json" \
  --data '{
    "model": "claude-3-5-sonnet-20240620",
    "max_tokens": 4096,
    "messages": [
      {"role": "user", "content": "Ce licee de top sunt în Cluj-Napoca?"}
    ],
    "mcp_servers": [
      {
        "type": "url",
        "url": "https://aiparte.ro/scoli/server_scoli/mcp",
        "name": "aiparte_edu_romania"
      }
    ]
  }'

Testarea Directă a Serverului MCP

Următoarele metode vă permit să verificați dacă serverul MCP funcționează corect, independent de un client AI (precum Claude sau Gemini).

Metoda A: Folosind cURL din linia de comandă

Testarea directă a serverului MCP folosind protocolul JSON-RPC 2.0 standard.

Verificarea #1: Inițializarea conexiunii MCP

Prima cerere trebuie să fie initialize pentru a stabili conexiunea și a negocia capabilitățile:

curl -X POST https://aiparte.ro/scoli/server_scoli/mcp \
-H "Content-Type: application/json" \
-d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
        "protocolVersion": "2025-06-18",
        "capabilities": {
            "tools": {}
        },
        "clientInfo": {
            "name": "curl-test-client",
            "version": "1.0.0"
        }
    }
}'

Răspuns corect: Vei primi un JSON cu "result" care conține "capabilities" și "serverInfo".

Verificarea #2: Descoperirea uneltelor disponibile

După inițializare, poți cere lista de unelte disponibile:

curl -X POST https://aiparte.ro/scoli/server_scoli/mcp \
-H "Content-Type: application/json" \
-d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list"
}'

Răspuns corect: Vei primi un JSON cu "result" care conține o listă "tools" cu toate uneltele disponibile.

Verificarea #3: Execuția unei unelte

Testează execuția unei unelte specifice cu parametrii corecți:

curl -X POST https://aiparte.ro/scoli/server_scoli/mcp \
-H "Content-Type: application/json" \
-d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
        "name": "get_informatii_extinse",
        "arguments": {
            "nume_scoala": "Liceul Teoretic Avram Iancu",
            "localitate": "Cluj-Napoca"
        }
    }
}'

Răspuns corect: Vei primi un JSON cu "result" care conține "content" cu rezultatul de la unealtă.

Metoda B: Folosind un script Python (Pentru verificări automate)

Poți crea un mic script Python pentru a rula aceste teste automat. Asigură-te că ai instalat librăria necesară: pip install requests.

Creează un fișier numit test_mcp.py cu următorul conținut:

import requests
import json

MCP_ENDPOINT_URL = "https://aiparte.ro/scoli/server_scoli/mcp"

def test_initialize():
    print("--- TEST 1: Inițializarea MCP ---")
    try:
        payload = {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "initialize",
            "params": {
                "protocolVersion": "2025-06-18",
                "capabilities": {"tools": {}},
                "clientInfo": {"name": "python-test", "version": "1.0.0"}
            }
        }
        response = requests.post(MCP_ENDPOINT_URL, json=payload, timeout=10)
        response.raise_for_status()

        print(f"Status: {response.status_code}")
        data = response.json()
        if 'result' in data and 'capabilities' in data['result']:
            print("SUCCES: Serverul MCP a fost inițializat corect.")
            print(f"Server info: {data['result'].get('serverInfo', {}).get('name', 'N/A')}")
        else:
            print("EROARE: Răspunsul la inițializare este invalid.")
            print("Răspuns primit:", data)

    except Exception as e:
        print(f"EROARE: {e}")
    print("-" * 40)

def test_tools_list():
    print("--- TEST 2: Lista de unelte ---")
    try:
        payload = {
            "jsonrpc": "2.0",
            "id": 2,
            "method": "tools/list"
        }
        response = requests.post(MCP_ENDPOINT_URL, json=payload, timeout=10)
        response.raise_for_status()

        print(f"Status: {response.status_code}")
        data = response.json()
        if 'result' in data and 'tools' in data['result']:
            tools = data['result']['tools']
            print(f"SUCCES: Au fost descoperite {len(tools)} unelte.")
            for tool in tools[:3]:  # Afișează primele 3
                print(f"- {tool['name']}: {tool['description'][:50]}...")
        else:
            print("EROARE: Răspunsul pentru tools/list este invalid.")
            print("Răspuns primit:", data)

    except Exception as e:
        print(f"EROARE: {e}")
    print("-" * 40)

def test_tool_execution():
    print("--- TEST 3: Execuția unei unelte ---")
    try:
        payload = {
            "jsonrpc": "2.0",
            "id": 3,
            "method": "tools/call",
            "params": {
                "name": "get_informatii_extinse",
                "arguments": {
                    "nume_scoala": "Liceul Teoretic Avram Iancu",
                    "localitate": "Cluj-Napoca"
                }
            }
        }
        response = requests.post(MCP_ENDPOINT_URL, json=payload, timeout=15)
        response.raise_for_status()

        print(f"Status: {response.status_code}")
        data = response.json()
        if 'result' in data and 'content' in data['result']:
            print("SUCCES: Unealta a fost executată corect.")
            content = data['result']['content'][0]['text']
            print("Fragment din răspuns:", content[:200] + "...")
        else:
            print("EROARE: Răspunsul la execuția uneltei este invalid.")
            print("Răspuns primit:", data)

    except Exception as e:
        print(f"EROARE: {e}")
    print("-" * 40)

if __name__ == "__main__":
    test_initialize()
    test_tools_list()
    test_tool_execution()

Apoi, rulează scriptul din terminal cu comanda: python test_mcp.py și analizează outputul pentru a confirma funcționalitatea.


Metoda 2: Integrare Tradițională (Orchestrare de către Client)

În această abordare alternativă, aplicația ta (clientul) este "creierul" operațiunii. Tu ești responsabil pentru a apela endpoint-urile noastre, a procesa răspunsurile și a comunica cu modelul tău AI. Este o metodă flexibilă, compatibilă cu orice model AI.

  1. Obține o decizie de la AI: Apelezi endpoint-ul /chat pentru a primi o decizie despre ce unealtă să folosești.
  2. Execută unealta: Apelezi endpoint-ul /call_tool cu decizia primită pentru a obține datele brute.
  3. Formulează răspunsul final: Trimiți datele înapoi la modelul AI pentru a construi un răspuns prietenos.

Exemplu: Integrarea cu un model compatibil OpenAI (Python)

Următorul exemplu în Python demonstrează acest flux complet. Folosește librăria openai, dar logica este similară pentru orice alt furnizor care respectă același protocol (Google Gemini, Anthropic Claude, etc.). Am înlocuit un client real cu o simulare (Mock) pentru a putea rula codul fără o cheie API.

Cod Python complet pentru orchestrarea AI-ului cu MCP

import os
import json
import requests
from types import SimpleNamespace
import uuid

# --- Clasă de simulare pentru a nu necesita o cheie API OpenAI ---
# Un client real s-ar inițializa așa:
# from openai import OpenAI
# client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
class MockOpenAIClient:
    def __init__(self):
        self.chat = self
        self.completions = self

    def create(self, model, messages, tools=None, tool_choice=None):
        last_message = messages[-1]

        # Cazul 1: AI-ul primește o întrebare și trebuie să decidă dacă folosește o unealtă.
        if last_message.get("role") == "user":
            user_content = last_message.get("content", "").lower()
            if "licee" in user_content and "cluj" in user_content:
                print("--- Simulare AI: Am decis să folosesc 'get_school_general_info'.\n")
                # Returnează un obiect care seamănă cu răspunsul real al API-ului
                return SimpleNamespace(
                    choices=[SimpleNamespace(
                        message=SimpleNamespace(
                            tool_calls=[SimpleNamespace(
                                id="call_abc123",
                                type="function",
                                function=SimpleNamespace(
                                    name="get_school_general_info",
                                    arguments='{"localitate": "Cluj-Napoca", "tip_unitate": "liceu"}'
                                )
                            )],
                            content=None
                        )
                    )]
                )
        
        # Cazul 2: AI-ul a primit datele de la unealtă și trebuie să formuleze răspunsul final.
        if last_message.get("role") == "tool":
            print("--- Simulare AI: Am primit datele, formulez răspunsul final.\n")
            tool_data = json.loads(last_message.get("content", "[]"))
            school_names = [school.get('nume', 'N/A') for school in tool_data]
            final_text = f"Sigur, iată câteva licee din Cluj-Napoca: {', '.join(school_names)}."
            return SimpleNamespace(
                choices=[SimpleNamespace(
                    message=SimpleNamespace(tool_calls=None, content=final_text)
                )]
            )

        # Cazul implicit: nu înțelege ce să facă.
        return SimpleNamespace(
            choices=[SimpleNamespace(
                message=SimpleNamespace(tool_calls=None, content="Nu am înțeles. Puteți reformula?")
            )]
        )

# --- START PROGRAM PRINCIPAL ---

# Inițiem clientul (cel simulat în cazul nostru)
client = MockOpenAIClient()

# URL-ul către serverul nostru MCP
MCP_TOOL_ENDPOINT = "https://aiparte.ro/scoli/server_scoli/call_tool"

# Generăm ID-uri unice pentru această sesiune
SESSION_ID = f"demo-py-{uuid.uuid4().hex[:8]}"
USER_IP = "127.0.0.1"

# 1. Definim uneltele într-un format pe care AI-ul îl înțelege (JSON Schema).
tools_definition = [
    {
        "type": "function", 
        "function": {
            "name": "get_school_general_info",
            "description": "Obține o listă de școli pe baza unor filtre precum localitate, județ, tip.",
            "parameters": {
                "type": "object",
                "properties": {
                    "localitate": {"type": "string", "description": "Localitatea unde se caută școala, ex: 'București'"},
                    "judet": {"type": "string", "description": "Județul unde se caută școala, ex: 'Cluj'"},
                    "tip_unitate": {"type": "string", "description": "Tipul de școală, ex: 'liceu', 'grădiniță'"},
                },
                "required": [],
            },
        }
    },
    # Aici ai adăuga definițiile pentru celelalte unelte: get_informatii_extinse, etc.
]

# 2. Începem conversația cu întrebarea utilizatorului.
user_question = "Ce licee sunt în Cluj?"
messages = [{"role": "user", "content": user_question}]
print(f"Utilizator: {user_question}\n")

# 3. Trimitem întrebarea la AI, împreună cu lista de unelte disponibile.
response = client.chat.completions.create(
    model="gpt-4-turbo",
    messages=messages,
    tools=tools_definition,
    tool_choice="auto",
)

response_message = response.choices[0].message
# Adăugăm răspunsul gol al AI-ului (care conține doar apelul de unealtă) la istoric
messages.append({"role": "assistant", "content": response_message.content, "tool_calls": response_message.tool_calls})

# 4. Verificăm dacă AI-ul a decis să folosească o unealtă.
if response_message.tool_calls:
    tool_call = response_message.tool_calls[0] # Preluăm primul (și singurul) apel
    function_name = tool_call.function.name
    function_args = json.loads(tool_call.function.arguments)

    print(f"Aplicația: AI-ul vrea să ruleze `{function_name}` cu argumentele: {function_args}\n")

    # Construim payload-ul pentru serverul nostru MCP
    args_str = ", ".join([f"{key}=\"{value}\"" for key, value in function_args.items()])
    tool_call_str = f"{function_name}({args_str})"
    
    mcp_payload = {"tool_call": tool_call_str}
    print(f"Aplicația: Trimit către MCP următorul payload: {mcp_payload}\n")

    # 5. Executăm apelul către serverul MCP.
    try:
        tool_response = requests.post(
            MCP_TOOL_ENDPOINT, 
            params={
                "session_id": SESSION_ID, 
                "user_ip": USER_IP,
                "client_id": f"demo-client-{SESSION_ID}"
            },
            json=mcp_payload,
            timeout=10 # Adăugăm un timeout
        )
        tool_response.raise_for_status()
        tool_output_data = tool_response.json()
        print(f"Aplicația: Am primit de la MCP: {json.dumps(tool_output_data, indent=2, ensure_ascii=False)}\n")

        # 6. Trimitem rezultatul înapoi la AI.
        messages.append({
            "tool_call_id": tool_call.id,
            "role": "tool",
            "name": function_name,
            "content": json.dumps(tool_output_data, ensure_ascii=False),
        })

        # Apelăm AI-ul a doua oară, de data aceasta cu datele de la unealtă în istoric
        second_response = client.chat.completions.create(model="gpt-4-turbo", messages=messages)
        
        # 7. Afișăm răspunsul final, formulat de AI pe baza datelor primite.
        final_answer = second_response.choices[0].message.content
        print(f"Răspuns final de la AI:\n------------------------\n{final_answer}")

    except requests.exceptions.RequestException as e:
        print(f"Eroare la apelarea MCP: {e}")

else:
    # Dacă AI-ul a răspuns direct, fără unelte.
    print(f"Răspuns direct de la AI: {response_message.content}")

Referința completă a uneltelor, cu exemple de apel pentru /call_tool, se găsește mai jos.


Referință Detaliată a Uneltelor (pentru Metoda 2)

Mai jos găsiți detalii complete și exemple de cod pentru fiecare unealtă. Exemplele ilustrează apelarea prin endpoint-ul /call_tool.

get_school_general_info

Scop: Returnează o listă generală de unități de învățământ pe baza unor criterii de filtrare.

Parametri

ParametruTipDescriere
nume_scoalastringO parte din numele școlii.
localitatestringNumele localității.
judetstringNumele județului.
tip_unitatestringTipul unității (ex: 'liceu', 'grădiniță').
tip_finantarestring'Buget' sau 'Taxă'.
limitintegerNumărul maxim de rezultate (implicit 20).

Exemple de Cod (pentru /call_tool)

curl -X POST "https://aiparte.ro/scoli/server_scoli/call_tool?session_id=demo-session-123&user_ip=127.0.0.1&client_id=demo-client-456" \
-H "Content-Type: application/json" \
-d '{"tool_call": "get_school_general_info(localitate=\"Cluj-Napoca\", tip_unitate=\"grădiniță\")"}'
import requests
import uuid

session_id = f"demo-session-{uuid.uuid4().hex[:8]}"
api_url = "https://aiparte.ro/scoli/server_scoli/call_tool"
payload = {"tool_call": "get_school_general_info(localitate='Cluj-Napoca', tip_unitate='grădiniță')"}
params = {
    "session_id": session_id,
    "user_ip": "127.0.0.1", 
    "client_id": f"demo-client-{session_id}"
}
response = requests.post(api_url, params=params, json=payload)
print(response.json())
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');

const sessionId = `demo-session-${uuidv4().substring(0, 8)}`;
const api_url = "https://aiparte.ro/scoli/server_scoli/call_tool";
const payload = { tool_call: "get_school_general_info(localitate='Cluj-Napoca', tip_unitate='grădiniță')" };
const params = {
    session_id: sessionId,
    user_ip: "127.0.0.1",
    client_id: `demo-client-${sessionId}`
};

axios.post(api_url, payload, { params }).then(r => console.log(r.data));

get_informatii_extinse

Scop: Oferă toate detaliile disponibile despre o singură unitate. Căutarea după cod_siiir este cea mai precisă.

Parametri

ParametruTipDescriere
cod_siiirstringCodul SIIIR unic al școlii (preferat).
nume_scoalastringNumele exact sau parțial al școlii.
localitatestringNumele localității pentru a restrânge căutarea.
judetstringNumele județului.

Exemple de Cod (pentru /call_tool)

curl -X POST "https://aiparte.ro/scoli/server_scoli/call_tool?session_id=demo-session-123&user_ip=127.0.0.1&client_id=demo-client-456" \
-H "Content-Type: application/json" \
-d '{"tool_call": "get_informatii_extinse(cod_siiir=\"401234567\")"}'
import requests
import uuid

session_id = f"demo-session-{uuid.uuid4().hex[:8]}"
api_url = "https://aiparte.ro/scoli/server_scoli/call_tool"
payload = {"tool_call": "get_informatii_extinse(cod_siiir='401234567')"}
params = {
    "session_id": session_id,
    "user_ip": "127.0.0.1",
    "client_id": f"demo-client-{session_id}"
}
response = requests.post(api_url, params=params, json=payload)
print(response.json())
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');

const sessionId = `demo-session-${uuidv4().substring(0, 8)}`;
const api_url = "https://aiparte.ro/scoli/server_scoli/call_tool";
const payload = { tool_call: "get_informatii_extinse(cod_siiir='401234567')" };
const params = {
    session_id: sessionId,
    user_ip: "127.0.0.1",
    client_id: `demo-client-${sessionId}`
};

axios.post(api_url, payload, { params }).then(r => console.log(r.data));

get_school_distinctions

Scop: Returnează o listă de școli cu distincții și scoruri de performanță. Ideal pentru comparații.

Parametri

Identici cu cei de la get_school_general_info.

Exemple de Cod (pentru /call_tool)

curl -X POST "https://aiparte.ro/scoli/server_scoli/call_tool?session_id=demo-session-123&user_ip=127.0.0.1&client_id=demo-client-456" \
-H "Content-Type: application/json" \
-d '{"tool_call": "get_school_distinctions(judet=\"Cluj\", tip_unitate=\"Liceu\")"}'
import requests
import uuid

session_id = f"demo-session-{uuid.uuid4().hex[:8]}"
api_url = "https://aiparte.ro/scoli/server_scoli/call_tool"
payload = {"tool_call": "get_school_distinctions(judet='Cluj', tip_unitate='Liceu')"}
params = {
    "session_id": session_id,
    "user_ip": "127.0.0.1",
    "client_id": f"demo-client-{session_id}"
}
response = requests.post(api_url, params=params, json=payload)
print(response.json())
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');

const sessionId = `demo-session-${uuidv4().substring(0, 8)}`;
const api_url = "https://aiparte.ro/scoli/server_scoli/call_tool";
const payload = { tool_call: "get_school_distinctions(judet='Cluj', tip_unitate='Liceu')" };
const params = {
    session_id: sessionId,
    user_ip: "127.0.0.1",
    client_id: `demo-client-${sessionId}`
};

axios.post(api_url, payload, { params }).then(r => console.log(r.data));

get_nearby_schools

Scop: Găsește școlile aflate într-o anumită rază față de o școală de referință.

Parametri

ParametruTipDescriere
nume_scoalastringNumele școlii de referință (punctul central).
localitatestringLocalitatea școlii de referință.
radius_kmintegerRaza în kilometri (implicit 5).
tip_unitatestringFiltrează școlile găsite după tip (opțional).

Exemple de Cod (pentru /call_tool)

curl -X POST "https://aiparte.ro/scoli/server_scoli/call_tool?session_id=demo-session-123&user_ip=127.0.0.1&client_id=demo-client-456" \
-H "Content-Type: application/json" \
-d '{"tool_call": "get_nearby_schools(nume_scoala=\"Liceul Teoretic Avram Iancu\", localitate=\"Cluj-Napoca\", radius_km=2)"}'
import requests
import uuid

session_id = f"demo-session-{uuid.uuid4().hex[:8]}"
api_url = "https://aiparte.ro/scoli/server_scoli/call_tool"
payload = {"tool_call": "get_nearby_schools(nume_scoala='Liceul Teoretic Avram Iancu', localitate='Cluj-Napoca', radius_km=2)"}
params = {
    "session_id": session_id,
    "user_ip": "127.0.0.1",
    "client_id": f"demo-client-{session_id}"
}
response = requests.post(api_url, params=params, json=payload)
print(response.json())
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');

const sessionId = `demo-session-${uuidv4().substring(0, 8)}`;
const api_url = "https://aiparte.ro/scoli/server_scoli/call_tool";
const payload = { tool_call: "get_nearby_schools(nume_scoala='Liceul Teoretic Avram Iancu', localitate='Cluj-Napoca', radius_km=2)" };
const params = {
    session_id: sessionId,
    user_ip: "127.0.0.1",
    client_id: `demo-client-${sessionId}`
};

axios.post(api_url, payload, { params }).then(r => console.log(r.data));

send_summary_email

Scop: Trimite un rezumat al conversației la o adresă de email specificată.

Parametri

ParametruTipDescriere
recipient_emailstringAdresa de email a destinatarului.
summary_contentstringTextul care trebuie trimis în corpul email-ului.

Exemple de Cod (pentru /call_tool)

curl -X POST "https://aiparte.ro/scoli/server_scoli/call_tool?session_id=demo-session-123&user_ip=127.0.0.1&client_id=demo-client-456" \
-H "Content-Type: application/json" \
-d '{"tool_call": "send_summary_email(recipient_email=\"exemplu@domeniu.ro\", summary_content=\"Acesta este rezumatul discuției.\")"}'
import requests
import uuid

session_id = f"demo-session-{uuid.uuid4().hex[:8]}"
api_url = "https://aiparte.ro/scoli/server_scoli/call_tool"
payload = {"tool_call": "send_summary_email(recipient_email='exemplu@domeniu.ro', summary_content='Acesta este rezumatul discuției.')"}
params = {
    "session_id": session_id,
    "user_ip": "127.0.0.1",
    "client_id": f"demo-client-{session_id}"
}
response = requests.post(api_url, params=params, json=payload)
print(response.json())
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');

const sessionId = `demo-session-${uuidv4().substring(0, 8)}`;
const api_url = "https://aiparte.ro/scoli/server_scoli/call_tool";
const payload = { tool_call: "send_summary_email(recipient_email='exemplu@domeniu.ro', summary_content='Acesta este rezumatul discuției.')" };
const params = {
    session_id: sessionId,
    user_ip: "127.0.0.1",
    client_id: `demo-client-${sessionId}`
};

axios.post(api_url, payload, { params }).then(r => console.log(r.data));