Skip to content

fix(mcp-run): load the project-root .env set and expand env state anchors to the project root - #61

Merged
ScriptedAlchemy merged 2 commits into
mainfrom
fix/mcp-run-env-and-anchor
Aug 31, 2026
Merged

fix(mcp-run): load the project-root .env set and expand env state anchors to the project root#61
ScriptedAlchemy merged 2 commits into
mainfrom
fix/mcp-run-env-and-anchor

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Closes #58, Closes #59.

Summary

mcp run now owns the two operator seams that kept consumer wrapper scripts alive after RFC #50 Phase 1:

  • mcp run loads no .env: RFC #50 §3.1 promised loadEnv, so operator-credential consumers must keep wrapper scripts #58.env loading. The runner loads the project-root .env set by default, via rsbuild's loadEnv conventions (.env, .env.local, .env.<mode>, .env.<mode>.local, with the existing --mode flag selecting variants) — the same files createRslib reads for the same consumers at build time. --env-file <path> (repeatable, Node --env-file dialect, later files win) replaces the conventional set; --no-env skips the layer. A named file that cannot be read is a hard error, never a silent skip. Loading targets a scratch object, so the runner's own process.env is never mutated.
  • mcp run expands ${PLUGIN_ROOT} and the AGENT_BUNDLE_PLUGIN_ROOT anchor to the artifact target root, fragmenting durable state per rebuild #59 — env-anchor expansion. Plugin-root path tokens in env values — including the injected AGENT_BUNDLE_PLUGIN_ROOT durable-state anchor — now expand to the resolved project root, not the ephemeral artifact/<target> root, so consumer state survives artifact rebuilds. args/cwd stay artifact-rooted (args[0] is the content-hashed bundle inside the target root), exactly as the issue requires. Implemented as issue direction 1 with the per-field split applied at the mcp run seam: the launch wraps the adapter's resolveValue and swaps the pluginRoot root for the env field only, so adapters, McpRuntimeRoots, and mcp list/invoke (which deliberately use an ephemeral state root) are untouched. --plugin-root <path> restores a byte-faithful copied-artifact rehearsal; host-install semantics are unchanged.

Documented environment precedence (canonical table in docs/entry-conventions.md)

Lowest to highest: manifest env (declared entries + injected anchor, tokens expanded) < .env file layer < operator process.env. This also fixes #59's ordering finding: manifest env was previously spread last and silently beat operator exports.

Consumer validation (movie-library)

movie-library/scripts/run-stdio.sh (57 lines) survives today purely for these seams. After this ships it can drop:

  • the .env sourcing block (set -a; . "$ENV_FILE"; set +a plus the missing-file warning) — a bare mcp run now loads the same file;
  • the MOVIE_LIBRARY_ROOT / MOVIE_LIBRARY_STATE_DIR state pins — the anchor its state-root.ts already trusts now lands state at <plugin>/.runtime, the exact location the pins produce;
  • the cd "$PLUGIN_ROOT" state-root motivation (--root pins the runner state root; the cd remains only if wanted for npx bin resolution).

What's left is the server-id sugar (publicmovie-library-public) — the optional "thin alias" the RFC's migration table predicted, not a required wrapper.

Tests

  • mcp run > layers the launch environment… — launch-env composition unit test (injected spawn): .env fills gaps, .env beats manifest env, operator exports beat both (including AGENT_BUNDLE_PLUGIN_ROOT), .env.<mode> variants, --env-file replacement, --no-env, --plugin-root rehearsal, unreadable-file error, no .env leakage into the runner's own env, args/cwd staying artifact-rooted.
  • mcp run > anchors consumer state at the project root under a bare CLI mcp run — integration: a consumer entry trusting the documented anchor writes state; it lands at <project>/.runtime with the .env credential visible, and nothing lands under artifact/portable/.
  • mcp run > rejects --env-file combined with --no-env — CLI flag conflict.

Gate

pnpm build, pnpm typecheck, pnpm lint, pnpm test:unit all green; integration files package-build, cli, api, mcp, public-api, target-mcp-runtime, path-token-resolver all green (no packed test references mcp run). Changeset: agent-bundle minor.

Note for concurrent work

feat/rfc50-phase2-framework also edits src/cli.ts, but in the inspect/validate hunks — no textual or semantic overlap with the mcp run command changes here.

…hors to the project root

mcp run now owns the operator-environment seam RFC #50 §3.1 promised: the
project-root .env set (rsbuild loadEnv conventions) loads by default, with
repeatable --env-file overrides and a --no-env opt-out. The launch
environment layers lowest to highest as manifest env < .env files <
operator process.env, so an exported variable is never silently beaten by
the manifest spread. Plugin-root tokens in env values — including the
injected AGENT_BUNDLE_PLUGIN_ROOT durable-state anchor — expand to the
resolved project root instead of the rebuildable artifact target root,
while args/cwd stay artifact-rooted; --plugin-root restores the
byte-faithful copied-artifact rehearsal.

Closes #58, closes #59.
@changeset-bot

changeset-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e87b755

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
agent-bundle Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T03:13:30.950703Z b1b7ed5 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Aug 31, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@61
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/rsc-runtime@61

commit: e87b755

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1b7ed5e8a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +114 to +115
resolveValue: (field, roots, value) =>
runtime.resolveValue(field, field === 'env' ? { ...roots, pluginRoot: envPluginRoot } : roots, value),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Re-anchor Codex's relative plugin-root environment values

For Codex source-built servers, the adapter serializes plugin-root environment values—including AGENT_BUNDLE_PLUGIN_ROOT—as literal ./ paths (src/adapters/codex.ts lines 252–255), rather than as tokens. Changing only the pluginRoot passed to runtime.resolveValue therefore leaves those values untouched, and because the child still runs with the artifact target as its cwd, durable state continues to land inside the rebuildable artifact; --plugin-root is also ineffective in this case. Handle Codex's relative env representation explicitly so this new durability contract works for every supported target.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in e87b755: env values now also pass through the target's own resolveStdioArgument relative rule against the durable env plugin root, so Codex's ./-serialized anchor (and any ./-relative env value) re-anchors to the project root — or to --plugin-root — instead of riding the artifact cwd. Identity for token-interpolating targets. Regression-tested with a codex-target launch asserting AGENT_BUNDLE_PLUGIN_ROOT and a declared STATE_DIR land at the project root.

Codex has no path-token interpolation: the adapter serializes the
plugin-root env anchor as a literal "./" resolved against a plugin-root
cwd. The env-field root swap alone left those values pointing into the
artifact via the launch cwd. Env values now also pass through the
target's own resolveStdioArgument relative rule against the durable env
plugin root — identity for every token-interpolating target.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant