Documentação
Servidor MCPFerramentas

Ferramentas

As dez ferramentas do servidor MCP da Minha CNH — request_report, get_report, wait_for_report, ask_doutor_multas, calculate_points e mais, com input, output e exemplos tools/call.

O servidor expõe dez ferramentas, com paridade total com a API REST. A descoberta (tools/list) é pública; a execução (tools/call) exige autenticação — ver Conectar.

FerramentaDescriçãoConsome crédito
request_reportSolicita um relatório de CNH para um CPFSim, 1 crédito por relatório novo
get_reportConsulta um relatório pelo reportIdNão
wait_for_reportEspera (com tempo limite) um relatório terminar e já retorna o resultadoNão
find_report_by_external_refRecupera um relatório pelo seu externalRefNão
ask_doutor_multasPergunta genérica sobre legislação de trânsito, respondida via RAG sobre o CTBNão
get_credit_balanceConsulta o saldo de crédito pós-pago da organizaçãoNão
get_usageConsulta o uso corrente contra os rate limits configuradosNão
list_reportsLista os relatórios mais recentes da organizaçãoNão
lookup_violation_codeConsulta um código de infração RENAINFNão
calculate_pointsCalcula a situação de pontos na CNH sob a Lei 14.071Não

Próxima fronteira: ask_doutor_multas com contexto de relatório

ask_doutor_multas já aceita state e reportId no input, mas ambos são reservados e ignorados nesta versão — a busca é sempre genérica sobre CTB/legislação estadual, sem viés de UF e sem contexto de um relatório específico (nenhum PII entra no prompt). Um modo report-grounded, que cruza a pergunta com o diagnóstico de um relatório já entregue, é a única ferramenta ainda em desenho — e continuará gratuita nesta forma genérica quando chegar.

request_report

Solicita um novo relatório de CNH. Assíncrono: a resposta chega imediatamente com o reportId, e o diagnóstico é entregue depois (ver Modelo assíncrono).

Idempotente por (CPF, externalRef) dentro de uma janela de 1 hora: repetir a mesma chamada nesse intervalo retorna o relatório já existente, sem cobrar de novo. Passada a hora, a mesma externalRef cria — e cobra — um novo relatório. Para recuperar um relatório antigo pela sua referência (sem janela), use find_report_by_external_ref; os detalhes estão em Idempotência.

Input

CampoTipoObrigatórioDescrição
cpfstringSimCPF brasileiro, 11 dígitos (formatação é ignorada)
externalRefstringNãoSua referência interna; também usada como chave de idempotência

Output

CampoTipoDescrição
reportIdstringIdentificador do relatório, use em get_report
statusstringPROCESSING no momento da criação
driverobjectDados do motorista resolvido pelo CPF (id, cpf, nome, número/categoria da CNH, validade, UF de emissão)
statusUrlstringCaminho REST equivalente (GET /api/v1/reports/{id}) para consultar o mesmo relatório
estimatedDeliveryAtstringEstimativa ISO 8601 de entrega
webhookConfiguredbooleanSe a organização tem webhook configurado

Exemplo — tools/call

curl -X POST "https://minhacnh.doutormultas.com.br/api/mcp" \
  -H "Authorization: Bearer cnh_live_<prefix>_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "request_report",
      "arguments": {
        "cpf": "123.456.789-09",
        "externalRef": "driver-42"
      }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [{ "type": "text", "text": "{ \"reportId\": \"clxyz...\", ... }" }],
    "structuredContent": {
      "reportId": "clxyz...",
      "status": "PROCESSING",
      "driver": {
        "id": "cldrv...",
        "cpf": "123.456.789-09",
        "name": "Maria Silva",
        "licenseNumber": "12345678900",
        "licenseCategory": "AB",
        "expiryDate": "2027-03-15T00:00:00Z",
        "issueState": "SP"
      },
      "statusUrl": "/api/v1/reports/clxyz...",
      "estimatedDeliveryAt": "2026-06-01T12:05:00Z",
      "webhookConfigured": true
    }
  }
}

get_report

Consulta um relatório pelo reportId. Enquanto o relatório está em processamento, diagnosis vem null e status é PROCESSING. Um reportId de outra organização retorna not-found — nunca vaza dados de outro tenant.

Input

CampoTipoObrigatórioDescrição
reportIdstringSimIdentificador retornado por request_report

Output

O mesmo shape do relatório serializado pela API REST — ver Shape do Payload, incluindo o campo opcional aiAnalysis (análise por IA) quando disponível.

Exemplo — tools/call

curl -X POST "https://minhacnh.doutormultas.com.br/api/mcp" \
  -H "Authorization: Bearer cnh_live_<prefix>_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 4,
    "method": "tools/call",
    "params": {
      "name": "get_report",
      "arguments": { "reportId": "clxyz..." }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "content": [{ "type": "text", "text": "{ \"reportId\": \"clxyz...\", \"status\": \"DELIVERED\", ... }" }],
    "structuredContent": {
      "reportId": "clxyz...",
      "status": "DELIVERED",
      "driver": { "id": "cldrv...", "cpf": "123.456.789-09" },
      "diagnosis": { "..." : "..." }
    }
  }
}

wait_for_report

Espera pelo relatório terminar e já retorna o resultado, poupando um loop de polling manual com get_report. A espera é limitada: internamente faz polling a cada poucos segundos e, se o tempo limite passar antes de status virar DELIVERED ou ERROR, retorna imediatamente uma resposta de PROCESSING em vez de continuar bloqueado — nunca fica esperando indefinidamente. Não consome crédito (a cobrança já aconteceu em request_report).

Input

CampoTipoObrigatórioDescrição
reportIdstringSimIdentificador retornado por request_report
timeoutSecondsnumberNãoTempo máximo de espera, de 1 a 240 (padrão 120)

Output

Se o relatório terminar dentro do tempo limite, o mesmo shape retornado por get_report (status: "DELIVERED" ou "ERROR" com o diagnóstico). Se o tempo limite for atingido antes disso:

CampoTipoDescrição
reportIdstringO mesmo reportId recebido no input
statusstringSempre PROCESSING neste caso
pollAfterSecondsnumberSugestão de quanto esperar antes de chamar wait_for_report ou get_report de novo

Exemplo — tools/call

curl -X POST "https://minhacnh.doutormultas.com.br/api/mcp" \
  -H "Authorization: Bearer cnh_live_<prefix>_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 5,
    "method": "tools/call",
    "params": {
      "name": "wait_for_report",
      "arguments": { "reportId": "clxyz...", "timeoutSeconds": 60 }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 5,
  "result": {
    "content": [{ "type": "text", "text": "{ \"reportId\": \"clxyz...\", \"status\": \"PROCESSING\", \"pollAfterSeconds\": 30 }" }],
    "structuredContent": {
      "reportId": "clxyz...",
      "status": "PROCESSING",
      "pollAfterSeconds": 30
    }
  }
}

find_report_by_external_ref

Recupera um relatório usando o seu próprio externalRef (a chave de idempotência que você passou para request_report) — útil quando o reportId se perdeu do lado do integrador, mas a referência interna (ex.: o ID do motorista no seu sistema) ainda é conhecida. Retorna o relatório mais recente que casa com o externalRef, no mesmo shape do get_report. Um externalRef que só existe em outra organização retorna not-found — nunca vaza dados de outro tenant. Gratuita.

Input

CampoTipoObrigatórioDescrição
externalRefstringSimA referência que você passou em request_report

Output

O mesmo shape do relatório serializado pela API REST — ver Shape do Payload, incluindo o campo opcional aiAnalysis (análise por IA) quando disponível.

Exemplo — tools/call

curl -X POST "https://minhacnh.doutormultas.com.br/api/mcp" \
  -H "Authorization: Bearer cnh_live_<prefix>_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 6,
    "method": "tools/call",
    "params": {
      "name": "find_report_by_external_ref",
      "arguments": { "externalRef": "driver-42" }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 6,
  "result": {
    "content": [{ "type": "text", "text": "{ \"reportId\": \"clxyz...\", \"status\": \"DELIVERED\", ... }" }],
    "structuredContent": {
      "reportId": "clxyz...",
      "status": "DELIVERED",
      "driver": { "id": "cldrv...", "cpf": "123.456.789-09" },
      "diagnosis": { "..." : "..." }
    }
  }
}

ask_doutor_multas

Pergunta genérica sobre legislação de trânsito brasileira (CTB e legislação estadual), respondida com busca (RAG) sobre a mesma base de conhecimento do chatbot Doutor Multas. Não conhece nenhum motorista ou relatório específico — não envie CPF ou outro dado pessoal na pergunta, a ferramenta não usa PII e a resposta nunca cita dados de um relatório. Gratuita, mas com rate limit próprio por organização (o mesmo perMinute configurado para a API, contado separadamente do uso pago de relatórios) — ver get_usage.

Input

CampoTipoObrigatórioDescrição
questionstringSimPergunta em texto livre, 1 a 1000 caracteres
statestring (2 letras)NãoUF, ex. "SP" — reservado para uma futura busca com viés de estado; ignorado nesta versão. Se enviado, tem de ter exatamente 2 caracteres
reportIdstringNãoReservado para um futuro modo com contexto de relatório; ignorado nesta versão

Output

CampoTipoDescrição
answerstringResposta em pt-BR gerada a partir do contexto recuperado
citationsstring[]IDs das fontes usadas para compor a resposta
disclaimerstringAviso de que é informação geral, não aconselhamento jurídico

Exemplo — tools/call

curl -X POST "https://minhacnh.doutormultas.com.br/api/mcp" \
  -H "Authorization: Bearer cnh_live_<prefix>_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 7,
    "method": "tools/call",
    "params": {
      "name": "ask_doutor_multas",
      "arguments": { "question": "Quantos pontos tira uma multa por excesso de velocidade acima de 50%?" }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "content": [{ "type": "text", "text": "{ \"answer\": \"...\", \"citations\": [\"ctb-art-218\"], \"disclaimer\": \"...\" }" }],
    "structuredContent": {
      "answer": "Excesso de velocidade acima de 50% é infração gravíssima e soma 7 pontos na CNH...",
      "citations": ["ctb-art-218"],
      "disclaimer": "Informação geral sobre legislação de trânsito, não constitui aconselhamento jurídico."
    }
  }
}

get_credit_balance

Saldo de crédito pós-pago corrente da organização, em centavos. Não recebe argumentos.

Input — nenhum ({}).

Output

CampoTipoDescrição
balancenumberSaldo atual em centavos (negativo = saldo devedor consumido da linha de crédito)
creditLineLimitnumberPiso negativo que o saldo pode atingir antes de novos relatórios serem bloqueados (0 = sem linha de crédito)
currencystringSempre "BRL"

Exemplo — tools/call

curl -X POST "https://minhacnh.doutormultas.com.br/api/mcp" \
  -H "Authorization: Bearer cnh_live_<prefix>_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 8,
    "method": "tools/call",
    "params": { "name": "get_credit_balance", "arguments": {} }
  }'
{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {
    "content": [{ "type": "text", "text": "{ \"balance\": -1250, \"creditLineLimit\": -50000, \"currency\": \"BRL\" }" }],
    "structuredContent": { "balance": -1250, "creditLineLimit": -50000, "currency": "BRL" }
  }
}

get_usage

Uso da API de relatórios nas janelas corrente por minuto e por dia, contra os rate limits configurados para a organização, além do preço por relatório em centavos. Não recebe argumentos.

Input — nenhum ({}).

Output

CampoTipoDescrição
perMinuteobject{ used, limit } — chamadas na janela do último minuto
perDayobject{ used, limit } — chamadas na janela das últimas 24h
pricePerReportnumberPreço por relatório novo, em centavos

Exemplo — tools/call

curl -X POST "https://minhacnh.doutormultas.com.br/api/mcp" \
  -H "Authorization: Bearer cnh_live_<prefix>_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 9,
    "method": "tools/call",
    "params": { "name": "get_usage", "arguments": {} }
  }'
{
  "jsonrpc": "2.0",
  "id": 9,
  "result": {
    "content": [{ "type": "text", "text": "{ \"perMinute\": { \"used\": 3, \"limit\": 60 }, \"perDay\": { \"used\": 120, \"limit\": 5000 }, \"pricePerReport\": 1990 }" }],
    "structuredContent": {
      "perMinute": { "used": 3, "limit": 60 },
      "perDay": { "used": 120, "limit": 5000 },
      "pricePerReport": 1990
    }
  }
}

list_reports

Lista os relatórios mais recentes da organização, do mais novo para o mais antigo. O CPF vem mascarado (123.***.***-09) — para o relatório completo, use get_report com o reportId.

O filtro status é aplicado depois de buscar a página

status filtra client-side, sobre a página já buscada com limit. Isso significa que o resultado pode vir com menos itens do que limit quando um filtro de status é usado — se você espera N relatórios com um status específico, peça um limit maior do que N para compensar itens de outros status que forem descartados do lado do servidor.

Input

CampoTipoObrigatórioDescrição
limitnumberNãoQuantos relatórios buscar antes do filtro de status, de 1 a 100 (padrão 20)
statusstringNãoFiltra por PROCESSING, DELIVERED ou ERROR — ver aviso acima

Output

CampoTipoDescrição
reportsarrayLista de { reportId, status, externalRef, cpfMasked, createdAt, deliveredAt }

Exemplo — tools/call

curl -X POST "https://minhacnh.doutormultas.com.br/api/mcp" \
  -H "Authorization: Bearer cnh_live_<prefix>_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 10,
    "method": "tools/call",
    "params": {
      "name": "list_reports",
      "arguments": { "limit": 10, "status": "DELIVERED" }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 10,
  "result": {
    "content": [{ "type": "text", "text": "{ \"reports\": [ { \"reportId\": \"clxyz...\", \"status\": \"DELIVERED\", ... } ] }" }],
    "structuredContent": {
      "reports": [
        {
          "reportId": "clxyz...",
          "status": "DELIVERED",
          "externalRef": "driver-42",
          "cpfMasked": "123.***.***-09",
          "createdAt": "2026-06-01T12:00:00Z",
          "deliveredAt": "2026-06-01T12:05:00Z"
        }
      ]
    }
  }
}

lookup_violation_code

Consulta um código de infração RENAINF (ex.: "5010", ou com desdobramento "5029-1") e retorna descrição, artigo do CTB, pontos, categoria de gravidade, multiplicador, parte responsável e órgão competente. Se o código não existir no dataset, cai para uma busca sem o desdobramento antes de retornar not-found. Gratuita.

Sem valores de multa em R$

O dataset não contém valores de multa em reais — apenas pontos, artigo do CTB e metadados de gravidade. Não espere um campo de valor monetário na resposta; para o valor da multa, consulte a fonte oficial (DETRAN/SENATRAN) do código.

Input

CampoTipoObrigatórioDescrição
codestringSimCódigo RENAINF, ex.: "5010" ou "5029-1"
desdobnumberNãoVariante de desdobramento, quando aplicável

Output

CampoTipoDescrição
codestringCódigo RENAINF consultado
descriptionstringDescrição da infração
ctbArticlestringArtigo do Código de Trânsito Brasileiro
pointsnumber | nullPontos na CNH
categorystring | nullGravidade: leve, media, grave ou gravissima
multipliernumber | nullMultiplicador aplicado à gravidade base
infratorstringParte responsável pela infração (ex.: Condutor)
orgaoCompetentestringÓrgão competente para autuação
isAutosuspensionbooleanSe a infração gera suspensão automática da CNH
suspensionMonthsnumberMeses de suspensão, presente apenas quando isAutosuspension é true

Exemplo — tools/call

curl -X POST "https://minhacnh.doutormultas.com.br/api/mcp" \
  -H "Authorization: Bearer cnh_live_<prefix>_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 11,
    "method": "tools/call",
    "params": {
      "name": "lookup_violation_code",
      "arguments": { "code": "5010" }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 11,
  "result": {
    "content": [{ "type": "text", "text": "{ \"code\": \"5010\", \"description\": \"Dirigir veículo sem possuir CNH ou Permissão para Dirigir\", ... }" }],
    "structuredContent": {
      "code": "5010",
      "description": "Dirigir veículo sem possuir CNH ou Permissão para Dirigir",
      "ctbArticle": "162, I",
      "points": 7,
      "category": "gravissima",
      "multiplier": 3,
      "infrator": "Condutor",
      "orgaoCompetente": "ESTADUAL/RODOV",
      "isAutosuspension": false
    }
  }
}

calculate_points

Calcula a situação de pontos na CNH sob a Lei 14.071/2020: o limite de pontos aplicável, quanto resta até o limite, o percentual já usado e uma faixa de risco. O limite depende do perfil do condutor:

  • Condutor EAR (Exerce Atividade Remunerada) — limite sempre 40, independente de gravíssimas.
  • 2 ou mais infrações gravíssimas no período — limite 20.
  • 1 infração gravíssima — limite 30.
  • 0 infrações gravíssimas — limite 40.

O riskLevel é derivado do percentual usado: critical a partir de 90%, high a partir de 75%, medium a partir de 50%, e low abaixo disso. Gratuita.

Input

CampoTipoObrigatórioDescrição
currentPointsnumberSimPontos acumulados na janela móvel de 12 meses
gravissimasCountnumberSimQuantas infrações gravíssimas estão nessa janela
isEARbooleanNãoSe o condutor tem EAR (mantém o limite em 40)

Output

CampoTipoDescrição
limitnumberLimite de pontos aplicável
currentPointsnumberPontos informados (limitados a 0–100)
remainingPointsnumberPontos restantes até o limite
percentagenumberPercentual do limite já utilizado
riskLevelstringlow, medium, high ou critical
isOverLimitbooleanSe os pontos atuais já atingiram ou ultrapassaram o limite

Exemplo — tools/call

curl -X POST "https://minhacnh.doutormultas.com.br/api/mcp" \
  -H "Authorization: Bearer cnh_live_<prefix>_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 12,
    "method": "tools/call",
    "params": {
      "name": "calculate_points",
      "arguments": { "currentPoints": 18, "gravissimasCount": 2 }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 12,
  "result": {
    "content": [{ "type": "text", "text": "{ \"limit\": 20, \"currentPoints\": 18, \"remainingPoints\": 2, \"percentage\": 90, \"riskLevel\": \"critical\", \"isOverLimit\": false }" }],
    "structuredContent": {
      "limit": 20,
      "currentPoints": 18,
      "remainingPoints": 2,
      "percentage": 90,
      "riskLevel": "critical",
      "isOverLimit": false
    }
  }
}