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.
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.
URL Server MCP: https://aiparte.ro/scoli/server_scoli/mcp
Nume Server (sugerat): aiparte_edu_romania
Autentificare: Nu este necesară.
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"
}
]
}'
Următoarele metode vă permit să verificați dacă serverul MCP funcționează corect, independent de un client AI (precum Claude sau Gemini).
Testarea directă a serverului MCP folosind protocolul JSON-RPC 2.0 standard.
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"
.
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.
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ă.
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.
Î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.
/chat
pentru a primi o decizie despre ce unealtă să folosești./call_tool
cu decizia primită pentru a obține datele brute.
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.
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.
Mai jos găsiți detalii complete și exemple de cod pentru fiecare unealtă. Exemplele ilustrează apelarea prin endpoint-ul /call_tool
.
Scop: Returnează o listă generală de unități de învățământ pe baza unor criterii de filtrare.
Parametru | Tip | Descriere |
---|---|---|
nume_scoala | string | O parte din numele școlii. |
localitate | string | Numele localității. |
judet | string | Numele județului. |
tip_unitate | string | Tipul unității (ex: 'liceu', 'grădiniță'). |
tip_finantare | string | 'Buget' sau 'Taxă'. |
limit | integer | Numărul maxim de rezultate (implicit 20). |
/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));
Scop: Oferă toate detaliile disponibile despre o singură unitate. Căutarea după cod_siiir
este cea mai precisă.
Parametru | Tip | Descriere |
---|---|---|
cod_siiir | string | Codul SIIIR unic al școlii (preferat). |
nume_scoala | string | Numele exact sau parțial al școlii. |
localitate | string | Numele localității pentru a restrânge căutarea. |
judet | string | Numele județului. |
/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));
Scop: Returnează o listă de școli cu distincții și scoruri de performanță. Ideal pentru comparații.
Identici cu cei de la get_school_general_info
.
/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));
Scop: Găsește școlile aflate într-o anumită rază față de o școală de referință.
Parametru | Tip | Descriere |
---|---|---|
nume_scoala | string | Numele școlii de referință (punctul central). |
localitate | string | Localitatea școlii de referință. |
radius_km | integer | Raza în kilometri (implicit 5). |
tip_unitate | string | Filtrează școlile găsite după tip (opțional). |
/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));
Scop: Trimite un rezumat al conversației la o adresă de email specificată.
Parametru | Tip | Descriere |
---|---|---|
recipient_email | string | Adresa de email a destinatarului. |
summary_content | string | Textul care trebuie trimis în corpul email-ului. |
/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));