fix: make MCP server registration idempotent#25
Merged
Conversation
`claude mcp add` exits non-zero when the server already exists. Since the .claude directory is volume-mounted and persists between runs, the second `jackin load` would abort the entrypoint at the tirith registration step, preventing Claude from launching. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
donbeave
added a commit
that referenced
this pull request
Apr 20, 2026
…ation fix: make MCP server registration idempotent
donbeave
added a commit
that referenced
this pull request
Apr 21, 2026
…ation fix: make MCP server registration idempotent
donbeave
added a commit
that referenced
this pull request
Apr 21, 2026
fix: make MCP server registration idempotent
donbeave
added a commit
that referenced
this pull request
Apr 21, 2026
fix: make MCP server registration idempotent
donbeave
added a commit
that referenced
this pull request
Apr 21, 2026
fix: make MCP server registration idempotent
donbeave
added a commit
that referenced
this pull request
May 7, 2026
fix: make MCP server registration idempotent Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com> Co-authored-by: Codex <codex@openai.com>
donbeave
added a commit
that referenced
this pull request
May 7, 2026
fix: make MCP server registration idempotent Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com> Co-authored-by: Codex <codex@openai.com>
donbeave
added a commit
that referenced
this pull request
May 7, 2026
#25) `claude mcp add` exits non-zero when the server already exists. Since the .claude directory is volume-mounted and persists between runs, the second `jackin load` would abort the entrypoint at the tirith registration step, preventing Claude from launching. Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com> Co-authored-by: Claude <noreply@anthropic.com>
donbeave
added a commit
that referenced
this pull request
May 7, 2026
fix: make MCP server registration idempotent Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com> Co-authored-by: Codex <codex@openai.com>
donbeave
added a commit
that referenced
this pull request
May 22, 2026
Address review findings for stale comments, the `instance_action_accepts_status` negative-match idiom, and two workflow rough edges. Comments - `crates/jackin-container/src/daemon.rs:12` module doc: replace the stale "daemon is persistent: does not exit when the last session dies" line with the actual behavior (exits when sessions empty so the container reaps cleanly), matching memory file `container-lifecycle-policy.md`. - `daemon.rs::ClientFrame::Command`: drop "Phase 3 has no senders yet" transitional reference. - `daemon.rs::handle_attach_client` / `drain_and_exit`: move the "Per-client connection handler..." docstring onto `handle_attach_client` where it actually applies, and leave the Shutdown-and-pause description on `drain_and_exit`. - `dialog.rs::PALETTE_ITEMS`: drop the "the New agent session entry was removed" PR-history sentence; keep the "Next/Previous tab not in palette" rationale because that *describes current design*, not history. - `runtime/attach.rs::reconnect_or_create_session`: delete the fossilised `TMUX= prevents nested-session warnings` docstring (function never set TMUX=, and tmux is gone from the runtime entirely) and collapse the redundant `has_sessions`/`let _` pair. - `src/console/manager/render/list.rs::compute_sidebar_layout`: rewrite the misleading "Global mounts header still renders alone" comment to describe the actual `show_global`/`show_role_global` truth table. `instance_action_accepts_status` exhaustive grid (#25) - Convert to a 2D `match (action, status)` with positive matches per arm. The previous `!matches!(status, Purged)` shape silently flipped half the action × status grid whenever a new `InstanceStatus` variant was added — exactly the kind of regression the project's single-reviewer staffing rule says compile-time exhaustiveness should catch. Workflow polish (#41) - `preview.yml`: `gh release delete preview` previously masked all errors via `2>/dev/null || true`, so auth / rate-limit failures surfaced as a confusing "tag already exists" error from the next step. Branch on "release not found" (legit on fresh repo) vs any other failure (error out with the captured stderr). - Same job: add `set -euo pipefail` to both the publish-binary block and the release-publish block so a mid-pipeline failure doesn't silently continue. Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
donbeave
added a commit
that referenced
this pull request
May 23, 2026
Address review findings for stale comments, the `instance_action_accepts_status` negative-match idiom, and two workflow rough edges. Comments - `crates/jackin-container/src/daemon.rs:12` module doc: replace the stale "daemon is persistent: does not exit when the last session dies" line with the actual behavior (exits when sessions empty so the container reaps cleanly), matching memory file `container-lifecycle-policy.md`. - `daemon.rs::ClientFrame::Command`: drop "Phase 3 has no senders yet" transitional reference. - `daemon.rs::handle_attach_client` / `drain_and_exit`: move the "Per-client connection handler..." docstring onto `handle_attach_client` where it actually applies, and leave the Shutdown-and-pause description on `drain_and_exit`. - `dialog.rs::PALETTE_ITEMS`: drop the "the New agent session entry was removed" PR-history sentence; keep the "Next/Previous tab not in palette" rationale because that *describes current design*, not history. - `runtime/attach.rs::reconnect_or_create_session`: delete the fossilised `TMUX= prevents nested-session warnings` docstring (function never set TMUX=, and tmux is gone from the runtime entirely) and collapse the redundant `has_sessions`/`let _` pair. - `src/console/manager/render/list.rs::compute_sidebar_layout`: rewrite the misleading "Global mounts header still renders alone" comment to describe the actual `show_global`/`show_role_global` truth table. `instance_action_accepts_status` exhaustive grid (#25) - Convert to a 2D `match (action, status)` with positive matches per arm. The previous `!matches!(status, Purged)` shape silently flipped half the action × status grid whenever a new `InstanceStatus` variant was added — exactly the kind of regression the project's single-reviewer staffing rule says compile-time exhaustiveness should catch. Workflow polish (#41) - `preview.yml`: `gh release delete preview` previously masked all errors via `2>/dev/null || true`, so auth / rate-limit failures surfaced as a confusing "tag already exists" error from the next step. Branch on "release not found" (legit on fresh repo) vs any other failure (error out with the captured stderr). - Same job: add `set -euo pipefail` to both the publish-binary block and the release-publish block so a mid-pipeline failure doesn't silently continue. Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
donbeave
added a commit
that referenced
this pull request
May 23, 2026
Address review findings for stale comments, the `instance_action_accepts_status` negative-match idiom, and two workflow rough edges. Comments - `crates/jackin-container/src/daemon.rs:12` module doc: replace the stale "daemon is persistent: does not exit when the last session dies" line with the actual behavior (exits when sessions empty so the container reaps cleanly), matching memory file `container-lifecycle-policy.md`. - `daemon.rs::ClientFrame::Command`: drop "Phase 3 has no senders yet" transitional reference. - `daemon.rs::handle_attach_client` / `drain_and_exit`: move the "Per-client connection handler..." docstring onto `handle_attach_client` where it actually applies, and leave the Shutdown-and-pause description on `drain_and_exit`. - `dialog.rs::PALETTE_ITEMS`: drop the "the New agent session entry was removed" PR-history sentence; keep the "Next/Previous tab not in palette" rationale because that *describes current design*, not history. - `runtime/attach.rs::reconnect_or_create_session`: delete the fossilised `TMUX= prevents nested-session warnings` docstring (function never set TMUX=, and tmux is gone from the runtime entirely) and collapse the redundant `has_sessions`/`let _` pair. - `src/console/manager/render/list.rs::compute_sidebar_layout`: rewrite the misleading "Global mounts header still renders alone" comment to describe the actual `show_global`/`show_role_global` truth table. `instance_action_accepts_status` exhaustive grid (#25) - Convert to a 2D `match (action, status)` with positive matches per arm. The previous `!matches!(status, Purged)` shape silently flipped half the action × status grid whenever a new `InstanceStatus` variant was added — exactly the kind of regression the project's single-reviewer staffing rule says compile-time exhaustiveness should catch. Workflow polish (#41) - `preview.yml`: `gh release delete preview` previously masked all errors via `2>/dev/null || true`, so auth / rate-limit failures surfaced as a confusing "tag already exists" error from the next step. Branch on "release not found" (legit on fresh repo) vs any other failure (error out with the captured stderr). - Same job: add `set -euo pipefail` to both the publish-binary block and the release-publish block so a mid-pipeline failure doesn't silently continue. Co-authored-by: Codex <codex@openai.com> Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
donbeave
added a commit
that referenced
this pull request
May 23, 2026
Address review findings for stale comments, the `instance_action_accepts_status` negative-match idiom, and two workflow rough edges. Comments - `crates/jackin-container/src/daemon.rs:12` module doc: replace the stale "daemon is persistent: does not exit when the last session dies" line with the actual behavior (exits when sessions empty so the container reaps cleanly), matching memory file `container-lifecycle-policy.md`. - `daemon.rs::ClientFrame::Command`: drop "Phase 3 has no senders yet" transitional reference. - `daemon.rs::handle_attach_client` / `drain_and_exit`: move the "Per-client connection handler..." docstring onto `handle_attach_client` where it actually applies, and leave the Shutdown-and-pause description on `drain_and_exit`. - `dialog.rs::PALETTE_ITEMS`: drop the "the New agent session entry was removed" PR-history sentence; keep the "Next/Previous tab not in palette" rationale because that *describes current design*, not history. - `runtime/attach.rs::reconnect_or_create_session`: delete the fossilised `TMUX= prevents nested-session warnings` docstring (function never set TMUX=, and tmux is gone from the runtime entirely) and collapse the redundant `has_sessions`/`let _` pair. - `src/console/manager/render/list.rs::compute_sidebar_layout`: rewrite the misleading "Global mounts header still renders alone" comment to describe the actual `show_global`/`show_role_global` truth table. `instance_action_accepts_status` exhaustive grid (#25) - Convert to a 2D `match (action, status)` with positive matches per arm. The previous `!matches!(status, Purged)` shape silently flipped half the action × status grid whenever a new `InstanceStatus` variant was added — exactly the kind of regression the project's single-reviewer staffing rule says compile-time exhaustiveness should catch. Workflow polish (#41) - `preview.yml`: `gh release delete preview` previously masked all errors via `2>/dev/null || true`, so auth / rate-limit failures surfaced as a confusing "tag already exists" error from the next step. Branch on "release not found" (legit on fresh repo) vs any other failure (error out with the captured stderr). - Same job: add `set -euo pipefail` to both the publish-binary block and the release-publish block so a mid-pipeline failure doesn't silently continue. Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com> Co-authored-by: Codex <codex@openai.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
|| truetoclaude mcp addcommands in the entrypoint so they don't abort the script when the MCP server is already registered from a previous runRoot cause
claude mcp addexits non-zero when the server already exists. The.claudedirectory is volume-mounted and persists between container restarts, so on the secondjackin loadthe tirith registration fails,set -euo pipefailkills the entrypoint, and Claude never launches.Introduced by #22.
Test plan
cargo nextest run— 213 tests passjackin loadtwice in a row — second run should launch Claude successfully🤖 Generated with Claude Code