Now I have all the context I need. Let me produce the section content.

# Section 6: Bridge MCP Tool Implementations

## Overview

This section implements the four MCP tool functions that bridge AI requests to the live Godot editor addon over WebSocket. Each tool lives in its own file under `src/godotiq/tools/bridge/` and exports a plain async function (no decorator). The `@mcp.tool()` decorators and error-handling wrappers live in `src/godotiq/tools/bridge/__init__.py` (created by section 05).

## Dependencies

- **Section 04** provides the bridge protocol and connection layer: `BridgeRequest`, `BridgeResponse`, `BridgeEvent`, `BridgeConnection`, `BridgeConnectionError`, `BridgeTimeoutError`, `BridgeError` -- all in `src/godotiq/bridge/`.
- **Section 05** provides:
  - `src/godotiq/bridge/session.py` with `get_bridge()` and `get_bridge_or_none()` functions
  - `src/godotiq/tools/bridge/__init__.py` which imports these tool functions, wraps them in `@mcp.tool()` decorators, and catches bridge exceptions to return user-friendly error dicts

You do NOT need to create or modify section 04 or 05 files. You only create the four tool implementation files listed below.

## Files to Create

```
src/godotiq/tools/bridge/screenshot.py
src/godotiq/tools/bridge/run.py
src/godotiq/tools/bridge/perf_snapshot.py
src/godotiq/tools/bridge/editor_context.py
```

No existing files are modified by this section.

## Tests

Tests for these tools live in `tests/test_bridge/test_tools.py` (created by section 07), but the test stubs are documented here so the implementer understands the contract.

### `tests/test_bridge/test_tools.py`

All tests mock the bridge connection. Use `unittest.mock.AsyncMock` and `unittest.mock.patch` to replace `get_bridge` / `get_bridge_or_none` from `godotiq.bridge.session`. Class-based organization.

```python
# TestScreenshotTool
# Test: default params call request_with_retry with scale=0.5, format=webp, viewport=game
# Test: invalid scale (negative) is clamped to 0.5
# Test: invalid format falls back to webp
# Test: result contains image, format, width, height

# TestRunTool
# Test: action=play calls bridge.request("run") with scene param
# Test: action=stop calls bridge.request("stop")
# Test: result contains action field

# TestPerfSnapshotTool
# Test: calls bridge.request("perf_snapshot")
# Test: result contains fps field

# TestEditorContextTool
# Test: when connected, result has addon_connected=True and editor_version
# Test: when disconnected, result has addon_connected=False and note field
```

**Mock pattern for tool tests:**

```python
from unittest.mock import AsyncMock, patch

class TestScreenshotTool:
    async def test_default_params(self):
        mock_bridge = AsyncMock()
        mock_bridge.request_with_retry.return_value = {
            "image": "base64data",
            "format": "webp",
            "width": 960,
            "height": 540,
        }
        with patch("godotiq.tools.bridge.screenshot.get_bridge", return_value=mock_bridge):
            from godotiq.tools.bridge.screenshot import screenshot
            result = await screenshot()
        # Assert request_with_retry was called with expected params
        # Assert result contains image, format, width, height keys
```

For `editor_context` disconnected test, patch `get_bridge_or_none` to return `None` and verify the fallback dict contains `addon_connected: False` and a `note` field.

## Implementation Details

### `src/godotiq/tools/bridge/screenshot.py`

**Function signature:**

```python
async def screenshot(
    viewport: str = "game",
    scale: float = 0.5,
    format: str = "webp",
) -> dict:
    """Capture a screenshot from the running Godot game.

    Args:
        viewport: Which viewport to capture ("game").
        scale: Image scale factor (0.0-1.0). Values outside range clamped to 0.5.
        format: Image format ("webp", "png", "jpg"). Invalid values fall back to "webp".

    Returns:
        Dict with image (base64), format, width, height.
    """
```

**Behavior:**
1. Validate `scale`: if `scale <= 0` or `scale > 1.0`, clamp to `0.5`.
2. Validate `format`: if format not in `{"webp", "png", "jpg"}`, fall back to `"webp"`.
3. Call `get_bridge()` from `godotiq.bridge.session` to obtain the connection.
4. Call `bridge.request_with_retry("screenshot", params={"viewport": viewport, "scale": scale, "format": format}, max_retries=3, retry_delay=0.5)` with the 30-second timeout configured via the protocol's `TOOL_TIMEOUTS`.
5. Return the response dict as-is (it contains `image`, `format`, `width`, `height`).

The retry logic is important because the game viewport may not be ready immediately after launch. The `request_with_retry` method (from `BridgeConnection` in section 04) handles the retry loop internally.

### `src/godotiq/tools/bridge/run.py`

**Function signature:**

```python
async def run(
    action: str = "play",
    scene: str = "main",
) -> dict:
    """Start or stop the Godot game from the editor.

    Args:
        action: "play" to start, "stop" to halt the running game.
        scene: Which scene to run - "main", "current", or a res:// path. Only used with action="play".

    Returns:
        Dict with action and scene fields.
    """
```

**Behavior:**
1. Call `get_bridge()` to obtain the connection.
2. If `action == "stop"`: call `bridge.request("stop")` with no params. Return `{"action": "stop"}`.
3. Otherwise (default `"play"`): call `bridge.request("run", params={"scene": scene})`. Return `{"action": "play", "scene": scene}`.

The scene parameter supports three values on the Godot side:
- `"main"` -- calls `EditorInterface.play_main_scene()`
- `"current"` -- calls `EditorInterface.play_current_scene()`
- Any `res://` path -- calls `EditorInterface.play_custom_scene(path)`

The Python side simply passes the value through; validation happens in the Godot addon.

### `src/godotiq/tools/bridge/perf_snapshot.py`

**Function signature:**

```python
async def perf_snapshot() -> dict:
    """Capture a performance snapshot from the running Godot game.

    Returns:
        Dict with fps, draw_calls, triangles, objects, texture_mem, buffer_mem,
        video_mem, total_nodes, orphan_nodes.
    """
```

**Behavior:**
1. Call `get_bridge()` to obtain the connection.
2. Call `bridge.request("perf_snapshot")` with no params.
3. Return the response dict as-is. The Godot runtime collects metrics via `Engine`, `RenderingServer`, and `Performance` APIs and returns them as a JSON object.

### `src/godotiq/tools/bridge/editor_context.py`

**Function signature:**

```python
async def editor_context() -> dict:
    """Get the current editor state from the Godot addon.

    Returns connected editor state (open scenes, selected nodes, game state, project path)
    when the addon is running, or a filesystem-based fallback when disconnected.

    Returns:
        Dict with editor context and addon_connected flag.
    """
```

**Behavior:**
1. Call `get_bridge_or_none()` from `godotiq.bridge.session`. This returns `None` on failure instead of raising.
2. **If connected** (bridge is not None):
   - Call `bridge.request("editor_context")` to get live editor state.
   - Add `"addon_connected": True` to the result dict.
   - Return the augmented result.
3. **If not connected** (bridge is None):
   - Read `GODOTIQ_PROJECT_ROOT` environment variable (via `os.environ.get`).
   - Return a fallback dict:
     ```python
     {
         "addon_connected": False,
         "project_path": project_path or "",
         "note": "GodotIQ addon not connected. Editor state unavailable. "
                 "Enable the addon in Godot to get live editor context.",
     }
     ```

This graceful fallback pattern is unique to `editor_context` -- the other three tools require a live connection and will let exceptions propagate to the wrapper in `tools/bridge/__init__.py`.

## Integration with `tools/bridge/__init__.py`

Section 05 creates the `__init__.py` that wires these functions to MCP. The pattern looks like this (for reference only -- do NOT create this file):

```python
from godotiq.server import mcp
from godotiq.bridge.session import get_bridge, get_bridge_or_none
from godotiq.bridge.connection import BridgeConnectionError, BridgeTimeoutError, BridgeError

from godotiq.tools.bridge.screenshot import screenshot as _screenshot
from godotiq.tools.bridge.run import run as _run
from godotiq.tools.bridge.perf_snapshot import perf_snapshot as _perf_snapshot
from godotiq.tools.bridge.editor_context import editor_context as _editor_context

@mcp.tool(name="godotiq_screenshot", annotations={...})
async def godotiq_screenshot(viewport="game", scale=0.5, format="webp", ctx=None):
    try:
        return await _screenshot(viewport, scale, format)
    except BridgeConnectionError as e:
        return {"error": str(e), "hint": "Enable the GodotIQ addon..."}
    except BridgeTimeoutError as e:
        return {"error": str(e), "hint": "The game may not be running..."}
    except BridgeError as e:
        return {"error": f"[{e.code}] {e.error_message}"}
```

Each tool file should import `get_bridge` (or `get_bridge_or_none` for `editor_context`) from `godotiq.bridge.session` directly, so the call chain is: `__init__.py` wrapper -> tool function -> `get_bridge()` -> bridge connection -> WebSocket.

## Import Structure Summary

Each tool file needs these imports:

| File | Imports |
|------|---------|
| `screenshot.py` | `from godotiq.bridge.session import get_bridge` |
| `run.py` | `from godotiq.bridge.session import get_bridge` |
| `perf_snapshot.py` | `from godotiq.bridge.session import get_bridge` |
| `editor_context.py` | `from godotiq.bridge.session import get_bridge_or_none`; `import os` |

## Constraints

- These files contain **no** `@mcp.tool()` decorators -- that responsibility belongs to section 05's `__init__.py`.
- These files do **not** catch `BridgeConnectionError`, `BridgeTimeoutError`, or `BridgeError` -- exception handling is done by the wrappers in `__init__.py`.
- The one exception is `editor_context.py`, which uses `get_bridge_or_none()` (a try/except wrapper around `get_bridge()`) to implement its fallback behavior. It never raises bridge exceptions.
- Do **not** modify any Phase 1 files (parsers, existing tools, session.py, config.py, etc.).
- All functions must have type hints and docstrings per project conventions.

---

## Post-Implementation Notes

### Files Created/Overwritten
- `src/godotiq/tools/bridge/screenshot.py` — overwritten Section 05 stub
- `src/godotiq/tools/bridge/run.py` — overwritten Section 05 stub
- `src/godotiq/tools/bridge/perf_snapshot.py` — overwritten Section 05 stub
- `src/godotiq/tools/bridge/editor_context.py` — overwritten Section 05 stub

### Deviations from Plan
1. **screenshot.py imports protocol constants**: Uses `SCREENSHOT_MAX_RETRIES` and `SCREENSHOT_RETRY_DELAY` from `godotiq.bridge.protocol` instead of hardcoding `3` and `0.5` (code review fix for DRY compliance).

### Test Results
- 460 tests passed (all Phase 1 tests, ignoring test_bridge)