Skip to content

feat(server): run the MCP servers from mcp.json in chats on pi's built-in engine - #691

Merged
Rinat S (rsolmano) merged 4 commits into
mainfrom
mcp-stack/3-mcp-engine
Oct 13, 2026
Merged

Rinat S (rsolmano) merged 4 commits into
mainfrom
mcp-stack/3-mcp-engine

Conversation

@danyaberezun

@danyaberezun danyaberezun commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

pi ships an MCP engine, but ThinkRail never activated it: pi's SDK leaves built-in extensions to the
embedder, so the mcp.json servers a user already has for pi are not available in ThinkRail chats.

Turning pi's built-in on as-is would be unsafe for an IDE that opens arbitrary repositories:

  • Repository servers start on open. A cloned repository's .pi/mcp.json would start local commands
    as soon as a chat opens.
  • Risky tools run unasked. Tools that may change data would run without the user being asked.
  • stdio servers outlive their chat. pi's MCP stops servers in a session_shutdown handler, and a
    slow handler of another extension delays it.

Approach

  • The built-in as a descriptor. Parent sessions get pi's mcp built-in as a replaceable built-in
    descriptor (createMcpEngine), together with tool-search.
    • Configuration comes from a host loader at parity with pi's own mcp.json loader, pinned by
      parity tests against pi's error messages.
    • Replacement. An installed extension that owns /mcp replaces the built-in instead of running
      alongside it.
    • Off switch. -builtin:mcp in pi settings turns it off, as in pi. This is resolved by name
      through feat: upgrade pi to 1.1.0, gate session reloads and teardown, import the login env #689's loader.
    • Subagents never load an engine. The Agent tool's description says so, so the parent calls MCP
      tools itself and passes the results in the task.
  • Repository servers need trust and approval.
    • User-level servers (~/.pi/agent/mcp.json) run as configured. The project record can disable
      a user-level server or change its exposure, and it never writes the user's file.
    • Repository entries. An entry from .pi/mcp.json loads only when the project is trusted (feat: pass each project's trust to pi and name what trusting it loads #690)
      and the entry is approved at its exact fingerprint (Project.mcpApprovals, covering the whole
      entry). Any edit lapses the approval.
    • No approval route yet. This PR reads approvals. The next PR writes them.
    • Trust notice. The notice counts a repository's MCP servers and says trust alone does not start
      them.
  • Confirmation before risky calls.
    • Read-only tools. An MCP tool marked read-only (readOnlyHint) runs unasked.
    • Every other tool asks Deny · Allow once · Allow in this chat. Deny is kept away from Allow in
      this chat.
    • No answer fails closed. A missing answer blocks the call, and an accepted steer cancels the
      pending dialog.
    • tool_search is activated when an extension registers a server, so deferred tools stay
      reachable.
  • Shutdown order. The engine captures pi's MCP session_shutdown handlers and runs them first in
    feat: upgrade pi to 1.1.0, gate session reloads and teardown, import the login env #689's bounded session_shutdown. Servers stop within the budget even when another extension's handler
    never settles. That also holds after a session whose preparation failed once pi had started its
    servers.
  • Durable result summary. pi drops structuredContent from finalized messages, and images make
    results large. A tool_result handler therefore writes details.thinkrail: a bounded (64 KiB
    structured content), base64-free summary of the content blocks. It stays the same after a reload and
    on another client.

Changes

  • packages/server/src/agent/mcp/ (new sub-module with its own SPEC.md), with tests and a stdio
    fixture:
    • config.ts: pi-parity loader, host exposure normalization, fingerprints, approval gating;
    • engine.ts: the built-in descriptors and shutdown capture;
    • sessionHost.ts: the per-session host extension, with confirm and the summary writer;
    • results.ts.
  • agentSessionManager.ts:
    • creates an MCP session host per parent session (policy from setMcpPolicyResolver);
    • a queued steer cancels pending confirmations;
    • emitSessionShutdown runs the engine's shutdown first.
  • mcpPolicyOf(project) (in agent/mcp/config.ts) wired as the policy resolver in host/server.ts.
  • Contracts:
    • Project.mcpApprovals / Project.mcpOverrides, ProjectTrustSummary.mcpServers;
    • McpResultSummary, McpExposure / McpProjectOverride.
    • No protocol change.
  • Trust summary. projectTrust.ts counts .pi/mcp.json entries, and the web trust notice names
    them.
  • pi-subagents. The Agent description says subagents cannot call MCP tools.
  • Docs. goal-and-requirements.md ("Trust is explicit") and packages/shared/SPEC.md: the login
    environment import now also names mcp.json ${VAR} references and stdio MCP servers.

Notes for reviewers:

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

Welcome trust notice — repository with one .pi/mcp.json server

Before After
Welcome trust notice — repository with one .pi/mcp.json server, before Welcome trust notice — repository with one .pi/mcp.json server, 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 ← this PR
  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

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

  • MCP cleanup is now shared per load and awaited before replacement startup. A closed load cannot restart from a delayed session_start or registration event.
  • Runner-scoped cleanup covers chat/workspace removal and graceful/emergency shutdown; regressions include stubborn processes and hung handlers.
  • Faux-provider fixtures explicitly select their model instead of relying on ambient credentials.

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 → 1943 pass, 10 skip, 0 fail
  • bun test packages/server/src/agent/mcpEngine.test.ts packages/server/src/agent/mcpConfirm.test.ts, run twice → 15 pass each time. They use a real stdio server, a faux provider and pi's real engine, and cover:
    • connect and call;
    • confirm and steer-cancel;
    • termination on delete and quit, including a server that ignores EOF and SIGTERM;
    • the shutdown budget, and a failed preparation;
    • -builtin:mcp, replacement by an installed extension, and subagents;
    • the durable summary.
  • cd apps/web && bun test → 1580 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

Focused live specs (real Central, bun run e2e:full <files>): agent, subagents and queue → 4 passed. No live spec drives an MCP server; the engine tests above run pi's real engine against a stdio server. The complete bun run e2e:full suite was not run.

The complete bun run e2e:full suite ran on the top of the stack (#695), which contains this change: no-agent 8/8 shards; agent 53 passed, 2 failed — error-turn.live.spec.ts:35 and visualize.live.spec.ts:62, which fail on origin/main too.

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.

Comment thread packages/server/src/agent/mcp/results.ts Outdated
@danyaberezun
danyaberezun force-pushed the mcp-stack/3-mcp-engine branch from f1577d7 to 331320b Compare October 9, 2026 10:32

@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/3-mcp-engine branch from 331320b to 6bc56f9 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.

@danyaberezun
danyaberezun force-pushed the mcp-stack/3-mcp-engine branch from 6bc56f9 to 7d16eaf Compare October 9, 2026 12:24

@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/3-mcp-engine branch from 7d16eaf to 521dc15 Compare October 9, 2026 12: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/3-mcp-engine branch from 521dc15 to 627cbed Compare October 9, 2026 13:17

@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/3-mcp-engine branch from 627cbed to 2240947 Compare October 9, 2026 13:39

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

Comment thread packages/server/src/agent/mcp/sessionHost.ts
Comment thread packages/server/src/agent/mcp/engine.ts Outdated
Comment thread packages/server/src/agent/mcp/sessionHost.ts
@danyaberezun
danyaberezun force-pushed the mcp-stack/3-mcp-engine branch from 2240947 to a3974a7 Compare October 11, 2026 07:13
Base automatically changed from mcp-stack/2-project-trust to main October 11, 2026 20:22
@rsolmano

Copy link
Copy Markdown
Collaborator

Rebased the stack onto current main (it now contains #689 and the squash of #690) and force-pushed with a lease. What changed in this PR:

  • Trust summary conflict. The final feat: pass each project's trust to pi and name what trusting it loads #690 replaced nativeResources: boolean with piResources: ProjectResourceKind[]. ProjectTrustSummary keeps main's shape and adds mcpServers. chat/projectTrust (moved from panels/ on main) counts .pi/mcp.json entries as pending pi-level content too. So a repository whose only gated content is mcp.json still shows the notice, and its grant still sends resources: true. Tests cover both cases, on the server and in the web app.
  • piResourceTrustFor stays module-private, as on main. host/server.ts uses main's piTrustsProjectPath.
  • Conflicts in the agent, host, projects, contracts and panels specs were merged onto main's text, and the MCP additions were carried over.

The readOnlyHint thread is still open pending a product decision.

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

@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 findings.

Comment thread packages/server/src/agent/agentSessionManager.ts

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

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

🌐 Website preview

Preview retired when this pull request closed.

@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 4 commits October 13, 2026 13:56
…t-in engine

- Parent sessions load pi's mcp and tool-search built-ins as replaceable
  descriptors, with a host loader at parity with pi's; -builtin:mcp and an
  extension owning /mcp turn the built-in off; subagents get no engine.
- Repository-defined servers load only in a trusted project and at their
  approved fingerprint (Project.mcpApprovals); any edit lapses approval.
  Approvals are read here; the next changes write them.
- Tools not marked read-only ask Deny / Allow once / Allow in this chat,
  fail closed, and an accepted steer cancels the pending confirmation.
- pi's MCP shutdown runs first in the bounded session_shutdown.
- MCP results carry a durable, base64-free summary in details.thinkrail.
blocks copied every URI, name and MIME string of every returned content
item unbounded. Blocks are now kept in order until the budget is spent
(omittedBlocks counts the rest) and structuredContent gets what remains.

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

Comment thread apps/web/src/chat/projectTrust.ts
@rsolmano
Rinat S (rsolmano) dismissed jetbrains-air[bot]’s stale review October 13, 2026 12:26

Pushed back on the single finding (see the thread): #693 in this stack already makes ProjectTrustSummary.mcpServers optional and derives a missing count as zero, the layers merge in order, and UI and host ship together, so no protocol capability is needed.

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

3 participants