CB-614: the fleet cannot share the IDE MCP — and the lead's own IDE step is currently a no-op #117

Open
opened 2026-08-17 16:31:59 +02:00 by ltms · 2 comments
Owner

Raised by the operator on 2026-08-17: "how do the fleet share IDE mcp".

Measured, not assumed. Three separate findings, in order of how urgent they are.

1. LIVE DEFECT — the IntelliJ project is not open, so the lead's mandated IDE step fails today

CLAUDE.md §"IDE MCP tools & validation workflow" tells every lead, as a mandatory step after
editing any file: ide_sync_files, then ide_diagnostics, passing
project_path = /Users/dai.ha/LTMS/claude-bridge/bridged.

That call returns an error right now:

ide_index_status{project_path: "/Users/dai.ha/LTMS/claude-bridge/bridged"}
  -> {"error":"project_not_found", ...}
ide_index_status{project_path: "/Users/dai.ha/LTMS/claude-bridge"}
  -> {"error":"project_not_found", ...}

available_projects lists only Magnolia's dev-7.0 workspace and its ~70 modules. No
claude-bridge project is open in the IDE at all, under either path.

Two things follow, and the second is worse than the first:

  • The step is a no-op. Any lead following CLAUDE.md gets an error, and the "clear all errors
    and warnings" gate is never actually run. mvn clean install still works, so the build gate is
    fine — but inspections (unused params, redundant modifiers, resource leaks, "always same arg")
    are exactly the class a build does not catch, and that class is currently unchecked.
  • CLAUDE.md may also name the wrong path. .idea/ is at the repo root
    (/Users/dai.ha/LTMS/claude-bridge/.idea), not at bridged/. So the documented
    project_path of .../claude-bridge/bridged looks wrong independently of the project being
    closed. Cannot confirm which is right until the project is opened.

Fix: operator reopens claude-bridge in IntelliJ, then we re-measure which project_path
the tools actually accept and correct CLAUDE.md to match. This is not a fleet feature; it is a
one-line correction plus an IDE window.

Acceptance: ide_index_status{project_path: <the correct path>} returns a status rather than
project_not_found, and CLAUDE.md names that exact path.

2. Sharing the mount is trivial — and that is the trap

Both IDE MCP servers are loopback network servers, not stdio:

server transport address
jetbrains sse http://localhost:64342/sse
intellij-index http http://127.0.0.1:29170/index-mcp/streamable-http

nc -z confirms both ports are open. Any process on this host can connect. Nothing in bridged
blocks a member from reaching them.

The only reason members do not have them is incidental: they live in the project .mcp.json,
which is --skip-worktree and not committed, so a member's worktree checkout has no copy. Members
do already inherit user-scope servers from ~/.claude.json (gitea, context7, ccs-*) —
so the mechanism for giving them more is already proven and would be a three-line change.

We should not make that change. Reasons in §3.

3. Why a shared IDE is the wrong shape for a fleet

(a) The IDE indexes one path per project; a member works in a different path. Members get git
worktrees under /Users/dai.ha/LTMS/.bridged-worktrees/<name>/. Measured against the live
cb401-manual worktree: project_not_found. To make it work you would have to open every worktree
as its own IntelliJ project at spawn and close it at teardown. Sum of maxLoad across profiles is
13, so that is up to 13 IntelliJ projects indexing a fresh Maven tree at once. That is an IDE
farm, not a message bus.

(b) The IDE is single-user and stateful. ide_sync_files, reformat_file,
ide_refactor_rename, open_file_in_editor and the debugger all mutate one shared IDE. Two
members refactoring at once collide in a way neither can observe. jetbrains also exposes
execute_terminal_command and build_project, which would let a member run commands in the
operator's IDE context, outside its own worktree.

(c) It does not survive 2.0. A loopback port on the operator's laptop is not reachable from
another host. Wiring the fleet to it now builds a dependency that federation has to break.

(d) It is the wrong failure mode. Best case a member gets project_not_found — loud, fine.
Worst case a path resolves against the main checkout and returns diagnostics for a different
copy of the file, clean, because the member's edit is not there. A clean result read as evidence is
the seam-vs-caller family again: three instances already this month.

4. What a member actually needs, and the shape that fits

Not the IDE. A member needs to answer "does my change compile, pass, and violate an inspection?"
It already has two of three — mvn -f bridged/pom.xml clean install works fine inside a worktree.
The gap is inspections only.

The shape that fits a fleet is headless and per-worktree, so each member gets its own answer
with no shared state:

  • /Applications/IntelliJ IDEA.app/Contents/bin/inspect.sh — confirmed present on this host.
  • Qodana — not installed here (which qodana finds nothing).

Not yet measured, and must be before this is planned: whether inspect.sh runs against a
worktree that has no .idea of its own, how long it takes, and whether its findings match what the
GUI reports. It is slow (minutes) and needs a project directory. Treat this section as a direction,
not a design.

Scope

2.0, not 1.1. By the 1.1 admission rule — would it still be broken with exactly one host? —
finding 1 qualifies, but 1.1 is tagged (v1.1.0, 2026-08-17) and reopening a closed milestone for
newly-discovered scope is worse than carrying it forward. Finding 1 is an operator action, not a
code change, and needs no ticket state to happen.

Proposed outcome

  1. Operator reopens the project; correct CLAUDE.md's project_path. (finding 1, do now)
  2. Record the decision not to share the IDE mount with members, with the reasons in §3, in
    wiki/13-User-Guide.md §1 ("what it is not") and wiki/11-Features.md. Right now the decision
    exists only as a habit, so it will be re-litigated. (cheap, do next)
  3. Measure inspect.sh in a worktree before committing to anything in §4.
Raised by the operator on 2026-08-17: *"how do the fleet share IDE mcp"*. Measured, not assumed. Three separate findings, in order of how urgent they are. ## 1. LIVE DEFECT — the IntelliJ project is not open, so the lead's mandated IDE step fails today `CLAUDE.md` §"IDE MCP tools & validation workflow" tells every lead, as a **mandatory** step after editing any file: `ide_sync_files`, then `ide_diagnostics`, passing `project_path = /Users/dai.ha/LTMS/claude-bridge/bridged`. That call returns an error right now: ``` ide_index_status{project_path: "/Users/dai.ha/LTMS/claude-bridge/bridged"} -> {"error":"project_not_found", ...} ide_index_status{project_path: "/Users/dai.ha/LTMS/claude-bridge"} -> {"error":"project_not_found", ...} ``` `available_projects` lists **only** Magnolia's `dev-7.0` workspace and its ~70 modules. No `claude-bridge` project is open in the IDE at all, under either path. Two things follow, and the second is worse than the first: - **The step is a no-op.** Any lead following `CLAUDE.md` gets an error, and the "clear all errors and warnings" gate is never actually run. `mvn clean install` still works, so the build gate is fine — but inspections (unused params, redundant modifiers, resource leaks, "always same arg") are exactly the class a build does **not** catch, and that class is currently unchecked. - **`CLAUDE.md` may also name the wrong path.** `.idea/` is at the repo root (`/Users/dai.ha/LTMS/claude-bridge/.idea`), not at `bridged/`. So the documented `project_path` of `.../claude-bridge/bridged` looks wrong independently of the project being closed. Cannot confirm which is right until the project is opened. **Fix:** operator reopens `claude-bridge` in IntelliJ, then we re-measure which `project_path` the tools actually accept and correct `CLAUDE.md` to match. This is not a fleet feature; it is a one-line correction plus an IDE window. **Acceptance:** `ide_index_status{project_path: <the correct path>}` returns a status rather than `project_not_found`, and `CLAUDE.md` names that exact path. ## 2. Sharing the mount is trivial — and that is the trap Both IDE MCP servers are **loopback network servers**, not stdio: | server | transport | address | |---|---|---| | `jetbrains` | sse | `http://localhost:64342/sse` | | `intellij-index` | http | `http://127.0.0.1:29170/index-mcp/streamable-http` | `nc -z` confirms both ports are open. Any process on this host can connect. Nothing in `bridged` blocks a member from reaching them. The only reason members do not have them is incidental: they live in the **project** `.mcp.json`, which is `--skip-worktree` and not committed, so a member's worktree checkout has no copy. Members *do* already inherit **user-scope** servers from `~/.claude.json` (`gitea`, `context7`, `ccs-*`) — so the mechanism for giving them more is already proven and would be a three-line change. **We should not make that change.** Reasons in §3. ## 3. Why a shared IDE is the wrong shape for a fleet **(a) The IDE indexes one path per project; a member works in a different path.** Members get git worktrees under `/Users/dai.ha/LTMS/.bridged-worktrees/<name>/`. Measured against the live `cb401-manual` worktree: `project_not_found`. To make it work you would have to open every worktree as its own IntelliJ project at spawn and close it at teardown. Sum of `maxLoad` across profiles is **13**, so that is up to 13 IntelliJ projects indexing a fresh Maven tree at once. That is an IDE farm, not a message bus. **(b) The IDE is single-user and stateful.** `ide_sync_files`, `reformat_file`, `ide_refactor_rename`, `open_file_in_editor` and the debugger all mutate one shared IDE. Two members refactoring at once collide in a way neither can observe. `jetbrains` also exposes `execute_terminal_command` and `build_project`, which would let a member run commands in the operator's IDE context, outside its own worktree. **(c) It does not survive 2.0.** A loopback port on the operator's laptop is not reachable from another host. Wiring the fleet to it now builds a dependency that federation has to break. **(d) It is the wrong failure mode.** Best case a member gets `project_not_found` — loud, fine. Worst case a path resolves against the **main** checkout and returns diagnostics for a different copy of the file, clean, because the member's edit is not there. A clean result read as evidence is the [seam-vs-caller family](#113) again: three instances already this month. ## 4. What a member actually needs, and the shape that fits Not the IDE. A member needs to answer *"does my change compile, pass, and violate an inspection?"* It already has two of three — `mvn -f bridged/pom.xml clean install` works fine inside a worktree. The gap is **inspections only**. The shape that fits a fleet is **headless and per-worktree**, so each member gets its own answer with no shared state: - `/Applications/IntelliJ IDEA.app/Contents/bin/inspect.sh` — confirmed present on this host. - Qodana — **not** installed here (`which qodana` finds nothing). **Not yet measured, and must be before this is planned:** whether `inspect.sh` runs against a worktree that has no `.idea` of its own, how long it takes, and whether its findings match what the GUI reports. It is slow (minutes) and needs a project directory. Treat this section as a direction, not a design. ## Scope **2.0, not 1.1.** By the 1.1 admission rule — *would it still be broken with exactly one host?* — finding 1 qualifies, but 1.1 is tagged (`v1.1.0`, 2026-08-17) and reopening a closed milestone for newly-discovered scope is worse than carrying it forward. Finding 1 is an operator action, not a code change, and needs no ticket state to happen. ## Proposed outcome 1. Operator reopens the project; correct `CLAUDE.md`'s `project_path`. *(finding 1, do now)* 2. Record the decision **not** to share the IDE mount with members, with the reasons in §3, in `wiki/13-User-Guide.md` §1 ("what it is not") and `wiki/11-Features.md`. Right now the decision exists only as a habit, so it will be re-litigated. *(cheap, do next)* 3. Measure `inspect.sh` in a worktree before committing to anything in §4.
ltms added this to the 2.0 — one operation centre, many hosts milestone 2026-08-17 16:31:59 +02:00
Author
Owner

Correction to §4, and the headless question answered

The operator pushed back on my framing, and they were right. I wrote "the gap is inspections
only"
. That is wrong. The IDE's value is the index: ide_find_references,
ide_call_hierarchy, ide_type_hierarchy, ide_find_implementations answer questions grep
cannot. A member in a worktree has none of that, and no build gate replaces it. §4 above understates
the problem — read this comment instead.

Follow-up question: can IntelliJ Community run headless?

Yes — three headless modes, all present in the Community distribution

Measured in the installed IDE at /Applications/IntelliJ IDEA.app/Contents/bin:

entry point does stays up?
inspect.sh headless code inspection no — batch, exits
format.sh headless reformat no — batch, exits
remote-dev-server.sh warmup --project-dir=<p> headless indexing, no GUI no — indexes, exits
remote-dev-server.sh <cmd> <project> remote-dev backend host yes

macOS is supported, not rejected — launcher.sh:99 sets IS_DARWIN=1 and dispatches to
../MacOS/<launcher>.

But what is installed on this host is Ultimate, not Community: productCode: IU, version
2026.1.2 build 261.24374.151. There is no CE install here.

The catch — the MCP server is not IntelliJ

This is the finding that changes the design.

plugin id : com.github.hechtcarmel.jetbrainsindexmcpplugin
name      : "IDE Index MCP Server"
version   : 4.16.3
path      : ~/Library/Application Support/JetBrains/IntelliJIdea2026.1/plugins/

It is a third-party community plugin, not a JetBrains component. So "can IntelliJ run
headless"
and "can the index MCP run headless" are different questions:

  • The IDE's headless modes above are batch commands that run and exit. They never serve MCP.
  • The MCP endpoint is served by a running IDE process. Confirmed: lsof -iTCP:29170 shows PID
    7209, command idea, holding the listen socket.

So today there is exactly one MCP index endpoint, and it belongs to one GUI IDE, indexing one
project path.

The shape that could actually work

Two facts make a per-worktree design plausible:

  1. The port is configurable. McpSettings$State carries serverPort, with
    getDefaultServerPort and a notification.serverPortInUse path. So N instances on N ports is
    not ruled out by the plugin.
  2. Remote-dev backends already use per-project config dirs. That is why launcher.sh has an
    installPlugins command at all (line 559). Each backend gets its own plugins directory — which
    also means the MCP plugin would have to be installed into every backend, not inherited.

Sketch: bridged allocates a worktree per member today. It could allocate a headless IDE backend
and an MCP port
alongside it, and write that port into the member's .mcp.json at spawn. Port
allocation per member is work bridged already does for panes, so it is the right owner.

What that would cost, and why Community is the better fit

Each backend indexes a full Maven tree — minutes of CPU and GBs of RAM, up to maxLoad sum = 13
at once. That cost is the real objection, not the plumbing.

On edition: Community is arguably the correct choice for the fleet, and the operator's instinct
here is right. CE is free, so N backends raise no seat question, while N Ultimate backends do. CE
covers Java, Maven and git, which is everything this repo needs; Ultimate's extras are not used by
members. The lead can stay on Ultimate.

Not verified — do not plan on these

  • Whether jetbrains-index-mcp-plugin loads and serves at all inside a remote-dev backend. Its
    description says some refactorings are "fully headless", but that means "works with no editor
    open", not "works with no IDE".
  • Whether the plugin is even installable on CE.
  • warmup timing and memory for claude-bridge on this host.
  • Whether a backend's index answers identically to the GUI's.

The first bullet is the gate. If the plugin does not serve in a backend, the whole per-worktree
design collapses and the honest answer stays "members get mvn, the lead gets the index".

Next measurement, cheapest decisive one first: run
remote-dev-server.sh warmup --project-dir=<a worktree> and see whether it completes, how long it
takes, and whether anything binds an MCP port. That answers the gate without committing to a design.

## Correction to §4, and the headless question answered The operator pushed back on my framing, and they were right. I wrote *"the gap is inspections only"*. That is wrong. The IDE's value is the **index**: `ide_find_references`, `ide_call_hierarchy`, `ide_type_hierarchy`, `ide_find_implementations` answer questions `grep` cannot. A member in a worktree has none of that, and no build gate replaces it. §4 above understates the problem — read this comment instead. Follow-up question: *can IntelliJ Community run headless?* ### Yes — three headless modes, all present in the Community distribution Measured in the installed IDE at `/Applications/IntelliJ IDEA.app/Contents/bin`: | entry point | does | stays up? | |---|---|---| | `inspect.sh` | headless code inspection | no — batch, exits | | `format.sh` | headless reformat | no — batch, exits | | `remote-dev-server.sh warmup --project-dir=<p>` | **headless indexing, no GUI** | no — indexes, exits | | `remote-dev-server.sh <cmd> <project>` | remote-dev backend host | yes | macOS is supported, not rejected — `launcher.sh:99` sets `IS_DARWIN=1` and dispatches to `../MacOS/<launcher>`. **But what is installed on this host is Ultimate, not Community:** `productCode: IU`, version `2026.1.2` build `261.24374.151`. There is no CE install here. ### The catch — the MCP server is not IntelliJ This is the finding that changes the design. ``` plugin id : com.github.hechtcarmel.jetbrainsindexmcpplugin name : "IDE Index MCP Server" version : 4.16.3 path : ~/Library/Application Support/JetBrains/IntelliJIdea2026.1/plugins/ ``` It is a **third-party community plugin**, not a JetBrains component. So *"can IntelliJ run headless"* and *"can the index MCP run headless"* are different questions: - The IDE's headless modes above are **batch commands that run and exit**. They never serve MCP. - The MCP endpoint is served by a **running IDE process**. Confirmed: `lsof -iTCP:29170` shows PID 7209, command `idea`, holding the listen socket. So today there is exactly one MCP index endpoint, and it belongs to one GUI IDE, indexing one project path. ### The shape that could actually work Two facts make a per-worktree design plausible: 1. **The port is configurable.** `McpSettings$State` carries `serverPort`, with `getDefaultServerPort` and a `notification.serverPortInUse` path. So N instances on N ports is not ruled out by the plugin. 2. **Remote-dev backends already use per-project config dirs.** That is why `launcher.sh` has an `installPlugins` command at all (line 559). Each backend gets its own plugins directory — which also means the MCP plugin would have to be installed into every backend, not inherited. Sketch: `bridged` allocates a worktree per member today. It could allocate a **headless IDE backend and an MCP port** alongside it, and write that port into the member's `.mcp.json` at spawn. Port allocation per member is work `bridged` already does for panes, so it is the right owner. ### What that would cost, and why Community is the better fit Each backend indexes a full Maven tree — minutes of CPU and GBs of RAM, up to `maxLoad` sum = **13** at once. That cost is the real objection, not the plumbing. On edition: **Community is arguably the correct choice for the fleet**, and the operator's instinct here is right. CE is free, so N backends raise no seat question, while N Ultimate backends do. CE covers Java, Maven and git, which is everything this repo needs; Ultimate's extras are not used by members. The lead can stay on Ultimate. ### Not verified — do not plan on these - Whether `jetbrains-index-mcp-plugin` **loads and serves at all** inside a remote-dev backend. Its description says some refactorings are "fully headless", but that means "works with no editor open", not "works with no IDE". - Whether the plugin is even installable on CE. - `warmup` timing and memory for `claude-bridge` on this host. - Whether a backend's index answers identically to the GUI's. The first bullet is the gate. If the plugin does not serve in a backend, the whole per-worktree design collapses and the honest answer stays "members get `mvn`, the lead gets the index". **Next measurement, cheapest decisive one first:** run `remote-dev-server.sh warmup --project-dir=<a worktree>` and see whether it completes, how long it takes, and whether anything binds an MCP port. That answers the gate without committing to a design.
Author
Owner

Filed #162 (CB-634) to implement the member-side mount. It revisits §3 of this ticket: new measurement on the kb host shows the Index MCP is application-scoped and fails closed on an ambiguous project_path, and the enabled build exposes only 16 navigation tools (no terminal/build). That makes a bounded, opt-in, per-worktree mount safe, where §3 concluded — correctly, on the evidence it had — that sharing was the wrong shape.

Finding 1 here (the lead's own IDE step is a no-op / the project_path in CLAUDE.md may be wrong) stays with this ticket — it is an operator action, not part of #162.

Filed **#162 (CB-634)** to implement the member-side mount. It revisits §3 of this ticket: new measurement on the `kb` host shows the Index MCP is application-scoped and **fails closed** on an ambiguous `project_path`, and the enabled build exposes only 16 navigation tools (no terminal/build). That makes a bounded, opt-in, per-worktree mount safe, where §3 concluded — correctly, on the evidence it had — that sharing was the wrong shape. Finding 1 here (the lead's own IDE step is a no-op / the `project_path` in `CLAUDE.md` may be wrong) stays with this ticket — it is an operator action, not part of #162.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: fleet/fleetd#117