{"id":49403354,"url":"https://github.com/iamenr0s/ansible-mcp","last_synced_at":"2026-04-28T19:38:25.673Z","repository":{"id":346916496,"uuid":"1191915963","full_name":"iamenr0s/ansible-mcp","owner":"iamenr0s","description":"An MCP (Model Context Protocol) server that gives Claude AI direct access to your Ansible environment — run playbooks, execute Molecule tests, manage inventory, and drop into a shell, all from a conversation.","archived":false,"fork":false,"pushed_at":"2026-03-26T00:12:46.000Z","size":27,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-26T22:27:03.203Z","etag":null,"topics":["ansible","argocd","claude-ai","gitops","mcp-server"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iamenr0s.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-25T17:57:02.000Z","updated_at":"2026-03-26T00:18:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/iamenr0s/ansible-mcp","commit_stats":null,"previous_names":["iamenr0s/ansible-mcp"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/iamenr0s/ansible-mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamenr0s%2Fansible-mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamenr0s%2Fansible-mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamenr0s%2Fansible-mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamenr0s%2Fansible-mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iamenr0s","download_url":"https://codeload.github.com/iamenr0s/ansible-mcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamenr0s%2Fansible-mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32396771,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T19:38:08.556Z","status":"ssl_error","status_checked_at":"2026-04-28T19:37:55.688Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","argocd","claude-ai","gitops","mcp-server"],"created_at":"2026-04-28T19:38:23.841Z","updated_at":"2026-04-28T19:38:25.663Z","avatar_url":"https://github.com/iamenr0s.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ansible-mcp\n\n\u003e An MCP (Model Context Protocol) server that gives Claude AI direct access to your Ansible environment — run playbooks, manage inventory, diagnose hosts, audit security, manage vault secrets, and much more, all from a conversation.\n\n![Python](https://img.shields.io/badge/python-3.12-blue)\n![Ansible](https://img.shields.io/badge/ansible-10%2B-red)\n![License](https://img.shields.io/badge/license-MIT-green)\n![Tools](https://img.shields.io/badge/tools-42-brightgreen)\n\n---\n\n## Table of contents\n\n- [Overview](#overview)\n- [Architecture](#architecture)\n- [Tools](#tools)\n  - [Legacy / Backwards-Compat](#legacy--backwards-compat)\n  - [Inventory](#inventory)\n  - [Playbook Execution](#playbook-execution)\n  - [Playbook Authoring](#playbook-authoring)\n  - [Testing](#testing)\n  - [Project Management](#project-management)\n  - [Vault](#vault)\n  - [Galaxy](#galaxy)\n  - [Diagnostics](#diagnostics)\n  - [Security \u0026 Analysis](#security--analysis)\n- [Prerequisites](#prerequisites)\n- [Quick start (Docker Compose)](#quick-start-docker-compose)\n- [Project structure](#project-structure)\n- [Configuration](#configuration)\n  - [Environment variables](#environment-variables)\n  - [ansible.cfg](#ansiblecfg)\n  - [SSH keys](#ssh-keys)\n- [Project management](#project-management-1)\n- [Kubernetes deployment](#kubernetes-deployment)\n  - [Prerequisites](#kubernetes-prerequisites)\n  - [Secrets — HashiCorp Vault + ESO](#secrets--hashicorp-vault--eso)\n  - [Traefik ingress](#traefik-ingress)\n  - [ArgoCD GitOps](#argocd-gitops)\n  - [Deploy](#deploy)\n- [CI/CD pipeline](#cicd-pipeline)\n  - [CI — lint and Molecule tests](#ci--lint-and-molecule-tests)\n  - [Release — build, push, deploy](#release--build-push-deploy)\n  - [Dependabot](#dependabot)\n- [Connecting to Claude.ai](#connecting-to-claudeai)\n- [Sample role and Molecule scenario](#sample-role-and-molecule-scenario)\n- [Molecule drivers](#molecule-drivers)\n- [Troubleshooting](#troubleshooting)\n- [Contributing](#contributing)\n- [License](#license)\n\n---\n\n## Overview\n\nThis project implements an HTTP/SSE MCP server that wraps the Ansible ecosystem inside a Docker container and exposes it as 42 tools Claude can call mid-conversation.\n\nInstead of:\n```\nyou → Slack → teammate → \"what's the inventory flag again?\" → run → fix → re-run\n```\n\nIt becomes:\n```\nyou → \"run the webservers playbook in check mode against staging\"\nClaude → runs it, shows you the diff\nyou → \"looks good, run it for real\"\nClaude → done\n\nyou → \"diagnose high memory on the db servers\"\nClaude → gathers facts, runs health checks, identifies the process, suggests fix\nyou → \"auto-heal it\"\nClaude → safely restarts the offending service\n```\n\nClaude handles flags, reads output, spots failures, and can suggest or apply fixes — all within the same conversation.\n\n---\n\n## Architecture\n\n```\nClaude.ai (MCP client)\n    │  HTTPS / SSE\n    ▼\nTraefik ingress  ←── TLS termination, X-Accel-Buffering: no (required for SSE)\n    │  HTTP/1.1\n    ▼\n┌─────────────────────────────────────────┐\n│  server.py  (Starlette + uvicorn)       │\n│  ┌──────────────────────────────────┐   │\n│  │  SseServerTransport  /sse        │   │\n│  │  MCP SDK Server      /messages/  │   │\n│  │  call_tool() dispatcher          │   │\n│  └──────────┬───────────────────────┘   │\n│             │ subprocess                │\n│   ansible-playbook  molecule  bash      │\n│   ansible-vault  ansible-galaxy         │\n└─────────────┼───────────────────────────┘\n              │ SSH / WinRM\n    Managed nodes / infrastructure\n\nGitHub repo\n    │  git push\n    ▼\nGitHub Actions  ──── build \u0026 push image ──── GHCR\n    │  tag bump\n    ▼\nArgoCD  ──── Kustomize overlays ──── Kubernetes\n                                       ├── Traefik\n                                       ├── External Secrets Operator\n                                       └── HashiCorp Vault\n```\n\n---\n\n## Tools\n\n42 tools across 10 categories.\n\n### Legacy / Backwards-Compat\n\n| Tool | Description |\n|------|-------------|\n| `run_playbook` | Runs `ansible-playbook` (simple interface; prefer `ansible_playbook` for new use) |\n| `run_molecule` | Runs `molecule test/converge/verify/destroy/create/lint` against a role |\n| `manage_inventory` | Read, write, list, or delete inventory files under `workspace/inventory/` |\n| `check_versions` | Shows installed versions of all managed packages via `pip show` |\n| `run_shell` | Runs arbitrary bash commands inside the container workspace |\n\n### Inventory\n\n| Tool | Description |\n|------|-------------|\n| `ansible_inventory` | Lists all hosts and groups as structured JSON |\n| `inventory_graph` | Shows the hierarchical group structure of an inventory |\n| `inventory_find_host` | Finds a host's group memberships and merged variables |\n| `inventory_diff` | Compares two inventory files — added/removed hosts and groups |\n| `inventory_parse` | Parses with `ansible.cfg`-aware environment and returns structured data |\n\n### Playbook Execution\n\n| Tool | Description |\n|------|-------------|\n| `ansible_playbook` | Full-featured playbook runner (verbosity, limit, diff, skip_tags, extra_vars as JSON) |\n| `ansible_task` | Ad-hoc module execution against hosts (ping, shell, setup, copy, etc.) |\n| `ansible_role` | Executes a role via a generated temporary playbook |\n| `validate_playbook` | Syntax-checks a playbook without executing it |\n\n### Playbook Authoring\n\n| Tool | Description |\n|------|-------------|\n| `create_playbook` | Writes a new YAML playbook to the workspace and validates its syntax |\n| `validate_yaml` | Validates any YAML file, reporting line/column errors |\n\n### Testing\n\n| Tool | Description |\n|------|-------------|\n| `ansible_test_idempotence` | Runs a playbook twice and verifies zero changes on the second run |\n\n### Project Management\n\nRegister named projects with their own inventory, roles path, collections path, and custom environment variables. All playbook tools accept a `project` parameter that automatically applies the project's settings.\n\n| Tool | Description |\n|------|-------------|\n| `register_project` | Registers a project with inventory, roles, env vars, and optional default flag |\n| `list_projects` | Lists all registered projects and the current default |\n| `project_playbooks` | Discovers YAML playbook files within a project root |\n| `project_run_playbook` | Runs a playbook using the project's stored configuration |\n| `project_bootstrap` | Installs Galaxy dependencies and reports the Ansible environment |\n\n### Vault\n\n| Tool | Description |\n|------|-------------|\n| `vault_encrypt` | Encrypts a file with `ansible-vault` |\n| `vault_decrypt` | Decrypts a vault-encrypted file |\n| `vault_view` | Views encrypted file contents without decrypting to disk |\n| `vault_rekey` | Changes the encryption password on a vault file |\n\n### Galaxy\n\n| Tool | Description |\n|------|-------------|\n| `galaxy_install` | Installs roles and collections from a `requirements.yml` |\n| `galaxy_lock` | Captures installed versions to a lock file |\n\n### Diagnostics\n\n| Tool | Description |\n|------|-------------|\n| `ansible_gather_facts` | Collects system facts via the setup module |\n| `ansible_ping` | Tests Ansible connectivity using the ping module |\n| `ansible_remote_command` | Executes a shell command on remote hosts |\n| `ansible_fetch_logs` | Fetches and analyses remote log files with optional pattern filtering |\n| `ansible_service_manager` | Manages systemd services (start/stop/restart/status/enable/disable) |\n| `ansible_diagnose_host` | Comprehensive health assessment (CPU, memory, disk, load, network, failed services) |\n| `ansible_health_monitor` | Collects metrics at intervals and reports trends |\n\n### Security \u0026 Analysis\n\n| Tool | Description |\n|------|-------------|\n| `ansible_performance_baseline` | Benchmarks CPU, memory, and disk I/O |\n| `ansible_capture_baseline` | Snapshots processes, network listeners, and system state to a file |\n| `ansible_compare_states` | Diffs current state against a baseline snapshot to detect drift |\n| `ansible_auto_heal` | Diagnoses and optionally fixes: `high_cpu`, `high_memory`, `disk_full`, `service_failed`, `network_unreachable` |\n| `ansible_network_matrix` | Tests port connectivity across hosts and builds a connectivity matrix |\n| `ansible_security_audit` | Audits open ports, SSH config, world-writable files, failed logins, SUID files |\n| `ansible_log_hunter` | Searches multiple log files for a pattern and correlates events by time window |\n\n---\n\n## Prerequisites\n\n| Tool | Min version |\n|------|-------------|\n| Docker | 24+ |\n| Docker Compose | v2 |\n| (for K8s) kubectl | 1.28+ |\n| (for K8s) ArgoCD | 2.9+ |\n| (for K8s) cert-manager | 1.14+ |\n| (for K8s) Traefik | 2.10+ |\n| (for K8s) External Secrets Operator | 0.9+ |\n| (for drivers) KVM/QEMU on host | any |\n\n---\n\n## Quick start (Docker Compose)\n\n### 1. Clone and scaffold\n\n```bash\ngit clone https://github.com/YOUR_ORG/ansible-mcp.git\ncd ansible-mcp\n```\n\n### 2. Add your SSH key\n\n```bash\nmkdir -p ssh\ncp ~/.ssh/id_rsa ssh/\nchmod 400 ssh/id_rsa\n```\n\n### 3. Add your inventory\n\n```bash\ncat \u003e workspace/inventory/hosts.ini \u003c\u003c 'EOF'\n[webservers]\nweb01 ansible_host=192.168.1.10\n\n[all:vars]\nansible_user=ansible\nansible_python_interpreter=/usr/bin/python3\nEOF\n```\n\n### 4. Build and run\n\n```bash\ndocker compose build\ndocker compose up -d\n```\n\n### 5. Verify\n\n```bash\ncurl -N http://localhost:8000/sse\n# → event: endpoint\n#   data: /messages/?session_id=...\n```\n\n### 6. Connect Claude.ai\n\nGo to **Claude.ai → Settings → Integrations → Add integration** and enter:\n```\nhttp://\u003cYOUR_HOST_IP\u003e:8000/sse\n```\n\nClaude auto-discovers all 42 tools.\n\n---\n\n## Project structure\n\n```\nansible-mcp/\n├── server.py                          # MCP server — 42 tools (Starlette + SSE)\n├── requirements.txt                   # Python dependencies (incl. PyYAML)\n├── ansible.cfg                        # Ansible configuration\n├── Dockerfile\n├── docker-compose.yml\n├── .github/\n│   ├── dependabot.yaml\n│   └── workflows/\n│       ├── ci.yaml                    # Lint + Molecule on PR\n│       └── release.yaml               # Build, push, bump prod tag on release\n├── k8s/\n│   ├── argocd/\n│   │   ├── project.yaml               # ArgoCD AppProject\n│   │   └── application.yaml           # ArgoCD Application (prod overlay)\n│   └── manifests/\n│       ├── base/\n│       │   ├── kustomization.yaml\n│       │   ├── namespace.yaml\n│       │   ├── configmap.yaml\n│       │   ├── pvc.yaml               # 5Gi workspace volume\n│       │   ├── secret-store.yaml      # ESO: Vault SecretStore\n│       │   ├── external-secret.yaml   # ESO: ExternalSecret + ServiceAccount\n│       │   ├── deployment.yaml\n│       │   ├── service.yaml\n│       │   ├── ingress.yaml           # Traefik + Let's Encrypt\n│       │   └── middleware.yaml        # Traefik SSE middleware (X-Accel-Buffering: no)\n│       └── overlays/\n│           ├── dev/\n│           │   ├── kustomization.yaml\n│           │   └── patch-replicas.yaml\n│           └── prod/\n│               ├── kustomization.yaml\n│               └── patch-resources.yaml\n└── workspace/                         # Volume-mounted persistent storage\n    ├── inventory/\n    │   └── hosts.ini\n    ├── projects.json                  # Auto-created: registered project configs\n    └── roles/\n        └── example_role/\n            ├── tasks/main.yml\n            ├── defaults/main.yml\n            ├── handlers/main.yml\n            └── molecule/\n                └── default/\n                    ├── molecule.yml   # Podman driver, UBI9 + Debian12\n                    ├── converge.yml\n                    ├── verify.yml\n                    └── tests/\n                        └── test_example_role.py\n```\n\n---\n\n## Configuration\n\n### Environment variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MCP_HOST` | `0.0.0.0` | Bind address |\n| `MCP_PORT` | `8000` | Bind port |\n| `WORKSPACE_DIR` | `/workspace` | Base directory for all file operations |\n| `ANSIBLE_CONFIG` | `/etc/ansible/ansible.cfg` | Ansible config file path |\n\n### ansible.cfg\n\nThe included `ansible.cfg` is pre-configured with:\n\n- **SSH multiplexing** — `ControlMaster=auto`, `ControlPersist=60s` — speeds up consecutive task runs\n- **Fact caching** — JSON file cache with a 1-hour TTL avoids redundant `gather_facts` calls\n- **YAML output** — `stdout_callback = yaml` for readable playbook output in Claude's responses\n- **Privilege escalation** — `become = True` with `sudo` by default\n- **Galaxy servers** — both `galaxy.ansible.com` and `cloud.redhat.com/api/automation-hub/` pre-configured\n\nTo override at runtime, mount a custom config:\n```yaml\nvolumes:\n  - ./my-ansible.cfg:/etc/ansible/ansible.cfg:ro\n```\n\nOr set `ANSIBLE_CONFIG` to a path inside the workspace.\n\n### SSH keys\n\n**Docker Compose** — mount the `./ssh/` directory:\n```yaml\nvolumes:\n  - ./ssh:/root/.ssh:ro\n```\n\n**Kubernetes** — SSH keys are pulled from HashiCorp Vault by the External Secrets Operator (see [Secrets](#secrets--hashicorp-vault--eso)). No manual `kubectl create secret` required.\n\n---\n\n## Project management\n\nRegister named Ansible projects so tools automatically use the right inventory and environment:\n\n```\nyou → \"register the production project at infra/ with inventory infra/inventory/prod.ini\"\nClaude → calls register_project\n\nyou → \"run the database playbook against the production project\"\nClaude → calls project_run_playbook with project=production\n```\n\nProjects are stored in `workspace/projects.json` and survive container restarts via the volume mount. All playbook, inventory, diagnostics, vault, and galaxy tools accept a `project` parameter.\n\n---\n\n## Kubernetes deployment\n\n### Kubernetes prerequisites\n\nEnsure the following are running in your cluster before deploying:\n\n- **Traefik** as the ingress controller\n- **cert-manager** with a `letsencrypt` `ClusterIssuer`\n- **External Secrets Operator** (ESO)\n- **ArgoCD**\n- **HashiCorp Vault** accessible from the cluster\n\n### Secrets — HashiCorp Vault + ESO\n\nSSH keys are managed entirely through Vault — nothing sensitive ever touches Git.\n\n#### 1. Set up Vault (one-time)\n\n```bash\n# Enable KV v2 if not already enabled\nvault secrets enable -path=secret kv-v2\n\n# Store the SSH key\nvault kv put secret/ansible-mcp/ssh-keys \\\n  id_rsa=@$HOME/.ssh/id_rsa \\\n  id_rsa_pub=@$HOME/.ssh/id_rsa.pub\n\n# Create an access policy\nvault policy write ansible-mcp - \u003c\u003c'EOF'\npath \"secret/data/ansible-mcp/*\" {\n  capabilities = [\"read\"]\n}\nEOF\n\n# Enable Kubernetes auth (if not already enabled)\nvault auth enable kubernetes\n\n# Configure Kubernetes auth\nvault write auth/kubernetes/config \\\n  kubernetes_host=\"https://$KUBERNETES_PORT_443_TCP_ADDR:443\"\n\n# Bind the role to the service account\nvault write auth/kubernetes/role/ansible-mcp \\\n  bound_service_account_names=ansible-mcp-sa \\\n  bound_service_account_namespaces=ansible-mcp \\\n  policies=ansible-mcp \\\n  ttl=1h\n```\n\n#### 2. Update the SecretStore\n\nEdit `k8s/manifests/base/secret-store.yaml` and set your Vault address:\n\n```yaml\nspec:\n  provider:\n    vault:\n      server: \"https://vault.your-domain.com\"   # ← replace this\n```\n\nESO will automatically create the `ansible-ssh-keys` Kubernetes Secret and refresh it every hour.\n\nTo force an immediate refresh:\n```bash\nkubectl annotate externalsecret ansible-ssh-keys \\\n  force-sync=$(date +%s) --overwrite -n ansible-mcp\n```\n\n### Traefik ingress\n\nThe ingress is pre-configured for `ansible-mcp.apps.k8s.enros.me` with:\n\n- Let's Encrypt TLS via cert-manager (`tls-acme: \"true\"`)\n- Traefik `websecure` entrypoint\n- SSE streaming enabled via the `ansible-mcp-sse-headers` Middleware CRD (`X-Accel-Buffering: no`)\n\nTo use a different hostname, update `k8s/manifests/base/ingress.yaml`:\n```yaml\nspec:\n  rules:\n    - host: ansible-mcp.your-domain.com   # ← replace\n  tls:\n    - hosts:\n        - ansible-mcp.your-domain.com     # ← replace\n      secretName: ansible-mcp-tls\n```\n\n\u003e **Important:** The `middleware.yaml` (`X-Accel-Buffering: no`) is required. Without it Traefik buffers the SSE stream and Claude will not receive events in real time.\n\n### ArgoCD GitOps\n\n#### 1. Update the repo URL\n\nIn both `k8s/argocd/project.yaml` and `k8s/argocd/application.yaml`, replace:\n```yaml\nrepoURL: https://github.com/YOUR_ORG/ansible-mcp.git\n```\n\n#### 2. Register the repo with ArgoCD (if private)\n\n```bash\nargocd repo add https://github.com/YOUR_ORG/ansible-mcp.git \\\n  --username YOUR_USER \\\n  --password YOUR_TOKEN\n```\n\n#### 3. Sync behaviour\n\nThe Application is configured with:\n\n| Setting | Value | Effect |\n|---------|-------|--------|\n| `automated.prune` | `true` | Resources deleted from Git are removed from the cluster |\n| `automated.selfHeal` | `true` | Manual changes to the cluster are reverted |\n| `syncOptions.CreateNamespace` | `true` | Namespace is created if it doesn't exist |\n| `ignoreDifferences` | `/spec/replicas` | Manual replica scaling is not overwritten |\n| `retry.limit` | `5` | Retries with exponential backoff up to 3 minutes |\n\n### Deploy\n\n#### 1. Update the image registry\n\nIn `k8s/manifests/overlays/prod/kustomization.yaml`:\n```yaml\nimages:\n  - name: your-registry/ansible-mcp\n    newTag: \"1.0.0\"   # ← set your initial tag\n```\n\n#### 2. Build and push the image\n\n```bash\ndocker build -t ghcr.io/YOUR_ORG/ansible-mcp:1.0.0 .\ndocker push ghcr.io/YOUR_ORG/ansible-mcp:1.0.0\n```\n\n#### 3. Apply ArgoCD manifests\n\n```bash\nkubectl apply -f k8s/argocd/project.yaml\nkubectl apply -f k8s/argocd/application.yaml\n```\n\nArgoCD begins syncing immediately. Monitor progress:\n\n```bash\nargocd app get ansible-mcp\nargocd app wait ansible-mcp --health\nkubectl get all -n ansible-mcp\n```\n\n#### 4. Connect Claude.ai\n\n```\nhttps://ansible-mcp.apps.k8s.enros.me/sse\n```\n\n---\n\n## CI/CD pipeline\n\n### CI — lint and Molecule tests\n\nRuns on every pull request and push to `main`:\n\n```\nPR opened / push to main\n    ├── ansible-lint (example_role)\n    ├── kustomize overlay validation (dev + prod)\n    └── molecule test -s default (Podman driver, UBI9 + Debian12)\n```\n\n### Release — build, push, deploy\n\nTriggered when a GitHub Release is published (e.g. `v1.2.3`):\n\n```\nRelease published\n    ├── Docker image built with Buildx (GHA layer cache)\n    ├── Pushed to GHCR:\n    │     ghcr.io/YOUR_ORG/ansible-mcp:1.2.3\n    │     ghcr.io/YOUR_ORG/ansible-mcp:1.2\n    │     ghcr.io/YOUR_ORG/ansible-mcp:sha-abc1234\n    ├── kustomize edit set image patches overlays/prod/kustomization.yaml\n    ├── Bot commits newTag: \"1.2.3\" to main  [skip ci]\n    └── ArgoCD detects change → rolling update\n```\n\n#### Required permissions\n\nGo to **Repo → Settings → Actions → General → Workflow permissions** and set **Read and write permissions**. This allows the bot to commit the tag bump.\n\n#### Using a private registry\n\nReplace the GHCR login step in `.github/workflows/release.yaml` with:\n```yaml\n- name: Log in to private registry\n  uses: docker/login-action@v3\n  with:\n    registry: your-registry.example.com\n    username: ${{ secrets.REGISTRY_USER }}\n    password: ${{ secrets.REGISTRY_PASSWORD }}\n```\n\nThen add `REGISTRY_USER` and `REGISTRY_PASSWORD` to your repository secrets.\n\n### Dependabot\n\nAutomated dependency updates run every Monday at 08:00 UTC across three ecosystems:\n\n| Ecosystem | Group | Packages |\n|-----------|-------|---------|\n| GitHub Actions | `github-actions-all` | All actions — single weekly batch PR |\n| pip | `ansible-all` | ansible, ansible-core, molecule, molecule-*, testinfra |\n| pip | `mcp-server` | mcp, uvicorn, starlette |\n| Docker | — | `python:3.12-slim` base image |\n\n---\n\n## Connecting to Claude.ai\n\n1. Open **Claude.ai → Settings → Integrations → Add integration**\n2. Enter the SSE URL:\n   - **Docker Compose:** `http://\u003cYOUR_HOST_IP\u003e:8000/sse`\n   - **Kubernetes:** `https://ansible-mcp.apps.k8s.enros.me/sse`\n3. Claude auto-discovers all 42 tools\n\n---\n\n## Sample role and Molecule scenario\n\nThe repo includes `workspace/roles/example_role/` as a working reference:\n\n```\nexample_role/\n├── tasks/main.yml       # installs packages, starts a service\n├── defaults/main.yml    # example_role_packages, example_role_service\n├── handlers/main.yml    # restart handler\n└── molecule/default/\n    ├── molecule.yml     # Podman driver, UBI9 + Debian 12 platforms\n    ├── converge.yml     # applies the role\n    ├── verify.yml       # runs testinfra\n    └── tests/\n        └── test_example_role.py\n```\n\nRun the scenario locally:\n```bash\ncd workspace/roles/example_role\nmolecule test\n```\n\nOr ask Claude:\n\u003e *\"Run the molecule default scenario for example_role using the podman driver\"*\n\n---\n\n## Molecule drivers\n\n| Driver | Works out of the box | Notes |\n|--------|---------------------|-------|\n| `podman` | Yes | `podman` is installed in the image |\n| `libvirt` | No | Requires `libvirt-dev` in the Dockerfile and KVM on the host |\n| `qemu` | No | Requires KVM on the host |\n\nTo enable `libvirt` / `qemu` drivers, uncomment in `docker-compose.yml`:\n```yaml\nprivileged: true\ndevices:\n  - /dev/kvm:/dev/kvm\n```\n\nAnd add `libvirt-dev` to the `apt-get` block in the Dockerfile:\n```dockerfile\nRUN apt-get install -y --no-install-recommends \\\n    ... \\\n    libvirt-dev\n```\n\n---\n\n## Troubleshooting\n\n| Symptom | Fix |\n|---------|-----|\n| `curl /sse` returns nothing | Check `docker compose logs ansible-mcp` — uvicorn should show `Started server process` |\n| Claude can't reach the SSE endpoint | Ensure port 8000 is open; for K8s check the Traefik ingress and middleware are applied |\n| SSE drops immediately on Traefik | Verify `middleware.yaml` is applied and the ingress annotation references `ansible-mcp-sse-headers@kubernetescrd` |\n| SSH key not found | For Docker: check `./ssh/id_rsa` exists and is `chmod 400`. For K8s: run `kubectl describe externalsecret ansible-ssh-keys -n ansible-mcp` |\n| Vault auth failing | Confirm `vault write auth/kubernetes/role/ansible-mcp` binds `ansible-mcp-sa` in namespace `ansible-mcp` |\n| ESO not syncing | Check `SecretSyncedError` conditions with `kubectl describe externalsecret ansible-ssh-keys -n ansible-mcp` |\n| `molecule-libvirt` install fails | Add `libvirt-dev` to `apt-get` in the Dockerfile, rebuild |\n| KVM permission denied | Add `--device /dev/kvm` or set `privileged: true` |\n| Molecule times out in K8s | Increase `resources.limits` in the overlay patch and molecule `timeout` in `molecule.yml` |\n| ArgoCD sync stuck | Run `argocd app get ansible-mcp` for error details; force with `argocd app sync --force` |\n| ArgoCD can't pull repo | Run `argocd repo add` with valid credentials before applying the Application manifest |\n| `validate_yaml` import error | Ensure `PyYAML\u003e=6.0.0` is in `requirements.txt` and the image is rebuilt |\n| `ansible_health_monitor` times out | Reduce `samples` or `interval`; default is 3 samples × 10s = 30s total |\n\n---\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch: `git checkout -b feat/my-feature`\n3. Make your changes and ensure CI passes (`molecule test`, `ansible-lint`)\n4. Open a pull request against `main`\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamenr0s%2Fansible-mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiamenr0s%2Fansible-mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamenr0s%2Fansible-mcp/lists"}