feat(mcp-pool): Toggle all switch for poolable MCP servers - #2433
Conversation
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsThe diff is clean. Validation on the batch path mirrors the single-name path ( No findings. [OPUS-REVIEWED] 629f377 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSAdvisory design-level review of Design-Verdict: PASS Real N-clicks/N-writes problem fixed at the root — an additive batch form on the existing endpoint, one atomic write, single form untouched. The one design wrinkle (binary switch on a mixed list briefly pools excluded servers) is already acknowledged, correctly scoped as a tri-state control change, and tracked in #2434 with the backend already supporting arbitrary subsets — nothing further to act on here. [DESIGN-REVIEWED] 629f377 |
UX Review (Fable 5) — ✅ PASSAdvisory UX-level review of Reviewing the frontend diff, the two committed screenshots, and the copy against the sibling MCP pool page conventions — done. Findings below. UX-Verdict: PASS One switch with an honest "N of M poolable servers pooled" subline, held rows during writes, an atomic error message — the flow reads cold and keeps its promises. The earlier subline-denominator concern is fixed in this diff ( [UX-REVIEWED] 629f377 |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
0001f84 to
54ce4c0
Compare
|
Dispositioning UX Review's three suggestions at
One local-only note for transparency: the full frontend suite exited non-zero |
54ce4c0 to
bc7507d
Compare
|
Three CI reds on Backend Tests (3.10 shard 2) and Backend Tests (Windows shard 2) — one and the Inclusive Language — two comments I wrote said "master switch". Replaced with Verified locally after the fix: That last one also settles the intermittent |
bc7507d to
4fcdc7d
Compare
|
UX Review's CONCERNS round, dispositioned at
Mixed state reads as plain off — partially addressed, rest deferred. The Rename Gates after the change: |
Flipping every server's poolable flag meant one click per row. This adds a Toggle all switch at the top of the list, plus a batch form of the poolable endpoint so the whole set is one config write and one pool re-apply instead of N racing writes.
4fcdc7d to
629f377
Compare
|
Both suggestions from the PASS round are fixed at Dead control when nothing is eligible. The row rendered as a permanently Escape-hatch rows contradicting the count. A
Gates: |
…irodotdev#2433) Flipping every server's poolable flag meant one click per row. This adds a Toggle all switch at the top of the list, plus a batch form of the poolable endpoint so the whole set is one config write and one pool re-apply instead of N racing writes.
1. What is the problem?
The MCP Pool page lists every MCP server across the user's agent configs with a
per-row poolable switch, and that row switch is the only way to change the
allowlist. Turning pooling on for a whole install therefore costs one click per
server — 34 clicks on my machine, 48 rows on a fully-configured one — and each
click is its own
POST /api/mcp-gateway/servers/poolable, its own config.jsonrewrite, and its own in-process pool re-apply.
2. Why this issue matters to the user
Poolability is close to an all-or-nothing decision in practice: a user who wants
the shared gateway wants their stdio servers pooled, and a user backing out wants
them all off. The page made the common case the most laborious one, and doing it
by hand also means N sequential re-applies (each one relinks sessions) where one
would do. There was no way to see the aggregate state either — you had to read
34 switches to answer "is pooling on?".
3. How our fix solves it
Symptom: no way to flip the whole list; 34 clicks, 34 config writes.
Cause: the surface only ever exposed the per-row write, and the endpoint only
accepted a single
name.Fix, in two halves:
website/src/pages/settings/McpPoolableServers.tsx): aToggle allrow at the top of the list with a
N of M pooledsubline. Its state is derivedfrom the eligible rows only —
poolableEligible()drops the rows this UI cannever write (denylisted, non-stdio, or poolable solely via the agent-JSON
poolable:trueescape hatch). Without that filter a single denylisted serverwould pin the toggle-all switch off forever and make its click a permanent no-op.
toggleAllTargets()sends only the eligible rows that disagree with the nextstate, so a half-on list costs a write for the difference rather than a rewrite
of every row. Row switches are held while the bulk write is in flight, and the
toggle-all switch is held while any row write is — the two would otherwise race the
same allowlist.
src/kiro_crew/dashboard/handlers/mcp.py): the poolable endpointnow also accepts
{"names": [...], "poolable": bool}. One config write underthe existing config lock, one
_mcp_gateway_apply_poolable(), one SEL recordnaming every server touched. The single-
nameform is untouched — samevalidation order, same response shape — so nothing else that calls it changes.
_MAX_POOLABLE_BATCH(200) fences a hand-rolled request from turning one writeinto an unbounded one.
Because the write is one atomic request, the allowlist can never land
half-flipped, which is also why the error path reports the whole set instead of
per-row failures.
4. What tests we did
filtering, checked-state with and without locked rows, unchecked when nothing
is eligible, and target selection in both directions including the no-op case.
TestGatewaySetPoolableBatch): the batch validationmatrix (non-list, non-string member, empty, whitespace-only, traversal name,
missing boolean), oversized batch, non-object body, add/remove semantics
including a name absent from the allowlist,
applyawaited exactly once forthe whole batch, the audit naming every server, and a regression test that the
single-
nameform still answers withname.pytest(262 passed across every suite touchingpoolable),
isort/flake8/mypyclean,tsc -bclean, fullvitest869/869 files with
I18N_BASE_REFset,npm run i18n:checkall 12 checks OK,eslint0 errors.kirocrew pod(127.0.0.1:7964, 5 servers): one clicktook the page from
0 of 5 poolable servers pooledto5 of 5 poolable servers pooledwith all five rowswitches following, and a second click returned it to
0 of 5— asserted onaria-checkedand the subline text, not just eyeballed.If the images do not render, they are in the Files changed tab under
temp-screenshots/mcp-pool-toggle-all/.5. Any other suggestions on the work
UX Review's three advisory suggestions, dispositioned:
54ce4c018.n_of_m_poolednowreads
{{on}} of {{total}} poolable servers pooledin all 13 catalogs, so thecount names its own scope instead of implying every visible row. Screenshots
re-captured against the new string and re-pinned.
Toggle alltoPool all— REBUTTED. The label is the one thingthe requester specified by name ("add a toggle called toggle all"), and the row
label
Pool {{name}}names a single server's action while the toggle-all switchnames the control. Happy to rename if the maintainers prefer the row verb to
win.
MCP Pool: Toggle all is binary, so clearing a mixed list pools everything first #2434. Clearing a mixed list does
cost an on-then-off round trip that briefly pools excluded servers. Fixing it
properly means changing the control to an indeterminate tri-state, which is a
control-type change rather than a tweak; the batch endpoint already accepts an
arbitrary subset, so no backend work is needed when it happens.
Two further notes:
{{on}} of {{total}}string rather than a countedplural key, since both numbers are always present and no locale needs a
singular form for the pattern.
(per-agent, per-transport); nothing here assumes "all".