# Deploy Ansible MCP server on Watchtower ## Purpose Deploy a custom Ansible MCP server on Watchtower so AI tools can query inventory, validate syntax, and run allowlisted playbooks through guarded tool calls. ## Scope - Host: `watchtower` inventory group - Playbook: `ansible/playbooks/ai/deploy_ansible_mcp_watchtower.yml` - Runtime path: `/opt/ansible-mcp` - Service name: `ansible-mcp` - State and logs: `/var/lib/ansible-mcp` ## Features delivered - MCP tools: - `health` - `list_inventory` - `validate_syntax` - `run_playbook` - `get_job_status` - `cancel_job` - Path guardrails for playbook execution (allowlisted directories only) - Optional explicit playbook allowlist for high-trust execution scopes - Write-mode guardrails: - global write toggle - explicit confirm gate for write actions - Auth guardrail: - bearer token required when `ANSIBLE_MCP_API_TOKEN` is configured - Input guardrails: - max `extra_vars` payload size - blocked `extra_vars` key list - Background run tracking with per-run logs and status records - JSONL audit records at `/var/lib/ansible-mcp/audit/events.jsonl` ## Prerequisites 1. Watchtower host is reachable from control node. 2. Python 3 is installed on Watchtower. 3. Inventory contains a valid `watchtower` group. 4. Ansible control node has access to this repository at `/home/chester/homelab`. ## Deploy Run from `ansible/`: ```bash cd /home/chester/homelab/ansible export ANSIBLE_MCP_API_TOKEN='set-a-strong-token-before-deploy' ansible-playbook -i inventory/hosts.ini playbooks/ai/deploy_ansible_mcp_watchtower.yml ``` Validate only: ```bash cd /home/chester/homelab/ansible ansible-playbook -i inventory/hosts.ini playbooks/ai/deploy_ansible_mcp_watchtower.yml --check ``` ## Runtime configuration The playbook sets these environment variables in the systemd unit: - `ANSIBLE_MCP_REPO_ROOT=/home/chester/homelab/ansible` - `ANSIBLE_MCP_INVENTORY=inventory/hosts.ini` - `ANSIBLE_MCP_ALLOWED_PLAYBOOK_DIRS=playbooks` - `ANSIBLE_MCP_ALLOWED_PLAYBOOKS=` (optional comma-separated explicit allowlist) - `ANSIBLE_MCP_API_TOKEN=` (required for HTTP transport in current playbook) - `ANSIBLE_MCP_ALLOW_WRITE=true` - `ANSIBLE_MCP_REQUIRE_CONFIRM=true` - `ANSIBLE_MCP_DEFAULT_TIMEOUT=900` - `ANSIBLE_MCP_MAX_TIMEOUT=3600` - `ANSIBLE_MCP_MAX_EXTRA_VARS_BYTES=16384` - `ANSIBLE_MCP_BLOCKED_EXTRA_VARS_KEYS=ansible_password,ansible_become_password,vault_password` - `ANSIBLE_MCP_STATE_DIR=/var/lib/ansible-mcp` - `ANSIBLE_MCP_TRANSPORT=streamable-http` - `ANSIBLE_MCP_HOST=0.0.0.0` - `ANSIBLE_MCP_PORT=8449` ## Verify ```bash # Service state sudo systemctl status ansible-mcp --no-pager # Recent logs sudo journalctl -u ansible-mcp -n 80 --no-pager # Listening port ss -ltnp | grep 8449 ``` ## Client connection example For MCP clients that support HTTP transport: ```json { "mcpServers": { "ansible-watchtower": { "type": "http", "url": "http://10.0.0.200:8449/mcp", "headers": { "Authorization": "Bearer ${env:ANSIBLE_MCP_API_TOKEN}" } } } } ``` If you terminate TLS upstream (recommended), expose this endpoint through your existing ingress and use an HTTPS URL. ## Operational safety notes - Keep `ANSIBLE_MCP_REQUIRE_CONFIRM=true` in write mode. - Keep `ANSIBLE_MCP_API_TOKEN` set and rotate it regularly. - Prefer explicit `ANSIBLE_MCP_ALLOWED_PLAYBOOKS` over broad directory allowlists. - Restrict `ANSIBLE_MCP_ALLOWED_PLAYBOOK_DIRS` to known-safe playbook roots. - Do not grant broad filesystem access to the service user. - Treat background run logs in `/var/lib/ansible-mcp/logs` as audit artifacts. ## Rollback ```bash sudo systemctl disable --now ansible-mcp sudo rm -f /etc/systemd/system/ansible-mcp.service sudo systemctl daemon-reload ``` Optional cleanup: ```bash sudo rm -rf /opt/ansible-mcp /var/lib/ansible-mcp ```