# Plano de Implementação: Consolidar Tools do MCP Server

## Visão Geral

Reduzir de 33 para ~24 tools aplicando:
1. Helper compartilhado para seleção de modelo
2. Remoção de 3 tools redundantes
3. Consolidação de listagens, perguntas e timeline

## Fase 1: Helper de Seleção Inteligente de Modelo

Criar helper reutilizável para todas as tools de análise.

### Tarefas

- [x] Criar arquivo `tools/_helpers.py`
- [x] Implementar `analisar_com_selecao_modelo()`

### Detalhes Técnicos

**Arquivo:** `tools/_helpers.py`

```python
from __future__ import annotations

from services.llm_service import get_llm_service

LIMITE_GEMINI = 3_000_000  # 3M chars
LIMITE_GROK = 7_000_000    # 7M chars


async def analisar_com_selecao_modelo(
    contexto: str,
    prompt: str,
    max_tokens: int = 2000,
) -> tuple[str, str]:
    """
    Analisa com seleção automática de modelo pelo tamanho.

    Returns:
        tuple: (resposta, modelo_usado)
    """
    llm = get_llm_service()

    tamanho = len(contexto)
    if tamanho > LIMITE_GEMINI:
        modelo = "grok-fast"
    else:
        modelo = "gemini-flash"

    resposta = await llm.query(
        pergunta=prompt,
        contexto=contexto,
        model=modelo,
        max_tokens=max_tokens,
    )

    return resposta.text, modelo
```

---

## Fase 2: Aplicar Helper em Análise Jurídica

Substituir model hardcoded pelo helper em todas as 8 tools.

### Tarefas

- [x] Atualizar `situacao_atual` para usar helper
- [x] Atualizar `extrair_pedidos` para usar helper
- [x] Atualizar `analisar_decisao` para usar helper
- [x] Atualizar `identificar_prazos` para usar helper
- [x] Atualizar `analise_risco` para usar helper
- [x] Atualizar `argumentos_por_polo` para usar helper
- [x] Atualizar `cronologia_detalhada` para usar helper
- [x] Atualizar `parecer_juridico` para usar helper

### Detalhes Técnicos

**Arquivo:** `tools/analise_juridica.py`

**Antes (em cada tool):**
```python
resposta = await llm.query(
    pergunta=prompt,
    contexto=contexto,
    model="gemini-flash",  # HARDCODED
    max_tokens=2000,
)
return {"analise": resposta.text}
```

**Depois:**
```python
from tools._helpers import analisar_com_selecao_modelo

resposta, modelo = await analisar_com_selecao_modelo(
    contexto=contexto,
    prompt=prompt,
    max_tokens=2000,
)
return {"analise": resposta, "modelo": modelo}
```

---

## Fase 3: Adicionar Snippet em Buscas

Retornar contexto ao redor do termo encontrado.

### Tarefas

- [x] Adicionar parâmetro `com_snippet: bool = True` em `buscar_em_documentos`
- [x] Implementar extração de snippet (200 chars ao redor do termo)

### Detalhes Técnicos

**Arquivo:** `tools/documentos.py`

```python
def _extrair_snippet(conteudo: str, termo: str, chars: int = 200) -> str:
    """Extrai snippet ao redor do termo encontrado."""
    termo_lower = termo.lower()
    conteudo_lower = conteudo.lower()

    pos = conteudo_lower.find(termo_lower)
    if pos == -1:
        return conteudo[:chars] + "..."

    inicio = max(0, pos - chars // 2)
    fim = min(len(conteudo), pos + len(termo) + chars // 2)

    snippet = conteudo[inicio:fim]
    if inicio > 0:
        snippet = "..." + snippet
    if fim < len(conteudo):
        snippet = snippet + "..."

    return snippet


@mcp.tool
async def buscar_em_documentos(
    termo: str,
    numero_processo: str | None = None,
    limite: int = 10,
    com_snippet: bool = True,  # NOVO
) -> dict[str, Any]:
    # ... busca existente ...

    resultados = []
    for r in response.data:
        doc = {
            "id": r.get("id"),
            "tipo": r.get("tipo"),
            "nome": r.get("nome") or "",
            "processo": r.get("numero_processo"),
        }
        if com_snippet and r.get("conteudo"):
            doc["snippet"] = _extrair_snippet(r["conteudo"], termo)
        resultados.append(doc)

    return {"total": len(resultados), "documentos": resultados}
```

---

## Fase 4: Deletar Tools Redundantes

Remover 3 tools que não agregam valor.

### Tarefas

- [x] Deletar `ultima_movimentacao` de `tools/timeline.py`
- [x] Deletar `modelos_disponiveis` de `tools/queries.py`
- [x] Deletar `tipos_movimentacoes` de `tools/timeline.py`
- [x] Remover imports e registros no `__init__.py`

### Detalhes Técnicos

**Arquivo:** `tools/timeline.py`

Deletar função `ultima_movimentacao` (linhas ~126-155)
Deletar função `tipos_movimentacoes` (linhas ~158-178)

**Arquivo:** `tools/queries.py`

Deletar função `modelos_disponiveis` (linhas ~139-153)

---

## Fase 5: Consolidar Listagens de Documentos

4 tools → 1 parametrizável.

### Tarefas

- [x] Criar nova `listar_documentos` unificada [complexo]
- [x] Deletar `preview_documentos`
- [x] Deletar `documentos_recentes`
- [x] Deletar `listar_nomes_documentos`

### Detalhes Técnicos

**Arquivo:** `tools/documentos.py`

```python
@mcp.tool
async def listar_documentos(
    numero_processo: str,
    limite: int = 30,
    ordenar_por: str = "data",      # "data" | "tipo" | "sequencia"
    incluir_preview: bool = False,   # True = preview 500 chars
    formato: str = "completo",       # "completo" | "compacto" | "agrupado"
    ctx: Context = None,
) -> dict[str, Any]:
    """
    Lista documentos de um processo com opções flexíveis.

    Args:
        numero_processo: Número do processo
        limite: Máximo de documentos (default 30, max 100)
        ordenar_por: "data" (recentes primeiro), "tipo", "sequencia"
        incluir_preview: Se True, inclui preview de 500 chars
        formato: "completo" (lista), "compacto" (sem preview), "agrupado" (por tipo)

    Substitui: preview_documentos, documentos_recentes, listar_nomes_documentos
    """
    client = get_supabase_client()
    limite = min(limite, MAX_DOCS_LISTA)

    # Buscar documentos
    campos = "id, sequencia, nome, tipo_nome, data_juntada, tamanho_texto"
    if incluir_preview:
        campos += ", conteudo"

    query = client._client.table("documents").select(campos).eq("numero_processo", numero_processo)

    # Ordenação
    if ordenar_por == "data":
        query = query.order("data_juntada", desc=True)
    elif ordenar_por == "tipo":
        query = query.order("tipo_nome").order("sequencia", desc=True)
    else:
        query = query.order("sequencia", desc=True)

    response = query.limit(limite).execute()
    docs = response.data or []

    # Formatar resultado
    if formato == "agrupado":
        por_tipo = {}
        for d in docs:
            tipo = d.get("tipo_nome") or "Outros"
            if tipo not in por_tipo:
                por_tipo[tipo] = []
            item = {
                "seq": d.get("sequencia"),
                "nome": d.get("nome"),
                "data": str(d.get("data_juntada"))[:10] if d.get("data_juntada") else None,
                "chars": d.get("tamanho_texto"),
            }
            por_tipo[tipo].append(item)
        return {"numero_processo": numero_processo, "total": len(docs), "por_tipo": por_tipo}

    # Formato completo ou compacto
    resultados = []
    for d in docs:
        item = {
            "id": d.get("id"),
            "sequencia": d.get("sequencia"),
            "tipo": d.get("tipo_nome"),
            "nome": d.get("nome") or "",
            "data_juntada": d.get("data_juntada"),
            "tamanho": d.get("tamanho_texto"),
        }
        if incluir_preview and d.get("conteudo"):
            item["preview"] = d["conteudo"][:500]
        resultados.append(item)

    return {"total": len(resultados), "documentos": resultados}
```

---

## Fase 6: Consolidar Perguntas

3 tools → 1 parametrizável.

### Tarefas

- [x] Criar nova `perguntar` unificada em queries.py
- [x] Deletar `sobre_processo` de processos.py
- [x] Atualizar `resumo_processo` para usar a nova perguntar (ou deletar)

### Detalhes Técnicos

**Arquivo:** `tools/queries.py`

```python
@mcp.tool
async def perguntar(
    numero_processo: str,
    pergunta: str = "Faça uma análise completa deste processo",
    modelo: str | None = None,  # None = seleção automática
    ctx: Context = None,
) -> dict[str, Any]:
    """
    Pergunta sobre um processo usando LLM.

    Args:
        numero_processo: Número do processo
        pergunta: Pergunta em linguagem natural
        modelo: "gemini-flash", "grok-fast" ou None (automático)

    Substitui: sobre_processo, perguntar (antiga)
    """
    from tools._helpers import analisar_com_selecao_modelo

    client = get_supabase_client()
    contexto = await client.obter_contexto_processo(numero_processo)

    if not contexto:
        return {"erro": "Processo não encontrado", "sucesso": False}

    if modelo:
        llm = get_llm_service()
        resposta = await llm.query(
            pergunta=pergunta,
            contexto=contexto,
            model=modelo,
            max_tokens=4000,
        )
        return {"sucesso": True, "modelo": modelo, "resposta": resposta.text}

    resposta, modelo_usado = await analisar_com_selecao_modelo(
        contexto=contexto,
        prompt=pergunta,
        max_tokens=4000,
    )

    return {"sucesso": True, "modelo": modelo_usado, "resposta": resposta}
```

---

## Fase 7: Consolidar Timeline

2 tools → 1 parametrizável.

### Tarefas

- [x] Unificar `timeline_processo` e `movimentacoes_processo`
- [x] Manter `cronologia_detalhada` separada (usa LLM)

### Detalhes Técnicos

**Arquivo:** `tools/timeline.py`

```python
@mcp.tool
async def listar_movimentacoes(
    numero_processo: str,
    tipo: str | None = None,
    limite: int = 30,
    formato: str = "completo",  # "completo" | "compacto"
    ctx: Context = None,
) -> dict[str, Any]:
    """
    Lista movimentações de um processo.

    Args:
        numero_processo: Número do processo
        tipo: Filtrar por tipo (opcional)
        limite: Máximo de movimentações (default 30, max 100)
        formato: "completo" ou "compacto" (descrição truncada)

    Substitui: timeline_processo, movimentacoes_processo
    """
    client = get_supabase_client()
    limite = min(limite, MAX_LIMITE_MOVS)

    query = (
        client._client.table("movimentacoes")
        .select("id, data_hora, descricao, tipo, complemento")
        .eq("numero_processo", numero_processo)
        .order("data_hora", desc=True)
    )

    if tipo:
        query = query.ilike("tipo", f"%{tipo}%")

    response = query.limit(limite).execute()
    movs = response.data or []

    resultados = []
    for m in movs:
        item = {
            "data_hora": m.get("data_hora"),
            "tipo": m.get("tipo"),
            "descricao": m.get("descricao"),
        }
        if formato == "compacto":
            item["descricao"] = (item["descricao"] or "")[:100]
        if m.get("complemento"):
            item["complemento"] = m["complemento"]
        resultados.append(item)

    return {"total": len(resultados), "movimentacoes": resultados}
```

---

## Fase 8: Atualizar Documentação

Refletir mudanças no CLAUDE.md.

### Tarefas

- [x] Atualizar contagem de tools (33 → 25)
- [x] Documentar tools consolidadas com novos parâmetros
- [x] Remover documentação das tools deletadas

### Detalhes Técnicos

**Arquivo:** `CLAUDE.md`

Atualizar seções:
- Tools (33 total) → Tools (24 total)
- Documentos: remover preview_documentos, documentos_recentes, listar_nomes_documentos
- Timeline: remover ultima_movimentacao, tipos_movimentacoes
- Queries: remover modelos_disponiveis

---

## Verificação Final

### Tarefas

- [x] Rodar `ruff check --fix . && ruff format .`
- [x] Rodar `mypy .`
- [x] Testar tools consolidadas no Claude.ai (requer teste manual)
- [x] Verificar seleção de modelo funciona em análise jurídica (requer teste manual)
