# Plano de Implementacao: Otimizar MCP Server para Contexto Inteligente

## Visao Geral

Criar tools inteligentes que:
1. Usam campo `markdown` da tabela `jobs` quando cabe no contexto
2. Escolhem automaticamente entre Gemini Flash e Grok Fast pelo tamanho
3. Fazem selecao inteligente de documentos quando contexto e muito grande
4. Normalizam acentos para buscar documentos pelo nome

## Fase 1: Helpers e Metodos do Supabase Client

Adicionar metodos auxiliares no cliente Supabase para suportar as novas tools.

### Tarefas

- [x] Adicionar funcao `_normalize_text()` para remover acentos
- [x] Adicionar metodo `obter_markdown_processo()` para buscar campo markdown
- [x] Adicionar metodo `listar_metadados_documentos()` para listar sem conteudo
- [x] Adicionar metodo `buscar_documentos_por_nomes()` com normalizacao
- [x] Adicionar metodo `selecionar_documentos_inteligente()` com prioridade por tipo

### Detalhes Tecnicos

**Arquivo:** `services/supabase_client.py`

```python
import unicodedata

def _normalize_text(text: str) -> str:
    """Remove acentos para busca."""
    normalized = unicodedata.normalize('NFD', text)
    return ''.join(c for c in normalized if unicodedata.category(c) != 'Mn').lower()
```

```python
async def obter_markdown_processo(self, numero_processo: str) -> dict | None:
    """Busca campo markdown da tabela jobs."""
    response = (
        self._client.table("jobs")
        .select("numero_processo, markdown")
        .eq("numero_processo", numero_processo)
        .eq("status", "completed")
        .maybe_single()
        .execute()
    )
    return response.data if response else None
```

```python
async def listar_metadados_documentos(self, numero_processo: str) -> list[dict]:
    """Lista metadados dos documentos SEM conteudo."""
    response = (
        self._client.table("documents")
        .select("sequencia, nome, tipo_nome, data_juntada, tamanho_texto")
        .eq("numero_processo", numero_processo)
        .order("sequencia", desc=True)
        .execute()
    )
    return response.data or []
```

```python
async def buscar_documentos_por_nomes(self, numero_processo: str, nomes: list[str]) -> list[dict]:
    """Busca docs por nome com normalizacao de acentos."""
    response = (
        self._client.table("documents")
        .select("id, sequencia, nome, tipo_nome, data_juntada, conteudo")
        .eq("numero_processo", numero_processo)
        .order("sequencia", desc=True)
        .execute()
    )
    todos = response.data or []
    termos = [_normalize_text(n) for n in nomes]

    encontrados = []
    for doc in todos:
        nome_norm = _normalize_text(doc.get("nome") or "")
        for termo in termos:
            if termo in nome_norm:
                encontrados.append(doc)
                break
    return encontrados
```

```python
async def selecionar_documentos_inteligente(self, numero_processo: str, max_chars: int = 7_000_000) -> list[dict]:
    """Seleciona documentos priorizando por tipo."""
    PRIORIDADE_NOMES = [
        "sentenca", "acordao", "decisao",
        "denuncia", "peticao_inicial", "contestacao",
        "despacho", "certid",
    ]

    response = (
        self._client.table("documents")
        .select("id, sequencia, nome, tipo_nome, data_juntada, tamanho_texto, conteudo")
        .eq("numero_processo", numero_processo)
        .order("sequencia", desc=True)
        .execute()
    )
    todos_docs = response.data or []

    def prioridade_doc(doc):
        nome = _normalize_text(doc.get("nome") or "")
        for i, termo in enumerate(PRIORIDADE_NOMES):
            if termo in nome:
                return i
        return 999

    docs_ordenados = sorted(todos_docs, key=prioridade_doc)

    selecionados = []
    chars_total = 0
    for doc in docs_ordenados:
        tamanho = doc.get("tamanho_texto") or len(doc.get("conteudo") or "")
        if chars_total + tamanho > max_chars:
            continue
        selecionados.append(doc)
        chars_total += tamanho

    return selecionados
```

---

## Fase 2: Helper de Montagem de Contexto

Criar funcao para montar contexto formatado dos documentos.

### Tarefas

- [x] Adicionar funcao `_montar_contexto_documentos()` em documentos.py

### Detalhes Tecnicos

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

```python
def _montar_contexto_documentos(docs: list[dict], max_chars: int = 7_000_000) -> str:
    """Monta contexto formatado para Gemini/Grok."""
    partes = []
    chars = 0

    for doc in docs:
        nome = doc.get("nome") or "Documento"
        tipo = doc.get("tipo_nome") or ""
        data = str(doc.get("data_juntada"))[:10] if doc.get("data_juntada") else ""
        conteudo = doc.get("conteudo") or ""

        parte = f"## {nome}\n**Tipo:** {tipo} | **Data:** {data}\n\n{conteudo}\n"

        if chars + len(parte) > max_chars:
            break

        partes.append(parte)
        chars += len(parte)

    return "\n---\n\n".join(partes)
```

---

## Fase 3: Nova Tool `analisar_processo`

Tool principal com selecao automatica de modelo e fallback.

### Tarefas

- [x] Criar tool `analisar_processo` em processos.py [complexo]
  - [x] Buscar markdown do processo
  - [x] Escolher modelo pelo tamanho (Gemini < 3M, Grok < 7M)
  - [x] Fallback para selecao inteligente se muito grande

### Detalhes Tecnicos

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

**Limites de Contexto:**
```python
LIMITE_GEMINI = 3_000_000   # 3M chars (~750k tokens)
LIMITE_GROK = 7_000_000     # 7M chars (~1.7M tokens)
```

**Logica de Selecao de Modelo:**
```python
if tamanho <= LIMITE_GEMINI:
    modelo = "gemini-flash"
elif tamanho <= LIMITE_GROK:
    modelo = "grok-fast"  # 2M tokens!
else:
    # Muito grande, usar selecao inteligente
    modelo = None
```

**Codigo Completo:**
```python
@mcp.tool
async def analisar_processo(
    numero_processo: str,
    pergunta: str = "Faca uma analise completa deste processo",
    ctx: Context = None,
) -> dict[str, Any]:
    """
    Analisa processo com estrategia inteligente e fallback de modelo.

    Estrategia:
    1. Busca markdown completo
    2. Escolhe modelo pelo tamanho:
       - <= 3M chars: Gemini Flash (1M tokens)
       - > 3M chars: Grok Fast (2M tokens!)
    3. Se markdown > 7M chars: selecao inteligente de documentos
    """
    client = get_supabase_client()
    llm = get_llm_service()

    LIMITE_GEMINI = 3_000_000
    LIMITE_GROK = 7_000_000

    # 1. Busca markdown completo
    job = await client.obter_markdown_processo(numero_processo)
    markdown = job.get("markdown") if job else None

    if markdown:
        tamanho = len(markdown)

        if tamanho <= LIMITE_GEMINI:
            modelo = "gemini-flash"
        elif tamanho <= LIMITE_GROK:
            modelo = "grok-fast"
        else:
            modelo = None

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

    # 2. Fallback: Selecao inteligente
    docs = await client.selecionar_documentos_inteligente(numero_processo, max_chars=LIMITE_GROK)

    if not docs:
        return {"erro": "Nenhum documento encontrado", "sucesso": False}

    contexto = _montar_contexto_documentos(docs, max_chars=LIMITE_GROK)
    modelo = "grok-fast" if len(contexto) > LIMITE_GEMINI else "gemini-flash"

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

    return {
        "sucesso": True,
        "estrategia": "selecao_inteligente",
        "modelo": modelo,
        "documentos_selecionados": len(docs),
        "contexto_chars": len(contexto),
        "resposta": resposta.text,
    }
```

---

## Fase 4: Nova Tool `listar_nomes_documentos`

Tool compacta para Claude ver quais documentos existem.

### Tarefas

- [x] Criar tool `listar_nomes_documentos` em documentos.py
- [x] Agrupar documentos por tipo para facilitar leitura

### Detalhes Tecnicos

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

```python
@mcp.tool
async def listar_nomes_documentos(
    numero_processo: str,
    ctx: Context = None,
) -> dict[str, Any]:
    """
    Lista APENAS nomes dos documentos (ultra compacto, ~3k chars).

    Use para ver quais documentos existem antes de analisar.
    Retorna: sequencia, nome, tipo, data, tamanho (SEM conteudo).
    """
    client = get_supabase_client()
    docs = await client.listar_metadados_documentos(numero_processo)

    por_tipo = {}
    for d in docs:
        tipo = d.get("tipo_nome") or "Outros"
        if tipo not in por_tipo:
            por_tipo[tipo] = []
        por_tipo[tipo].append({
            "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"),
        })

    return {
        "numero_processo": numero_processo,
        "total": len(docs),
        "por_tipo": por_tipo,
    }
```

---

## Fase 5: Nova Tool `analisar_documentos_por_nome`

Tool para buscar documentos especificos pelo nome com normalizacao.

### Tarefas

- [x] Criar tool `analisar_documentos_por_nome` em documentos.py

### Detalhes Tecnicos

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

```python
@mcp.tool
async def analisar_documentos_por_nome(
    numero_processo: str,
    nomes: list[str],
    pergunta: str = "Analise estes documentos",
    ctx: Context = None,
) -> dict[str, Any]:
    """
    Busca documentos pelo nome e envia para Gemini/Grok.

    Normaliza acentos automaticamente:
    - "sentenca" encontra "Sentenca.pdf"
    - "denuncia" encontra "Denncia.pdf"
    - "peticao" encontra "Petio.pdf"

    Args:
        numero_processo: Numero do processo
        nomes: Lista de nomes ou partes de nomes
        pergunta: O que analisar
    """
    client = get_supabase_client()
    llm = get_llm_service()

    docs = await client.buscar_documentos_por_nomes(numero_processo, nomes)

    if not docs:
        return {"erro": f"Nenhum doc com nomes: {nomes}", "sucesso": False}

    contexto = _montar_contexto_documentos(docs, max_chars=7_000_000)

    # Escolhe modelo pelo tamanho
    modelo = "grok-fast" if len(contexto) > 3_000_000 else "gemini-flash"

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

    return {
        "sucesso": True,
        "documentos": [d.get("nome") for d in docs],
        "total": len(docs),
        "modelo": modelo,
        "resposta": resposta.text,
    }
```

---

## Fase 6: Ajustes nas Tools Existentes

Remover truncamentos e aumentar limites.

### Tarefas

- [x] Remover truncamento `[:50]` e `[:40]` de nomes em documentos.py
- [x] Aumentar constantes MAX_DOCS_LISTA e MAX_DOCS_BUSCA

### Detalhes Tecnicos

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

**Alterar constantes (inicio do arquivo):**
```python
MAX_DOCS_LISTA = 100  # Era: 20
MAX_DOCS_BUSCA = 50   # Era: 15
```

**Remover truncamentos:**

Linha com `"nome": (d.get("nome") or "")[:50]` alterar para:
```python
"nome": d.get("nome") or ""
```

Fazer isso em:
- `listar_documentos`
- `preview_documentos`
- `buscar_em_documentos`
- `documentos_recentes`

---

## Fase 7: Documentacao

Atualizar CLAUDE.md com as novas tools.

### Tarefas

- [x] Documentar `analisar_processo` em CLAUDE.md
- [x] Documentar `listar_nomes_documentos` em CLAUDE.md
- [x] Documentar `analisar_documentos_por_nome` em CLAUDE.md
- [x] Atualizar secao de limites com novos valores

### Detalhes Tecnicos

**Arquivo:** `CLAUDE.md`

Adicionar na secao de Tools:

```markdown
#### `analisar_processo`
Analisa processo com selecao inteligente de modelo.
```python
analisar_processo(
    numero_processo: str,
    pergunta: str = "Faca uma analise completa"
) -> {"estrategia": str, "modelo": str, "resposta": str}
# Usa Gemini (<3M chars) ou Grok (>3M chars, ate 7M!)
```

#### `listar_nomes_documentos`
Lista APENAS nomes dos documentos (compacto, sem conteudo).
```python
listar_nomes_documentos(
    numero_processo: str
) -> {"total": int, "por_tipo": {...}}
```

#### `analisar_documentos_por_nome`
Busca documentos por nome com normalizacao de acentos.
```python
analisar_documentos_por_nome(
    numero_processo: str,
    nomes: list[str],  # ["sentenca", "denuncia"] encontra "Sentenca.pdf", "Denncia.pdf"
    pergunta: str = "Analise estes documentos"
) -> {"documentos": [...], "resposta": str}
```
```

Atualizar secao de Limites:

```markdown
### Limites de Contexto

| Modelo | Limite | Uso |
|--------|--------|-----|
| Gemini Flash | 3M chars (~750k tokens) | Processos pequenos/medios |
| Grok Fast | 7M chars (~1.7M tokens) | Processos grandes |
| Selecao Inteligente | > 7M chars | Processos gigantes |
```
