Skip to content

Comments

feat(#672): Add Keep Server Running toggle in Unity Editor Window and HTTP mode support#725

Closed
whatevertogo wants to merge 1 commit intoCoplayDev:betafrom
whatevertogo:feat-672-keep-server-running
Closed

feat(#672): Add Keep Server Running toggle in Unity Editor Window and HTTP mode support#725
whatevertogo wants to merge 1 commit intoCoplayDev:betafrom
whatevertogo:feat-672-keep-server-running

Conversation

@whatevertogo
Copy link
Contributor

@whatevertogo whatevertogo commented Feb 11, 2026

Summary

Fixes #672 - MCP server can now stay running even when Unity disconnects, enabling automatic reconnection when Unity comes back after Domain Reload, test runs, or entering/leaving Play Mode.

Changes

Unity Editor (C#)

  • Added "Keep Server Running" toggle in Advanced Settings
  • Toggle is enabled only when HTTP transport is active (stdio mode shows explanation)
  • Preference persisted via EditorPrefs (EditorPrefKeys.KeepServerRunning)
  • WebSocket transport sends keep_server_running flag in registration message

Server (Python)

  • PluginSession now stores keep_server_running flag per session
  • PluginHub._handle_register() extracts and passes the flag to registry
  • server_lifespan() in HTTP mode now bypasses legacy stdio connection pool
  • When keep_server_running=True, server maintains connection for auto-reconnect

Tests

  • Added basic test verifying message format and PluginSession field

User Flow

  1. User enables "Keep Server Running" toggle in Unity MCP Editor Window (Advanced tab)
  2. Server receives flag during Unity registration
  3. When Unity disconnects (Domain Reload, Play Mode, etc.), server stays running
  4. When Unity reconnects, automatic reconnection occurs via existing PluginHub mechanism

Summary by Sourcery

Support keeping the MCP server running across Unity disconnects, with HTTP mode-aware connection management and per-session keep-alive state.

New Features:

  • Add per-session keep_server_running flag so the MCP server can stay available for Unity auto-reconnection.
  • Introduce a persisted Unity Editor preference for keeping the server running across domain reloads and play mode changes.

Enhancements:

  • Adjust server startup and shutdown to bypass the legacy stdio connection pool in HTTP mode and respect sessions that request the server to keep running.
  • Extend plugin registration to propagate the keep_server_running flag from Unity to PluginSession records for use by connection management logic.

Tests:

  • Add tests covering the keep_server_running message field and the corresponding PluginSession attribute.

Summary by CodeRabbit

  • New Features

    • Added a "Keep Server Running" mode: per-session option to keep the server active after Unity disconnects, configurable via a new toggle in Advanced Settings. Connection handling updated to respect this mode across HTTP and stdio workflows.
  • Tests

    • Added test coverage validating the Keep Server Running behavior and session state handling.

Copilot AI review requested due to automatic review settings February 11, 2026 21:30
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 11, 2026

Reviewer's Guide

Implements a per-session "Keep Server Running" feature controlled from the Unity editor (only in HTTP transport mode), propagates it through the WebSocket registration flow into the Python server, and adjusts server startup/shutdown and tests so the MCP server can stay alive across Unity disconnects for auto-reconnect.

Sequence diagram for keep_server_running registration and reconnection

sequenceDiagram
    actor UnityDeveloper
    participant UnityEditorWindow
    participant WebSocketClient
    participant PluginHub
    participant PluginRegistry
    participant PluginSession

    UnityDeveloper->>UnityEditorWindow: Open MCP Editor Window (Advanced tab)
    UnityDeveloper->>UnityEditorWindow: Enable KeepServerRunning toggle
    UnityEditorWindow->>WebSocketClient: Build RegisterMessage with keep_server_running = true
    WebSocketClient->>PluginHub: WebSocket connect
    WebSocketClient->>PluginHub: RegisterMessage(keep_server_running = true)

    PluginHub->>PluginHub: Extract keep_server_running from payload
    PluginHub->>PluginRegistry: register(session_id, project_name, project_hash, unity_version, project_path, user_id, keep_server_running)

    PluginRegistry->>PluginSession: Create PluginSession
    PluginRegistry->>PluginSession: Set keep_server_running = true
    PluginRegistry-->>PluginHub: PluginSession instance
    PluginHub-->>WebSocketClient: RegisteredMessage(session_id)

    rect rgb(230,230,230)
        note over PluginHub,PluginRegistry: Unity disconnects (Domain Reload, Play Mode, exit)
        WebSocketClient--xPluginHub: WebSocket closed
        PluginHub->>PluginRegistry: Session remains stored (keep_server_running = true)
    end

    UnityDeveloper->>UnityEditorWindow: Reopen project / reenter Play Mode
    UnityEditorWindow->>WebSocketClient: New RegisterMessage (same project_hash, keep_server_running = true)
    WebSocketClient->>PluginHub: RegisterMessage
    PluginHub->>PluginRegistry: register(... keep_server_running = true)
    PluginRegistry-->>PluginHub: PluginSession (replaced or reused)
    PluginHub-->>WebSocketClient: RegisteredMessage (auto-reconnect established)
Loading

File-Level Changes

Change Details Files
Adjust server startup/shutdown to treat HTTP mode separately and respect per-session keep-alive state before disconnecting Unity instances.
  • Extend server_lifespan globals to include keep_server_running state.
  • Change _emit_startup to skip initializing the legacy Unity connection pool when HTTP mode is enabled and log that PluginHub will manage connections.
  • On shutdown, compute whether any session has keep_server_running enabled and only disconnect the legacy pool when needed.
  • Improve shutdown logging to indicate Keep-Running mode and clear keep_server_running state at the end of lifespan.
Server/src/main.py
Propagate keep_server_running from Unity registration messages into plugin sessions used by the server.
  • Extract keep_server_running from incoming RegisterMessage payloads with a default of False in the WebSocket handler.
  • Pass keep_server_running into the registry.register call when creating or replacing a session.
  • Add keep_server_running field to PluginSession dataclass and plumb it through the register method arguments and construction.
Server/src/transport/plugin_hub.py
Server/src/transport/plugin_registry.py
Prepare middleware and editor configuration to support keep-server-running behavior and preferences.
  • Update Unity instance middleware to import the main module, making global keep_server_running state accessible alongside the legacy pool.
  • Add a new EditorPrefs key for persisting the Keep Server Running toggle in the Unity editor.
Server/src/transport/unity_instance_middleware.py
MCPForUnity/Editor/Constants/EditorPrefKeys.cs
Add tests to validate keep_server_running message shape and PluginSession integration.
  • Create a dedicated test module for the Keep Server Running feature.
  • Verify that a registration-like payload can carry keep_server_running True/False and that PluginSession instances correctly reflect the field and its default value.
Server/tests/test_keep_running_mode.py

Assessment against linked issues

Issue Objective Addressed Explanation
#672 Keep the MCP server running instead of shutting down when the last Unity connection is dropped (including HTTP mode), so it can be reused for later reconnections.
#672 Have the Unity MCP client automatically start/restart a session (auto-connect and reconnect) when a server is available, ideally configurable via a UI setting. The PR adds a "Keep Server Running" toggle and plumbs a keep_server_running flag through the registration and server lifecycle, enabling the server to stay alive for reconnection. However, there are no changes shown that implement Unity-side auto-start of a session when a server is available or any new auto-connect/reconnect logic in the Unity client; the diff only adds an EditorPrefs key on the Unity side, not behavior. The need to manually initiate a session from Unity is not removed.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

Adds a "Keep Server Running" toggle exposed in the Unity Editor, propagated in the registration payload, stored per-session on the server, and used to influence startup/shutdown behavior so sessions can request the server remain running after Unity disconnects.

Changes

Cohort / File(s) Summary
Editor Prefs & UI
MCPForUnity/Editor/Constants/EditorPrefKeys.cs, MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs, MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.uxml
Added KeepServerRunning EditorPref key; new "Keep Server Running" toggle in Advanced UI, persisted to EditorPrefs, exposes OnKeepServerRunningChanged event and triggers UI updates.
Client Transport
MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs
Include keep_server_running boolean in register payload, reading value from EditorPrefs.
Transport Models & Registry
Server/src/transport/models.py, Server/src/transport/plugin_registry.py, Server/src/transport/plugin_hub.py
Added keep_server_running to RegisterMessage and PluginSession; PluginRegistry.register() accepts and stores the flag; _handle_register extracts and persists flag per session.
Server lifecycle
Server/src/main.py
Introduced per-session keep_server_running mapping; differentiated HTTP vs stdio startup behavior (HTTP disables legacy pool); shutdown checks per-session flags to conditionally avoid disconnecting/stop server and logs keep-running mode; clears state after shutdown.
Tests
Server/tests/test_keep_running_mode.py
Added tests for RegisterMessage keep_server_running true/false and PluginSession default/explicit behavior.
Unity Test Project files
TestProjects/UnityMCPTests/Packages/manifest.json, TestProjects/UnityMCPTests/ProjectSettings/ProjectVersion.txt, TestProjects/UnityMCPTests/ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json
Bumped several package versions and Unity project version; removed two fields from coverage Settings.json.

Sequence Diagram(s)

sequenceDiagram
    participant UI as UI (McpAdvancedSection)
    participant Pref as EditorPrefs
    participant Client as WebSocketTransportClient
    participant Server as MCP Server (main.py)
    participant Registry as PluginRegistry

    UI->>Pref: SetBool(KeepServerRunning, value)
    Client->>Pref: GetBool(KeepServerRunning)
    Pref-->>Client: value
    Client->>Server: RegisterMessage{..., keep_server_running}
    Server->>Registry: register(session_id, ..., keep_server_running)
    Registry-->>Server: PluginSession stored (keep_server_running)
    Note over Server: On last client disconnect
    Server->>Server: any session.keep_server_running?
    alt any true
        Server->>Server: Skip shutdown (remain running)
    else
        Server->>Server: Proceed with shutdown
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • dsarno

Poem

🐰 A little toggle, a steady hum,
Keep the server awake when the clients run.
No more surprise exits, no frantic restart,
A rabbit-approved setting to keep things smart! 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Most changes are in scope, but updates to test project package versions and Unity version appear unrelated to the keep-running feature requirements. Remove or document the rationale for package dependency updates (com.unity.ai.navigation, com.unity.collab-proxy, etc.) and Unity version bump (2021.3.45f2 to 2022.3.62f3c1).
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly describes the main changes: adding a Keep Server Running toggle in the Unity Editor and implementing HTTP mode support.
Linked Issues check ✅ Passed The PR successfully implements the two main objectives from issue #672: keeping the server running when Unity disconnects and exposing a configurable UI option in the Unity Editor.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering all required sections with clear explanations of changes, testing, and objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The new keep_server_running global is referenced and cleared in server_lifespan but not initialized anywhere in main.py, which risks a NameError at shutdown—consider explicitly defining it (e.g., as a dict[str, bool] = {}) near the other globals.
  • In test_keep_running_message_format, you only assert fields on raw dicts; wiring this through the actual RegisterMessage model (and any serialization/deserialization path used in production) would better validate that the new flag is correctly integrated into the message schema.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `keep_server_running` global is referenced and cleared in `server_lifespan` but not initialized anywhere in `main.py`, which risks a `NameError` at shutdown—consider explicitly defining it (e.g., as a `dict[str, bool] = {}`) near the other globals.
- In `test_keep_running_message_format`, you only assert fields on raw dicts; wiring this through the actual `RegisterMessage` model (and any serialization/deserialization path used in production) would better validate that the new flag is correctly integrated into the message schema.

## Individual Comments

### Comment 1
<location> `Server/src/main.py:188-193` </location>
<code_context>
-            _unity_connection_pool = get_unity_connection_pool()
+            # Initialize connection pool and discover instances (stdio mode only)
+            # In HTTP mode, PluginHub handles connections directly
+            if enable_http_server:
+                _unity_connection_pool = None  # No legacy pool in HTTP mode
+                logger.info("HTTP mode enabled - PluginHub will manage Unity connections")
+            else:
+                _unity_connection_pool = get_unity_connection_pool()
             instances = _unity_connection_pool.discover_all_instances()

             if instances:
</code_context>

<issue_to_address>
**issue (bug_risk):** HTTP mode path sets the pool to None but still unconditionally calls discover_all_instances on it.

To avoid the runtime failure in HTTP mode, gate the discovery call on `enable_http_server`—for example, move `instances = _unity_connection_pool.discover_all_instances()` into the `else:` block so it only runs when the pool is initialized, or skip instance discovery entirely when running in HTTP mode.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d85d1fd51f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

project_hash = payload.project_hash
unity_version = payload.unity_version
project_path = payload.project_path
keep_server_running = getattr(payload, 'keep_server_running', False)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add keep_server_running to RegisterMessage schema

PluginHub._handle_register reads getattr(payload, 'keep_server_running', False), but RegisterMessage (in transport/models.py) does not define that field, and Pydantic drops unknown keys by default. In practice, a Unity client sending "keep_server_running": true will still be registered with False, so the new keep-running toggle never takes effect.

Useful? React with 👍 / 👎.

# In HTTP mode, PluginHub manages connections independently.
# Only disconnect legacy pool if it was initialized (stdio mode).
# Check if any session has keep_server_running enabled.
has_keep_running = any(keep_server_running.values()) if keep_server_running else False

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Initialize keep_server_running before lifespan cleanup

The shutdown path now evaluates any(keep_server_running.values()), but keep_server_running is never initialized at module scope or assigned in this function. This causes a NameError during lifespan teardown, so server shutdown errors out regardless of whether any sessions were active.

Useful? React with 👍 / 👎.

logger.info("HTTP mode enabled - PluginHub will manage Unity connections")
else:
_unity_connection_pool = get_unity_connection_pool()
instances = _unity_connection_pool.discover_all_instances()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip stdio discovery when HTTP mode disables pool

In HTTP mode this branch explicitly sets _unity_connection_pool = None, but execution still falls through to _unity_connection_pool.discover_all_instances(). That triggers an AttributeError on each HTTP startup, causing noisy startup warnings and misleading connection-failure telemetry instead of cleanly bypassing stdio discovery.

Useful? React with 👍 / 👎.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements (or begins implementing) Issue #672 by introducing a “keep server running” capability intended to let the MCP server survive Unity disconnects and support reconnect flows, particularly in HTTP transport mode.

Changes:

  • Adds a keep_server_running boolean to PluginSession and threads it through PluginRegistry.register(...).
  • Updates PluginHub._handle_register() to read keep_server_running from the register payload and store it in the registry.
  • Adjusts server_lifespan() startup/shutdown behavior for HTTP mode and adds a new test module plus a Unity EditorPrefs key constant.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
Server/tests/test_keep_running_mode.py Adds tests for the new flag (currently contains issues that will fail tests / not validate the real model).
Server/src/transport/unity_instance_middleware.py Adds imports intended for keep-running state access (currently unused).
Server/src/transport/plugin_registry.py Extends PluginSession and PluginRegistry.register() with keep_server_running.
Server/src/transport/plugin_hub.py Extracts keep_server_running from registration payload and passes it into the registry.
Server/src/main.py Attempts to bypass legacy stdio connection pool in HTTP mode and conditionally disconnect based on keep-running state (currently has runtime-breaking issues).
MCPForUnity/Editor/Constants/EditorPrefKeys.cs Adds EditorPrefKeys.KeepServerRunning constant (no other Unity-side wiring found in this PR).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 189 to 193
else:
# Initialize connection pool and discover instances
_unity_connection_pool = get_unity_connection_pool()
instances = _unity_connection_pool.discover_all_instances()
# Initialize connection pool and discover instances (stdio mode only)
# In HTTP mode, PluginHub handles connections directly
if enable_http_server:
_unity_connection_pool = None # No legacy pool in HTTP mode
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In HTTP mode you set _unity_connection_pool = None, but then immediately call _unity_connection_pool.discover_all_instances(), which will raise AttributeError. Guard the discovery/connection logic so it only runs when the legacy pool is initialized (stdio mode), or move the instances = ... line inside the else branch.

Copilot uses AI. Check for mistakes.
Comment on lines 255 to +257
finally:
if _unity_connection_pool:
# In HTTP mode, PluginHub manages connections independently.
# Only disconnect legacy pool if it was initialized (stdio mode).
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep_server_running is referenced as a global dict (keep_server_running.values(), keep_server_running.clear()), but there is no module-level definition in main.py, so this will raise NameError during shutdown. Also, the new state is stored on PluginSession.keep_server_running, so this shutdown logic should derive the flag from the registry’s sessions (or define/populate the intended global state consistently).

Copilot uses AI. Check for mistakes.
project_hash = payload.project_hash
unity_version = payload.unity_version
project_path = payload.project_path
keep_server_running = getattr(payload, 'keep_server_running', False)
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getattr(payload, 'keep_server_running', False) will always fall back to False because RegisterMessage (in Server/src/transport/models.py) does not define a keep_server_running field, and Pydantic will ignore unknown keys by default. Add keep_server_running: bool = False to the RegisterMessage model (or configure it to allow extra fields) so the flag actually propagates into the registry.

Suggested change
keep_server_running = getattr(payload, 'keep_server_running', False)
keep_server_running = False

Copilot uses AI. Check for mistakes.
Comment on lines 148 to 150
# Also import main module to access global keep_server_running dict
import sys
from .. import main
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These imports (sys and from .. import main) are unused in this function as written, and will trip linters / add unnecessary coupling. Remove them, or use them explicitly if you intended to read/write shared keep-running state here.

Suggested change
# Also import main module to access global keep_server_running dict
import sys
from .. import main

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +48
session = PluginSession(
session_id="test-session-123",
project_name="TestProject",
project_hash="abc123",
unity_version="2022.3.0f0",
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PluginSession requires registered_at and connected_at (and tools defaults), but this test constructs it without those required args, so it will raise TypeError. Consider creating sessions via PluginRegistry.register(...) (which sets timestamps) or pass explicit registered_at/connected_at values in the test.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +18
def test_keep_running_message_format(self):
"""Verify keep_server_running field in RegisterMessage."""
# Test that RegisterMessage can be created with keep_server_running=True
msg_true = {
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test only asserts that a plain dict contains the keep_server_running key; it doesn’t validate that the server-side RegisterMessage model accepts/retains this field. To prevent regressions, consider parsing with RegisterMessage.model_validate(...) (or equivalent) and asserting the resulting model has the expected keep_server_running value.

Copilot uses AI. Check for mistakes.
Comment on lines 7 to 8
import pytest

Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pytest is imported but unused in this test module. If your CI runs an import/lint check, this will fail; otherwise it’s still safe to remove for cleanliness.

Suggested change
import pytest

Copilot uses AI. Check for mistakes.
internal const string CustomerUuid = "MCPForUnity.CustomerUUID";

internal const string ApiKey = "MCPForUnity.ApiKey";
internal const string KeepServerRunning = "MCPForUnity.KeepServerRunning";
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description mentions a Unity Editor UI toggle and sending a keep_server_running flag during WebSocket registration, but the only Unity-side change in this PR appears to be adding the EditorPrefs key. A repo-wide search shows no other references to EditorPrefKeys.KeepServerRunning or any C# code emitting keep_server_running in a registration message, so the feature looks incomplete or the description is ahead of the code.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Server/src/main.py (1)

190-222: ⚠️ Potential issue | 🔴 Critical

NameError in HTTP mode: instances is not defined.

When enable_http_server is True, the if branch (lines 192-194) sets _unity_connection_pool = None but never defines instances. Execution then falls through to line 199 (if instances:), which will raise a NameError at runtime for any HTTP-mode startup where skip_connect is False.

🐛 Proposed fix
             if enable_http_server:
                 _unity_connection_pool = None  # No legacy pool in HTTP mode
                 logger.info("HTTP mode enabled - PluginHub will manage Unity connections")
+                instances = []  # No legacy discovery in HTTP mode
             else:
                 _unity_connection_pool = get_unity_connection_pool()
                 instances = _unity_connection_pool.discover_all_instances()
🤖 Fix all issues with AI agents
In `@Server/src/main.py`:
- Around line 136-138: The global keep_server_running dict is never written to,
so shutdown logic never sees per-session keep-alive flags; fix by either
updating this dict when sessions register or by querying the registry at
shutdown: add code in PluginHub._handle_register to set
main.keep_server_running[session.session_id] = session.keep_server_running
(using the PluginSession/PluginHub path that creates the session), and ensure
you clear the key on unregister, OR change the shutdown routine that computes
has_keep_running to iterate PluginRegistry (or call
PluginRegistry.get_session(sid)) and check PluginSession.keep_server_running for
any active sessions instead of reading the unused keep_server_running dict.

In `@Server/src/transport/unity_instance_middleware.py`:
- Around line 148-150: The two imports introduced near the comment ("import sys"
and "from .. import main") are unused and create a circular dependency with
main.py (which imports UnityInstanceMiddleware); remove those dead imports and
the misleading comment about accessing keep_server_running, and ensure no other
code in UnityInstanceMiddleware references sys or main (if needed, access the
keep_server_running dict via a clearly documented API or pass it in instead of
importing main).

In `@Server/tests/test_keep_running_mode.py`:
- Around line 37-39: Change the boolean assertions to use identity checks:
replace equality comparisons with identity comparisons for the
"keep_server_running" flag in the test (update the assertions referencing
msg_true["keep_server_running"] and msg_false["keep_server_running"] to use "is
True" and "is False" respectively, and keep the key existence check
"keep_server_running" in msg_false as-is).
- Around line 15-39: Replace the tautological dict checks in
test_keep_running_message_format with actual instantiation/validation of the
RegisterMessage model: create RegisterMessage instances (e.g., via
RegisterMessage(**msg_true) and RegisterMessage(**msg_false) or
RegisterMessage.parse_obj(msg_true/msg_false), then assert
instance.keep_server_running is True/False and/or that instance.dict()/json()
contains the "keep_server_running" key; update imports if needed to reference
RegisterMessage.
🧹 Nitpick comments (1)
Server/src/transport/plugin_hub.py (1)

367-367: Unnecessary getattr — use direct attribute access for consistency.

payload is typed as RegisterMessage, which always has keep_server_running (with a default of False). The other fields on lines 363-366 use direct attribute access (payload.project_name, etc.). This should be consistent.

♻️ Suggested change
-        keep_server_running = getattr(payload, 'keep_server_running', False)
+        keep_server_running = payload.keep_server_running

Comment on lines +136 to +138
# Per-session keep_server_running flag, indexed by session_id
# Set by middleware when Unity registers with the flag enabled
keep_server_running: dict[str, bool] = {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

keep_server_running dict is never populated — shutdown keep-alive logic is inert.

The global keep_server_running: dict[str, bool] is declared here and checked during shutdown (line 259), but no code in this PR ever writes to it. The PluginHub._handle_register stores the flag on PluginSession in the registry, but never updates main.keep_server_running. Similarly, the unity_instance_middleware.py imports main but doesn't write to this dict.

As a result, has_keep_running (line 259) is always False, and the keep-server-running feature has no effect on shutdown behavior. You need to bridge the registration path to this global state — either by having _handle_register (or the registry) update this dict, or by querying PluginRegistry sessions during shutdown instead of maintaining a separate dict.

🔧 Option: query the registry directly at shutdown instead of maintaining a separate dict
-# Per-session keep_server_running flag, indexed by session_id
-# Set by middleware when Unity registers with the flag enabled
-keep_server_running: dict[str, bool] = {}

Then at shutdown (lines 256-271), query the registry:

-        has_keep_running = any(keep_server_running.values()) if keep_server_running else False
+        has_keep_running = False
+        if _plugin_registry is not None:
+            try:
+                sessions = await _plugin_registry.list_sessions()
+                has_keep_running = any(s.keep_server_running for s in sessions.values())
+            except Exception:
+                pass
 
         if _unity_connection_pool and not has_keep_running:
             _unity_connection_pool.disconnect_all()
 
         shutdown_msg = "MCP for Unity Server shut down"
         if has_keep_running:
             shutdown_msg += " (Keep-Running mode: maintaining server for Unity reconnection)"
         logger.info(shutdown_msg)
-
-        # Note: keep_server_running state is per-session, not server-global.
-        # Cleared when sessions expire, used by middleware to pass state.
-        keep_server_running.clear()
🤖 Prompt for AI Agents
In `@Server/src/main.py` around lines 136 - 138, The global keep_server_running
dict is never written to, so shutdown logic never sees per-session keep-alive
flags; fix by either updating this dict when sessions register or by querying
the registry at shutdown: add code in PluginHub._handle_register to set
main.keep_server_running[session.session_id] = session.keep_server_running
(using the PluginSession/PluginHub path that creates the session), and ensure
you clear the key on unregister, OR change the shutdown routine that computes
has_keep_running to iterate PluginRegistry (or call
PluginRegistry.get_session(sid)) and check PluginSession.keep_server_running for
any active sessions instead of reading the unused keep_server_running dict.

Comment on lines 15 to 39
def test_keep_running_message_format(self):
"""Verify keep_server_running field in RegisterMessage."""
# Test that RegisterMessage can be created with keep_server_running=True
msg_true = {
"project_name": "TestProject",
"project_hash": "hash123",
"unity_version": "2022.3.0f0",
"project_path": "/Test/Path",
"keep_server_running": True
}

# Test that RegisterMessage can be created with keep_server_running=False
msg_false = {
"project_name": "TestProject",
"project_hash": "hash456",
"unity_version": "2022.3.0f0",
"project_path": "/Test/Path",
"keep_server_running": False
}

# Verify the field name and values are correct
assert "keep_server_running" in msg_true
assert msg_true["keep_server_running"] == True
assert "keep_server_running" in msg_false
assert msg_false["keep_server_running"] == False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

test_keep_running_message_format doesn't actually test RegisterMessage.

This test creates plain Python dicts and asserts that keys exist in those dicts — it's a tautology. It should instantiate RegisterMessage to verify the model accepts and serializes the keep_server_running field correctly.

🔧 Proposed fix: actually test RegisterMessage
+from transport.models import RegisterMessage
+
+
 class TestKeepServerRunningMode:
     """Tests for keep_server_running functionality."""
 
     def test_keep_running_message_format(self):
         """Verify keep_server_running field in RegisterMessage."""
-        # Test that RegisterMessage can be created with keep_server_running=True
-        msg_true = {
-            "project_name": "TestProject",
-            "project_hash": "hash123",
-            "unity_version": "2022.3.0f0",
-            "project_path": "/Test/Path",
-            "keep_server_running": True
-        }
-
-        # Test that RegisterMessage can be created with keep_server_running=False
-        msg_false = {
-            "project_name": "TestProject",
-            "project_hash": "hash456",
-            "unity_version": "2022.3.0f0",
-            "project_path": "/Test/Path",
-            "keep_server_running": False
-        }
-
-        # Verify the field name and values are correct
-        assert "keep_server_running" in msg_true
-        assert msg_true["keep_server_running"] == True
-        assert "keep_server_running" in msg_false
-        assert msg_false["keep_server_running"] == False
+        msg_true = RegisterMessage(
+            project_hash="hash123",
+            keep_server_running=True,
+        )
+        assert msg_true.keep_server_running is True
+
+        msg_false = RegisterMessage(
+            project_hash="hash456",
+            keep_server_running=False,
+        )
+        assert msg_false.keep_server_running is False
+
+        # Verify default is False
+        msg_default = RegisterMessage(project_hash="hash789")
+        assert msg_default.keep_server_running is False
🧰 Tools
🪛 Ruff (0.15.0)

[error] 37-37: Avoid equality comparisons to True; use msg_true["keep_server_running"]: for truth checks

Replace with msg_true["keep_server_running"]

(E712)


[error] 39-39: Avoid equality comparisons to False; use not msg_false["keep_server_running"]: for false checks

Replace with not msg_false["keep_server_running"]

(E712)

🤖 Prompt for AI Agents
In `@Server/tests/test_keep_running_mode.py` around lines 15 - 39, Replace the
tautological dict checks in test_keep_running_message_format with actual
instantiation/validation of the RegisterMessage model: create RegisterMessage
instances (e.g., via RegisterMessage(**msg_true) and
RegisterMessage(**msg_false) or RegisterMessage.parse_obj(msg_true/msg_false),
then assert instance.keep_server_running is True/False and/or that
instance.dict()/json() contains the "keep_server_running" key; update imports if
needed to reference RegisterMessage.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs (1)

602-613: ⚠️ Potential issue | 🟠 Major

EditorPrefs.GetBool may be called off the main thread during reconnect.

SendRegisterAsync is reachable from the reconnect path (Task.Run(() => AttemptReconnectAsync(...)) at line 677), which runs on a thread-pool thread. EditorPrefs is a main-thread-only Unity API.

Follow the existing pattern: capture the value into a field in StartAsync() (where _projectName, _apiKey, etc. are already captured on the main thread) and reference the field here.

Proposed fix

Add a field:

  private string _apiKey;
+ private bool _keepServerRunning;
  private bool _disposed;

Capture it in StartAsync() alongside the other main-thread reads (around line 87):

  _apiKey = HttpEndpointUtility.IsRemoteScope()
      ? EditorPrefs.GetString(EditorPrefKeys.ApiKey, string.Empty)
      : string.Empty;
+ _keepServerRunning = EditorPrefs.GetBool(EditorPrefKeys.KeepServerRunning, false);

Then use the field in SendRegisterAsync:

  ["project_path"] = _projectPath,
- ["keep_server_running"] = EditorPrefs.GetBool(EditorPrefKeys.KeepServerRunning, false)
+ ["keep_server_running"] = _keepServerRunning
MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs (1)

315-318: ⚠️ Potential issue | 🟡 Minor

keepServerRunningToggle is not refreshed in UpdatePathOverrides.

Lines 316–318 re-sync debugLogsToggle, devModeForceRefreshToggle, and useBetaServerToggle from EditorPrefs, but the new keepServerRunningToggle is omitted. If UpdatePathOverrides is called after external EditorPrefs changes (e.g., scripted resets), the toggle will show a stale value.

Proposed fix
             useBetaServerToggle.value = EditorConfigurationCache.Instance.UseBetaServer;
+            if (keepServerRunningToggle != null)
+                keepServerRunningToggle.value = EditorPrefs.GetBool(EditorPrefKeys.KeepServerRunning, false);
             UpdateDeploymentSection();
🤖 Fix all issues with AI agents
In `@MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs`:
- Around line 114-133: McpAdvancedSection currently always shows and allows
toggling keepServerRunningToggle without checking transport mode; update
McpAdvancedSection to detect the active transport (e.g., via a TransportMode
property/event or a supplied enum like HTTP vs STDIO) and gate the control: when
transport is HTTP enable and show keepServerRunningToggle (and preserve
tooltip/label), when transport is STDIO disable or hide the toggle and replace
it with an explanatory Label; expose a public method or property on
McpAdvancedSection (e.g., SetTransportMode(TransportMode mode) or
SetKeepServerRunningEnabled(bool)) so the parent MCPForUnityEditorWindow can
call it, and ensure that changes still call existing callbacks
OnKeepServerRunningChanged and OnHttpServerCommandUpdateRequested and respect
EditorPrefKeys.KeepServerRunning when enabled.

In `@Server/src/main.py`:
- Around line 190-196: The early "return" inside the async context manager when
enable_http_server is True causes the generator to exit before yielding
(triggering "generator didn't yield"); instead of returning, set
_unity_connection_pool = None and allow normal fall-through so the
asynccontextmanager can reach its single yield — remove the "return" and ensure
the code path when enable_http_server is True (and skip_connect is False) still
reaches the yield at the end of the context manager (preserve the existing
logging about PluginHub managing Unity connections and any stdio skip logic
using skip_connect and _unity_connection_pool).

Comment on lines +114 to +133
if (keepServerRunningToggle != null)
{
// When enabled, server stays running after Unity disconnects (for auto-reconnect)
keepServerRunningToggle.tooltip = "When enabled, MCP server will stay running even when Unity disconnects. "
+ "Useful during development to avoid manual server restart after Domain Reload, test runs, or entering Play Mode. "
+ "Only works with HTTP transport mode.";
var keepRunningLabel = keepServerRunningToggle?.parent?.Q<Label>();
if (keepRunningLabel != null)
{
keepRunningLabel.tooltip = keepServerRunningToggle.tooltip;
}
// Load current value and register callback
keepServerRunningToggle.value = EditorPrefs.GetBool(EditorPrefKeys.KeepServerRunning, false);
keepServerRunningToggle.RegisterValueChangedCallback(evt =>
{
EditorPrefs.SetBool(EditorPrefKeys.KeepServerRunning, evt.newValue);
OnKeepServerRunningChanged?.Invoke(evt.newValue);
OnHttpServerCommandUpdateRequested?.Invoke();
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n -C5 'keepServerRunning|keep-server-running|KeepServerRunning' --type=cs -g '!*Test*'

Repository: CoplayDev/unity-mcp

Length of output: 9272


🏁 Script executed:

rg -n 'SetEnabled.*keepServerRunning|keepServerRunning.*SetEnabled' --type=cs -g '!*Test*'

Repository: CoplayDev/unity-mcp

Length of output: 45


🏁 Script executed:

rg -n -B5 -A5 'keepServerRunningToggle|KeepServerRunning' --type=cs -g '!*Test*' | grep -i 'enabled\|visible\|transport\|http\|stdio'

Repository: CoplayDev/unity-mcp

Length of output: 2488


🏁 Script executed:

rg -n 'McpAdvancedSection' --type=cs -g '!*Test*'

Repository: CoplayDev/unity-mcp

Length of output: 685


🏁 Script executed:

cat -n MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs | head -100

Repository: CoplayDev/unity-mcp

Length of output: 4234


🏁 Script executed:

rg -n 'advancedSection|SetEnabled|transport.*mode|http.*mode' -A2 -B2 MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs --type=cs

Repository: CoplayDev/unity-mcp

Length of output: 1869


🏁 Script executed:

rg -n 'transport.*mode|TransportMode|HTTP|STDIO' --type=cs -g '!*Test*' | grep -i 'enabled\|visible\|toggle\|advanced' | head -20

Repository: CoplayDev/unity-mcp

Length of output: 1593


🏁 Script executed:

rg -n 'public.*void.*Set|public.*void.*Update|public.*void.*Enable|public.*void.*Disable' MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs --type=cs

Repository: CoplayDev/unity-mcp

Length of output: 186


🏁 Script executed:

rg -n -A10 'OnKeepServerRunningChanged' --type=cs -g '!*Test*'

Repository: CoplayDev/unity-mcp

Length of output: 2525


🏁 Script executed:

rg -n 'transport.*mode\|TransportMode\|IsHttpMode\|IsStdioMode' --type=cs -g '!*Test*' | head -30

Repository: CoplayDev/unity-mcp

Length of output: 45


🏁 Script executed:

rg -n 'TransportMode|transport.*mode|IsHttpMode|HttpMode|StdioMode' --type=cs -g '!*Test*' | head -40

Repository: CoplayDev/unity-mcp

Length of output: 5434


🏁 Script executed:

rg -n 'enum.*Transport|class.*Transport' --type=cs -g '!*Test*' | head -20

Repository: CoplayDev/unity-mcp

Length of output: 1058


🏁 Script executed:

rg -n -B3 -A3 'OnKeepServerRunningChanged' MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs --type=cs

Repository: CoplayDev/unity-mcp

Length of output: 45


🏁 Script executed:

wc -l MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs

Repository: CoplayDev/unity-mcp

Length of output: 135


🏁 Script executed:

cat -n MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs | tail -n +240 | head -n 100

Repository: CoplayDev/unity-mcp

Length of output: 4939


🏁 Script executed:

rg -n 'public|private.*void.*Transport|SetEnabled|SetDisplay|style\.display' MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs --type=cs

Repository: CoplayDev/unity-mcp

Length of output: 711


Transport-mode gating is missing for the keep-server-running toggle.

The PR description specifies the toggle should be "enabled only when HTTP transport is active; stdio mode shows an explanation," but McpAdvancedSection has no transport-mode detection or SetEnabled/visibility logic. The toggle will appear and remain functional regardless of the active transport mode. There is no public method to control the toggle's enabled state, and MCPForUnityEditorWindow does not wire up any transport-mode event handlers. Either add transport-mode gating to McpAdvancedSection or confirm this is managed by a parent coordinator.

🤖 Prompt for AI Agents
In `@MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs` around
lines 114 - 133, McpAdvancedSection currently always shows and allows toggling
keepServerRunningToggle without checking transport mode; update
McpAdvancedSection to detect the active transport (e.g., via a TransportMode
property/event or a supplied enum like HTTP vs STDIO) and gate the control: when
transport is HTTP enable and show keepServerRunningToggle (and preserve
tooltip/label), when transport is STDIO disable or hide the toggle and replace
it with an explanatory Label; expose a public method or property on
McpAdvancedSection (e.g., SetTransportMode(TransportMode mode) or
SetKeepServerRunningEnabled(bool)) so the parent MCPForUnityEditorWindow can
call it, and ensure that changes still call existing callbacks
OnKeepServerRunningChanged and OnHttpServerCommandUpdateRequested and respect
EditorPrefKeys.KeepServerRunning when enabled.

Comment on lines 190 to 196
# Initialize connection pool and discover instances (stdio mode only)
# In HTTP mode, PluginHub handles connections directly
if enable_http_server:
_unity_connection_pool = None # No legacy pool in HTTP mode
logger.info("HTTP mode enabled - PluginHub will manage Unity connections")
# Skip stdio-specific connection logic in HTTP mode
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

return in HTTP mode prevents the async context manager from yielding — server startup will crash.

Inside an @asynccontextmanager, the generator must yield exactly once. When enable_http_server is True and skip_connect is False, the return on line 196 exits the generator before reaching the yield on line 253. This causes contextlib.asynccontextmanager to raise RuntimeError: generator didn't yield, which will crash the server on every HTTP-mode launch.

Replace the return with a fall-through so the code reaches the yield block.

🐛 Proposed fix
             if enable_http_server:
                 _unity_connection_pool = None  # No legacy pool in HTTP mode
                 logger.info("HTTP mode enabled - PluginHub will manage Unity connections")
-                # Skip stdio-specific connection logic in HTTP mode
-                return
             else:
                 _unity_connection_pool = get_unity_connection_pool()
                 instances = _unity_connection_pool.discover_all_instances()
-
-            if instances:
-                logger.info(
-                    f"Discovered {len(instances)} Unity instance(s): {[i.id for i in instances]}")
-
-                # Try to connect to default instance
-                try:
-                    _unity_connection_pool.get_connection()
-                    logger.info(
-                        "Connected to default Unity instance on startup")
-
-                    # Record successful Unity connection (deferred)
-                    threading.Timer(1.0, lambda: record_telemetry(
-                        RecordType.UNITY_CONNECTION,
-                        {
-                            "status": "connected",
-                            "connection_time_ms": (time.perf_counter() - start_clk) * 1000,
-                            "instance_count": len(instances)
-                        }
-                    )).start()
-                except Exception as e:
-                    logger.warning(
-                        f"Could not connect to default Unity instance: {e}")
-            else:
-                logger.warning("No Unity instances found on startup")
+
+                if instances:
+                    logger.info(
+                        f"Discovered {len(instances)} Unity instance(s): {[i.id for i in instances]}")
+
+                    # Try to connect to default instance
+                    try:
+                        _unity_connection_pool.get_connection()
+                        logger.info(
+                            "Connected to default Unity instance on startup")
+
+                        # Record successful Unity connection (deferred)
+                        threading.Timer(1.0, lambda: record_telemetry(
+                            RecordType.UNITY_CONNECTION,
+                            {
+                                "status": "connected",
+                                "connection_time_ms": (time.perf_counter() - start_clk) * 1000,
+                                "instance_count": len(instances)
+                            }
+                        )).start()
+                    except Exception as e:
+                        logger.warning(
+                            f"Could not connect to default Unity instance: {e}")
+                else:
+                    logger.warning("No Unity instances found on startup")
🤖 Prompt for AI Agents
In `@Server/src/main.py` around lines 190 - 196, The early "return" inside the
async context manager when enable_http_server is True causes the generator to
exit before yielding (triggering "generator didn't yield"); instead of
returning, set _unity_connection_pool = None and allow normal fall-through so
the asynccontextmanager can reach its single yield — remove the "return" and
ensure the code path when enable_http_server is True (and skip_connect is False)
still reaches the yield at the end of the context manager (preserve the existing
logging about PluginHub managing Unity connections and any stdio skip logic
using skip_connect and _unity_connection_pool).

@whatevertogo whatevertogo force-pushed the feat-672-keep-server-running branch 3 times, most recently from ca308ce to 6a5be34 Compare February 11, 2026 21:54
@whatevertogo whatevertogo marked this pull request as draft February 11, 2026 21:54
@whatevertogo whatevertogo force-pushed the feat-672-keep-server-running branch from 9d417ff to 02c22fb Compare February 11, 2026 22:26
Add ability for Unity to keep the MCP server running even after
Unity Editor closes, allowing the server to persist for reconnection.

Changes:
- Add Keep Server Running toggle in Unity Editor Window (UXML + C#)
- Pass keep_running flag from Unity through PluginHub registration
- Server maintains keep_server_running state per session
- Shutdown logic respects keep_running flags before disconnecting
- Add tests for keep running mode and session state tracking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@whatevertogo whatevertogo force-pushed the feat-672-keep-server-running branch from 02c22fb to b4ab121 Compare February 11, 2026 22:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Request: developer experience - connect/reconnect automatically, keep server running

1 participant