Package: agent-bundle (mcp run), at main da5df1d (PR #52, RFC #50 Phase 1). Found by the movie-library Phase-2 migration onto that commit.
What we tried
Replace the consumer's bash launcher wholesale with agent-bundle mcp run --server <id> --target portable --artifact artifact, as RFC #50 §3.1 specifies:
agent-bundle mcp run --server <name> [--target <t>] — the missing foreground runner: resolve the (content-hashed) entry from the artifact manifest, apply .env loading (rsbuild's loadEnv, as createRslib does at createRslib.ts#L76-L81), exec node. This deletes the resolve-and-exec core of run-stdio.sh.
The shipped runner resolves and execs, but the .env half never landed: runMcpForeground (packages/agent-bundle/src/services/mcp-run.ts) spawns with exactly
env: { ...inheritedEnv, ...launch.env }, // process.env + expanded manifest env
and there are zero loadEnv references anywhere in packages/agent-bundle/src at da5df1d (checked with git grep).
Why it matters
Real plugins keep operator configuration in a plugin-root .env: movie-library's carries tracker session cookies, the seedbox SSH alias, and state-directory pins. A bare mcp run boots the server with none of it — servers come up credential-less. So the launcher class the RFC set out to delete survives: movie-library's run-stdio.sh shrank from 95 to 57 lines (the resolve-and-exec core is gone) but stays, purely to source .env and pin state env before delegating to mcp run. The RFC even predicted this in its migration table — "a thin alias may keep the .env/state-dir operator ergonomics" — this issue asks for the runner to own that seam instead of every consumer keeping an alias.
Repro sketch
echo 'MY_TRACKER_COOKIE=secret' > .env
agent-bundle build --output artifact
agent-bundle mcp run --server my-server --target portable --artifact artifact
# server boots; process.env.MY_TRACKER_COOKIE is undefined in every handler
Current workaround
A wrapper script that sources the plugin .env (and exports state pins) before exec-ing agent-bundle mcp run … — i.e. the thing mcp run was meant to retire.
Suggested fix direction
Either shape works; the RFC promised the first:
- Default
loadEnv at the resolved project root. runMcp (src/api.ts) already computes workspaceRoot = resolve(options.root), so the root to load from is in hand; rsbuild's loadEnv conventions (.env, .env.local, mode variants) match what createRslib gives the same consumers at build time.
- Or an explicit, repeatable
--env-file <path> flag, if implicit file loading is considered too magical for a foreground runner that inherits stdio.
Either way, precedence should be documented: values from .env fill gaps only — a variable already present in the real process.env wins. Worth documenting in the same pass where the manifest env sits: today it is spread last and silently beats both process.env and any .env layer (see the companion anchor-expansion issue in Related, where that ordering blocks operator overrides).
Related: #59 (companion mcp-run finding, the ${PLUGIN_ROOT} anchor expansion), #50 (RFC whose §3.1 specified this behavior) — both empirically resolved by the movie-library Phase-2 migration against da5df1d; same consumer port as the #42–#47 series.
Package:
agent-bundle(mcp run), at mainda5df1d(PR #52, RFC #50 Phase 1). Found by the movie-library Phase-2 migration onto that commit.What we tried
Replace the consumer's bash launcher wholesale with
agent-bundle mcp run --server <id> --target portable --artifact artifact, as RFC #50 §3.1 specifies:The shipped runner resolves and execs, but the
.envhalf never landed:runMcpForeground(packages/agent-bundle/src/services/mcp-run.ts) spawns with exactlyand there are zero
loadEnvreferences anywhere inpackages/agent-bundle/srcatda5df1d(checked withgit grep).Why it matters
Real plugins keep operator configuration in a plugin-root
.env: movie-library's carries tracker session cookies, the seedbox SSH alias, and state-directory pins. A baremcp runboots the server with none of it — servers come up credential-less. So the launcher class the RFC set out to delete survives: movie-library'srun-stdio.shshrank from 95 to 57 lines (the resolve-and-exec core is gone) but stays, purely to source.envand pin state env before delegating tomcp run. The RFC even predicted this in its migration table — "a thin alias may keep the.env/state-dir operator ergonomics" — this issue asks for the runner to own that seam instead of every consumer keeping an alias.Repro sketch
Current workaround
A wrapper script that sources the plugin
.env(and exports state pins) before exec-ingagent-bundle mcp run …— i.e. the thingmcp runwas meant to retire.Suggested fix direction
Either shape works; the RFC promised the first:
loadEnvat the resolved project root.runMcp(src/api.ts) already computesworkspaceRoot = resolve(options.root), so the root to load from is in hand; rsbuild'sloadEnvconventions (.env,.env.local, mode variants) match whatcreateRslibgives the same consumers at build time.--env-file <path>flag, if implicit file loading is considered too magical for a foreground runner that inherits stdio.Either way, precedence should be documented: values from
.envfill gaps only — a variable already present in the realprocess.envwins. Worth documenting in the same pass where the manifest env sits: today it is spread last and silently beats bothprocess.envand any.envlayer (see the companion anchor-expansion issue in Related, where that ordering blocks operator overrides).Related: #59 (companion mcp-run finding, the ${PLUGIN_ROOT} anchor expansion), #50 (RFC whose §3.1 specified this behavior) — both empirically resolved by the movie-library Phase-2 migration against
da5df1d; same consumer port as the #42–#47 series.