Skip to main content
gaal.yaml is meant to live in source control. Anything sensitive needs to stay out of it. This page covers the patterns that work cleanly with gaal’s design.

gaal does not expand variables

This is the most important rule. gaal writes gaal.yaml values into target files literally, no ${VAR} substitution, no $HOME expansion (other than the special ~/ prefix on file paths). If you write:
mcps:
  - name: github
    target: ~/.config/claude/claude_desktop_config.json
    inline:
      command: npx
      args: ["-y", "@modelcontextprotocol/server-github"]
      env:
        GITHUB_PERSONAL_ACCESS_TOKEN: ${GITHUB_TOKEN}
…then the literal string ${GITHUB_TOKEN} lands in claude_desktop_config.json. The agent process that launches the MCP server is responsible for resolving ${GITHUB_TOKEN} from the environment. For Claude Desktop, Cursor, Codex, and most agents, this means the variable is read from the environment of the agent process when it spawns the server. Set the variable in the way that environment expects, your shell profile, a launchctl plist, a systemd unit file, etc.

Where to put the secret

The right place is outside gaal.yaml in a file that already isn’t in source control.

Shell profile

# ~/.config/fish/conf.d/secrets.fish
set -gx GITHUB_TOKEN ghp_xxx...
# ~/.zshrc
export GITHUB_TOKEN=ghp_xxx...
The agent inherits these when it launches the MCP server.

A secret manager

If you use 1Password, Bitwarden, pass, or similar, source the value into your shell profile from there. gaal doesn’t need to know.

Per-host overrides via the user-scope config

For host-specific non-secret values (paths, project roots), use the user-scope file:
# ~/.config/gaal/config.yaml
mcps:
  - name: filesystem
    target: ~/.config/claude/claude_desktop_config.json
    inline:
      command: uvx
      args: [mcp-server-filesystem, /Users/me/work]
The workspace gaal.yaml can then declare cross-machine defaults; the user file overrides on this specific host. See Scopes for merge rules.

Path expansion

gaal does expand the ~/ prefix on target: paths to the user’s home directory. Everywhere else in the file (including inside args:), paths are written through verbatim. If your MCP server expects $HOME expansion in its arguments, that’s the agent’s job, not gaal’s.

What never to put in gaal.yaml

  • API tokens, passwords, OAuth client secrets.
  • Private SSH keys.
  • AWS / GCP / Azure credentials.
  • Any value that would compromise something if a teammate read it.

Configure MCP servers

Scopes

Sandbox mode