Skip to content

feat(adapters)!: Pi connects through its native MCP client - #121

Merged
DaXu999 merged 12 commits into
mainfrom
feat/pi-native-mcp
Nov 9, 2026
Merged

DaXu999 merged 12 commits into
mainfrom
feat/pi-native-mcp

Conversation

@DaXu999

@DaXu999 DaXu999 commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Why

Pi has shipped its own MCP client since 0.99. The Pi column still connected through pi-mcp-adapter, an extension that registers one mcp proxy tool, hides the catalogue behind it and replaces Pi's client when loaded. The host image pinned Pi 0.84 to keep that working. Pi's own client reads a different file in a different place, so the adapter's config was rejected outright on current Pi.

What

  • Breaking: Pi connects through its native MCP client. harnessMcpConfigFile("pi") returns .pi/mcp.json (was .mcp.json). piMcpConfig writes url, the bearer token as an Authorization header with every $ doubled so Pi reads it literally, timeout in seconds only when the host sets one, and exposure: "direct" so each tool is a tool of its own named mcp__<server>__<tool>. The runtime makes a generated file's directory before writing it.
  • The launch is --mode rpc --approve. --approve is what lets Pi read .pi/mcp.json from the scratch workspace; the docs state plainly what else it trusts. --no-builtin-tools and --no-session are gone: Pi keeps read, bash, edit and write and its own session records, and a host narrows either through args with --tools or --no-session.
  • Manifest and conformance. Pi's MCP manifest says mcpSupport: "native" and mcpExtension is gone. scripts/mcp-conformance.mjs stamps the Pi column native, declares the four built-ins as harnessLocal, and stops on mcpExtensionVersion without mcpExtension.
  • Docs: the toolshare and MCP API docs describe the file, the naming, what --approve trusts and the $ escaping; ADR 0014 gains a dated note; CHANGELOG entry under Unreleased.

19 files: 579 lines added, 212 removed. Of those, regenerated docs/api is 79 added and 71 removed, and tests are 225 added and 18 removed.

What a host has to update

  • Pi 0.99 or newer, and no pi-mcp-adapter: an extension that replaces Pi's client stops Pi reading any mcp.json.
  • Instructions that tell Pi's model its only tool is mcp are wrong now; the tools are mcp__sharedos__<tool>.
  • A host that writes the file itself makes .pi/ first and doubles $ in the header.
  • Pi writes one session file per turn under its agent directory unless the host appends --no-session.

Related

Sibling of #123 (the harness branch, which owns every non-Pi change) and #122 (the limit notice). This branch owns every Pi change. It is rebased onto main after both, with one docs commit for the Pi rows the harness branch wrote, and the checks below ran on the rebased head.

Checks

  • pnpm check is green on the rebased head: format, typecheck, vitest 1254, test:release 7/7, docs:api:check and conformance:check.
  • Checked end to end against Pi 1.5.0 with a private HOME and a local fake model endpoint: the turn completes, files.read reaches the bridge with the token, a wrong or missing header is refused, and the session file appears where the docs say.
  • Reviewed by a second agent; every finding is fixed on the branch.

Pi has shipped a native MCP client since 0.99. It reads .pi/mcp.json from
the directory it runs in, only for a trusted project, and ignores a root
.mcp.json, so the config SharedOS wrote was never read. The shape was
also the one pi-mcp-adapter takes, which Pi rejects.

piMcpConfig now emits Pi's own shape: url, the bearer token in
headers.Authorization, timeout in seconds when the host set one, and
direct exposure so each tool reaches the model as mcp__<server>__<tool>.
harnessMcpConfigFile names it .pi/mcp.json, the runtime makes a config
file's directory before writing it, and the Pi launch passes --approve so
Pi trusts the file written for the turn. The vendor manifest stamps
mcpSupport native, and the MCP conformance script records an extension
only when the host configuration declares one.
Pi 0.99 and later need no extension. The docs now say where Pi reads the
per-turn .pi/mcp.json, why the launch passes --approve and what else that
trusts, how Pi names the catalogue's tools, and that the startup wait for
direct servers is Pi's own. ADR 0014 gets a dated note rather than a
rewrite. The systems-cost text and its renderer no longer name Pi's
extension as the proxy example.
Pi expands $NAME and ${NAME} in a header value and reads $$ as one
dollar sign, so a host-chosen token with a dollar sign in it reached the
bridge rewritten and was refused. piMcpConfig now doubles each one. The
harnessToolAlias comment says Pi also rewrites the server part of a tool
name, which the alias does not, and a test comment gives the right reason
Pi skips an auth key.
The stand-in harness now sends the headers its config names, read the
way Pi reads them, and keeps each response's HTTP status. New tests: a
token with dollar signs reaches a bridge that wants it, a wrong or missing
header is refused and the turn sees no catalogue, a turn file named
.pi/mcp.json is refused before launch, and the Pi manifest says
mcpSupport native.
A host configuration that gave mcpExtensionVersion without mcpExtension
had the version dropped and the column stamped native, which says the
opposite of what was declared. The script now stops before probing.
The toolshare page claimed no flag keeps Pi to the catalogue for one run.
--tools 'mcp__<server>__*' does, and a host appends it through args;
SharedOS does not pass it, so the model also sees the tools of servers in
Pi's own mcp.json and Pi's codemode tool unless the host restricts them.
A new section states what --approve trusts in Pi's own words, and advises
fixed turn-file names and a workspaceRoot with no .agents/skills above it.
The CHANGELOG lists what a host updates for Pi's native client.
PI_MCP_HARNESS no longer passes --no-builtin-tools, so the launch is
--mode rpc --no-session --approve. SharedOS decides the calls that reach
it over MCP and restricts none of a harness's own tools, as on
feat/harness-any-version. Pi keeps read, bash, edit and write, and a host
may narrow them through args with --tools. The MCP conformance script
declares those four as Pi's harnessLocal, the docs and the CHANGELOG say
Pi keeps its own tools, and the launch test asserts the new arguments.
SharedOS sets only what it takes to run the CLI, connect it to the bridge
and let the bridge's tools run, so PI_MCP_HARNESS no longer passes
--no-session and its launch is --mode rpc --approve. Pi keeps its own
session records, as it would for any user, under ~/.pi/agent/sessions/ by
default; a host that does not want them appends --no-session through
args. The live Pi column in native-conformance.mjs drops the flag too.
The spec comment, the launch test, the toolshare docs and the CHANGELOG
Pi entry say so.
@DaXu999
DaXu999 force-pushed the feat/pi-native-mcp branch from 747996b to 6c631cc Compare November 9, 2026 19:41
@DaXu999
DaXu999 merged commit 4aa93bb into main Nov 9, 2026
2 checks passed
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.

1 participant