Skip to content

feat(web): track MCP status and list each chat's MCP servers in its resources - #693

Merged
Rinat S (rsolmano) merged 7 commits into
mainfrom
mcp-stack/5-web-mcp-state
Oct 13, 2026
Merged

Rinat S (rsolmano) merged 7 commits into
mainfrom
mcp-stack/5-web-mcp-state

Conversation

@danyaberezun

@danyaberezun danyaberezun commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The host now reports MCP servers and their per-chat status (#692), but the web app has nowhere to keep
that state, and a chat doesn't show which MCP servers it runs or whether they connected.

Approach

  • Generation-guarded MCP slice. The store gets an MCP slice per workspace.
    • installMcpList folds an mcp.list answer. The answer is the authoritative set of live chats as of
      the read token captured before the request.
    • applyMcpStatus folds mcp.status pushes. Status generations only grow, so a late or stale
      snapshot never overwrites a newer one.
    • A deleted chat drops its snapshot. A removed workspace drops its entry. A reconnect to a host below
      MCP_PROTOCOL_VERSION drops everything.
  • Starting watch. watchMcpWorkspace reads mcp.list once; a host capture that outlives its caller’s wait budget can still publish the connected state.
    It then re-reads with bounded backoff (1 → 13 s, six attempts per streak) only while a live chat of
    that workspace still reports a server as starting.
  • One state vocabulary in lib/mcpState: labels, tones, and the active and attention sets.
  • Chat resources. The inspector lists the chat's MCP servers (from SessionResources.mcpServers)
    in a third section, with attention states first.
    • Each row shows state, tool count and transport.
    • Row actions: disable / enable in this chat (mcp.setSessionOverride), and Reconnect. An
      extension-registered server can't be disabled per chat.
    • The trigger's count adds connected and starting servers, but it breathes only while work runs.

Changes

  • New files:
    • apps/web/src/lib/mcpState.ts;
    • apps/web/src/store/mcp.ts, with the fold and its guards;
    • apps/web/src/store/selectMcpServers.ts: selectMcpRead, selectMcpWorkspaceStarting;
    • apps/web/src/transport/mcp.ts: the read path and watchMcpWorkspace;
    • the mcp.status subscription in wireTransport.ts.
  • appStore.ts: mcpByWorkspace, mcpRevision, installMcpList, applyMcpStatus, and cleanup on
    chat delete, workspace removal and reconnect.
  • selectors.ts:
    • selectSupportsMcp;
    • orderMcpResources;
    • MCP fields in selectChatResourceGroups (mcpServers, mcpActiveCount), gated on host support.
  • Chat resources:
    • chat/resources/mcpRow.tsx, plus the inspector section and the trigger's working prop;
    • useChatResources gains setMcpEnabled / reconnectMcp;
    • ChatView watches mcp.list while the inspector shows MCP rows.
  • Docs: specs and UI-TERMINOLOGY.md.

Notes for reviewers:

  • Only the per-session snapshots are read in this PR (by the starting watch); the rest of the mcp.list answer that the
    slice keeps (servers, file errors, handled elsewhere) is what Settings reads in feat: manage MCP servers in Settings #694.
  • With the inspector showing status, pi's startup "MCP servers need attention" notice becomes status only
    (mcpSessions.ts, one line + its test and spec).
  • The trigger's count now includes connected and starting servers; breathing moves to the new working prop, so an idle
    connected server doesn't animate the header.
  • resources.test.tsx's import-boundary regex allows .transport property access (mcpRow reads server.transport).
  • The inspector's MCP rows get their first browser test in feat: manage MCP servers in Settings #694's e2e/mcp-settings.spec.ts (it adds the server through
    the Settings form).
  • Until feat: manage MCP servers in Settings #694, a server that needs sign-in is signed in from the chat with /mcp login <name>, and a
    repository-defined server can't be approved in the app; its row shows the state without an action.
  • The one-line wording edits in packages/contracts/SPEC.md and packages/server/src/agent/SPEC.md only
    name the new "Disable in this chat" action.

Screenshots

Captured with the e2e harness's hermetic fixture project, 1440×900 at 2×, dark theme; the same throwaway scenario ran on the base PR's tree (before) and on this PR's tree (after).

Chat header with one connected MCP server

Before After
Chat header with one connected MCP server, before Chat header with one connected MCP server, after

Resources inspector

Before After
Resources inspector, before Resources inspector, after

Related issues

No tracked issue. This is part of the MCP support stack (milestone 1); review and merge in order:

  1. feat: upgrade pi to 1.1.0, gate session reloads and teardown, import the login env #689 feat: upgrade pi to 1.1.0, gate session reloads and teardown, import the login env
  2. feat: pass each project's trust to pi and name what trusting it loads #690 feat: pass each project's trust to pi and name what trusting it loads
  3. feat(server): run the MCP servers from mcp.json in chats on pi's built-in engine #691 feat(server): run the MCP servers from mcp.json in chats on pi's built-in engine
  4. feat(server): serve MCP server management, status and sign-in to clients #692 feat(server): serve MCP server management, status and sign-in to clients
  5. feat(web): track MCP status and list each chat's MCP servers in its resources #693 feat(web): track MCP status and list each chat's MCP servers in its resources ← this PR
  6. feat: manage MCP servers in Settings #694 feat: manage MCP servers in Settings
  7. feat(web): render MCP tool calls, resources and tool_search as cards #695 feat(web): render MCP tool calls, resources and tool_search as cards

Base: #692, so this diff shows only this layer. The branches remain stacked for merge order; checks on the current head are listed by GitHub.

Review hardening (2026-10-12)

Rebased onto main at 9d6ad0b2, preserving Extensions Settings/reload integration. MCP uses protocol 83; the extension catalog retains 82. Existing MCP product policy and pi engine ownership are unchanged.

  • All list-producing reads/mutations are ordered per workspace and connection generation; a post-mutation read cannot reuse a pre-mutation request.
  • Legacy attention notifications remain visible and redacted; newer clients use status. Project-trust responses from pre-MCP hosts default the absent count to zero.

Latest verification

These results supersede the earlier counts/environment limitations below. The latest combined stack tree was compared byte-for-byte with the locally verified canonical tree; each restacked layer also received focused tests and server/web typechecks.

  • Fast gates (check:deps, boundaries, seams, spec-surface, compiler, lint) and typecheck → green; all 20 packages, including desktop. Compiler baseline was not relaxed.
  • bun run test, with isolated HOME/data/agent directories and no ambient provider credentials → all 20 package tasks plus root scripts passed. After the last UI-only preset correction, all 1,831 frontend src tests were rerun successfully; other package inputs were unchanged.
  • Final combined stack (2451693b) complete no-agent browser suite → 552 passed, all four isolated shards, using the repository’s standard CI retry configuration. Earlier local repeats exposed the unchanged touch-momentum flake, reproduced on clean main 3/8 times; no scroll code/assertion was changed or skipped. The final suite includes the preset opt-in recovery and JSON partial-add regressions.
  • Focused real-Central acceptance → 9 agent/resources/queue/tool-card cases plus 1 disabled-extension case passed; agent + queue rerun after the final retry correction → 2 passed. The complete paid-provider suite was not run.
  • Final CLI compilation (bun apps/cli/scripts/build-binary.ts) + bun run smoke:binary → passed, including the shared MCP artifact probe; vanilla-pi package parity passed under Node 22.19.0.
  • Additional review → no remaining local blockers; no suppressions were added. Spec validation retains only two pre-existing dangling task links.

Stack status (2026-10-13): rebased onto main bf4da661 on top of #723 (pi 1.2.0), which fixes the vanilla-pi parity gate broken by the
pi-codemode 1.2.0 release. #723 merges first; this stack then rebases to drop that commit. The branches remain stacked in the original
order and merge one at a time through the merge queue.

Pi 1.2.0 (2026-10-13)

Every pi adaptation in this layer was re-checked against the installed 1.2.0 package (see pi-compatibility.md in #695): the public
MCP exports, loader/writer code, shutdown ordering and startup dispatch are unchanged, so nothing is removed. Pi 1.2 widens
structuredContent to any JSON value and stops listing resources on connect (connected · N tools · resources); the status parser now
counts tools in that dotted wording (it previously read 0 for it) and both changes have fixtures.

Testing

  • Final stack on pi 1.2.0 / main bf4da661: fast gates + bun run typecheck 20/20; isolated bun run test 20/20 packages;
    bun run check:pi-packages OK (fresh npm install of pi 1.2.0 under Node 22.19); bun run e2e 551 passed + the known touch-momentum
    flaky (passed on retry); build-binary + smoke:binary passed; focused live agent/queue/chat-resources/tool-cards/subagents
    specs 11 passed; independent review of the upgrade and the new stack commits → no findings.

On this PR's tree:

  • bunx turbo run typecheck --filter='!@thinkrail/desktop' → 19/19
  • bun test packages/server packages/pi-delegation packages/pi-subagents packages/pi-background-commands packages/pi-todos packages/pi-dag packages/pi-thinkrail-workflow packages/contracts packages/shared packages/artifact-tests → 2001 pass, 10 skip, 0 fail
  • cd apps/web && bun test → 1596 pass
  • bun test ./scripts/ → 91 pass; bunx turbo run test --continue for every other package (desktop excluded) → 17/17 tasks
  • bun run lint → clean, no warnings; check:deps, check:boundaries, check:seams, check:spec-surface → OK
  • bun run e2e → all 8 shards passed (491 tests)
  • bun run build:binary && bun run smoke:binary → smoke OK

Desktop: @thinkrail/desktop's typecheck could not run as configured on this machine. Its electrobun prepare step waits on a shared cache lock that another process holds. Run without prepare, cd apps/desktop && tsc --noEmit reports the same 18 electrobun-declaration errors (TS7016/TS7006/TS7031) as on origin/main and none from this change. Its bun test passes (123 pass, 1 skip, as on origin/main). This change touches no desktop file.

Checklist

  • Fast gates pass: bun run lint, bun run typecheck, bun run test
  • E2E suite passes for app-affecting changes (bun run e2e, or bun run e2e:full when touching agent behavior)
  • Before/after screenshots are included for frontend changes, or marked not applicable
  • Relevant SPEC.md / top-level specs updated to reflect any boundary, contract, or behavior change
  • I have read the Contributing guide and agree to the Code of Conduct

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Requesting changes — please address the blocking inline finding.

@danyaberezun
danyaberezun force-pushed the mcp-stack/5-web-mcp-state branch from 514bc9e to 5592dc3 Compare October 9, 2026 10:42

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@danyaberezun
danyaberezun force-pushed the mcp-stack/5-web-mcp-state branch from 5592dc3 to e9122cd Compare October 9, 2026 11:08

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@danyaberezun
danyaberezun force-pushed the mcp-stack/5-web-mcp-state branch from e9122cd to 4939d80 Compare October 9, 2026 11:25

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@danyaberezun
danyaberezun force-pushed the mcp-stack/5-web-mcp-state branch from 4939d80 to 0103b02 Compare October 9, 2026 11:43

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@danyaberezun
danyaberezun force-pushed the mcp-stack/5-web-mcp-state branch from 0103b02 to 9968e95 Compare October 9, 2026 11:52

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@danyaberezun
danyaberezun force-pushed the mcp-stack/5-web-mcp-state branch from 9968e95 to dbb0ee7 Compare October 9, 2026 12:12

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

Comment thread apps/web/src/chat/ChatView.tsx Resolved

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@rsolmano
Rinat S (rsolmano) force-pushed the mcp-stack/5-web-mcp-state branch from 4846eee to cab5eec Compare October 13, 2026 11:21

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@rsolmano
Rinat S (rsolmano) force-pushed the mcp-stack/5-web-mcp-state branch from cab5eec to f14b296 Compare October 13, 2026 11:56

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@rsolmano
Rinat S (rsolmano) force-pushed the mcp-stack/5-web-mcp-state branch from f14b296 to 2533c3a Compare October 13, 2026 12:57

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

Base automatically changed from mcp-stack/4-mcp-host-service to main October 13, 2026 13:19
@rsolmano
Rinat S (rsolmano) force-pushed the mcp-stack/5-web-mcp-state branch from 2533c3a to 7e25338 Compare October 13, 2026 13:19

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

danyaberezun and others added 7 commits October 13, 2026 15:26
…esources

- A generation-guarded store slice folds mcp.list reads and mcp.status
  pushes per workspace and session.
- watchMcpWorkspace re-reads with bounded backoff while a server starts.
- The Resources inspector lists the chat's MCP servers with state, tool
  count and transport, and offers disable/enable in this chat and Reconnect.
- pi's startup MCP attention notice becomes status only now that the
  inspector shows it.
…p.status channel

The interceptor turned the startup notice into a status update for every
client, but only clients at MCP_PROTOCOL_VERSION receive mcp.status. The
notice is now re-sent as a toast to sockets below that version.
…ration

The inspector's bounded mcp.list re-read was never reset after a
disconnect, so a reconnect could leave a server stuck at Starting.
Two watchers reading from the same revision could land out of order and
drop a chat the newer list had installed, leaving its server at Starting.
…ration

A read captured before a reconnect would be rejected at installation, so a
watcher started after the reconnect issues its own read instead.
@rsolmano
Rinat S (rsolmano) force-pushed the mcp-stack/5-web-mcp-state branch from 7e25338 to 6db2497 Compare October 13, 2026 13:26

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved — ready to merge.

@rsolmano
Rinat S (rsolmano) added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit b900408 Oct 13, 2026
17 checks passed
@rsolmano
Rinat S (rsolmano) deleted the mcp-stack/5-web-mcp-state branch October 13, 2026 13: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.

2 participants