githubnext / githubnext/ado-aw
MCPG step fails with docker: invalid reference format. — extra \ \ after -e ADO_MCP_AUTH_TOKEN in generated YAML (v0.36.0)
- Dominant language
- Rust
- Stars
- 23
- Forks
- 8
- Avg merge
- 4d 9h
- Merged PRs (30d)
- 22
Description
## Summary
Compiled pipelines that use the built-in `azure-devops` MCP tool fail at the **Start MCP Gateway (MCPG)** step with `docker: invalid reference format.` The MCPG container never starts, the `/health` poll loop runs out, and the Agent job exits 1 (`Bash exited with code '1'`).
Root cause: `start_mcpg_step` in `src/compile/agentic_pipeline.rs` double-appends a `\` line continuation to the docker-env flag emitted by `generate_mcpg_docker_env`. The emitted YAML contains a literal `\ \` token, which bash interprets as a one-character argument (a single space) inserted between `-e ADO_MCP_AUTH_TOKEN=…` and the image, so `docker run` consumes that space as the image reference.
Affects: **every** agent whose front matter declares `tools.azure-devops` (or anything else that contributes an extension `pipeline_env` mapping). In our repository this took down five pipelines simultaneously after a routine `ado-aw compile` upgrade to v0.36.0.
## Observed Run
- Build:
- Definition: `ado-aw reviewer` (compiled from `agents/pr-reviewer.md`)
- Source branch: `refs/heads/jamesadevine/test-pr-reviewer-bait`
- Result: `failed` after 2m 25s
- Failed step: **Start MCP Gateway (MCPG)** (timeline record `691d60ec-3d15-5b34-11ae-186526790c53`, task log 43)
- Compiler version: `ado-aw 0.36.0` (verified via `ado-aw --version`; `aw_info.json.compiler_version = "0.36.0"`)
- MCPG image: `ghcr.io/github/gh-aw-mcpg:v0.3.25`
### Failing step log (verbatim, lines 51-55)
```
2026-06-15T20:17:15.6783133Z MCPG started (PID: 2650)
2026-06-15T20:17:15.6949924Z docker: invalid reference format.
2026-06-15T20:17:15.6951878Z See 'docker run --help'.
2026-06-15T20:17:45.9518262Z
2026-06-15T20:17:45.9535713Z ##[error]Bash exited with code '1'.
```
(The 30s gap is the `/health` poll that times out because the docker container never started.)
`ado-aw audit 613512 --json` corroborates: `mcpg/stderr.log` contains exactly two lines — `docker: invalid reference format.` / `See 'docker run --help'.` — and `safe_outputs.ndjson` is empty (the agent never ran).
### Generated YAML (pr-reviewer.yml, lines 475-486)
```yaml
echo "$MCPG_CONFIG" | docker run -i --rm \
--name mcpg \
--network host \
--entrypoint /app/awmg \
-v /var/run/docker.sock:/var/run/docker.sock \
-e MCP_GATEWAY_PORT="$(MCP_GATEWAY_PORT)" \
-e MCP_GATEWAY_DOMAIN="$(MCP_GATEWAY_DOMAIN)" \
-e MCP_GATEWAY_API_KEY="$(MCP_GATEWAY_API_KEY)" \
-e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \ \ # ← BUG: trailing "\ \"
ghcr.io/github/gh-aw-mcpg:v0.3.25 \
--routed --listen 0.0.0.0:80 --config-stdin --log-dir /tmp/gh-aw/mcp-logs \
> "$GATEWAY_OUTPUT" 2> >(tee /tmp/gh-aw/mcp-logs/stderr.log >&2) &
```
In bash, `\ ` is an escaped space (literal space character treated as part of an argument) and `\` is a line continuation. Sequence `\ \` therefore produces:
1. a one-character argument consisting of a single space, then
2. the line continuation.
`docker run`’s positional-argument parser scans for the first non-option token and treats it as `IMAGE`. That token is `" "` (a single space) — an invalid reference. The actual `ghcr.io/github/gh-aw-mcpg:v0.3.25` is then parsed as the `COMMAND`.
Removing the stray space-escape (i.e. just `-e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \`) makes the docker invocation valid again.
## Root cause in `ado-aw`
The duplication happens in `start_mcpg_step` (`src/compile/agentic_pipeline.rs`, ~lines 1334-1369 in v0.36.0). It reads the value returned by `generate_mcpg_docker_env` and re-wraps every line:
```rust
// src/compile/agentic_pipeline.rs (v0.36.0)
let docker_env_lines: String =
if mcpg_docker_env.trim().is_empty() || mcpg_docker_env.trim() == "\\" {
"\\\n \\".to_string()
} else {
mcpg_docker_env
.lines()
.map(|l| format!("{l} \\")) // <-- appends ` \` to every line
.collect::>()
.join("\n ")
};
```
But `generate_mcpg_docker_env` (`src/compile/common.rs`, ~lines 2034-2092) already appends a trailing ` \` to its returned string:
```rust
// src/compile/common.rs (v0.36.0)
env_flags.sort();
if env_flags.is_empty() {
"\\".to_string()
} else {
let flags = env_flags.join(" \\\n");
format!("{} \\", flags) // <-- already ends with ` \`
}
```
So for a single-entry input (`-e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \`), `.lines()` yields one line ending in ` \`, and `format!("{l} \\")` appends a second ` \` → `-e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \ \`.
For multi-entry inputs the same defect would corrupt only the last flag, but in the common ADO-tool case there’s exactly one extension-contributed flag, so this is the line that breaks.
## Reproduction
Minimal `agent.md`:
```yaml
---
name: repro
description: repro
on:
workflow_dispatch:
permissions:
read: any-arm-service-connection
tools:
azure-devops:
toolsets:
- repos
safe-outputs:
add-pr-comment:
max: 1
---
Stub.
```
```
ado-aw compile agent.md
grep -n 'ADO_MCP_AUTH_TOKEN' agent.yml
# → -e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \ \
```
Run on Azure DevOps → MCPG step fails with `docker: invalid reference format.`
## Impact
- **All** compiled pipelines that include the ADO MCP tool with `permissions.read` against an ARM service connection are unrunnable on `ado-aw 0.36.0`.
- In this repository, 5 of 5 such pipelines are affected: `ctf.yml:470`, `dotnet-dependency-updater.yml:392`, `pr-reviewer.yml:483`, `release-readiness.yml:464`, `test-pr-reviewer-bait.yml:459` — each ending in `-e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \ \`.
- The Agent job runs for the full `/health` timeout (30s) before failing, wasting build minutes and obscuring the root cause behind a generic `Bash exited with code '1'`.
- The MD-source markdown is unchanged from a working `ado-aw 0.35.x` build; only recompiling under v0.36.0 introduced the bad output.
## Suggested fix direction
Either:
- Have `generate_mcpg_docker_env` return the env flags without the trailing ` \` (let the caller own continuations), or
- Drop the `format!("{l} \\")` line wrap in `start_mcpg_step` and just `.join("\n ")` the already-terminated lines.
A regression test that compiles a minimal agent with an ADO tool + ARM service connection and asserts the emitted YAML never contains the literal substring `\ \` (or, more strictly, runs `bash -n` against the rendered step body) would prevent reintroduction.
## Diagnostic artifacts
- `aw_info.json`: `{"compiler_version":"0.36.0","engine":"copilot","model":"claude-opus-4.7","source":"agents/pr-reviewer.md","target":"standalone"}`
- `mcpg-config.json` (sanitized): standard ADO MCP entry (`container: node:20-slim`, `entrypoint: npx`, args `-y @azure-devops/mcp msazuresphere -d repos -a envvar`) + HTTP safeoutputs entry.
- `audit-613512.json` (overview): Setup `succeeded` (26s), Agent `failed` (1m38s), no Detection / SafeOutputs jobs scheduled because Agent stage failed.
- Full step log:
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.