Skip to content

Suggest a free port inline + docs link for foreign port conflicts#608

Merged
dsarno merged 3 commits into
mainfrom
claude/github-issue-607-2zv546
Jun 27, 2026
Merged

Suggest a free port inline + docs link for foreign port conflicts#608
dsarno merged 3 commits into
mainfrom
claude/github-issue-607-2zv546

Conversation

@dsarno

@dsarno dsarno commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Closes #607.

Summary

Implements the cheap version from #607. When the HTTP port (default 8000) is held by a genuinely foreign (non-godot-ai) process, the dock lands in terminal INCOMPATIBLE and tells the user what to do but leaves them with the chore — hunt for a free port, then hand-edit each MCP client config. This surfaces a concrete free port inline and adds a docs link carrying the reconfigure steps.

Changes

  • Inline copy names a concrete free port. _crash_body_for_state in mcp_dock.gd now appends a suggestion for the INCOMPATIBLE && !can_recover_incompatible case, e.g.:

    Port 8001 is free — set godot_ai/http_port in Editor Settings, then update your client config (How to change the port, below).

    The suggestion comes from the existing ClientConfigurator.suggest_free_port(...), which already routes through WindowsPortReservation.suggest_non_excluded_port, so the suggested port won't itself fail with WinError 10013 on Windows. String/seam change only.

  • Docs link in the crash panel. A new "How to change the port" button surfaces in the crash panel only for the foreign-occupant case, opening docs/port-conflicts.md — a short guide covering "change the port + reconfigure your clients" (the per-client reconfigure steps that don't fit inline).

  • New doc: docs/port-conflicts.md.

Scope / no behavior change

  • Applies only to INCOMPATIBLE && !can_recover_incompatible (genuinely foreign occupant). When can_recover_incompatible is true (a stale/older godot-ai we can prove we own), Restart Server stays the primary action and the docs link/free-port hint are suppressed — reclaiming 8000 beats fleeing to a random port.
  • No change to PORT_EXCLUDED / FOREIGN_PORT states or their port_picker_panel.

Acceptance (cheap version)

  • Foreign-occupant INCOMPATIBLE crash body names a concrete free port (reservation-aware on Windows)
  • Docs link surfaced from the dock covering "change the port + reconfigure clients"
  • No behavior change for PORT_EXCLUDED / FOREIGN_PORT / recoverable-godot-ai (can_recover_incompatible) cases

Testing

  • script/ci-check-gdscript — clean (no parse errors).
  • Full Godot handler suite via script/ci-godot-tests against a live server + headless editor: 1505/1524 passed, 0 failed (gap is pre-existing platform skips).
  • New test_dock.gd coverage: foreign-occupant body names a free port + points at godot_ai/http_port; recoverable body keeps Restart Server copy and omits the free-port hint; docs button visibility is gated to the foreign case.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WwE7zCBJ6qvjk7wyw3X5aU


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added a version-pinned “How to change the port” help button in the dock’s crash panel for non-recoverable incompatible server situations.
    • Enhanced incompatible-server messaging with concrete free-port suggestions for both HTTP and WebSocket settings, including client reconfiguration guidance.
  • Documentation

    • Added a step-by-step guide for resolving “Port 8000 is occupied” conflicts and updating MCP client URLs.
  • Tests

    • Expanded dock UI/integration coverage for recoverable vs genuinely foreign incompatible cases, including verification that the reconfiguration docs link is pinned to the installed plugin version.

When a non-godot-ai process holds the HTTP port, the dock lands in
terminal INCOMPATIBLE and tells the user to change the port — but leaves
them to hunt for a free one and to figure out the per-client reconfigure
themselves.

Cheap-version improvements from #607:

- The foreign-occupant crash body (INCOMPATIBLE && !can_recover_incompatible)
  now names a concrete free port via ClientConfigurator.suggest_free_port,
  which already routes through the Windows winnat reservation table so the
  suggested port won't itself fail with WinError 10013.
- A "How to change the port" docs button surfaces in the crash panel for
  that case only, linking to docs/port-conflicts.md, which carries the
  per-client reconfigure steps that don't fit inline.

No behavior change for PORT_EXCLUDED / FOREIGN_PORT or the recoverable
(older godot-ai) case, which keeps Restart Server as its primary action.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WwE7zCBJ6qvjk7wyw3X5aU
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a69473f-da57-425e-9998-dba1b0e249bc

📥 Commits

Reviewing files that changed from the base of the PR and between 3c6c98a and c10feed.

📒 Files selected for processing (3)
  • docs/port-conflicts.md
  • plugin/addons/godot_ai/mcp_dock.gd
  • test_project/tests/test_dock.gd
✅ Files skipped from review due to trivial changes (1)
  • docs/port-conflicts.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • test_project/tests/test_dock.gd
  • plugin/addons/godot_ai/mcp_dock.gd

📝 Walkthrough

Walkthrough

Adds a port-conflict guide, updates the crash panel for unrecoverable incompatible servers with a free-port hint and docs link, and adds dock tests for incompatible-body and button visibility behavior.

Changes

Foreign port conflict UX

Layer / File(s) Summary
Port conflict guide
docs/port-conflicts.md
Adds a port-8000 conflict guide covering free-port selection, godot_ai/http_port, client reconfiguration, and reverting to 8000.
Crash panel guidance
plugin/addons/godot_ai/mcp_dock.gd
Adds the crash-panel docs button and changes INCOMPATIBLE crash copy to include a generated free-port hint when recovery is unavailable.
Crash panel tests
test_project/tests/test_dock.gd
Adds tests for the foreign and recoverable incompatible crash-body text, docs URL pinning, and docs button visibility rules.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant McpDock
  participant Docs as docs/port-conflicts.md
  participant EditorSettings
  participant MCPClients
  McpDock-->>User: show free-port hint and docs button
  User->>Docs: open port-conflict guide
  User->>EditorSettings: set godot_ai/http_port
  User->>MCPClients: update client URLs/configs
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

A rabbit saw port 8000 sigh,
Then hopped to 8001 nearby.
With docs in paw and clients in tow,
The dock said “all set” and let me go. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: inline port suggestions plus a docs link for foreign port conflicts.
Linked Issues check ✅ Passed The changes satisfy #607: they add a reservation-aware free-port hint, a docs link, and keep scope limited to non-recoverable INCOMPATIBLE cases.
Out of Scope Changes check ✅ Passed The diff stays focused on port-conflict UX, docs, and tests, with no unrelated code changes visible.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/github-issue-607-2zv546

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugin/addons/godot_ai/mcp_dock.gd`:
- Line 46: The PORT_CONFLICT_DOCS constant in mcp_dock.gd is hard-linked to
blob/main, so the docs button can point to the wrong guide for installed
releases. Update the docs-opening logic around PORT_CONFLICT_DOCS and the
related button handler to use a versioned URL derived from the installed plugin
version, or resolve and open the packaged local markdown instead, so the
referenced instructions match the shipped build.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6402f2fa-d991-4254-a07c-681d80420328

📥 Commits

Reviewing files that changed from the base of the PR and between 10da557 and c739823.

📒 Files selected for processing (3)
  • docs/port-conflicts.md
  • plugin/addons/godot_ai/mcp_dock.gd
  • test_project/tests/test_dock.gd

Comment thread plugin/addons/godot_ai/mcp_dock.gd Outdated
@codecov

codecov Bot commented Jun 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Addresses review feedback: the crash-panel "How to change the port" button
linked to blob/main, so a shipped release would open the tip-of-main guide
rather than the one that shipped with that build — drifting away from the
build's actual UI as the doc evolves.

Resolve the docs URL against the installed plugin version at click time
(releases are tagged `v<version>`). The button only exists in builds that
ship docs/port-conflicts.md, so the versioned ref always resolves.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WwE7zCBJ6qvjk7wyw3X5aU

dsarno commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai Good catch on the blob/main drift. Fixed in 3c6c98a: the docs button now resolves the URL against the installed plugin version (blob/v<version>/docs/port-conflicts.md) via _port_conflict_docs_url(). Releases are tagged v<version> and the button only exists in builds that ship the doc, so the versioned ref always resolves — and a shipped build never sends users to a tip-of-main guide that has drifted from its UI. Added a test pinning the versioned-URL behavior.

On the earlier "Linked Issues" warning about Windows reservation-awareness: that's already satisfied — ClientConfigurator.suggest_free_port() routes through WindowsPortReservation.suggest_non_excluded_port() (client_configurator.gd:141-143), so the suggested port clears the winnat reservation table.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@dsarno Thanks — that resolves my concern about the docs-link drift.

And agreed on the Windows point: if ClientConfigurator.suggest_free_port() already flows through WindowsPortReservation.suggest_non_excluded_port(), then the free-port hint is reservation-aware as intended.


✏️ Learnings added
Learnt from: dsarno
URL: https://github.com/hi-godot/godot-ai/pull/608

Timestamp: 2026-06-27T02:50:43.463Z
Learning: In `plugin/addons/godot_ai/client_configurator.gd`, `ClientConfigurator.suggest_free_port()` routes through `WindowsPortReservation.suggest_non_excluded_port()` on Windows, so suggested ports are reservation-aware and avoid WinNAT-excluded ports.

Learnt from: dsarno
URL: https://github.com/hi-godot/godot-ai/pull/608

Timestamp: 2026-06-27T02:50:43.463Z
Learning: In `plugin/addons/godot_ai/mcp_dock.gd`, the port-conflict docs button should resolve to a versioned docs URL using `blob/v<version>/docs/port-conflicts.md` rather than `blob/main`, because releases are tagged `v<version>` and shipped builds include the matching docs.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@Clubhouse1661

Copy link
Copy Markdown
Collaborator

This looks good as the cheap/manual-path fix from #607, especially now that the docs link is pinned to the installed version.

One coordination note: the current hint/docs are HTTP-only:

set godot_ai/http_port

But the existing incompatible-server message tells users to change both HTTP and WS ports, and the server really does need the pair to stay valid. So I think the manual guidance should name both settings, e.g. godot_ai/http_port=8123 and godot_ai/ws_port=9501, rather than only suggesting a free HTTP port.

@dsarno

dsarno commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

This looks good as the cheap/manual-path fix from #607, especially now that the docs link is pinned to the installed version.

One coordination note: the current hint/docs are HTTP-only:

set godot_ai/http_port

But the existing incompatible-server message tells users to change both HTTP and WS ports, and the server really does need the pair to stay valid. So I think the manual guidance should name both settings, e.g. godot_ai/http_port=8123 and godot_ai/ws_port=9501, rather than only suggesting a free HTTP port.

Good catch fixing....

Addresses review feedback: the crash-body hint and docs only named
godot_ai/http_port, but this branch (INCOMPATIBLE && !can_recover_incompatible)
also fires for an incompatible godot-ai server we can't prove we own — which
commonly holds both the HTTP and WS ports. Suggesting only a free HTTP port
would then leave the new server unable to bind WS, and it contradicted the
embedded "change both HTTP and WS ports" message.

The hint now suggests a free value for each port (both reservation-aware) and
names godot_ai/http_port and godot_ai/ws_port; only the HTTP port reaches
client configs, so the wording keeps that distinction. docs/port-conflicts.md
and the tests track the change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WwE7zCBJ6qvjk7wyw3X5aU

dsarno commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

@Clubhouse1661 Good catch — fixed in c10feed. You're right that this branch isn't purely HTTP-only: it also fires for an incompatible godot-ai server we can't get strong proof on, which commonly holds both 8000 and 9500, so suggesting only a free HTTP port would leave the new server unable to bind WS (and it contradicted the embedded "change both HTTP and WS ports" message).

The hint now reads e.g.:

Ports 8001 (HTTP) and 9501 (WS) are free — set godot_ai/http_port and godot_ai/ws_port in Editor Settings, then update your client config with the new HTTP port (How to change the port, below).

Both suggestions go through the reservation-aware suggest_free_port. Only the HTTP port reaches client configs (WS is server↔plugin), so the wording keeps that distinction, and docs/port-conflicts.md + the tests track it. Full suite green locally (1506/1525, 0 failed).


Generated by Claude Code

@dsarno dsarno merged commit 804d069 into main Jun 27, 2026
30 of 31 checks passed
dsarno added a commit that referenced this pull request Jun 27, 2026
… (#610)

* Add foreign-occupant smoke for non-recoverable INCOMPATIBLE path (#609)

The recoverable port-conflict path has live cross-OS coverage via
`ci-stale-server-smoke`: it plants a godot-ai server simulator on port
8000, then asserts the plugin kills it and respawns. The mirror-image
foreign-occupant path — a genuinely non-godot-ai process holds the port,
so the plugin gets no ownership proof and must land in terminal
INCOMPATIBLE (can_recover_incompatible == false) and make NO kill attempt
— had no end-to-end guard, only mocked GDScript unit tests.

Close that gap:

- `ci-stale-server-smoke` gains `--mode {stale,foreign}` (default stale,
  so the existing CI invocation is unchanged). Foreign mode plants a plain
  listener that 404s on /godot-ai/status, launches Godot headless, and
  asserts the plugin observably: logs `MCP | proof: (none)`, logs a
  suggested-free-port diagnosis naming a concrete alternate port, never
  logs `MCP | strong proof:` or `MCP | killed pids`, and leaves the
  planted listener's PID alive. Shared harness helpers
  (find_godot/discover_user_dir/wait_for_log_lines/termination/diagnostics)
  are reused across both modes.

- New `foreign-server-smoke` CI job on the same OS matrix as
  stale-server-smoke. It's a sibling job rather than a second step so the
  stale run's respawned port-8000 server can't bleed into the scenario.

- `server_lifecycle.gd` now logs the suggested free port whenever it lands
  in non-recoverable INCOMPATIBLE (the single `_set_incompatible_server`
  chokepoint). This makes the upstream classification observable from the
  editor log for CI; the dock crash body (#608) renders the same hint but
  only to the UI, not stdout. Reservation-aware on Windows via
  `ClientConfigurator.suggest_free_port`.

Verified locally: both smoke modes PASS against a headless 4.6 editor, and
the full live unit suite is green (1506/1525, 0 failed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0153n16KG5VL4NDMUjzX1tWa

* Harden terminate_proc against process-exit races in smoke cleanup

terminate_proc() runs from a finally block, but poll() and the subsequent
terminate()/send_signal()/kill() are not atomic: if the child exits in
between, the signal call raises ProcessLookupError and turns an otherwise
passing smoke into a failure. Wrap the signalling in exception handling for
the already-exited case and reap after kill() so the child can't linger as
a zombie. SIGTERM/Windows behavior is unchanged.

Addresses a CodeRabbit review finding on #610.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0153n16KG5VL4NDMUjzX1tWa

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

Improve foreign-process port-conflict UX: suggest a free port inline + docs link

3 participants