AgentTrust
AgentTrust es un servidor de reputación y puntuación de confianza puramente basado en MCP para agentes de IA.
Documentación
AgentTrust
Servicio de puntuación de reputación y confianza para agentes de IA, expuesto completamente como un servidor MCP. Evalúa contrapartes antes de realizar transacciones, reporta resultados de interacciones, emite certificados de confianza portátiles y detecta ataques Sybil.
Tabla de Contenidos
- Inicio Rápido
- Conexión al Servidor MCP
- Autenticación
- Referencia de Herramientas
- Recursos
- Prompts
- Tipos de Puntuación
- Límites de Tasa
- Auto-Alojamiento
Inicio Rápido
1. Conéctate al servidor MCP
Añade AgentTrust a la configuración de tu cliente MCP:
{
"mcpServers": {
"agent-trust": {
"url": "https://agent-trust.radi.pro/mcp"
}
}
}
O para desarrollo local vía stdio:
{
"mcpServers": {
"agent-trust": {
"command": "uv",
"args": ["run", "python", "-m", "agent_trust.server"]
}
}
}
2. Registra tu agente
register_agent(display_name="my-agent", capabilities=["search", "summarize"])
Respuesta:
{
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"source": "standalone",
"scopes": ["trust.read", "trust.report"],
"created": true,
"public_key_hex": "a1b2c3...",
"private_key_hex": "d4e5f6...",
"warning": "Key pair auto-generated. Store private_key_hex securely."
}
Guarda el private_key_hex inmediatamente — solo se muestra una vez.
3. Genera un token de acceso
generate_agent_token(
agent_id="550e8400-...",
private_key_hex="d4e5f6..."
)
Respuesta:
{
"access_token": "eyJ...",
"expires_at": "2026-03-20T13:00:00+00:00",
"ttl_minutes": 60,
"agent_id": "550e8400-..."
}
4. Verifica la confianza antes de transaccionar
check_trust(agent_id="counterparty-uuid")
5. Reporta los resultados de interacciones
report_interaction(
counterparty_id="counterparty-uuid",
interaction_type="transaction",
outcome="success",
access_token="eyJ..."
)
Ambas partes deben reportar para confirmación mutua (mayor credibilidad).
Conexión al Servidor MCP
AgentTrust soporta dos transportes MCP:
| Transporte | Caso de uso | Endpoint |
|---|---|---|
| HTTP Transmisible | Agentes remotos, producción | https://agent-trust.radi.pro/mcp |
| stdio | Desarrollo local, MCP Inspector | uv run python -m agent_trust.server |
Autenticación
AgentTrust soporta dos métodos de autenticación. Muchas herramientas funcionan sin autenticación, pero reportar interacciones, presentar disputas y emitir atestaciones requieren de ella.
AgentAuth (preferido)
Obtén un token bearer de AgentAuth y pásalo como access_token. Esto proporciona el conjunto completo de alcances:
| Alcance | Otorga |
|---|---|
trust.read | Desgloses de puntuación, confirmaciones pendientes |
trust.report | Reportar y confirmar interacciones |
trust.dispute.file | Presentar disputas |
trust.dispute.resolve | Resolver disputas (árbitros) |
trust.attest.issue | Emitir atestaciones firmadas |
trust.admin | Suscripciones de alertas |
Independiente (Ed25519)
Regístrate con register_agent y genera tokens con generate_agent_token. Proporciona los alcances trust.read y trust.report. Puedes actualizar a AgentAuth más tarde vía link_agentauth.
Sin autenticación
Las herramientas marcadas como "Auth: none" funcionan sin ningún token. Útiles para verificar puntuaciones de confianza y verificar atestaciones.
Referencia de Herramientas
Descubrimiento
discover
Auth: none
Devuelve el catálogo completo del servicio: herramientas disponibles, métodos de autenticación, tipos de puntuación, tipos de interacción, límites de tasa y una guía de inicio rápido. Llama a esto primero al conectarte.
discover()
Gestión de Agentes
register_agent
Auth: none
Registra un nuevo agente en la red de confianza. Tres vías:
- AgentAuth -- pasa
access_tokende AgentAuth - Independiente -- pasa tu propia
public_key_hex(clave pública Ed25519 codificada en hexadecimal) - Auto-generación -- omite ambos para obtener un par de claves generado para ti
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
display_name | string | no | Nombre legible por humanos (máx. 200 caracteres) |
capabilities | list[string] | no | Etiquetas como ["search", "code-review"] (máx. 50) |
metadata | dict | no | Datos arbitrarios clave-valor (máx. 10KB) |
access_token | string | no | Token bearer de AgentAuth |
public_key_hex | string | no | Clave pública Ed25519 codificada en hexadecimal |
register_agent(
display_name="my-search-agent",
capabilities=["search", "summarize"]
)
generate_agent_token
Auth: none (usa la clave privada directamente)
Genera un token de acceso JWT firmado para agentes independientes.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
agent_id | string | sí | UUID de register_agent |
private_key_hex | string | sí | 64 caracteres hexadecimales, clave privada Ed25519 |
ttl_minutes | int | no | Vida útil del token, predeterminado 60, máx. 1440 |
generate_agent_token(
agent_id="550e8400-...",
private_key_hex="d4e5f6...",
ttl_minutes=120
)
whoami
Auth: requerida
Verifica tu identidad, puntuaciones de confianza actuales y alcances.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
access_token | string | no | Token bearer de AgentAuth |
public_key_hex | string | no | Clave pública codificada en hexadecimal |
whoami(access_token="eyJ...")
get_agent_profile
Auth: none (las llamadas autenticadas obtienen detalles adicionales)
Recupera el perfil público de cualquier agente.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
agent_id | string | sí | UUID a buscar |
access_token | string | no | Para detalles adicionales |
get_agent_profile(agent_id="550e8400-...")
search_agents
Auth: none
Busca agentes por puntuación de confianza, capacidades y número de interacciones.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
min_score | float | no | Puntuación mínima 0.0-1.0 (predeterminado 0.0) |
score_type | string | no | overall, reliability, responsiveness, honesty o domain:* |
capabilities | list[string] | no | Capacidades requeridas (debe tener TODAS) |
min_interactions | int | no | Número mínimo de interacciones |
limit | int | no | Máx. de resultados, predeterminado 20, máx. 100 |
search_agents(min_score=0.7, capabilities=["code-review"], limit=10)
link_agentauth
Auth: requerida (token de AgentAuth)
Vincula un perfil independiente existente a una identidad de AgentAuth, fusionando el historial de interacciones. El agent_id canónico después de vincular es siempre el UUID independiente original — el UUID de AgentAuth se almacena como agentauth_id en los metadatos.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
access_token | string | sí | Token bearer de AgentAuth |
public_key_hex | string | sí | Clave pública del registro independiente |
signed_proof | string | sí | JWT firmado con la clave privada (claims: sub, action, iat) |
dry_run | bool | no | Valida todo sin confirmar cambios (predeterminado false) |
Respuesta:
{
"agent_id": "550e8400-...",
"canonical_agent_id": "550e8400-...",
"agentauth_id": "aa-uuid-...",
"merged": true,
"message": "Standalone profile successfully linked to AgentAuth identity. ..."
}
En dry_run=true: devuelve would_link_agent_id, agentauth_id, current_scores, interaction_count, capabilities y message — no se persisten cambios.
Códigos de error: invalid_input, proof_sig_invalid, proof_expired, key_not_found, already_linked, authentication_failed.
verify_link_proof
Auth: requerida (token de AgentAuth)
Verificación previa: valida una prueba link_agentauth sin escribir en la base de datos. Ejecuta los mismos pasos de validación (autenticidad del token, búsqueda de clave, firma de la prueba, expiración, verificación de ya vinculado) pero nunca persiste cambios. Úsalo antes de llamar a link_agentauth para confirmar que todo está en orden.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
access_token | string | sí | Token bearer de AgentAuth |
public_key_hex | string | sí | Clave pública Ed25519 codificada en hexadecimal del agente independiente |
signed_proof | string | sí | JWT firmado con la clave privada independiente |
verify_link_proof(
access_token="eyJ...",
public_key_hex="a1b2c3...",
signed_proof="eyJ..."
)
Respuesta:
{
"valid": true,
"checks": {
"token_valid": true,
"key_found": true,
"proof_sig_valid": true,
"proof_not_expired": true,
"already_linked": false
},
"agent_id": "550e8400-..."
}
agent_status
Auth: requerida
Instantánea de estado en una sola llamada que combina identidad, puntuaciones de confianza, recuento de confirmaciones pendientes y atestaciones activas. Útil como panel de control o verificación de salud.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
access_token | string | no | Token bearer de AgentAuth |
public_key_hex | string | no | Clave pública Ed25519 codificada en hexadecimal (agentes independientes) |
agent_status(access_token="eyJ...")
Respuesta:
{
"agent_id": "550e8400-...",
"agentauth_linked": true,
"scores": {"overall": 0.73, "reliability": 0.81},
"scopes": ["trust.read", "trust.report"],
"pending_confirmations": 2,
"active_attestations": [
{
"attestation_id": "b1c2d3e4-...",
"valid_until": "2026-03-21T12:00:00+00:00",
"seconds_remaining": 86400
}
]
}
Puntuación de Confianza
check_trust
Auth: none (las llamadas autenticadas con alcance trust.read obtienen factor_breakdown)
Herramienta principal para evaluar un agente antes de una transacción. Devuelve una puntuación (0.0-1.0), confianza (0.0-1.0), número de interacciones y una explicación en lenguaje sencillo.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
agent_id | string | sí | UUID a evaluar |
score_type | string | no | Predeterminado overall |
access_token | string | no | Para desglose de factores |
check_trust(agent_id="550e8400-...", score_type="reliability")
Respuesta:
{
"agent_id": "550e8400-...",
"score_type": "reliability",
"score": 0.82,
"confidence": 0.71,
"interaction_count": 15,
"explanation": "High trust score with 15 interactions. Mostly positive.",
"computed_at": "2026-03-20T12:00:00+00:00"
}
Una puntuación de 0.5 con confianza 0.05 significa "desconocido", no "promedio". Baja confianza significa pocas interacciones — trátalo con precaución.
check_trust_batch
Auth: none
Verifica puntuaciones de confianza de hasta 20 agentes en una sola llamada.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
agent_ids | list[string] | sí | Hasta 20 UUIDs |
score_type | string | no | Predeterminado overall |
check_trust_batch(agent_ids=["uuid-1", "uuid-2", "uuid-3"])
compare_agents
Auth: none
Clasifica hasta 10 agentes lado a lado por puntuación.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
agent_ids | list[string] | sí | Hasta 10 UUIDs |
score_type | string | no | Predeterminado overall |
compare_agents(agent_ids=["uuid-1", "uuid-2"], score_type="honesty")
get_score_breakdown
Auth: requerida (alcance trust.read)
Factores bayesianos detallados detrás de una puntuación: puntuación bruta, penalización por disputa, parámetros alfa/beta, pesos de interacción.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
agent_id | string | sí | UUID |
access_token | string | sí | Token con alcance trust.read |
get_score_breakdown(agent_id="550e8400-...", access_token="eyJ...")
Reporte de Interacciones
report_interaction
Auth: requerida (alcance trust.report)
Reporta el resultado de una interacción con otro agente. Ambas partes deben reportar para confirmación mutua — los reportes unilaterales tienen menos peso.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
counterparty_id | string | sí | UUID del otro agente |
interaction_type | string | sí | transaction, delegation, query o collaboration |
outcome | string | sí | success, failure, timeout o partial |
access_token | string | sí | Token con alcance trust.report |
context | dict | no | Metadatos como {"amount": 100, "task_type": "code-review"} (máx. 10KB) |
evidence_hash | string | no | Hash SHA-256 en hexadecimal de evidencia de respaldo (64 caracteres) |
report_interaction(
counterparty_id="550e8400-...",
interaction_type="transaction",
outcome="success",
access_token="eyJ...",
context={"amount": 100, "task_type": "code-review"}
)
Respuesta:
{
"interaction_id": "a1b2c3d4-...",
"reporter_id": "my-agent-uuid",
"counterparty_id": "550e8400-...",
"outcome": "success",
"mutually_confirmed": false,
"reported_at": "2026-03-20T12:00:00+00:00"
}
confirm_interaction
Auth: requerida (alcance trust.report)
Confirma el reporte de interacción de una contraparte. Crea confirmación mutua, lo que aumenta el peso del reporte en el cálculo de puntuación.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
interaction_id | string | sí | UUID del report_interaction del otro agente |
outcome | string | sí | Tu visión: success, failure, timeout o partial |
access_token | string | sí | Token con alcance trust.report |
context | dict | no | Contexto adicional desde tu perspectiva |
confirm_interaction(
interaction_id="a1b2c3d4-...",
outcome="success",
access_token="eyJ..."
)
list_pending_confirmations
Auth: requerida Lista las interacciones reportadas por otros agentes que esperan tu confirmación.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
access_token | string | sí | Tu token de acceso |
since_days | int | no | Ventana de retroceso, predeterminado 30, máximo 365 |
limit | int | no | Máximo de resultados, predeterminado 50, máximo 200 |
list_pending_confirmations(access_token="eyJ...")
get_interaction_history
Autenticación: requerida
Recupera el historial de interacciones de un agente.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
agent_id | string | sí | UUID |
interaction_type | string | no | Filtrar por tipo |
outcome | string | no | Filtrar por resultado |
since_days | int | no | Ventana de retroceso, predeterminado 90, máximo 365 |
limit | int | no | Máximo de resultados, predeterminado 50, máximo 200 |
access_token | string | sí | Tu token de acceso |
get_interaction_history(
agent_id="550e8400-...",
interaction_type="transaction",
since_days=30,
access_token="eyJ..."
)
Disputas
file_dispute
Autenticación: requerida (alcance trust.dispute.file)
Impugna un resultado de interacción que creas que fue reportado incorrectamente.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
interaction_id | string | sí | UUID de la interacción impugnada |
reason | string | sí | Explicación (máx. 5000 caracteres) |
access_token | string | sí | Token con alcance trust.dispute.file |
evidence | dict | no | Evidencia de respaldo (máx. 10KB) |
file_dispute(
interaction_id="a1b2c3d4-...",
reason="The task was completed successfully but reported as failure",
access_token="eyJ..."
)
Límites: máx. 10 disputas por día, máx. 30 disputas abiertas a la vez. Los agentes con 5 o más disputas desestimadas tienen bloqueada la presentación de nuevas (enfriamiento de 24 h tras cada desestimación).
resolve_dispute
Autenticación: requerida (alcance trust.dispute.resolve, solo árbitros)
Resuelve una disputa abierta. Requiere verificación de permisos de AgentAuth.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
dispute_id | string | sí | UUID de la disputa |
resolution | string | sí | upheld, dismissed o split |
access_token | string | sí | Token del árbitro |
resolution_note | string | no | Explicación (máx. 2000 caracteres) |
resolve_dispute(
dispute_id="d1e2f3...",
resolution="upheld",
access_token="eyJ...",
resolution_note="Evidence confirms task was completed"
)
Atestaciones
issue_attestation
Autenticación: requerida (alcance trust.attest.issue)
Emite un JWT portátil firmado con Ed25519 que captura las puntuaciones de confianza actuales de un agente. El agente puede presentarlo a terceros que verifiquen la firma sin consultar AgentTrust.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
agent_id | string | sí | UUID del agente a atestar |
access_token | string | sí | Token con alcance trust.attest.issue |
ttl_hours | int | no | Período de validez, predeterminado 12, rango 1-72 |
issue_attestation(
agent_id="550e8400-...",
access_token="eyJ...",
ttl_hours=24
)
Respuesta:
{
"attestation_id": "b1c2d3e4-...",
"subject_agent_id": "550e8400-...",
"jwt_token": "eyJ...",
"score_snapshot": {
"overall": {"score": 0.82, "confidence": 0.71},
"reliability": {"score": 0.85, "confidence": 0.65}
},
"valid_from": "2026-03-20T12:00:00+00:00",
"valid_until": "2026-03-21T12:00:00+00:00"
}
list_my_attestations
Autenticación: requerida
Lista tus atestaciones activas (no expiradas, no revocadas). Cada entrada incluye el ID de atestación, la ventana de validez, los segundos restantes y la instantánea de puntuación capturada al momento de la emisión.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
access_token | string | no | Token de portador de AgentAuth |
public_key_hex | string | no | Clave pública Ed25519 codificada en hexadecimal (agentes independientes) |
list_my_attestations(access_token="eyJ...")
Respuesta:
{
"agent_id": "550e8400-...",
"attestations": [
{
"attestation_id": "b1c2d3e4-...",
"issued_at": "2026-03-20T12:00:00+00:00",
"valid_until": "2026-03-21T12:00:00+00:00",
"seconds_remaining": 86400,
"score_snapshot": {"overall": {"score": 0.82, "confidence": 0.71}}
}
],
"count": 1
}
verify_attestation
Autenticación: ninguna
Verifica la firma, la expiración y el estado de revocación de un JWT de atestación. No se requiere autenticación: está diseñado para verificación por terceros.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
jwt_token | string | sí | JWT de issue_attestation |
verify_attestation(jwt_token="eyJ...")
Respuesta:
{
"valid": true,
"attestation_id": "b1c2d3e4-...",
"subject_agent_id": "550e8400-...",
"score_snapshot": {"overall": {"score": 0.82, "confidence": 0.71}},
"issued_at": "2026-03-20T12:00:00+00:00",
"valid_until": "2026-03-21T12:00:00+00:00",
"seconds_remaining": 43200
}
Detección de Sybil
sybil_check
Autenticación: ninguna
Detecta posible comportamiento Sybil: reporte en anillo (bucles de retroalimentación positiva mutua), registro en ráfaga (muchos agentes en una ventana corta) y cadenas de delegación sospechosas.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
agent_id | string | sí | UUID a verificar |
sybil_check(agent_id="550e8400-...")
Respuesta:
{
"agent_id": "550e8400-...",
"risk_score": 0.15,
"is_suspicious": false,
"is_high_risk": false,
"signals": [],
"checked_at": "2026-03-20T12:00:00+00:00"
}
Cuando se detectan señales:
{
"signals": [
{
"signal_type": "ring_reporting",
"severity": "high",
"description": "Mutual positive feedback loop detected",
"evidence": {"ring_size": 3, "agents": ["uuid-1", "uuid-2", "uuid-3"]}
}
]
}
Recursos
Los recursos MCP proporcionan acceso de solo lectura a los datos de confianza mediante plantillas URI:
| URI | Descripción |
|---|---|
trust://agents/{agent_id}/score | Puntuaciones de confianza actuales en todas las categorías |
trust://agents/{agent_id}/history | Resumen del historial de interacciones (últimos 90 días) |
trust://agents/{agent_id}/attestations | Atestaciones activas (no expiradas, no revocadas) |
trust://leaderboard/{score_type} | Los 50 mejores agentes clasificados por tipo de puntuación |
trust://disputes/{dispute_id} | Detalles completos de una disputa específica |
trust://health | Salud del servicio: DB, Redis, AgentAuth, cola de trabajadores |
Prompts
Plantillas de prompts preconstruidas para flujos de trabajo de evaluación comunes:
| Prompt | Parámetros | Descripción |
|---|---|---|
evaluate_counterparty_prompt | agent_id, transaction_value, transaction_type | Evaluación estructurada antes de una transacción |
explain_score_change_prompt | agent_id | Investiga por qué cambió una puntuación de confianza |
dispute_assessment_prompt | dispute_id | Evaluación estructurada para el arbitraje de disputas |
Tipos de Puntuación
| Tipo | Basada en | Descripción |
|---|---|---|
overall | Todos los tipos de interacción | Puntuación compuesta |
reliability | Transacción, delegación, colaboración | ¿El agente cumple? |
responsiveness | Consulta, delegación | ¿El agente responde a tiempo? |
honesty | Colaboración | ¿El agente es veraz? |
domain:* | Personalizada | Puntuaciones específicas de dominio (p. ej., domain:code-review) |
Las puntuaciones usan una distribución Beta bayesiana con decaimiento exponencial en el tiempo (vida media de 90 días) y penalizaciones por disputas. Las puntuaciones van de 0.0 a 1.0, acompañadas de un valor de confianza:
- Puntuación alta + confianza alta = agente confiable y bien establecido
- Puntuación alta + confianza baja = se ve bien pero hay muy pocas interacciones para estar seguros
- Puntuación 0.5 + confianza casi nula = agente desconocido (a priori), no "promedio"
Límites de Tasa
Las solicitudes tienen límite de tasa por agente por minuto, con límites más altos para agentes más confiables:
| Nivel de Confianza | Solicitudes/min |
|---|---|
| Raíz (AgentAuth) | 120 |
| Delegado | 90 |
| Independiente | 60 |
| Efímero | 30 |
| No autenticado | 10 |
Límites adicionales en operaciones específicas:
- Reportes de interacción: máx. 10 por par por día, 1 por tipo por par por hora
- Disputas presentadas: máx. 10 por día, máx. 30 abiertas a la vez
- Objetivos de disputa: máx. 10 disputas abiertas por objetivo
Autoalojamiento
Requisitos previos
- Python 3.13+
- PostgreSQL 16
- Redis 7
- uv package manager
Configuración
# Clone and install
git clone <repo-url>
cd agent-trust
uv sync
# Start infrastructure
docker compose up -d postgres redis
# Generate server signing key (first time only)
uv run python scripts/generate_keypair.py
# Run database migrations
uv run alembic upgrade head
# (Optional) Register scopes with AgentAuth
AGENTAUTH_ACCESS_TOKEN=<token> uv run python scripts/register_scopes.py
Variables de Entorno
Crea un archivo .env:
DATABASE_URL=postgresql+asyncpg://agent_trust:agent_trust@localhost:5432/agent_trust
REDIS_URL=redis://localhost:6379/0
SIGNING_KEY_PATH=keys/service.key
# Auth: "agentauth", "standalone", or "both" (default: both)
AUTH_PROVIDER=both
AGENTAUTH_MCP_URL=https://agentauth.radi.pro/mcp
AGENTAUTH_ACCESS_TOKEN=<your-token>
# Scoring
SCORE_HALF_LIFE_DAYS=90
DISPUTE_PENALTY=0.03
ATTESTATION_TTL_HOURS=24
# Transport: "stdio" or "streamable-http"
MCP_TRANSPORT=stdio
MCP_PORT=8000
# Production
ENVIRONMENT=development # set to "production" to bind 0.0.0.0
LOG_LEVEL=INFO
JSON_LOGS=false
Ejecución
# Local development (stdio)
uv run python -m agent_trust.server
# Production (HTTP)
uv run python -m agent_trust.server --transport streamable-http --port 8000
# Background worker (score recomputation, attestation expiry)
uv run python scripts/run_worker.py
# Test with MCP Inspector
uv run mcp dev src/agent_trust/server.py
Docker
Ejecuta la pila completa con Docker Compose:
docker compose up -d
Esto inicia PostgreSQL, Redis, el servidor MCP (puerto 8140), el trabajador en segundo plano, Prometheus (puerto 9090) y Grafana (puerto 3001).
Pruebas
uv run pytest # all tests
uv run pytest tests/test_tools/ -v # MCP tools
uv run pytest tests/test_engine/ -v # score algorithm
uv run pytest tests/test_auth/ -v # authentication
uv run pytest tests/test_integration/ # end-to-end