A Model Context Protocol (MCP) server that exposes your shell aliases as safe, discoverable tools. It parses configured alias files (e.g. ~/.bash_aliases) and surfaces them to MCP hosts such as the Gemini CLI.
- Discovers aliases from explicitly configured files.
- Emits MCP tools/resources for dry-run and real execution.
- Enforces allowlist-only safety rules and dry-run by default.
- Sandboxes execution with constrained environment, cwd policy, and timeouts.
- Writes structured audit logs for every invocation.
python3 -m venv .venv
source .venv/bin/activate
pip install -e .[dev]
# copy and edit a config
cp examples/sample_config.yaml config.yaml
# run the server over stdio
mcp-shell-aliases --config config.yaml --verbosePoint your MCP host at the mcp-shell-aliases executable (or python3 -m mcp_shell_aliases) with the same config file.
The server loads settings from config.yaml, environment variables (MCP_SHELL_ALIASES_*), and CLI flags. See docs/CONFIGURATION.md for the full reference. A minimal config:
alias_files:
- ~/.bash_aliases
allow_patterns:
- '^ls\b'- Aliases are classified using allowlist regexes. Anything that fails to match stays in dry-run mode.
- Real execution requires
dry_run=falseandconfirm=truetool arguments. - Commands execute via
/bin/bash -lcwith a scrubbed environment, bounded output, and timeouts. - Audit logs capture every call. See
docs/SECURITY.mdfor details.
Update your MCP host configuration to use the new server, for example in gemini-extension.json:
{
"mcp_server": {
"command": [
"python3",
"-m",
"mcp_shell_aliases",
"--config",
"${extensionPath}/config.yaml"
]
}
}Verify by listing the alias.catalog tool, reading the alias://catalog resource, and executing a safe alias with dry_run.
Some agentic tools prefer to talk over HTTP instead of stdio. You can start the server on a local port with:
mcp-shell-aliases \
--config config.yaml \
--transport http \
--http-host 127.0.0.1 \
--http-port 3921 \
--http-path /mcpThen point your host at http://127.0.0.1:3921/mcp. Use --transport sse or
--transport streamable-http for alternative FastMCP transports.
If using the Gemini CLI, ensure your project’s .gemini/settings.json has an
mcpServers entry with httpUrl (not command) for HTTP transports:
{
"mcpServers": {
"mcp-shell-aliases": {
"httpUrl": "http://127.0.0.1:3921/mcp"
}
}
}To add an HTTP server to the Gemini CLI, you must explicitly use the --transport http flag:
This repository includes a gemini-extension.json manifest so it can be installed directly with the Gemini CLI:
gemini extensions install https://github.com/<you>/mcp-shell-aliasesThe manifest configures the MCP server as a stdio server. It launches a small bootstrap.py that creates a local virtual environment under the extension folder, installs minimal runtime dependencies, and then starts the server via -m mcp_shell_aliases with the bundled config.yaml.
After installation, the server appears in the Gemini CLI under the extension’s name. You can override the config path by editing the installed extension’s manifest or by copying config.yaml into your project and updating your .gemini/settings.json entry.
You can sanity-check that the bootstrap and dependencies work in a clean environment using Docker (no Gemini CLI required):
bash scripts/docker-smoke.shThis builds a tiny image, provisions the venv inside the container, and starts the server in HTTP mode on localhost:3921 so you can hit it with curl while confirming the package installs cleanly. For stdio, override the CMD when running the container and attach a client.
Run a full install test that boots a fresh container, installs the Gemini CLI, installs this extension from a Git URL, and verifies it appears in gemini extensions list:
# Provide the Git URL for your repo and the Gemini CLI install command.
EXT_URL=https://github.com/<you>/mcp-shell-aliases \
GEMINI_INSTALL="npm i -g @google/gemini-cli" \
bash scripts/docker-gemini-extensions-test.sh
# Or pass the URL as an arg (EXT_URL is optional if repo has a remote):
bash scripts/docker-gemini-extensions-test.sh https://github.com/<you>/mcp-shell-aliasesNotes:
- The script uses
node:20-bookworm-slimand installs Python 3. It requires network access to fetch the CLI and your repo. - If the Gemini CLI package name differs in your environment, set
GEMINI_INSTALLto the correct install command (e.g.,npm i -g <pkg>).
gemini mcp add mcp-shell-aliases http://127.0.0.1:3921/mcp --transport httpNote: For HTTP and SSE transports, the mcp-shell-aliases server must be running independently before you add it to the Gemini CLI. The CLI will not automatically start HTTP/SSE servers.
If the CLI still can’t connect, switch the server to --transport sse and reuse
the same URL.
- ✅ Python/FastMCP implementation exposes
alias.execand browseable resources. - ✅ Safety rails enforced: dry-run default, allowlist patterns, timeouts, cwd policy, audit logs.
- ✅ Hot reload is available via on-demand catalog refresh (no file watcher yet).
- 🚧 Prompt helpers, inotify-style hot reload, and advanced hardening are still on the roadmap (see
TODO.md).
# lint
ruff check mcp_shell_aliases tests
# unit + contract suite (includes in-process MCP smoke tests)
pytest --cov=mcp_shell_aliases --cov-report=term-missing
# type checking (requires stub package: python -m pip install types-PyYAML)
mypy mcp_shell_aliasesThe test suite exercises alias parsing, sandbox execution, CLI entry points, and
the FastMCP server itself via the official Python client. See
docs/Testing.md for the complete breakdown and troubleshooting tips.
{
"tool": "alias.exec",
"arguments": {
"name": "ll",
"args": "~/projects",
"dry_run": true
}
}Unsafe aliases will always return dry-run results unless they match the configured allowlist patterns.
Use these parameters with alias.exec to control execution and working directory.
- name: alias name to run.
- args: optional string appended to the alias expansion.
- dry_run: true by default; set to false to execute.
- confirm: required when
dry_runis false. Acts as a safety gate. - cwd: working directory to run in; defaults to
default_cwd(usually~). Must be insideallow_cwd_roots. - timeout_seconds: optional positive integer; must be ≤ 5×
execution.default_timeout_seconds.
Response fields include command, cwd, exitCode, stdout, stderr, truncated, timedOut, dryRun, plus aliasSafe and sourceFile for context.
- Default behavior: If you do not pass
cwd, commands run indefault_cwd(home by default). - For repo‑specific aliases (e.g., git), pass a project path explicitly:
alias.exec {"name":"gst","dry_run": false, "confirm": true, "cwd": "/path/to/repo"}
- With agents: Ask the agent to include a
cwdthat points at your project root when calling tools. Example instruction: “When running git aliases, setcwdto the current project directory.”
- An alias is “safe” only if its expansion matches
allow_patterns. - Unsafe aliases can still be previewed with
dry_run: true. - Real execution requires both
dry_run: falseandconfirm: true.
- The server re-parses alias files and re-applies the in-memory allowlist on each request (hot reload).
- Editing
config.yamlitself (e.g., changingallow_patterns,default_cwd, oralias_files) requires restarting the server for changes to take effect.
See gemini-shell-aliases/CONTRIBUTING.md for development workflow and coding standards.