# Sprint 8a Research — Token Optimization

## Codebase Research

### Current State Summary

GodotIQ has **32 MCP tools** across 8 categories:
- **17 tools** already have `detail` parameter (brief/normal/full)
- **13 tools** need `detail` parameter added
- **1 tool** (`impact_check`) has misnamed `detail` parameter (used as change description, not output verbosity)
- **1 tool** (`ping`) is trivial — no detail needed

### Existing Infrastructure

#### output_limit.py (Already Exists)
**File:** `src/godotiq/tools/output_limit.py`

Character limits by detail level:
- `brief`: 2,000 chars
- `normal`: 8,000 chars
- `full`: No limit (None)

**Function:** `apply_output_limit(result: dict, detail: str, list_keys: list[str] | None = None) -> dict`

How it works:
1. Estimates JSON-serialized size of result dict
2. If size exceeds limit, progressively truncates lists (largest first)
3. Binary search for optimal truncation point
4. Adds metadata: `output_truncated`, `truncation_notice`, `{key}_total` fields
5. Supports auto-detection of truncatable list keys

Already imported and used by 17 tools.

#### Config System
**File:** `src/godotiq/config.py`

```python
token_optimization: {
    "default_detail": "normal",
    "enable_diff_cache": True,
    "compact_output": True,
    "max_nodes_per_response": 50,
}
server: {
    "default_detail": "normal",
    "max_nodes_per_response": 200,
}
```
Helper: `config.get_default_detail()` → returns project's default detail level.

### Tool Inventory

#### Tools WITH detail parameter (17):
| Category | Tool | Detail Handling |
|----------|------|----------------|
| Animation | animation_info | apply_output_limit on animation_players list |
| Animation | animation_audit | apply_output_limit on issues list; brief=counts only |
| Assets | asset_registry | apply_output_limit on assets, unused_list; brief=counts, normal=top 20, full=all |
| Code | dependency_graph | apply_output_limit on signals/references lists |
| Code | signal_map | apply_output_limit on signals/orphans/busiest/missing; max 30 |
| Code | validate | apply_output_limit on issues; brief=counts, capped at 100 |
| Flow | trace_flow | apply_output_limit on flow/failure_points |
| Memory | project_summary | apply_output_limit; brief=counts, normal=+arch, full=+scene_structure |
| Memory | file_context | accepts detail |
| Spatial | scene_map | apply_output_limit on nodes; brief=10, normal=20, full=30 |
| Spatial | spatial_audit | filters by severity; brief=critical+warning only |
| Bridge | perf_snapshot | brief=fps+draw_calls only |
| Bridge | scene_tree | brief=max 20 nodes |
| Bridge | exec | implicit |
| Bridge | state_inspect | brief=values, normal=+types, full=+source paths |
| Bridge | nav_query | implicit |
| Bridge | ui_map | implicit |

#### Tools WITHOUT detail parameter (14):
| Tool | Category | Output Size Risk |
|------|----------|-----------------|
| suggest_scale | Assets | SMALL (fixed) |
| impact_check | Code | MEDIUM (⚠️ `detail` param is misused as change description) |
| placement | Spatial | SMALL (fixed) |
| screenshot | Bridge | N/A (binary image) |
| run | Bridge | SMALL (fixed) |
| editor_context | Bridge | MEDIUM |
| node_ops | Bridge | MEDIUM |
| script_ops | Bridge | SMALL (fixed) |
| file_ops | Bridge | HIGH (can return large file lists) |
| input | Bridge | MEDIUM |
| watch | Bridge | MEDIUM |
| undo_history | Bridge | MEDIUM |
| save_scene | Bridge | SMALL (fixed) |
| camera | Bridge | SMALL (fixed) |

#### Implementation Patterns Used

**Pattern 1 (15 tools):** Standard apply_output_limit at return
```python
return apply_output_limit(result, detail, list_keys=["key1", "key2"])
```

**Pattern 2 (3 tools):** Manual filtering by detail level
```python
if detail == "brief": return {brief_data}
```

**Pattern 3 (1 tool - impact_check):** detail param used for change context description, not output verbosity. Needs renaming.

### Test Structure
- Location: `tests/test_tools/`
- Framework: pytest + pytest-asyncio, `asyncio_mode = "auto"`
- Existing detail tests: `test_output_limits.py`, `test_animation_reset_filter.py`
- No existing tests for impact_check, placement, suggest_scale detail behavior

---

## Web Research

### MCP Output Optimization Best Practices

#### No Official Response Size Limit in MCP Spec
- Claude Code: ~25,000 token limit, truncates beyond
- Go SDK: Hard 1 MB limit
- Claude Desktop: ~20,000 char errors
- GPT/Codex: Hard 10KB truncation
- Active proposal for clients to declare `max_response_bytes` (not yet standardized)

#### Datadog's MCP Server Lessons (Production)
1. **Paginate by token budget**, not record count (records vary in size)
2. **Trim JSON payloads** — return only needed fields (60-80% size reduction)
3. **Use plain text instead of JSON** for simple structured data
4. **Keep tool descriptions brief** (consume context every session)
5. **Design flexible tools**, not one tool per API endpoint

Source: [Datadog Engineering Blog](https://www.datadoghq.com/blog/engineering/mcp-server-agent-tools/)

#### Blockscout's Three-Layer Strategy
1. **Truncation**: Cut long fields with indicators
2. **Simplification**: Remove fields with no analytical value
3. **Context-aware pagination**: Opaque cursor tokens, small slices (10 items)

Source: [Blockscout Blog](https://www.blog.blockscout.com/mcp-explained-part-2-optimizations/)

#### CodeRabbit: Context Engineering Principles
- **Context confusion**: Model latches onto irrelevant detail
- **Context clash**: Conflicting context produces muddled outputs
- **Core principle**: Good context engineering = knowing what to leave out

Source: [CodeRabbit Blog](https://www.coderabbit.ai/blog/handling-ballooning-context-in-the-mcp-era-context-engineering-on-steroids)

### Python Output Truncation Patterns

#### Head-Tail Truncation (Custom Pattern)
```python
def head_tail_truncate(lines, head=5, tail=5):
    if len(lines) <= head + tail: return lines
    omitted = len(lines) - head - tail
    return lines[:head] + [f"... ({omitted} items omitted) ..."] + lines[-tail:]
```

#### Progressive Detail Levels
Enum-based approach:
```python
class DetailLevel(str, Enum):
    BRIEF = "brief"    # Summary: counts, totals, key metrics
    NORMAL = "normal"  # Standard output with essential details
    FULL = "full"      # Everything, no truncation
```

Reference models:
- MSBuild: 5 levels (Quiet→Diagnostic)
- pytest: 4 levels (qq→vv)

#### Key Patterns for GodotIQ
1. **Default to minimal output** — summaries by default, full only on request
2. **Strip irrelevant fields** from structured responses per detail level
3. **Include `truncated: true` flag** so LLM knows more data is available
4. **Head-tail truncation** for lists: first N + last M items
5. **Plain text for simple lists** instead of JSON to save tokens

---

## Testing Context

### Existing Test Setup
- pytest + pytest-asyncio
- Fixtures in `tests/fixtures/`
- Test files: `tests/test_tools/test_*.py`
- `asyncio_mode = "auto"` in pytest config
- Existing detail/output tests: `test_output_limits.py`, `test_animation_reset_filter.py`

### Testing Patterns Needed
1. Size assertion tests: `len(json.dumps(result)) < limit`
2. Backward compatibility: `detail="full"` matches previous behavior
3. Default behavior: no `detail` param → "normal" limits applied
4. Integration test: combined tool workflow under 30k chars total
