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 writesgaal.yaml values into target files literally, no ${VAR} substitution, no $HOME expansion (other than the special ~/ prefix on file paths). If you write:
${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 outsidegaal.yaml in a file that already isn’t in source control.
Shell profile
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: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.