Skip to content

feat(cli): substitute ${COMMONLY_AGENT_TOKEN} / ${COMMONLY_API_URL} in MCP config#238

Open
samxu01 wants to merge 1 commit intomainfrom
feat/cli-mcp-env-substitution
Open

feat(cli): substitute ${COMMONLY_AGENT_TOKEN} / ${COMMONLY_API_URL} in MCP config#238
samxu01 wants to merge 1 commit intomainfrom
feat/cli-mcp-env-substitution

Conversation

@samxu01
Copy link
Copy Markdown
Contributor

@samxu01 samxu01 commented Apr 26, 2026

Summary

Lets users keep their checked-in env files free of secrets. The wrapper substitutes the agent's runtime token + instance URL into the generated MCP config at spawn time, from values already on hand (the saved token record).

Why this is needed. Surfaced during the 2026-04-17 cross-agent demo validation: every env spec referencing commonly-mcp had to be hand-rewritten with the agent's cm_agent_* token after attach, because the token is minted at attach time and only known to the wrapper. Without substitution, a shared/checked-in env file is unusable across agents.

Recognised placeholders (substituted everywhere a string appears in the MCP config — env values, command args, and url fields):

Placeholder Substituted with
${COMMONLY_AGENT_TOKEN} per-(agent, pod) cm_agent_* runtime token
${COMMONLY_API_URL} instance URL the agent is attached to
${COMMONLY_INSTANCE_URL} alias for ${COMMONLY_API_URL}

Properties:

  • One-pass, literal substitution. No nested expansion, no shell quoting.
  • Unknown ${COMMONLY_*} placeholders are left intact so typos surface as runtime MCP errors, not silent empty strings.
  • Falsy ctx values are also no-ops (placeholder preserved) so users can diagnose missing context.

Plumbing: performRun now passes runtimeToken + instanceUrl into the adapter.spawn ctx; claude adapter passes them through to writeMcpConfig.

Example

// env.json — checked into source control, no secrets
{
  "mcp": [{
    "name": "commonly",
    "transport": "stdio",
    "command": ["commonly-mcp"],
    "env": {
      "COMMONLY_API_URL":     "${COMMONLY_API_URL}",
      "COMMONLY_AGENT_TOKEN": "${COMMONLY_AGENT_TOKEN}",
      "COMMONLY_DEFAULT_POD": "<your-pod-id>"
    }
  }]
}

After commonly agent attach claude --env env.json --pod <id>, the same file works for every attached agent without manual editing.

Test plan

  • cd cli && npm test131/133 passing (6 new: 5 substitution + 1 ctx plumbing; 2 skipped Linux-only)
  • Self-review against docs/REVIEW.md
  • Live-validated end-to-end on commonly-dev with placeholder env (no hardcoded tokens)

Out of scope (deferred)

🤖 Generated with Claude Code

… in MCP config

Lets users keep their checked-in env files free of secrets — the
wrapper substitutes the runtime token + instance URL at spawn time
from values it already has on hand (the saved token record).

Surfaced during the 2026-04-17 cross-agent demo: every spec referencing
commonly-mcp had to be hand-rewritten with the agent's runtime token
after attach, because the token is minted at attach time and only known
to the wrapper.

Recognised placeholders (substituted everywhere a string appears in the
MCP config — env values, command args, and url fields):
  \${COMMONLY_AGENT_TOKEN}   — per-(agent, pod) cm_agent_* runtime token
  \${COMMONLY_API_URL}       — instance URL the agent is attached to
  \${COMMONLY_INSTANCE_URL}  — alias for COMMONLY_API_URL

One-pass + literal substitution; no nested expansion, no shell quoting.
Unknown \${COMMONLY_*} placeholders are left intact so typos surface as
runtime MCP errors, not silent empty strings. Falsy ctx values are also
no-ops (placeholder preserved) so users can diagnose missing context.

Plumbing: performRun now passes runtimeToken + instanceUrl into the
adapter.spawn ctx; claude adapter passes them through to writeMcpConfig.

131/133 cli tests pass (6 new: 5 substitution + 1 ctx plumbing assertion;
2 skipped Linux-only paths).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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