Skip to content

[BUG] Copilot CLI adapter resolves environment variables at install time instead of preserving references #1152

@kkadete

Description

@kkadete

Describe the bug

When apm install generates ~/.copilot/mcp-config.json for the copilot target, environment variable references (e.g. ${env:COPILOT_MCP_JIRA_PERSONAL_TOKEN}) are resolved to their actual values at install time and baked into the file as plaintext secrets.

The Copilot CLI adapter should instead emit ${VARIABLE} references (stripping the env: prefix), because Copilot CLI natively supports runtime variable substitution using $VAR, ${VAR}, and ${VAR:-default} syntax — as documented in the official GitHub docs.

This issue was introduced by PR #947 (fix for #944), which was based on the incorrect premise that "Copilot CLI mcp-config.json has no native interpolation."

To Reproduce

  1. Define an MCP server in apm.yml with environment variable references:
    target: copilot
    dependencies:
      mcp:
        - name: my-atlassian
          transport: http
          url: https://my-instance.atlassian.net/mcp
          headers:
            "X-Atlassian-Jira-Personal-Token": "${env:COPILOT_MCP_JIRA_PERSONAL_TOKEN}"
  2. Ensure the environment variable COPILOT_MCP_JIRA_PERSONAL_TOKEN is set in your shell.
  3. Run apm install
  4. Inspect ~/.copilot/mcp-config.json

Expected behavior

The generated ~/.copilot/mcp-config.json should preserve environment variable references using Copilot CLI's native syntax:

{
  "mcpServers": {
    "my-atlassian": {
      "type": "http",
      "url": "https://my-instance.atlassian.net/mcp",
      "headers": {
        "X-Atlassian-Jira-Personal-Token": "${COPILOT_MCP_JIRA_PERSONAL_TOKEN}"
      }
    }
  }
}

Translation rules should be:

apm.yml syntax mcp-config.json output
${env:VAR} ${VAR}
${VAR} ${VAR}
$VAR $VAR

This mirrors what the VS Code adapter does (preserving ${env:VAR} for VS Code's runtime) but adapted to Copilot CLI's documented syntax.

Actual behavior

The generated ~/.copilot/mcp-config.json contains the resolved secret values in plaintext:

{
  "mcpServers": {
    "my-atlassian": {
      "type": "http",
      "url": "https://my-instance.atlassian.net/mcp",
      "headers": {
        "X-Atlassian-Jira-Personal-Token": "<actual-secret-value-resolved-from-env>"
      }
    }
  }
}

Security concern: This means secrets are stored in plaintext on disk in a user-global config file, which is both a security risk and breaks portability across machines/users.

Environment (please complete the following information):

  • OS: Windows 11
  • Python Version: 3.12
  • APM Version: 0.12.2
  • VSCode Version: 1.118.1

Logs

N/A — no error is raised; the behavior is silent and incorrect.

Additional context

Metadata

Metadata

Assignees

Labels

type/bugSomething does not work as documented.

Type

No type

Projects

Status

In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions