Skip to content

mcp run loads no .env: RFC #50 §3.1 promised loadEnv, so operator-credential consumers must keep wrapper scripts #58

Description

@ScriptedAlchemy

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions