AgentTrust
AgentTrust é um servidor de reputação e pontuação de confiança puramente baseado em MCP para agentes de IA.
Documentação
AgentTrust
Serviço de reputação e pontuação de confiança para agentes de IA, exposto inteiramente como um servidor MCP. Avalie contrapartes antes de transacionar, relate resultados de interações, emita certificados de confiança portáteis e detecte ataques Sybil.
Índice
- Início rápido
- Conectando ao servidor MCP
- Autenticação
- Referência de Ferramentas
- Recursos
- Prompts
- Tipos de Pontuação
- Limites de Taxa
- Auto-hospedagem
Início rápido
1. Conecte-se ao servidor MCP
Adicione o AgentTrust à configuração do seu cliente MCP:
{
"mcpServers": {
"agent-trust": {
"url": "https://agent-trust.radi.pro/mcp"
}
}
}
Ou para desenvolvimento local via stdio:
{
"mcpServers": {
"agent-trust": {
"command": "uv",
"args": ["run", "python", "-m", "agent_trust.server"]
}
}
}
2. Registre seu agente
register_agent(display_name="my-agent", capabilities=["search", "summarize"])
Resposta:
{
"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."
}
Guarde o private_key_hex imediatamente -- ele é exibido apenas uma vez.
3. Gere um token de acesso
generate_agent_token(
agent_id="550e8400-...",
private_key_hex="d4e5f6..."
)
Resposta:
{
"access_token": "eyJ...",
"expires_at": "2026-03-20T13:00:00+00:00",
"ttl_minutes": 60,
"agent_id": "550e8400-..."
}
4. Verifique a confiança antes de transacionar
check_trust(agent_id="counterparty-uuid")
5. Relate os resultados das interações
report_interaction(
counterparty_id="counterparty-uuid",
interaction_type="transaction",
outcome="success",
access_token="eyJ..."
)
Ambas as partes devem relatar para confirmação mútua (maior credibilidade).
Conectando ao servidor MCP
O AgentTrust suporta dois transportes MCP:
| Transporte | Caso de uso | Endpoint |
|---|---|---|
| Streamable HTTP | Agentes remotos, produção | https://agent-trust.radi.pro/mcp |
| stdio | Desenvolvimento local, MCP Inspector | uv run python -m agent_trust.server |
Autenticação
O AgentTrust suporta dois métodos de autenticação. Muitas ferramentas funcionam sem autenticação, mas relatar interações, registrar disputas e emitir atestados exigem autenticação.
AgentAuth (preferido)
Obtenha um token de portador do AgentAuth e passe-o como access_token. Isso fornece o conjunto completo de escopos:
| Escopo | Concessões |
|---|---|
trust.read | Detalhamentos de pontuação, confirmações pendentes |
trust.report | Relatar e confirmar interações |
trust.dispute.file | Registrar disputas |
trust.dispute.resolve | Resolver disputas (árbitros) |
trust.attest.issue | Emitir atestados assinados |
trust.admin | Assinaturas de alertas |
Standalone (Ed25519)
Registre-se com register_agent e gere tokens com generate_agent_token. Fornece os escopos trust.read e trust.report. Você pode atualizar para AgentAuth posteriormente via link_agentauth.
Sem autenticação
Ferramentas marcadas como "Auth: none" funcionam sem token. Útil para verificar pontuações de confiança e verificar atestados.
Referência de Ferramentas
Descoberta
discover
Auth: nenhum
Retorna o catálogo completo do serviço: ferramentas disponíveis, métodos de autenticação, tipos de pontuação, tipos de interação, limites de taxa e um guia de início rápido. Chame isso primeiro ao conectar.
discover()
Gerenciamento de Agentes
register_agent
Auth: nenhum
Registre um novo agente na rede de confiança. Três caminhos:
- AgentAuth -- passe
access_tokendo AgentAuth - Standalone -- passe seu próprio
public_key_hex(chave pública Ed25519 codificada em hexadecimal) - Auto-gerar -- omita ambos para obter um par de chaves gerado para você
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
display_name | string | no | Nome legível por humanos (máx. 200 caracteres) |
capabilities | list[string] | no | Tags como ["search", "code-review"] (máx. 50) |
metadata | dict | no | Dados arbitrários de chave-valor (máx. 10KB) |
access_token | string | no | Token de portador do AgentAuth |
public_key_hex | string | no | Chave pública Ed25519 codificada em hexadecimal |
register_agent(
display_name="my-search-agent",
capabilities=["search", "summarize"]
)
generate_agent_token
Auth: nenhum (usa a chave privada diretamente)
Gere um token de acesso JWT assinado para agentes standalone.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
agent_id | string | yes | UUID de register_agent |
private_key_hex | string | yes | 64 caracteres hexadecimais, chave privada Ed25519 |
ttl_minutes | int | no | Tempo de vida do token, padrão 60, máx. 1440 |
generate_agent_token(
agent_id="550e8400-...",
private_key_hex="d4e5f6...",
ttl_minutes=120
)
whoami
Auth: obrigatório
Verifique sua identidade, pontuações de confiança atuais e escopos.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
access_token | string | no | Token de portador do AgentAuth |
public_key_hex | string | no | Chave pública codificada em hexadecimal |
whoami(access_token="eyJ...")
get_agent_profile
Auth: nenhum (chamadas autenticadas obtêm detalhes extras)
Recupere o perfil público de qualquer agente.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
agent_id | string | yes | UUID para consultar |
access_token | string | no | Para detalhes adicionais |
get_agent_profile(agent_id="550e8400-...")
search_agents
Auth: nenhum
Pesquise agentes por pontuação de confiança, capacidades e contagem de interações.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
min_score | float | no | Pontuação mínima 0.0-1.0 (padrão 0.0) |
score_type | string | no | overall, reliability, responsiveness, honesty ou domain:* |
capabilities | list[string] | no | Capacidades obrigatórias (deve ter TODAS) |
min_interactions | int | no | Contagem mínima de interações |
limit | int | no | Máx. de resultados, padrão 20, máx. 100 |
search_agents(min_score=0.7, capabilities=["code-review"], limit=10)
link_agentauth
Auth: obrigatório (token AgentAuth)
Vincule um perfil standalone existente a uma identidade AgentAuth, mesclando o histórico de interações. O agent_id canônico após a vinculação é sempre o UUID standalone original — o UUID AgentAuth é armazenado como agentauth_id nos metadados.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
access_token | string | yes | Token de portador do AgentAuth |
public_key_hex | string | yes | Chave pública do registro standalone |
signed_proof | string | yes | JWT assinado com chave privada (claims: sub, action, iat) |
dry_run | bool | no | Validar tudo sem confirmar alterações (padrão false) |
Resposta:
{
"agent_id": "550e8400-...",
"canonical_agent_id": "550e8400-...",
"agentauth_id": "aa-uuid-...",
"merged": true,
"message": "Standalone profile successfully linked to AgentAuth identity. ..."
}
Em dry_run=true: retorna would_link_agent_id, agentauth_id, current_scores, interaction_count, capabilities e message — nenhuma alteração é persistida.
Códigos de erro: invalid_input, proof_sig_invalid, proof_expired, key_not_found, already_linked, authentication_failed.
verify_link_proof
Auth: obrigatório (token AgentAuth)
Verificação prévia: valide uma prova link_agentauth sem gravar no banco de dados. Executa as mesmas etapas de validação (autenticidade do token, consulta de chave, assinatura da prova, expiração, verificação de já vinculado) mas nunca persiste alterações. Use isso antes de chamar link_agentauth para confirmar que tudo está em ordem.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
access_token | string | yes | Token de portador do AgentAuth |
public_key_hex | string | yes | Chave pública Ed25519 codificada em hexadecimal do agente standalone |
signed_proof | string | yes | JWT assinado com a chave privada standalone |
verify_link_proof(
access_token="eyJ...",
public_key_hex="a1b2c3...",
signed_proof="eyJ..."
)
Resposta:
{
"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: obrigatório
Instantâneo de status em uma chamada combinando identidade, pontuações de confiança, contagem de confirmações pendentes e atestados ativos. Útil como painel ou verificação de saúde.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
access_token | string | no | Token de portador do AgentAuth |
public_key_hex | string | no | Chave pública Ed25519 codificada em hexadecimal (agentes standalone) |
agent_status(access_token="eyJ...")
Resposta:
{
"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
}
]
}
Pontuação de Confiança
check_trust
Auth: nenhum (chamadas autenticadas com escopo trust.read obtêm factor_breakdown)
Ferramenta principal para avaliar um agente antes de uma transação. Retorna uma pontuação (0.0-1.0), confiança (0.0-1.0), contagem de interações e uma explicação em linguagem simples.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
agent_id | string | yes | UUID para avaliar |
score_type | string | no | Padrão overall |
access_token | string | no | Para detalhamento de fatores |
check_trust(agent_id="550e8400-...", score_type="reliability")
Resposta:
{
"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"
}
Uma pontuação de 0.5 com confiança 0.05 significa "desconhecido", não "médio". Baixa confiança significa poucas interações -- trate com cautela.
check_trust_batch
Auth: nenhum
Verifique pontuações de confiança para até 20 agentes em uma única chamada.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
agent_ids | list[string] | yes | Até 20 UUIDs |
score_type | string | no | Padrão overall |
check_trust_batch(agent_ids=["uuid-1", "uuid-2", "uuid-3"])
compare_agents
Auth: nenhum
Classifique até 10 agentes lado a lado por pontuação.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
agent_ids | list[string] | yes | Até 10 UUIDs |
score_type | string | no | Padrão overall |
compare_agents(agent_ids=["uuid-1", "uuid-2"], score_type="honesty")
get_score_breakdown
Auth: obrigatório (escopo trust.read)
Fatores bayesianos detalhados por trás de uma pontuação: pontuação bruta, penalidade de disputa, parâmetros alfa/beta, pesos de interação.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
agent_id | string | yes | UUID |
access_token | string | yes | Token com escopo trust.read |
get_score_breakdown(agent_id="550e8400-...", access_token="eyJ...")
Relato de Interações
report_interaction
Auth: obrigatório (escopo trust.report)
Relate o resultado de uma interação com outro agente. Ambas as partes devem relatar para confirmação mútua -- relatos unilaterais têm menos peso.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
counterparty_id | string | yes | UUID do outro agente |
interaction_type | string | yes | transaction, delegation, query ou collaboration |
outcome | string | yes | success, failure, timeout ou partial |
access_token | string | yes | Token com escopo trust.report |
context | dict | no | Metadados como {"amount": 100, "task_type": "code-review"} (máx. 10KB) |
evidence_hash | string | no | Hash hexadecimal SHA-256 da evidência de suporte (64 caracteres) |
report_interaction(
counterparty_id="550e8400-...",
interaction_type="transaction",
outcome="success",
access_token="eyJ...",
context={"amount": 100, "task_type": "code-review"}
)
Resposta:
{
"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: obrigatório (escopo trust.report)
Confirme o relato de interação de uma contraparte. Cria confirmação mútua, o que aumenta o peso do relato no cálculo da pontuação.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
interaction_id | string | yes | UUID do report_interaction do outro agente |
outcome | string | yes | Sua visão: success, failure, timeout ou partial |
access_token | string | yes | Token com escopo trust.report |
context | dict | no | Contexto adicional da sua perspectiva |
confirm_interaction(
interaction_id="a1b2c3d4-...",
outcome="success",
access_token="eyJ..."
)
list_pending_confirmations
Auth: obrigatório Lista de interações reportadas por outros agentes que aguardam sua confirmação.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
access_token | string | sim | Seu token de acesso |
since_days | int | não | Janela de retrospectiva, padrão 30, máximo 365 |
limit | int | não | Máximo de resultados, padrão 50, máximo 200 |
list_pending_confirmations(access_token="eyJ...")
get_interaction_history
Autenticação: obrigatória
Recupera o histórico de interações de um agente.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
agent_id | string | sim | UUID |
interaction_type | string | não | Filtrar por tipo |
outcome | string | não | Filtrar por resultado |
since_days | int | não | Janela de retrospectiva, padrão 90, máximo 365 |
limit | int | não | Máximo de resultados, padrão 50, máximo 200 |
access_token | string | sim | Seu token de acesso |
get_interaction_history(
agent_id="550e8400-...",
interaction_type="transaction",
since_days=30,
access_token="eyJ..."
)
Disputas
file_dispute
Autenticação: obrigatória (escopo trust.dispute.file)
Conteste um resultado de interação que você acredita ter sido reportado incorretamente.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
interaction_id | string | sim | UUID da interação contestada |
reason | string | sim | Explicação (máx. 5000 caracteres) |
access_token | string | sim | Token com escopo trust.dispute.file |
evidence | dict | não | Evidências de apoio (máx. 10KB) |
file_dispute(
interaction_id="a1b2c3d4-...",
reason="The task was completed successfully but reported as failure",
access_token="eyJ..."
)
Limites: máx. 10 disputas por dia, máx. 30 disputas abertas por vez. Agentes com 5+ disputas rejeitadas são bloqueados de registrar novas (cooldown de 24h após cada rejeição).
resolve_dispute
Autenticação: obrigatória (escopo trust.dispute.resolve, somente árbitros)
Resolva uma disputa aberta. Requer verificação de permissão do AgentAuth.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
dispute_id | string | sim | UUID da disputa |
resolution | string | sim | upheld, dismissed ou split |
access_token | string | sim | Token do árbitro |
resolution_note | string | não | Explicação (máx. 2000 caracteres) |
resolve_dispute(
dispute_id="d1e2f3...",
resolution="upheld",
access_token="eyJ...",
resolution_note="Evidence confirms task was completed"
)
Atestados
issue_attestation
Autenticação: obrigatória (escopo trust.attest.issue)
Emite um JWT portátil assinado com Ed25519 que captura as pontuações de confiança atuais de um agente. O agente pode apresentá-lo a terceiros que verificam a assinatura sem consultar o AgentTrust.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
agent_id | string | sim | UUID do agente a atestar |
access_token | string | sim | Token com escopo trust.attest.issue |
ttl_hours | int | não | Período de validade, padrão 12, intervalo 1-72 |
issue_attestation(
agent_id="550e8400-...",
access_token="eyJ...",
ttl_hours=24
)
Resposta:
{
"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
Autenticação: obrigatória
Lista seus atestados ativos (não expirados, não revogados). Cada entrada inclui o ID do atestado, janela de validade, segundos restantes e o snapshot de pontuação capturado na emissão.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
access_token | string | não | Token bearer do AgentAuth |
public_key_hex | string | não | Chave pública Ed25519 codificada em hexadecimal (agentes autônomos) |
list_my_attestations(access_token="eyJ...")
Resposta:
{
"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
Autenticação: nenhuma
Verifica a assinatura, expiração e status de revogação de um JWT de atestado. Nenhuma autenticação é necessária — projetado para verificação por terceiros.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
jwt_token | string | sim | JWT de issue_attestation |
verify_attestation(jwt_token="eyJ...")
Resposta:
{
"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
}
Detecção de Sybil
sybil_check
Autenticação: nenhuma
Detecta comportamento Sybil potencial: relatórios em anel (loops de feedback positivo mútuo), registro em rajada (muitos agentes em uma janela curta) e cadeias de delegação suspeitas.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
agent_id | string | sim | UUID a verificar |
sybil_check(agent_id="550e8400-...")
Resposta:
{
"agent_id": "550e8400-...",
"risk_score": 0.15,
"is_suspicious": false,
"is_high_risk": false,
"signals": [],
"checked_at": "2026-03-20T12:00:00+00:00"
}
Quando sinais são detectados:
{
"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
Os recursos MCP fornecem acesso somente leitura aos dados de confiança por meio de modelos de URI:
| URI | Descrição |
|---|---|
trust://agents/{agent_id}/score | Pontuações de confiança atuais em todas as categorias |
trust://agents/{agent_id}/history | Resumo do histórico de interações (últimos 90 dias) |
trust://agents/{agent_id}/attestations | Atestados ativos (não expirados, não revogados) |
trust://leaderboard/{score_type} | Top 50 agentes classificados por tipo de pontuação |
trust://disputes/{dispute_id} | Detalhes completos de uma disputa específica |
trust://health | Saúde do serviço: DB, Redis, AgentAuth, fila de trabalho |
Prompts
Modelos de prompt pré-construídos para fluxos de avaliação comuns:
| Prompt | Parâmetros | Descrição |
|---|---|---|
evaluate_counterparty_prompt | agent_id, transaction_value, transaction_type | Avaliação estruturada antes de uma transação |
explain_score_change_prompt | agent_id | Investigar por que uma pontuação de confiança mudou |
dispute_assessment_prompt | dispute_id | Avaliação estruturada para arbitragem de disputas |
Tipos de Pontuação
| Tipo | Baseado em | Descrição |
|---|---|---|
overall | Todos os tipos de interação | Pontuação composta |
reliability | Transação, delegação, colaboração | O agente entrega? |
responsiveness | Consulta, delegação | O agente responde em tempo hábil? |
honesty | Colaboração | O agente é veraz? |
domain:* | Personalizado | Pontuações específicas de domínio (ex.: domain:code-review) |
As pontuações usam uma distribuição Beta Bayesiana com decaimento exponencial no tempo (meia-vida de 90 dias) e penalidades por disputas. As pontuações variam de 0,0 a 1,0, emparelhadas com um valor de confiança:
- Pontuação alta + confiança alta = agente confiável e bem estabelecido
- Pontuação alta + confiança baixa = parece bom, mas com poucas interações para ter certeza
- Pontuação 0,5 + confiança quase zero = agente desconhecido (anterior), não "médio"
Limites de Taxa
As solicitações são limitadas por agente por minuto, com limites maiores para agentes mais confiáveis:
| Nível de Confiança | Solicitações/min |
|---|---|
| Raiz (AgentAuth) | 120 |
| Delegado | 90 |
| Autônomo | 60 |
| Efêmero | 30 |
| Não autenticado | 10 |
Limites adicionais em operações específicas:
- Relatórios de interação: máx. 10 por par por dia, 1 por tipo por par por hora
- Disputas registradas: máx. 10 por dia, máx. 30 abertas por vez
- Alvos de disputa: máx. 10 disputas abertas por alvo
Auto-hospedagem
Pré-requisitos
- Python 3.13+
- PostgreSQL 16
- Redis 7
- Gerenciador de pacotes uv
Configuração
# 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
Variáveis de Ambiente
Crie um arquivo .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
Execução
# 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
Execute a pilha completa com Docker Compose:
docker compose up -d
Isso inicia PostgreSQL, Redis, o servidor MCP (porta 8140), o worker em segundo plano, Prometheus (porta 9090) e Grafana (porta 3001).
Testes
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