diff --git a/src/godotiq/server.py b/src/godotiq/server.py
index 160721e..bbaf9c5 100644
--- a/src/godotiq/server.py
+++ b/src/godotiq/server.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import logging
+import sys
 from contextlib import asynccontextmanager
 from pathlib import Path
 
@@ -10,6 +11,14 @@ from mcp.server.fastmcp import FastMCP
 
 from godotiq.session import GodotIQSession, discover_project_root
 
+# --- Startup logging to stderr so diagnostics are visible in MCP hosts ---
+_startup_handler = logging.StreamHandler(sys.stderr)
+_startup_handler.setFormatter(
+    logging.Formatter("[godotiq] %(levelname)s: %(message)s")
+)
+logging.getLogger("godotiq").addHandler(_startup_handler)
+logging.getLogger("godotiq").setLevel(logging.INFO)
+
 logger = logging.getLogger(__name__)
 
 
@@ -18,6 +27,7 @@ async def godotiq_lifespan(server):
     """Initialize GodotIQ session on server startup."""
     project_root = discover_project_root()
     if project_root is not None:
+        logger.info("Project root: %s", project_root)
         session = GodotIQSession(project_root)
         try:
             session.load()
@@ -26,7 +36,7 @@ async def godotiq_lifespan(server):
             session = None
         else:
             logger.info(
-                "GodotIQ session loaded: %s (%d scripts, %d scenes)",
+                "Session loaded: %s (%d scripts, %d scenes)",
                 session._index.project_name,
                 session._index.total_scripts,
                 session._index.total_scenes,
@@ -43,6 +53,13 @@ async def godotiq_lifespan(server):
 
 mcp = FastMCP(name="godotiq", lifespan=godotiq_lifespan)
 
+# Fix __main__ double-import: when run via `python -m godotiq.server`,
+# this module is loaded as __main__. Tool modules that do
+# `from godotiq.server import mcp` would trigger a second import,
+# creating a separate mcp instance. Register ourselves under the
+# canonical name so all imports share the same mcp object.
+sys.modules.setdefault("godotiq.server", sys.modules[__name__])
+
 
 PROMPT_DIR = Path(__file__).resolve().parent / "prompts"
 
@@ -54,8 +71,12 @@ async def godot_development_prompt() -> str:
 
 
 @mcp.tool()
-async def godotiq_ping() -> dict:
-    """Health check tool. Returns server status and version."""
+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"}
 
 
diff --git a/src/godotiq/tools/bridge/__init__.py b/src/godotiq/tools/bridge/__init__.py
index af32c7f..32c169c 100644
--- a/src/godotiq/tools/bridge/__init__.py
+++ b/src/godotiq/tools/bridge/__init__.py
@@ -24,6 +24,7 @@ from godotiq.tools.bridge.undo_history import undo_history as _undo_history
 from godotiq.tools.bridge.save_scene import save_scene as _save_scene
 from godotiq.tools.bridge.camera import camera as _camera
 from godotiq.tools.bridge.ui_map import ui_map as _ui_map
+from godotiq.tools.output_limit import apply_output_limit, _CHAR_LIMITS
 
 
 @mcp.tool(
@@ -42,6 +43,7 @@ async def godotiq_screenshot(
     quality: float = 0.5,
     camera_position: list[float] | None = None,
     camera_target: list[float] | None = None,
+    detail: str = "normal",
     ctx: Context = None,
 ) -> dict:
     """Visual verification of the game or editor viewport. Use scale=0.25, quality=0.3 for token-efficient checks. Prefer state_inspect for data — use screenshot only when you need visual confirmation.
@@ -53,6 +55,7 @@ async def godotiq_screenshot(
         quality: Encoding quality (0.1-1.0). Use 0.3 for routine checks, 0.8 for detail.
         camera_position: [x, y, z] — Editor only. Move camera before capture.
         camera_target: [x, y, z] — Editor only. Camera looks at this point.
+        detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
     """
     try:
         return await _screenshot(
@@ -81,6 +84,7 @@ async def godotiq_screenshot(
 async def godotiq_run(
     action: str = "play",
     scene: str = "main",
+    detail: str = "normal",
     ctx: Context = None,
 ) -> dict:
     """Start or stop the Godot game. Must call play before using any game-side tools (state_inspect, screenshot, input, nav_query, watch, ui_map). Waits for confirmed startup.
@@ -88,6 +92,7 @@ async def godotiq_run(
     Args:
         action: "play" to start, "stop" to halt the running game.
         scene: "main" (default), "current", or a res:// path.
+        detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
     """
     try:
         return await _run(action=action, scene=scene)
@@ -140,10 +145,15 @@ async def godotiq_perf_snapshot(
         "openWorldHint": True,
     },
 )
-async def godotiq_editor_context(ctx: Context = None) -> dict:
-    """Call FIRST alongside project_summary. Returns editor state: open scenes, selected nodes, is game running, project path. Essential for understanding current context before any operations."""
+async def godotiq_editor_context(detail: str = "normal", ctx: Context = None) -> dict:
+    """Call FIRST alongside project_summary. Returns editor state: open scenes, selected nodes, is game running, project path. Essential for understanding current context before any operations.
+
+    Args:
+        detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
+    """
     try:
-        return await _editor_context()
+        result = await _editor_context()
+        return apply_output_limit(result, detail)
     except Exception as e:
         return {"error": f"Unexpected error: {e}"}
 
@@ -198,6 +208,7 @@ async def godotiq_scene_tree(
 async def godotiq_node_ops(
     operations: list[dict] | None = None,
     scene: str | None = None,
+    detail: str = "normal",
     ctx: Context = None,
 ) -> dict:
     """Core scene editing tool. Batch operations with Ctrl+Z undo: move, rotate, scale, set_property, add_child, delete, duplicate, reparent. All ops in one call = one undo action. ALWAYS add validate:true for spatial operations (move/scale/add_child) to prevent placing objects inside walls or on top of each other. If ANY validated op is BLOCKED, the entire batch is rejected (atomic).
@@ -205,6 +216,7 @@ async def godotiq_node_ops(
     Args:
         operations: List of operation dicts, each with 'op' key. Max 50 per call.
         scene: Optional .tscn path for spatial validation (auto-detects main scene if omitted).
+        detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
     """
     try:
         scene_data = None
@@ -214,7 +226,8 @@ async def godotiq_node_ops(
         if any(op.get("validate") for op in ops):
             scene_data = _resolve_scene_data(ctx, scene)
 
-        return await _node_ops(operations=ops, scene_data=scene_data)
+        result = await _node_ops(operations=ops, scene_data=scene_data)
+        return apply_output_limit(result, detail)
     except BridgeConnectionError as e:
         return {"error": str(e), "hint": "Enable the GodotIQ addon in Godot editor and ensure it is running."}
     except BridgeTimeoutError as e:
@@ -281,6 +294,7 @@ async def godotiq_script_ops(
     validate_before_write: bool = True,
     auto_fix: bool = False,
     dry_run: bool = False,
+    detail: str = "normal",
     ctx: Context = None,
 ) -> dict:
     """Read, write, or patch GDScript files with convention validation. ALWAYS prefer this over raw file writes for .gd files. Patch mode (find-and-replace) is safest — only changes what you specify.
@@ -293,6 +307,7 @@ async def godotiq_script_ops(
         validate_before_write: Run convention checks before writing (default true).
         auto_fix: Auto-fix detected issues.
         dry_run: Validate without writing.
+        detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
     """
     try:
         return await _script_ops(
@@ -328,6 +343,7 @@ async def godotiq_file_ops(
     show_sizes: bool = False,
     content: str = "",
     destination: str = "",
+    detail: str = "normal",
     ctx: Context = None,
 ) -> dict:
     """Filesystem operations within the Godot project: list, read, write, move, delete, search, tree, uid_to_path, path_to_uid, rename (with reference updates). Respects protected files from .godotiq.json. For .gd files, prefer script_ops instead.
@@ -343,9 +359,10 @@ async def godotiq_file_ops(
         show_sizes: Include file sizes in tree.
         content: File content for write. For rename: "false" to skip ref updates.
         destination: Destination for move/rename.
+        detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
     """
     try:
-        return await _file_ops(
+        result = await _file_ops(
             op=op,
             path=path,
             query=query,
@@ -357,6 +374,15 @@ async def godotiq_file_ops(
             content=content,
             destination=destination,
         )
+        # Special case: truncate large string content for read ops
+        if op == "read" and "content" in result and "error" not in result:
+            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
+        return apply_output_limit(result, detail)
     except Exception as e:
         return {"error": f"Unexpected error: {e}"}
 
@@ -376,6 +402,7 @@ async def godotiq_input(
     continue_on_error: bool = False,
     wait_for: str = "",
     wait_for_timeout_ms: int = 5000,
+    detail: str = "normal",
     ctx: Context = None,
 ) -> dict:
     """Simulate player input in the running game: actions, keys, UI taps, waits. Use after ui_map to know what's on screen. Supports signal verification and side-effect tracking. Game must be running.
@@ -386,15 +413,17 @@ async def godotiq_input(
         continue_on_error: Keep going if a command fails.
         wait_for: Signal to wait for (e.g. "signal:OrderManager.order_accepted").
         wait_for_timeout_ms: Timeout for wait_for in ms.
+        detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
     """
     try:
-        return await _input_sim(
+        result = await _input_sim(
             commands=commands,
             track_side_effects=track_side_effects,
             continue_on_error=continue_on_error,
             wait_for=wait_for,
             wait_for_timeout_ms=wait_for_timeout_ms,
         )
+        return apply_output_limit(result, detail)
     except BridgeConnectionError as e:
         return {"error": str(e), "hint": "Enable the GodotIQ addon in Godot editor and ensure it is running."}
     except BridgeTimeoutError as e:
@@ -543,7 +572,8 @@ async def godotiq_watch(
         detail: "brief" (last 10 events only), "normal" (default), "full" (all events + timestamps).
     """
     try:
-        return await _watch(action=action, watches=watches, sample_interval_ms=sample_interval_ms, detail=detail)
+        result = await _watch(action=action, watches=watches, sample_interval_ms=sample_interval_ms, detail=detail)
+        return apply_output_limit(result, detail)
     except BridgeConnectionError as e:
         return {"error": str(e), "hint": "Enable the GodotIQ addon in Godot editor and ensure it is running."}
     except BridgeTimeoutError as e:
@@ -563,10 +593,15 @@ async def godotiq_watch(
         "openWorldHint": True,
     },
 )
-async def godotiq_undo_history(ctx: Context = None) -> dict:
-    """Review what was changed. Shows undo/redo state and recent GodotIQ action history. Call after node_ops to verify changes. Editor-side — no game needed."""
+async def godotiq_undo_history(detail: str = "normal", ctx: Context = None) -> dict:
+    """Review what was changed. Shows undo/redo state and recent GodotIQ action history. Call after node_ops to verify changes. Editor-side — no game needed.
+
+    Args:
+        detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
+    """
     try:
-        return await _undo_history()
+        result = await _undo_history()
+        return apply_output_limit(result, detail)
     except BridgeConnectionError as e:
         return {"error": str(e), "hint": "Enable the GodotIQ addon in Godot editor and ensure it is running."}
     except BridgeTimeoutError as e:
@@ -586,8 +621,12 @@ async def godotiq_undo_history(ctx: Context = None) -> dict:
         "openWorldHint": False,
     },
 )
-async def godotiq_save_scene(ctx: Context = None) -> dict:
-    """Persist editor changes to disk. Call after node_ops or any scene modifications. In-place save. Editor-side — no game needed."""
+async def godotiq_save_scene(detail: str = "normal", ctx: Context = None) -> dict:
+    """Persist editor changes to disk. Call after node_ops or any scene modifications. In-place save. Editor-side — no game needed.
+
+    Args:
+        detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
+    """
     try:
         return await _save_scene()
     except BridgeConnectionError as e:
@@ -614,6 +653,7 @@ async def godotiq_camera(
     position: list[float] | None = None,
     target: list[float] | None = None,
     node: str = "",
+    detail: str = "normal",
     ctx: Context = None,
 ) -> dict:
     """Editor 3D camera control: get current position, reposition, or focus on a node. Editor-side — no game needed.
@@ -623,6 +663,7 @@ async def godotiq_camera(
         position: [x, y, z] for look_at — camera position.
         target: [x, y, z] for look_at — camera target.
         node: Node name for focus_node.
+        detail: Output verbosity — "brief" (summary), "normal" (default), or "full" (everything).
     """
     try:
         return await _camera(action=action, position=position, target=target, node=node)
diff --git a/tests/test_tools/test_detail_levels.py b/tests/test_tools/test_detail_levels.py
index f09b146..d6ab06d 100644
--- a/tests/test_tools/test_detail_levels.py
+++ b/tests/test_tools/test_detail_levels.py
@@ -109,3 +109,170 @@ class TestPlacementDetail:
         sig = inspect.signature(godotiq_placement)
         assert "detail" in sig.parameters
         assert sig.parameters["detail"].default == "normal"
+
+
+# --- Section 05: Bridge tools detail parameter tests ---
+
+from unittest.mock import AsyncMock, patch
+
+
+class TestBridgeToolSignatures:
+    """All bridge tools that need detail added have correct signatures."""
+
+    @pytest.mark.parametrize("tool_name", [
+        "godotiq_screenshot",
+        "godotiq_run",
+        "godotiq_editor_context",
+        "godotiq_node_ops",
+        "godotiq_script_ops",
+        "godotiq_file_ops",
+        "godotiq_input",
+        "godotiq_undo_history",
+        "godotiq_save_scene",
+        "godotiq_camera",
+    ])
+    def test_bridge_tool_accepts_detail(self, tool_name: str) -> None:
+        """Each bridge tool accepts a detail parameter with default 'normal'."""
+        import godotiq.tools.bridge as bridge_mod
+        func = getattr(bridge_mod, tool_name)
+        sig = inspect.signature(func)
+        assert "detail" in sig.parameters, f"{tool_name} missing detail param"
+        assert sig.parameters["detail"].default == "normal", f"{tool_name} default != 'normal'"
+
+    def test_ping_accepts_detail(self) -> None:
+        """godotiq_ping accepts detail parameter with default 'normal'."""
+        from godotiq.server import godotiq_ping
+        sig = inspect.signature(godotiq_ping)
+        assert "detail" in sig.parameters
+        assert sig.parameters["detail"].default == "normal"
+
+    def test_watch_has_detail(self) -> None:
+        """godotiq_watch already has detail — verify it still works."""
+        from godotiq.tools.bridge import godotiq_watch
+        sig = inspect.signature(godotiq_watch)
+        assert "detail" in sig.parameters
+        assert sig.parameters["detail"].default == "normal"
+
+
+class TestBridgeHighOutputFiltering:
+    """High-output bridge tools apply output limiting."""
+
+    @pytest.mark.asyncio
+    async def test_file_ops_brief_applies_limit(self) -> None:
+        """file_ops with detail='brief' calls apply_output_limit."""
+        from godotiq.tools.bridge import godotiq_file_ops
+        large_result = {"entries": [{"name": f"file_{i}.gd"} for i in range(200)]}
+        with patch("godotiq.tools.bridge._file_ops", new_callable=AsyncMock, return_value=large_result):
+            result = await godotiq_file_ops(op="list", path="res://", detail="brief")
+        size = _output_size(result)
+        assert size < BRIEF_LIMIT
+
+    @pytest.mark.asyncio
+    async def test_file_ops_read_truncates_content(self) -> None:
+        """file_ops op='read' with large content truncates string."""
+        from godotiq.tools.bridge import godotiq_file_ops
+        large_content = "x" * 20_000
+        large_result = {"content": large_content, "path": "res://big.gd"}
+        with patch("godotiq.tools.bridge._file_ops", new_callable=AsyncMock, return_value=large_result):
+            result = await godotiq_file_ops(op="read", path="res://big.gd", detail="brief")
+        assert len(result["content"]) < 2000
+        assert result.get("content_truncated") is True
+
+    @pytest.mark.asyncio
+    async def test_node_ops_brief_applies_limit(self) -> None:
+        """node_ops with detail='brief' calls apply_output_limit."""
+        from godotiq.tools.bridge import godotiq_node_ops
+        large_result = {"results": [{"node": f"Node{i}", "status": "ok"} for i in range(200)]}
+        with patch("godotiq.tools.bridge._node_ops", new_callable=AsyncMock, return_value=large_result):
+            result = await godotiq_node_ops(operations=[{"op": "move"}], detail="brief")
+        size = _output_size(result)
+        assert size < BRIEF_LIMIT
+
+    @pytest.mark.asyncio
+    async def test_editor_context_brief_applies_limit(self) -> None:
+        """editor_context with detail='brief' calls apply_output_limit."""
+        from godotiq.tools.bridge import godotiq_editor_context
+        large_result = {"open_scenes": [f"scene_{i}.tscn" for i in range(200)]}
+        with patch("godotiq.tools.bridge._editor_context", new_callable=AsyncMock, return_value=large_result):
+            result = await godotiq_editor_context(detail="brief")
+        size = _output_size(result)
+        assert size < BRIEF_LIMIT
+
+    @pytest.mark.asyncio
+    async def test_input_brief_applies_limit(self) -> None:
+        """input with detail='brief' calls apply_output_limit."""
+        from godotiq.tools.bridge import godotiq_input
+        large_result = {"side_effects": [{"key": f"val_{i}"} for i in range(200)]}
+        with patch("godotiq.tools.bridge._input_sim", new_callable=AsyncMock, return_value=large_result):
+            result = await godotiq_input(commands=[{"key": "space"}], detail="brief")
+        size = _output_size(result)
+        assert size < BRIEF_LIMIT
+
+    @pytest.mark.asyncio
+    async def test_watch_brief_applies_limit(self) -> None:
+        """watch with detail='brief' applies apply_output_limit as safety net."""
+        from godotiq.tools.bridge import godotiq_watch
+        large_result = {"events": [{"ts": i, "val": f"v{i}"} for i in range(200)]}
+        with patch("godotiq.tools.bridge._watch", new_callable=AsyncMock, return_value=large_result):
+            result = await godotiq_watch(action="read", detail="brief")
+        size = _output_size(result)
+        assert size < BRIEF_LIMIT
+
+    @pytest.mark.asyncio
+    async def test_undo_history_brief_applies_limit(self) -> None:
+        """undo_history with detail='brief' applies apply_output_limit."""
+        from godotiq.tools.bridge import godotiq_undo_history
+        large_result = {"godotiq_actions": [{"action": f"op_{i}"} for i in range(200)]}
+        with patch("godotiq.tools.bridge._undo_history", new_callable=AsyncMock, return_value=large_result):
+            result = await godotiq_undo_history(detail="brief")
+        size = _output_size(result)
+        assert size < BRIEF_LIMIT
+
+
+class TestBridgeNoOpTools:
+    """No-op bridge tools accept detail but don't filter."""
+
+    @pytest.mark.asyncio
+    async def test_screenshot_accepts_detail(self) -> None:
+        """screenshot accepts detail but returns result unchanged."""
+        from godotiq.tools.bridge import godotiq_screenshot
+        mock_result = {"image": "base64data", "format": "webp"}
+        with patch("godotiq.tools.bridge._screenshot", new_callable=AsyncMock, return_value=mock_result):
+            result = await godotiq_screenshot(detail="brief")
+        assert result == mock_result
+
+    @pytest.mark.asyncio
+    async def test_run_accepts_detail(self) -> None:
+        """run accepts detail but returns result unchanged."""
+        from godotiq.tools.bridge import godotiq_run
+        mock_result = {"status": "playing"}
+        with patch("godotiq.tools.bridge._run", new_callable=AsyncMock, return_value=mock_result):
+            result = await godotiq_run(action="play", detail="brief")
+        assert result == mock_result
+
+    @pytest.mark.asyncio
+    async def test_save_scene_accepts_detail(self) -> None:
+        """save_scene accepts detail but returns result unchanged."""
+        from godotiq.tools.bridge import godotiq_save_scene
+        mock_result = {"saved": True}
+        with patch("godotiq.tools.bridge._save_scene", new_callable=AsyncMock, return_value=mock_result):
+            result = await godotiq_save_scene(detail="brief")
+        assert result == mock_result
+
+    @pytest.mark.asyncio
+    async def test_camera_accepts_detail(self) -> None:
+        """camera accepts detail but returns result unchanged."""
+        from godotiq.tools.bridge import godotiq_camera
+        mock_result = {"position": [0, 5, 10]}
+        with patch("godotiq.tools.bridge._camera", new_callable=AsyncMock, return_value=mock_result):
+            result = await godotiq_camera(action="get_position", detail="brief")
+        assert result == mock_result
+
+    @pytest.mark.asyncio
+    async def test_script_ops_accepts_detail(self) -> None:
+        """script_ops accepts detail but returns result unchanged."""
+        from godotiq.tools.bridge import godotiq_script_ops
+        mock_result = {"content": "extends Node", "path": "res://test.gd"}
+        with patch("godotiq.tools.bridge._script_ops", new_callable=AsyncMock, return_value=mock_result):
+            result = await godotiq_script_ops(op="read", path="res://test.gd", detail="brief")
+        assert result == mock_result
