ansible-flow-mcp
Give agents Ansible. Not the keys.
MCP server that exposes real Ansible modules and playbooks to AI agents — and a SSH hub/spoke fabric so multi-host automation is enrolled, bastion-scoped, and check-first by default.
| Track | What you get |
|---|---|
| Agent loop | search → schema → check → execute on allowlisted collections |
| Fleet fabric | One hub · join tokens · SSH ForceCommand spokes · fixed inventory |
OpenFlow dual-track · Marketing site · Campaign storyboard · Hub/spoke ops · Apache-2.0
Not affiliated with Red Hat or the Ansible project beyond using the public Ansible CLI and docs.
New here? Marketing site (story + Schema Lab) · full step-by-step docs/QUICKSTART.md — local MCP · compose lab · bare-metal hub/spoke · editor wiring.
./scripts/site_preview.sh # http://127.0.0.1:8765/ (gallery + live schemas)
Visual tour
| Why it exists | Agent loop |
| Hub / spoke fabric | Operators |
Screenshots live in docs/images/campaign-*.png. Re-shoot from docs/campaign/ with ./capture.sh.
Why this exists
Agents on a god-mode control node invent inventory, reach for shell, and treat every worker as an entrypoint. That is not a security model.
You need this when:
- You want Cursor / Claude / OpenCode to run Ansible like an operator, not freestyle root across the fleet
- Multi-host must mean bastion ops you already understand (SSH, inventory, enrollment) — not a mesh hop plane
- Prompt injection will still ask for bad ops — policy and topology must refuse
Two tracks
1. Agent loop — search → schema → check → execute
Curated module gallery. Slim argSpec before any run. Check mode default. Free-form modules denied. Playbooks path-jailed.
| Tool | Purpose |
|---|---|
search_modules |
Gallery search |
get_module_schema |
Slim argSpec for FQCN |
run_module |
Ad-hoc Ansible (check_mode default true) |
run_playbook |
ansible-playbook on a path-jailed .yml |
list_collections |
Collections in gallery |
Ritual (modules): search_modules → get_module_schema → run_module(..., check_mode=true) → apply only if appropriate.
Ritual (playbooks): confirm path under allowlisted roots → check → apply.
2. Hub/spoke — nothing is a target until enrolled
Secure multi-host mode: agent attaches to the hub only. Hub reaches spokes over SSH only. Spokes execute localhost and cannot lateral-move via this fabric.
| Full mesh (withdrawn) | Hub/spoke (shipped) | |
|---|---|---|
| Worker compromise | Could MCP-hop fleet-wide | No lateral MCP |
| Inventory | Gossip / replicas | Hub is source of truth |
| Agent attach | Any node | Hub only |
| Ops model | Mesh OS | Classic Ansible bastion |
Enrollment: hub init → issue-token (TTL, one-time jti) → spoke join (token + SSH identity) → hub inventory. Runtime: ForceCommand MCP session — no shell on the hub→spoke path.
Hub tools: list_nodes / hub_status, issue_token, revoke_node, groups (create_group, set_group_members, …), spoke_call, plus catalog run_* against enrolled hosts or groups only. Client-supplied -i is rejected in hub mode.
Deep ops: docs/HUB.md.
Operators
Day-2 surface matches the agent: enroll, group, hand the hub to OpenCode.
ansible-flow-mcp hub init --name ctrl-01
ansible-flow-mcp hub issue-token --name web-03 --ttl 15m
ansible-flow-mcp spoke join --token "$TOKEN" --hub user@hub:22 --public-addr web-03.example.com
ansible-flow-mcp hub session # MCP stdio for the agent
ansible-flow-mcp tui # servers · groups · invite · OpenCode
ansible-flow-mcp hub spoke-call --node web-03 --tool list_collections
Lab one-shot
cd lab && ./scripts/demo.sh
# then: ./scripts/tui.sh | ./scripts/opencode-host.sh
See lab/README.md.
Quick start
Full guide (all paths, verify, troubleshooting): docs/QUICKSTART.md
| Path | Guide section |
|---|---|
| Local MCP + Cursor/Claude | Path A |
| Compose lab (hub + 3 spokes) | Path B |
| Bare-metal hub/spoke | Path C |
Single-node / dev (short form)
Requirements: Python ≥ 3.11 · collection ansible.posix (JSON callback) · collections you will run.
pip install pulls ansible-core (provides ansible / ansible-playbook).
cd ansible-flow-mcp
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
ansible-galaxy collection install ansible.posix
pytest -q
ansible-flow-mcp
Editor snippets: examples/cursor-mcp.json, examples/claude-desktop.json.
Hub + OpenCode: examples/opencode-hub.jsonc · ansible-flow-mcp hub write-opencode-config.
{
"mcpServers": {
"ansible-flow": {
"command": "/path/to/ansible-flow-mcp/.venv/bin/ansible-flow-mcp"
}
}
}
Security (honest)
| Control | Behavior |
|---|---|
| Collection allowlist | Only configured collections |
| Module deny list | command / shell / raw / script denied by default |
| Check mode | Default true on run_module |
| Playbook jail | Allowlisted roots · size limit · .yml/.yaml only |
| No shell interpolation | argv-only subprocess |
| Hub inventory | Enrolled hosts only · no client -i · host key checking on |
| Spoke path | SSH ForceCommand · localhost exec · no peer fabric |
Residual: hub compromise = fleet (same class as any Ansible control node). Harden the bastion — see docs/SECURITY.md and docs/HUB.md.
Catalog & OpenFlow
catalog/collections-allowlist.yml— allowlist + deny free-form modulescatalog/gallery.json+catalog/schemas/— searchable gallery- Regenerate:
python scripts/generate_catalog.py - Galaxy factory TUI: scripts/factory/README.md
Dual-tracked with OpenFlow Ansible canvas gallery
(plan · umbrella).
| OpenFlow | This MCP server |
|---|---|
| Palette Ansible gallery | search_modules |
| Form | JSON module options | get_module_schema + run_module |
| Playbook resource | run_playbook |
| Control-node SSH / become | Inventory + Ansible config · hub→spoke SSH in hub mode |
Env (common)
| Variable | Meaning |
|---|---|
ANSIBLE_FLOW_CATALOG_DIR |
Override catalog path |
ANSIBLE_FLOW_COLLECTIONS |
Comma-separated allowlist override |
ANSIBLE_FLOW_INVENTORY |
Default -i (non-hub / dev) |
ANSIBLE_FLOW_TIMEOUT |
Seconds (default 120 module / 300 playbook) |
ANSIBLE_FLOW_PLAYBOOK_ROOTS |
Extra playbook roots (:-separated) |
ANSIBLE_FLOW_HUB_DIR |
Hub state (default: /var/lib/… if writable, else ~/.local/share/ansible-flow/hub) |
ANSIBLE_FLOW_SPOKE_DIR |
Spoke state (same pattern under …/spoke) |
License & publish
Apache-2.0
pip install build twine && python -m build
# twine upload dist/*
uvx --from ansible-flow-mcp ansible-flow-mcp
Metadata
Release files for ansible-flow-mcp 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ansible_flow_mcp-0.1.3.tar.gz | 12.0 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ansible_flow_mcp-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 46.2 MB
Release files / ansible_flow_mcp-0.1.3.tar.gz
| Download URL | ansible_flow_mcp-0.1.3.tar.gz |
|---|---|
| Size | 12.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ed43716bc5d54e30976514910438d254ca0c348a831b608daa546caf11260236
|
|
BLAKE2b-256 checksum How to use checksums |
e040028c5b14a3bc372126341efc73208c5c46c76f383d3742c68bdbcf97bfeb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 10, 2026.
Transparency logRelease files / ansible_flow_mcp-0.1.3-py3-none-any.whl
| Download URL | ansible_flow_mcp-0.1.3-py3-none-any.whl |
|---|---|
| Size | 34.2 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
51fa7612ec5d4334cccd8bc03581c0fb1415c8ef5425d50860e1a86ef7a4837f
|
|
BLAKE2b-256 checksum How to use checksums |
52793845067173bb5ee2b4b679ddfdb34b30f602f0a6289a1c1acb685316adfa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 10, 2026.
Transparency log