# Piano: neo-cortex MCP Output Fix

## Problema

La pipeline interna di neo-cortex v5 funziona — 9 fasi implementate, 217 test green, graph+dedup+decay+digestor+episodes+consolidator tutto a posto. Il problema è nello **strato MCP**: i tool tornano `model_dump()` / `__dict__` che scaricano JSON blob enormi (document field fino a 8000 char per record, metadata duplicati). Bruciamo 13k-25k token per query quando ne basterebbero 200-500.

Questo è il contrario di quello per cui abbiamo costruito il sistema: MBEL compression, concept graph, spreading activation — tutto lavoro buttato perché l'output MCP lo ignora.

## Stato attuale — 7 MCP tools

| Tool | Output attuale | Token stima | API calls | Problema |
|------|---------------|-------------|-----------|----------|
| `memory_query` | RecallResult.model_dump() (records interi + mbel) | ~13,500 | 2 Groq + 1 Jina | MBEL generato ma sepolto sotto record raw |
| `memory_timeline` | TimelineResult.model_dump() (MemoryRecord interi) | ~25,000 | 0 | Usa store.timeline() invece di index.timeline() |
| `memory_get` | MemoryRecord.__dict__ con document | ~2,500/record | 0 | Nessun modo di avere solo summary |
| `memory_search` | CompactMemory.__dict__ | ~500-1000 | 0 | OK, ma serializzazione inconsistente |
| `memory_stats` | CortexStats.model_dump() | ~200 | 0 | Usa store (full ChromaDB scan) invece di index (1 SQL) |
| `memory_dream` | DreamResult.model_dump() con cluster[] | ~50-500 | 0 | Lista IDs inutile nel cluster |
| `memory_ingest` | {"status":"ok","id":"..."} | ~20 | 1-2 Groq + 1 Jina | OK |

## Risorse GIA' implementate ma MAI usate dai tool MCP

- `MemoryIndex.timeline()` → `CompactMemory` (id, title, project, topic, activity, timestamp, energy) — ~50 tok/record
- `MemoryIndex.stats()` → single SQL query → {total, sessions, avg_energy, dream_count}
- `MemoryIndex.get_structured(id)` → `StructuredFields` (title, summary, facts, concepts, files_touched) — dati utili senza document blob
- `RecallResult.mbel` → già generato da `aggregate_to_mbel()`, ma sepolto nel JSON

## Piano intervento

### Fix 1: `memory_query` → ritorna MBEL + compact refs

**File**: `src/neo_cortex/mcp.py` linea 360-379

Oggi: `return result.model_dump()` (tutto il RecallResult con records interi)

Dopo: ritorna solo MBEL + lista compatta di riferimenti (id, title, project, activity, similarity). Il title/summary viene da `index.get_structured(id)`.

```
Output target (~200-400 token invece di ~13,500):
{
  "query": "encoding bug",
  "count": 3,
  "mbel": "> EncodingFix :: UTF8Encoding(false)\n@ BOM :: corruptsStdinJSON\n...",
  "refs": [
    {"id": "v3_abc", "title": "Fix encoding", "project": "neo-cortex", "similarity": 0.82},
    ...
  ]
}
```

### Fix 2: `memory_timeline` → usa index.timeline()

**File**: `src/neo_cortex/mcp.py` linea 437-456

Oggi: `_cortex.timeline()` → `store.timeline()` → MemoryRecord interi con document

Dopo: `_cortex._index.timeline()` → CompactMemory (id, title, project, topic, activity, energy)

```
Output target (~500 token per n=10 invece di ~25,000):
{
  "count": 10,
  "memories": [
    {"id": "v3_abc", "title": "Fix encoding", "project": "neo-cortex", "activity": "bugfix", "energy": 0.85},
    ...
  ]
}
```

### Fix 3: `memory_get` → ritorna structured, non document

**File**: `src/neo_cortex/mcp.py` linea 486-505

Oggi: `index.get_by_ids()` → MemoryRecord.__dict__ con document intero

Dopo: `index.get_structured(id)` per i dati utili (title, summary, facts, concepts, files_touched) + metadata base (project, activity, energy)

```
Output target (~200 token/record invece di ~2,500):
{
  "memories": [
    {"id": "v3_abc", "title": "Fix encoding", "summary": "Used UTF8Encoding(false)...",
     "facts": ["BOM corrupts stdin JSON", "..."], "project": "neo-cortex", "activity": "bugfix"}
  ]
}
```

### Fix 4: `memory_stats` → usa index.stats()

**File**: `src/neo_cortex/mcp.py` linea 382-394

Oggi: `_cortex.stats()` → `store.stats()` → full ChromaDB metadata scan

Dopo: `_cortex._index.stats(dream_count)` → single SQL query

### Fix 5: `memory_dream` → ritorna solo status + count

**File**: `src/neo_cortex/mcp.py` linea 397-409

Oggi: DreamResult con `cluster: list[str]` (tutti gli IDs ad alta energia)

Dopo: solo `{"status": "completed", "dream_count": 5, "boosted": 12, "decayed": 45}`

### Fix 6: `memory_search` → serializzazione pulita

**File**: `src/neo_cortex/mcp.py` linea 459-483

Oggi: `r.__dict__` (può includere campi interni Pydantic)

Dopo: `r.model_dump()` per consistenza

### Fix 7: Dead code cleanup

- **`consolidator.py`**: chiama `classifier.summarize_pattern()` e `classifier.classify_mb_target()` che NON ESISTONO → fix o rimuovi
- **`server.py`**: legacy v3.0.0, mai usato in produzione → lasciare ma marcare deprecato
- **Proxy mode in mcp.py**: `_get()` / `_post()` mai usati → lasciare come fallback

## Accesso pulito all'index

Oggi i tool MCP accedono a `_cortex._index` (attributo privato). Fix: aggiungere metodo pubblico in `cortex.py`:

```python
@property
def index(self) -> MemoryIndex | None:
    return self._index
```

## File da modificare

1. `src/neo_cortex/mcp.py` — Fix 1-6 (output di tutti i tool)
2. `src/neo_cortex/cortex.py` — property pubblica per index
3. `src/neo_cortex/models.py` — aggiungere campo `boosted`/`decayed` a DreamResult

## File da NON toccare

- `cortex.py` recall/ingest/dream logic — funziona, 217 test
- `store.py`, `embedder.py`, `classifier.py` — pipeline interna OK
- `memory_index.py` — ha già tutto quello che serve
- `graph.py`, `dedup.py`, `decay.py` — funzionano
- `subscriber.py`, `digestor.py`, `conversation_log.py` — funzionano
- `timeline_cli.py` — appena riscritto, output MBEL OK

## Verifica

1. `uv run pytest -x -q` → 217 test green
2. Test manuale ogni tool MCP (output compatto, non JSON blob)
3. Publish nuova versione → `uv tool upgrade` → verifica da Claude Code
