Metadata-Version: 2.4
Name: rocannon
Version: 0.1.0
Summary: Every Ansible module as a typed MCP tool
Keywords: ansible,mcp,model-context-protocol,automation,ibm-z,zos
Author: Adam Munawar Rahman
Author-email: Adam Munawar Rahman <msrahmanadam@gmail.com>
License-Expression: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Dist: fastmcp>=3.4.2
Requires-Dist: prompt-toolkit>=3.0.52
Requires-Dist: typer>=0.26.7
Requires-Dist: pydantic>=2.13.4
Requires-Dist: pyyaml>=6.0.3
Requires-Dist: litellm>=1.89.0 ; extra == 'ai'
Requires-Dist: ansible-core>=2.21.0 ; extra == 'all'
Requires-Dist: ansible-runner>=2.4.3 ; extra == 'all'
Requires-Dist: litellm>=1.89.0 ; extra == 'all'
Requires-Dist: opentelemetry-sdk>=1.42.1 ; extra == 'all'
Requires-Dist: opentelemetry-exporter-otlp>=1.42.1 ; extra == 'all'
Requires-Dist: ansible-core>=2.21.0 ; extra == 'ansible'
Requires-Dist: ansible-runner>=2.4.3 ; extra == 'ansible'
Requires-Dist: opentelemetry-sdk>=1.42.1 ; extra == 'otel'
Requires-Dist: opentelemetry-exporter-otlp>=1.42.1 ; extra == 'otel'
Requires-Python: >=3.12
Project-URL: Homepage, https://github.com/msradam/rocannon
Project-URL: Repository, https://github.com/msradam/rocannon
Project-URL: Issues, https://github.com/msradam/rocannon/issues
Provides-Extra: ai
Provides-Extra: all
Provides-Extra: ansible
Provides-Extra: otel
Description-Content-Type: text/markdown

<h1 align="center">Rocannon</h1>

<p align="center">
  <img src="https://raw.githubusercontent.com/msradam/rocannon/main/docs/assets/gryphon.svg" alt="" width="120">
</p>

Rocannon is an MCP server that registers every installed Ansible module as a
typed tool. It reads `ansible-doc -j` for each module at startup and builds a
Pydantic-validated function signature, then exposes the result over the MCP
protocol (stdio or HTTP). Any MCP client (Claude Code, Cursor, mcphost,
custom agents) calls the same tools an operator would call from a REPL.

Each registered tool carries the module's own `ansible-doc` metadata: a JSON
output schema for structured results, and MCP safety hints derived from the
module's attributes (read-only for fact modules, destructive and open-world for
`command`, `shell`, `script`, and `raw`).

Every module is also a top-level CLI subcommand:

```
rocannon ansible.builtin.command --target h1 --cmd 'uptime'
rocannon ansible.builtin.copy    --target h1 --src /etc/hosts --dest /tmp/h
```

Append `--record path/to/runbook.yml` to any invocation and Rocannon writes
each call as a new play in a real Ansible playbook. The resulting file runs
directly under `ansible-playbook -i <inv> path/to/runbook.yml`.

Add `--check` to preview a change without applying it (Ansible check mode) and
`--diff` to see what would change. Each is offered per module according to its
declared check-mode support, both on the CLI and as a parameter on the matching
MCP tool. `rocannon playbook run <name> --check` previews an entire saved runbook.

Sessions driven via the MCP server save the same way: as Ansible playbooks
under `.rocannon/playbooks/`. Rocannon also loads them back on next startup
as MCP prompts.

![demo](https://raw.githubusercontent.com/msradam/rocannon/main/docs/assets/demo.gif)

## Install

```bash
pip install 'rocannon[ansible]'
```

`ansible-doc` and `ansible-runner` must be on PATH. `rocannon doctor` reports
anything missing.

## Quickstart

The quickstart profile targets `localhost` with `ansible_connection=local`.

```bash
cd examples/quickstart
rocannon mcp doctor --profile profile.yml   # list registered tools
rocannon repl       --profile profile.yml   # operator shell
```

Inside the REPL:

```
rocannon> .target localhost
rocannon> ping
rocannon> command cmd="uptime"
rocannon> .save my_session
rocannon> .exit
```

`.save` writes `.rocannon/playbooks/my_session.yml` as a standard Ansible
playbook. Run it directly with `ansible-playbook -i hosts my_session.yml`, or
let Rocannon load it back as an MCP prompt next time the server starts.

## CLI

```
rocannon <fqcn>        invoke a module: rocannon ansible.builtin.copy ...
                       optional --record FILE appends each call to a playbook
rocannon mcp serve     start the MCP server (stdio or http)
rocannon mcp doctor    list registered tools, resources, prompts
rocannon repl          interactive shell on the same MCP server
rocannon run           legacy ad-hoc form (module FQCN + -a key=value)
rocannon doctor        system health (binaries, env, inventory)
rocannon doc <module>  print parsed schema for a module
rocannon search <q>    find modules by name or description
rocannon ls            list hosts/groups/modules from a profile
rocannon playbook      list/show/run saved playbooks
```

Per-module help (typed flags, defaults, descriptions from `ansible-doc`):
`rocannon ansible.builtin.copy --help`. Modules that support check mode also
accept `--check` and `--diff`.

## Profiles

A profile is a YAML file declaring inventory + modules:

```yaml
inventories:
  - ./hosts
modules:
  - ansible.builtin
  - ibm.ibm_zos_core
ansible_cfg: ./ansible.cfg          # optional
vault_password_file: ~/.vault_pass  # optional
extra_envvars:                      # optional
  ZOAU_HOME: /usr/lpp/IBM/zoautil
```

`modules` accepts a specific module (`ansible.builtin.copy`), a collection
(`ansible.builtin`), or a namespace (`community`).

Multiple profiles can live under `.rocannon/profiles/`:

```
.rocannon/profiles/
├── box1.yml
├── box2.yml
└── default.yml -> box1.yml
```

```bash
rocannon mcp serve                     # uses default.yml
rocannon mcp serve --profile box2
```

The active profile can be switched at runtime via three MCP tools:
`rocannon_list_profiles`, `rocannon_current_profile`, `rocannon_use_profile`.
The tool surface is the union of every profile's modules; a call to a module
that isn't in the active profile returns a structured error pointing at
`rocannon_use_profile`.

## MCP clients

A working `.mcp.json` ships at the repo root. Per-client snippets are in
[`examples/clients/`](examples/clients/).

| Client | Config location |
|---|---|
| Claude Code | `.mcp.json` at project root, or `claude mcp add` |
| Claude Desktop | macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Cursor | `.cursor/mcp.json` or `~/.cursor/mcp.json` |
| mcphost | `~/.mcphost.yml` or `--config <path>` |
| IBM Bob | `.bob/mcp.json` or `~/.bob/mcp_settings.json` |

All share the standard `mcpServers` envelope pointing at
`rocannon mcp serve --profile <your-profile.yml>`.

## Development

```bash
git clone https://github.com/msradam/rocannon.git
cd rocannon
uv sync
./tests/check.sh                # ruff format + lint + mypy + pytest
uv run pytest -m integration    # opt-in: spins up a real UBI9 container
```

See [`ARCHITECTURE.md`](ARCHITECTURE.md) for how the pieces fit together.

Rocannon is developed with AI assistance.

## The name

Ursula K. Le Guin coined the word "ansible" in her 1966 novel *Rocannon's
World*. The gryphon is a nod to the Windsteeds that Rocannon and his
companions ride.

## Credits

- Gryphon icon: [Gryphon by Aleksei Kovalenko from Noun Project](https://thenounproject.com/icon/gryphon-7096619/) (CC BY 3.0).
