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.
| Ferramenta | Descrição | Consome crédito |
|---|---|---|
request_report | Solicita um relatório de CNH para um CPF | Sim, 1 crédito por relatório novo |
get_report | Consulta um relatório pelo reportId | Não |
wait_for_report | Espera (com tempo limite) um relatório terminar e já retorna o resultado | Não |
find_report_by_external_ref | Recupera um relatório pelo seu externalRef | Não |
ask_doutor_multas | Pergunta genérica sobre legislação de trânsito, respondida via RAG sobre o CTB | Não |
get_credit_balance | Consulta o saldo de crédito pós-pago da organização | Não |
get_usage | Consulta o uso corrente contra os rate limits configurados | Não |
list_reports | Lista os relatórios mais recentes da organização | Não |
lookup_violation_code | Consulta um código de infração RENAINF | Não |
calculate_points | Calcula a situação de pontos na CNH sob a Lei 14.071 | Nã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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
cpf | string | Sim | CPF brasileiro, 11 dígitos (formatação é ignorada) |
externalRef | string | Não | Sua referência interna; também usada como chave de idempotência |
Output
| Campo | Tipo | Descrição |
|---|---|---|
reportId | string | Identificador do relatório, use em get_report |
status | string | PROCESSING no momento da criação |
driver | object | Dados do motorista resolvido pelo CPF (id, cpf, nome, número/categoria da CNH, validade, UF de emissão) |
statusUrl | string | Caminho REST equivalente (GET /api/v1/reports/{id}) para consultar o mesmo relatório |
estimatedDeliveryAt | string | Estimativa ISO 8601 de entrega |
webhookConfigured | boolean | Se 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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
reportId | string | Sim | Identificador 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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
reportId | string | Sim | Identificador retornado por request_report |
timeoutSeconds | number | Não | Tempo 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:
| Campo | Tipo | Descrição |
|---|---|---|
reportId | string | O mesmo reportId recebido no input |
status | string | Sempre PROCESSING neste caso |
pollAfterSeconds | number | Sugestã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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
externalRef | string | Sim | A 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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
question | string | Sim | Pergunta em texto livre, 1 a 1000 caracteres |
state | string (2 letras) | Não | UF, ex. "SP" — reservado para uma futura busca com viés de estado; ignorado nesta versão. Se enviado, tem de ter exatamente 2 caracteres |
reportId | string | Não | Reservado para um futuro modo com contexto de relatório; ignorado nesta versão |
Output
| Campo | Tipo | Descrição |
|---|---|---|
answer | string | Resposta em pt-BR gerada a partir do contexto recuperado |
citations | string[] | IDs das fontes usadas para compor a resposta |
disclaimer | string | Aviso 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
| Campo | Tipo | Descrição |
|---|---|---|
balance | number | Saldo atual em centavos (negativo = saldo devedor consumido da linha de crédito) |
creditLineLimit | number | Piso negativo que o saldo pode atingir antes de novos relatórios serem bloqueados (0 = sem linha de crédito) |
currency | string | Sempre "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
| Campo | Tipo | Descrição |
|---|---|---|
perMinute | object | { used, limit } — chamadas na janela do último minuto |
perDay | object | { used, limit } — chamadas na janela das últimas 24h |
pricePerReport | number | Preç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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
limit | number | Não | Quantos relatórios buscar antes do filtro de status, de 1 a 100 (padrão 20) |
status | string | Não | Filtra por PROCESSING, DELIVERED ou ERROR — ver aviso acima |
Output
| Campo | Tipo | Descrição |
|---|---|---|
reports | array | Lista 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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
code | string | Sim | Código RENAINF, ex.: "5010" ou "5029-1" |
desdob | number | Não | Variante de desdobramento, quando aplicável |
Output
| Campo | Tipo | Descrição |
|---|---|---|
code | string | Código RENAINF consultado |
description | string | Descrição da infração |
ctbArticle | string | Artigo do Código de Trânsito Brasileiro |
points | number | null | Pontos na CNH |
category | string | null | Gravidade: leve, media, grave ou gravissima |
multiplier | number | null | Multiplicador aplicado à gravidade base |
infrator | string | Parte responsável pela infração (ex.: Condutor) |
orgaoCompetente | string | Órgão competente para autuação |
isAutosuspension | boolean | Se a infração gera suspensão automática da CNH |
suspensionMonths | number | Meses 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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
currentPoints | number | Sim | Pontos acumulados na janela móvel de 12 meses |
gravissimasCount | number | Sim | Quantas infrações gravíssimas estão nessa janela |
isEAR | boolean | Não | Se o condutor tem EAR (mantém o limite em 40) |
Output
| Campo | Tipo | Descrição |
|---|---|---|
limit | number | Limite de pontos aplicável |
currentPoints | number | Pontos informados (limitados a 0–100) |
remainingPoints | number | Pontos restantes até o limite |
percentage | number | Percentual do limite já utilizado |
riskLevel | string | low, medium, high ou critical |
isOverLimit | boolean | Se 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
}
}
}Conectar
Como conectar ao servidor MCP da Minha CNH — API key server-to-server ou OAuth por usuário para clientes de IA desktop (Claude Desktop, Claude Cowork, ChatGPT).
Erros
Tratamento de erros do servidor MCP da Minha CNH — erros de protocolo/transporte (JSON-RPC) versus erros de negócio (resultado da ferramenta).