# Section 5: Add detail to Bridge Tools (Runtime Communication)

## Overview

This section adds the `detail: str = "normal"` parameter to **12 bridge tools** that lacked it, plus the `ping` tool in `server.py`. Bridge tools communicate with the Godot editor/game over WebSocket. The filtering strategy is: sub-modules are NOT modified; `apply_output_limit()` is called in the `__init__.py` wrapper after receiving the sub-module response.

The 12 bridge tools split into two categories:

- **High-output-risk** (6 tools): `file_ops`, `node_ops`, `editor_context`, `input`, `watch`, `undo_history` — these need real filtering via `apply_output_limit()`
- **No-op / small-output** (6 tools): `screenshot`, `run`, `save_scene`, `camera`, `script_ops`, `exec` — these only add the parameter to the signature for API consistency, with no filtering logic

Plus `ping` in `server.py`.

**Note on `watch`:** The wrapper already had `detail` in its signature and passes it to the sub-module. The work here added `apply_output_limit()` as a safety net after the sub-module call returns.

**Deviation from plan:** `godotiq_exec` was omitted from the original plan but was added during implementation (as no-op) to achieve complete coverage. The `file_ops` read truncation also gained a `max(..., 0)` guard against negative slice indices.

**Depends on:** Section 1 (output_limit.py enhancements — `truncate_output()` function and input validation for `apply_output_limit()`)

## Files to Modify

| File | Changes |
|------|---------|
| `src/godotiq/tools/bridge/__init__.py` | Add `detail` param to 11 wrapper functions, add `apply_output_limit()` calls for high-output tools |
| `src/godotiq/server.py` | Add `detail` param to `godotiq_ping` |

**Files NOT modified:** All bridge sub-modules (`file_ops.py`, `node_ops.py`, `editor_context.py`, `input_sim.py`, `undo_history.py`, `screenshot.py`, `run.py`, `save_scene.py`, `camera.py`, `script_ops.py`). They continue to return full data. Filtering is centralized in the wrapper.

## Tests (from TDD plan)

All tests go in `tests/test_tools/test_detail_levels.py`.

### Bridge tool signature tests

For each of the 11 bridge tools needing `detail` added, plus `ping`:

```python
# Test: {tool_name} function signature accepts detail parameter
# Test: {tool_name} detail parameter has default value "normal"
```

Use `inspect.signature()` to verify. Tools to test: `screenshot`, `run`, `editor_context`, `node_ops`, `script_ops`, `file_ops`, `input`, `undo_history`, `save_scene`, `camera`, `ping`.

### High-output-risk bridge tool tests

```python
# Test: file_ops with detail="brief" applies output limit
# Test: file_ops op="read" with large content truncates string value
# Test: node_ops with detail="brief" applies output limit
# Test: editor_context with detail="brief" applies output limit
# Test: input with detail="brief" applies output limit
# Test: watch with detail="brief" applies output limit
# Test: undo_history with detail="brief" applies output limit
```

Since bridge tools require a live Godot connection, these tests should mock the sub-module call to return a large synthetic result dict, then verify the wrapper applies `apply_output_limit()` and the output stays within limits.

### No-op bridge tool tests

```python
# Test: screenshot accepts detail parameter (no behavior change)
# Test: run accepts detail parameter (no behavior change)
# Test: save_scene accepts detail parameter (no behavior change)
# Test: camera accepts detail parameter (no behavior change)
# Test: script_ops accepts detail parameter (no behavior change)
```

## Implementation Details

### Import needed in `__init__.py`

Add at the top of `src/godotiq/tools/bridge/__init__.py`:

```python
from godotiq.tools.output_limit import apply_output_limit
```

### High-output-risk bridge tools

#### `file_ops` (HIGHEST priority)

**Current state:** No `detail` parameter in wrapper. Calls `_file_ops(...)` and returns result directly.

**Changes to `godotiq_file_ops`:**

1. Add `detail: str = "normal"` parameter to the function signature (before `ctx`)
2. Add to docstring: `detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).`
3. After receiving result from `_file_ops(...)`, apply filtering:
   - **Special case for `op="read"`:** The result contains a `"content"` string field that can be very large. Since `apply_output_limit()` only truncates lists (not strings), the content string must be manually truncated before calling `apply_output_limit()`. Use the character limits from `_CHAR_LIMITS`:

   ```python
   if op == "read" and "content" in result and "error" not in result:
       from godotiq.tools.output_limit import _CHAR_LIMITS
       limit = _CHAR_LIMITS.get(detail, _CHAR_LIMITS["normal"])
       if limit is not None and isinstance(result["content"], str) and len(result["content"]) > limit:
           original_len = len(result["content"])
           result["content"] = result["content"][:limit - 200]
           result["content_truncated"] = True
           result["content_original_chars"] = original_len
   ```

   - For all ops: call `apply_output_limit(result, detail)` on the result dict. Auto-detected list keys will handle `entries` (from list op), `matches` (from search op), etc.

#### `node_ops`

1. Add `detail: str = "normal"` parameter
2. After getting result from `_node_ops(...)`, call `apply_output_limit(result, detail)`. Auto-detection will find list keys like `"results"` and `"validation"`.

#### `editor_context`

1. Add `detail: str = "normal"` parameter
2. After getting result, call `apply_output_limit(result, detail)`. The editor context may contain lists like `"open_scenes"` or `"selected_nodes"` that auto-detection will pick up.

#### `input`

1. Add `detail: str = "normal"` parameter
2. After getting result, call `apply_output_limit(result, detail)`. Side-effect tracking results may contain lists that grow.

#### `undo_history`

1. Add `detail: str = "normal"` parameter
2. After getting result, call `apply_output_limit(result, detail)`. The `godotiq_actions` list can grow with deep undo stacks.

#### `watch`

The wrapper already has `detail` in its signature and passes it to the sub-module. Just add `apply_output_limit(result, detail)` after the sub-module call as a safety net.

### No-op / small-output bridge tools

Add `detail: str = "normal"` to the wrapper signature and docstring only. No `apply_output_limit()` call needed.

- **`screenshot`** — Add after `camera_target` (before `ctx`)
- **`run`** — Add after `scene` (before `ctx`)
- **`save_scene`** — Add before `ctx`
- **`camera`** — Add after `node` (before `ctx`)
- **`script_ops`** — Add after `dry_run` (before `ctx`)

### Docstring convention

For every tool that gets `detail` added, append this line to the Args section:

```
detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
```

### `ping` in `server.py`

**Current state:**

```python
@mcp.tool()
async def godotiq_ping() -> dict:
    """Health check tool. Returns server status and version."""
    return {"status": "ok", "version": "0.1.0"}
```

**Updated:**

```python
@mcp.tool()
async def godotiq_ping(detail: str = "normal") -> dict:
    """Health check tool. Returns server status and version.

    Args:
        detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
    """
    return {"status": "ok", "version": "0.1.0"}
```

## Error Handling

The `apply_output_limit()` call should be placed INSIDE the try block, after the successful sub-module return but before returning. Error dict fallback paths do NOT need `apply_output_limit()` since error dicts are always small.

Pattern:

```python
try:
    result = await _some_submodule(...)
    return apply_output_limit(result, detail)
except BridgeConnectionError as e:
    return {"error": str(e), "hint": "..."}
```

## Verification Checklist

- [ ] All 13 bridge wrappers in `__init__.py` accept `detail` parameter
- [ ] `ping` in `server.py` accepts `detail` parameter
- [ ] High-output tools call `apply_output_limit()` before returning
- [ ] `file_ops` has special string truncation for `op="read"` content
- [ ] No sub-module files were modified
- [ ] Error paths do not call `apply_output_limit()`
- [ ] The import `from godotiq.tools.output_limit import apply_output_limit` is present at top of `__init__.py`
