You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: align MCP config with MCPG spec — container/HTTP transport (#157)
* feat: align MCP config with MCPG spec - container/HTTP transport
Replace command-based MCP configuration with MCPG-native fields:
- **types.rs**: Replace `command` with `container`, `entrypoint`,
`entrypoint-args`, `url`, `headers`, `mounts` in McpOptions
- **standalone.rs**: Update McpgServerConfig and generate_mcpg_config()
to emit container/entrypointArgs (stdio) or url/headers (HTTP)
- **standalone.rs**: Add generate_mcpg_docker_env() for env passthrough
- Auto-maps SC_READ_TOKEN → AZURE_DEVOPS_EXT_PAT when permissions.read
is configured
- Forwards passthrough env vars ("") to MCPG via -e flags
- **base.yml**: Add {{ mcpg_docker_env }} marker for env forwarding
- **common.rs**: Update is_custom_mcp() to check container/url
- **onees.rs**: Update custom MCP detection for 1ES target
Tests:
- New fixture: azure-devops-mcp-agent.md (container-based ADO MCP)
- 4 new integration tests: fixture compilation, container config,
HTTP config, env passthrough
- 8 new unit tests: container/HTTP/entrypoint/mounts/env generation
- Update existing tests for new field names
Docs:
- New example: examples/azure-devops-mcp.md (ADO work item triage)
- Update AGENTS.md MCP section with container/HTTP docs and auth model
Aligns with MCPG Gateway Specification v1.13.0 §3.2.1 (containerization
requirement) and gh-aw front matter format.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: address PR review — env var name validation, docs, HTTP env warning
- Validate env var names against [A-Za-z_][A-Za-z0-9_]* to prevent
Docker flag injection via user-controlled front matter keys
- Document {{ mcpg_docker_env }} template marker in AGENTS.md
- Warn when env: is configured on HTTP MCPs (silently ignored)
- Add unit tests for env var name validation and injection rejection
- Update {{ mcpg_config }} docs for container/url (was command)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: docker run line continuation bug, mount/URL validation
- Fix broken bash line continuation when {{ mcpg_docker_env }} is
non-empty or empty — restructured template so marker outputs
include trailing backslash, validated with bash -n
- Add validate_mount_source() — warns on sensitive host path prefixes
(/etc, /root, /home, /proc, /sys, docker.sock)
- Add validate_mcp_url() — warns if URL doesn't use http(s):// scheme
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: skip HTTP MCPs in env passthrough, warn on container+url conflict
- Skip HTTP MCPs in generate_mcpg_docker_env (no child container to
forward env vars to)
- Warn when both container and url are set on same MCP entry
- Escalate docker.sock mount warning to eprintln (container escape risk)
- Remove undocumented ${VAR} passthrough — only empty string ("")
triggers env passthrough, matching AGENTS.md documentation
- Add indentation cross-reference comment for base.yml alignment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: validate Docker args, warn on inline secrets, consistent eprintln
- Add validate_docker_args() — scans args for --privileged, -v,
--cap-add, --security-opt and other privilege escalation flags
- Add warn_potential_secrets() — warns when env var names containing
'token', 'secret', 'key', 'password', 'pat' have inline values
instead of passthrough; warns on Authorization headers in plaintext
- Change validate_mcp_url to use eprintln! for consistency with other
compile-time warnings (was log::warn, invisible without --verbose)
- Change HTTP MCP env warning to eprintln! for consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: tighten validation, scope ADO token auto-map to requesting MCPs
- Remove dead /var/run/docker.sock from SENSITIVE_MOUNT_PREFIXES
(already caught by contains("docker.sock") early return)
- Fix prefix check: /etc no longer matches /etc-configs (require
exact match or trailing /)
- Fix Docker args: handle split-form flags (--pid host, --network
host, --ipc host) in addition to --flag=value form
- Scope ADO token auto-map: only inject AZURE_DEVOPS_EXT_PAT when
a container MCP actually requests it via env passthrough, not
whenever permissions.read is set
- Add test for dedup edge case: auto-mapped SC_READ_TOKEN form wins
over bare passthrough, appears exactly once
- Add test for no-request case: permissions.read without MCP
requesting AZURE_DEVOPS_EXT_PAT does not inject the token
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor: use replace_with_indent for mcpg_docker_env indentation
Move {{ mcpg_docker_env }} to its own indented line in base.yml so
replace_with_indent handles alignment automatically. Removes
hardcoded 12-space indentation from generate_mcpg_docker_env() —
future template reformats won't silently break bash syntax.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: container image validation, expand Docker flag list, fix headers arg
- Add validate_container_image() — rejects image names with unexpected
characters (defense-in-depth against injection)
- Expand DANGEROUS_DOCKER_FLAGS with --user/-u, --add-host, --entrypoint
- Fix warn_potential_secrets call: pass &opts.headers instead of empty
HashMap for container MCPs
- Add inline comment explaining lone backslash in empty env case
- Add case-insensitivity note on validate_mount_source
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: warn on missing permissions.read when MCP requests ADO token
- Emit eprintln warning when container MCP has AZURE_DEVOPS_EXT_PAT
passthrough but permissions.read is not configured (token would be
empty at runtime, causing silent auth failure)
- Remove --network host from AGENTS.md args example (contradicts
DANGEROUS_DOCKER_FLAGS warning, bypasses AWF proxy)
- Add entrypoint: field hint to --entrypoint Docker args warning
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* style: simplify if-let Some(_) to .is_some()
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
my-custom-tool: # custom MCP server (requires command field)
132
-
command: "node"
133
-
args: ["path/to/mcp-server.js"]
131
+
my-custom-tool: # containerized MCP server (requires container field)
132
+
container: "node:20-slim"
133
+
entrypoint: "node"
134
+
entrypoint-args: ["path/to/mcp-server.js"]
134
135
allowed:
135
136
- custom_function_1
136
137
- custom_function_2
@@ -589,13 +590,25 @@ Should be replaced with the markdown body (agent instructions) extracted from th
589
590
Should be replaced with the MCP Gateway (MCPG) configuration JSON generated from the `mcp-servers:` front matter. This configuration defines the MCPG server entries and gateway settings.
590
591
591
592
The generated JSON has two top-level sections:
592
-
- `mcpServers`: Maps server names to their configuration (type, command/url, tools, etc.)
593
+
- `mcpServers`: Maps server names to their configuration (type, container/url, tools, etc.)
SafeOutputs is always included as an HTTP backend (`type: "http"`) pointing to `localhost` (MCPG runs with `--network host`, so `localhost` is the host loopback). Custom MCPs with explicit `command:` are included as stdio servers (`type: "stdio"`). MCPs without a command are skipped (there are no built-in MCPs in the copilot CLI).
596
+
SafeOutputs is always included as an HTTP backend (`type: "http"`) pointing to `localhost` (MCPG runs with `--network host`, so `localhost` is the host loopback). Containerized MCPs with `container:` are included as stdio servers (`type: "stdio"` with `container`, `entrypoint`, `entrypointArgs`). HTTP MCPs with `url:` are included as HTTP servers. MCPs without a container or url are skipped.
596
597
597
598
Runtime placeholders (`${SAFE_OUTPUTS_PORT}`, `${SAFE_OUTPUTS_API_KEY}`, `${MCP_GATEWAY_API_KEY}`) are substituted by the pipeline at runtime before passing the config to MCPG.
598
599
600
+
## {{ mcpg_docker_env }}
601
+
602
+
Should be replaced with additional `-e` flags for the MCPG Docker run command, enabling environment variable passthrough from the pipeline to MCP containers.
603
+
604
+
When `permissions.read` is configured, the compiler automatically adds `-e AZURE_DEVOPS_EXT_PAT="$(SC_READ_TOKEN)"` to forward the ADO access token to MCP containers that need it (e.g., Azure DevOps MCP).
605
+
606
+
Additionally, any env vars in MCP configs with empty string values (`""`) are collected and forwarded as `-e VAR_NAME` flags, enabling passthrough from the pipeline environment through MCPG to MCP child containers.
607
+
608
+
Environment variable names are validated against `[A-Za-z_][A-Za-z0-9_]*` to prevent Docker flag injection.
609
+
610
+
If no passthrough env vars are needed, this marker is replaced with an empty string.
611
+
599
612
## {{ allowed_domains }}
600
613
601
614
Should be replaced with the comma-separated domain list for AWF's `--allow-domains` flag. The list includes:
@@ -1129,61 +1142,100 @@ cargo add <crate-name>
1129
1142
1130
1143
## MCP Configuration
1131
1144
1132
-
The `mcp-servers:` field configures MCP (Model Context Protocol) servers that are made available to the agent via the MCP Gateway (MCPG). All MCPs require explicit `command:` configuration — there are no built-in MCPs in the copilot CLI.
1133
-
The `mcp-servers:` field configures custom MCP (Model Context Protocol) servers that the agent can use. Each entry must include a `command:` field specifying the executable to spawn.
1145
+
The `mcp-servers:` field configures MCP (Model Context Protocol) servers that are made available to the agent via the MCP Gateway (MCPG). MCPs can be **containerized stdio servers** (Docker-based) or **HTTP servers** (remote endpoints). All MCP traffic flows through the MCP Gateway.
1134
1146
1135
-
### Custom MCP Servers
1147
+
### Docker Container MCP Servers (stdio)
1136
1148
1137
-
Define MCP servers by including a `command:` field:
1149
+
Run containerized MCP servers. MCPG spawns these as sibling Docker containers:
Connect to remote MCP servers accessible via HTTP:
1168
+
1169
+
```yaml
1170
+
mcp-servers:
1171
+
remote-ado:
1172
+
url: "https://mcp.dev.azure.com/myorg"
1173
+
headers:
1174
+
X-MCP-Toolsets: "repos,wit"
1175
+
X-MCP-Readonly: "true"
1176
+
allowed:
1177
+
- wit_get_work_item
1178
+
- repo_list_repos_by_project
1147
1179
```
1148
1180
1149
1181
### Configuration Properties
1150
1182
1151
-
- `command:`- The executable to run (e.g., `"node"`, `"python"`, `"dotnet"`)
1152
-
- `args:`- Array of command-line arguments passed to the command
1153
-
- `allowed:`- Array of function names agents are permitted to call (required for security)
1154
-
- `env:`- Optional environment variables for the MCP server process
1155
-
- `service-connection:`- (1ES target only) Override the service connection name used for this MCP. If not specified, defaults to `mcp-<name>-service-connection`
1183
+
**Container stdio servers:**
1184
+
- `container:`- Docker image to run (e.g., `"node:20-slim"`, `"ghcr.io/org/tool:latest"`)
1185
+
- `entrypoint:`- Container entrypoint override (equivalent to `docker run --entrypoint`)
1186
+
- `entrypoint-args:`- Arguments passed to the entrypoint (after the image in `docker run`)
1187
+
- `args:` - Additional Docker runtime arguments (inserted before the image in `docker run`). **Security note**: dangerous flags like `--privileged`, `--network host` will trigger compile-time warnings.
1188
+
- `mounts:`- Volume mounts in `"source:dest:mode"` format (e.g., `["/host/data:/app/data:ro"]`)
1189
+
1190
+
**HTTP servers:**
1191
+
- `url:`- HTTP endpoint URL for the remote MCP server
1192
+
- `headers:`- HTTP headers to include in requests (e.g., `Authorization`, `X-MCP-Toolsets`)
1193
+
1194
+
**Common (both types):**
1195
+
- `allowed:`- Array of tool names the agent is permitted to call (required for security)
1196
+
- `env:`- Environment variables for the MCP server process. Use `""` (empty string) for passthrough from the pipeline environment.
1197
+
- `service-connection:`- (1ES target only) Override the service connection name. Defaults to `mcp-<name>-service-connection`
1198
+
1199
+
### Environment Variable Passthrough
1200
+
1201
+
MCP containers may need secrets from the pipeline (e.g., ADO tokens). The `env:` field supports passthrough:
1202
+
1203
+
```yaml
1204
+
env:
1205
+
AZURE_DEVOPS_EXT_PAT: "" # Passthrough from pipeline environment
1206
+
STATIC_CONFIG: "some-value" # Literal value embedded in config
1207
+
```
1208
+
1209
+
When `permissions.read` is configured, the compiler automatically maps `SC_READ_TOKEN` → `AZURE_DEVOPS_EXT_PAT` on the MCPG container, so agents can access ADO APIs without manual wiring.
0 commit comments