Skip to content

feat(web): render MCP tool calls, resources and tool_search as cards - #695

Merged
Rinat S (rsolmano) merged 7 commits into
mainfrom
mcp-stack/7-mcp-tool-cards
Oct 13, 2026
Merged

Rinat S (rsolmano) merged 7 commits into
mainfrom
mcp-stack/7-mcp-tool-cards

Conversation

@danyaberezun

@danyaberezun danyaberezun commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

MCP calls in the chat fall back to the default tool card:

  • raw argument JSON;
  • image blocks, which pi also sends as text;
  • no way to read the full output pi saved for a truncated result;
  • pi's CLI instructions ("run /mcp login"), which don't apply in ThinkRail.

pi names MCP tools per server (mcp__<server>__<tool>), so an exact-name registration cannot catch them.

Approach

  • Prefix renderers. The tool registry gains registerToolRendererPrefix.
    • Every registry read (renderer, summary, chrome, prominence) goes through one resolver: the exact
      name first, then the longest app-local prefix, then the default.
    • Extension descriptors stay exact-name only.
    • Today the only prefix is mcp__.
  • Durable cards. The cards render from the host's durable details.thinkrail summary (feat(server): run the MCP servers from mcp.json in chats on pi's built-in engine #691) and
    pi's own result details ({ server, tool, fullOutputPath }), so a card looks the same live, after a
    transcript reload, and on another client.
    • Images appear once, in a shared strip, and the summary carries no base64.
    • Structured content renders as a collapsed JSON tree.
  • Full output. The card reads the full output through mcp.readOutput (feat(server): serve MCP server management, status and sign-in to clients #692), which serves only
    the result's recorded output file. expired and unavailable have their own states.
  • Sign-in errors become a Sign in link into Settings › MCP servers (feat: manage MCP servers in Settings #694) for MCP OAuth, or
    Settings › Providers for auth.provider; the latter keeps the provider identity instead of hiding it. On a host without MCP, or in the plan page's chat, the actions are hidden.
  • Other MCP tools. list_mcp_resources, list_mcp_resource_templates, read_mcp_resource and
    tool_search get their own cards, with a raw-text fallback for a truncated listing.

Changes

  • pi-compatibility.md: an architecture-design spec directly under architecture, linked from AGENTS.md; it records host adaptations, owning specs, upgrade checks and removal gates, separately from deliberate ThinkRail policy.
  • apps/web/src/chat/toolRegistry.tsx: registerToolRendererPrefix and the single resolver
    (+ tests).
  • apps/web/src/chat/tools/mcp/ (new sub-module with its own SPEC.md):
    • mcpResult.ts: parsing details.thinkrail, pi's flat result details (server, tool, fullOutputPath) and the content;
    • the cards: McpToolCard, McpResourceCards, ToolSearchCard;
    • their parts: McpCardParts, McpJsonTree, McpActions;
    • register.ts;
    • unit tests.
  • ChatActions: gains the optional readMcpOutput(toolCallId) / openMcpSettings() pair.
    ChatView supplies it only on an MCP-capable host.
  • McpToolDetails in contracts is the web's copy of pi's result details for mcp__* and the three MCP
    resource tools (the web app cannot import pi-coding-agent).
  • Three sentences in the contracts and agent/mcp specs move from "can be rendered" to "cards render", now
    that cards exist.
  • e2e/mcp-tool-cards.spec.ts: seeds historical results and pins:
    • the shared image strip and the collapsed JSON tree;
    • Full output through the real mcp.readOutput, for a recorded file and an expired one;
    • the Sign in deep link;
    • the truncated-listing fallback.

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).

mcp__* call with structured content and an image

Before After
mcp__* call with structured content and an image, before mcp__* call with structured content and an image, after

Structured content expanded (new in this PR)

Structured content expanded

Truncated result

Before After
Truncated result, before Truncated result, after

Full output (new in this PR)

Full output

list_mcp_resources

Before After
list_mcp_resources, before list_mcp_resources, after

tool_search

Before After
tool_search, before tool_search, after

Provider-backed authentication recovery

Same persisted MCP error fixture on this PR's updated base (#694, 693f5e44) and the updated cards. A provider-backed failure now keeps the provider name and opens Settings → Providers, rather than routing to MCP OAuth. Captured through the isolated browser harness without provider credentials.

Before After
Provider-backed MCP failure before Provider-backed MCP failure after

Provider settings destination

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
  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 ← this PR

Base: #694, 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.

  • Provider-backed authentication keeps the provider identity and opens Settings → Providers; MCP OAuth still opens MCP Settings.
  • pi-compatibility.md is an architecture-design spec with parent: architecture, linked from AGENTS.md, with owners, upgrade checks and removal gates. Deliberate host policy is explicitly separate from temporary workarounds.
  • The provider-auth before/after scenario is included above; the compatibility spec remains draft for user review.

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 → 1680 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 (497 tests)
  • bun run build:binary && bun run smoke:binary → smoke OK, including the MCP artifact probe

bun run e2e:full (complete suite, real Central) on this tree: no-agent phase all 8 shards passed (497 tests); agent phase 53 passed and 2 failed, both of which also fail on origin/main: error-turn.live.spec.ts:35 (its bad-model injection only rewrites a session.create that carries an explicit model, which New Workspace doesn't send by default) and visualize.live.spec.ts:62 (the model doesn't always mark a recommended option: 2 of 4 runs passed on origin/main, 4 of 5 reruns passed here).

Focused live specs on this tree (real Central, bun run e2e:full <files>): agent, chat-resources, queue and tool-cards → 9 passed.

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.

Approved — ready to merge.

@danyaberezun
danyaberezun force-pushed the mcp-stack/7-mcp-tool-cards branch from 26a68e9 to d40331f Compare October 9, 2026 10: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.

@danyaberezun
danyaberezun force-pushed the mcp-stack/7-mcp-tool-cards branch from d40331f to b7aa2f4 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/7-mcp-tool-cards branch from b7aa2f4 to edb6884 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/7-mcp-tool-cards branch from edb6884 to d2b5494 Compare October 9, 2026 11:44

@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/7-mcp-tool-cards branch from d2b5494 to 35c97a0 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.

@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/7-mcp-tool-cards branch from 3b3def0 to b5f85b7 Compare October 13, 2026 13:04

@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/7-mcp-tool-cards branch from b5f85b7 to b32aeee 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.

@rsolmano
Rinat S (rsolmano) force-pushed the mcp-stack/7-mcp-tool-cards branch from b32aeee to b11067c 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.

@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/7-mcp-tool-cards branch from b11067c to 5f453cf Compare October 13, 2026 13:48

@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/6-mcp-settings to main October 13, 2026 14:23
danyaberezun and others added 7 commits October 13, 2026 16:23
- registerToolRendererPrefix: exact names, then the longest prefix.
- MCP cards render from the durable details.thinkrail summary: one image
  strip, a collapsed JSON tree, Full output through mcp.readOutput, and a
  Sign in link into Settings › MCP servers.
- Resource listing/read and tool_search cards with a raw-text fallback.
…gn in button replaces it

Call output and a listing's per-server error keep the server's message but
lose the "Run /mcp …" sentence when the in-app action is offered; a
standalone render keeps pi's text verbatim.
@rsolmano
Rinat S (rsolmano) force-pushed the mcp-stack/7-mcp-tool-cards branch from 5f453cf to 282006d Compare October 13, 2026 14:23

@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
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 13, 2026
@rsolmano
Rinat S (rsolmano) added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit 17cfeec Oct 13, 2026
17 checks passed
@rsolmano
Rinat S (rsolmano) deleted the mcp-stack/7-mcp-tool-cards branch October 13, 2026 15:05
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