# Oduflow

> AI-first Odoo development and CI tool powered by reusable database templates. Provisions isolated, ephemeral Odoo environments on Docker — one per git branch — and exposes them to AI coding agents via MCP.

## Installation

### System Requirements

- Docker (Docker Engine or Docker Desktop)
- Python 3.10+
- Git
- fuse-overlayfs (for filestore overlay mounting; not needed on macOS)

### Install

Recommended — install via [uv](https://docs.astral.sh/uv/):

```bash
uv tool install oduflow
```

Alternative — install via pip:

```bash
pip install oduflow
```

### Configure

All settings are configured via `oduflow.toml`. Oduflow searches `ODUFLOW_TOML`, then `/etc/oduflow/oduflow.toml`, then `~/.oduflow/conf/oduflow.toml`.

Minimal configuration:

```toml
[team.1]
hostname = "localhost"
```

Full configuration reference:

```toml
[server]
host = "0.0.0.0"                     # HTTP bind address
port = 8000                           # HTTP port

[routing]
mode = "port"                         # "port" | "traefik" (auto-HTTPS)
# acme_email = "admin@example.com"    # required for traefik mode

[oauth]
# oauth_base_url = "https://oduflow.example.com"  # OAuth issuer; NOT needed in traefik (auto, per-team host). Set to pin an issuer or in port mode. OAuth client_id = team_<id> (non-secret); auth_token = client_secret and also works as a Bearer token

[database]
user = "odoo"
# password = "..."                    # auto-generated on first init; set to override
image = "postgres:15"

[storage]
# data_dir = "/srv/oduflow"           # default: /srv/oduflow or ~/.oduflow/data
overlay_threshold_mb = 50             # filestore size threshold for overlay vs copy

[lifecycle]
auto_stop_hours = 48                  # auto-stop after N hours without MCP/dashboard work; 0 disables
auto_delete_hours = 0                 # auto-delete N hours after stop; 0 disables (opt-in; DESTRUCTIVE)

# Per-team coding agent (dashboard Agent Chat / Agent CLI); opt-in, off by default.
# [agent]
# image = "oduist/oduflow-coder:0.2.3"

[team.1]
hostname = "localhost"
auth_token = ""                       # auto-filled in fresh configs; HTTP MCP Bearer token / OAuth client_secret
ui_password = ""                      # auto-filled in fresh configs; Web UI password for admin
port_range = [50000, 50100]           # port range for Odoo containers
# agent_enabled = false               # enable the per-team coding agent (Agent Chat / Agent CLI)
# agent_default = "claude"            # "claude" | "codex" — default agent for consoles/chats
# [team.1.agent_env]                  # provider credentials injected into the agent container
# CLAUDE_CODE_OAUTH_TOKEN = ""
# ANTHROPIC_API_KEY = ""
# OPENAI_API_KEY = ""
```

### Initialize

No separate init step is needed. On first launch Oduflow automatically creates a default `oduflow.toml` at `/etc/oduflow/oduflow.toml` when writable, otherwise at `~/.oduflow/conf/oduflow.toml`, and initializes shared infrastructure (Docker network, PostgreSQL, team directories). Fresh configs include generated `[database].password`, `[team.1].auth_token`, and `[team.1].ui_password`; the MCP token and Web Dashboard password are also printed in the startup log.

### Upgrade

```bash
uv tool upgrade oduflow
```

During upgrade, bundled files are overwritten. Add `# KEEP` as the first line of any customized file to prevent it from being overwritten.

### Set up a template

```bash
# From scratch
oduflow init-template --odoo-image odoo:19.0 --template-name default

# From production dump
# Place dump.sql and filestore/ into {data_dir}/team_1/templates/default/ then:
oduflow reload-template default

# Sync template from S3 or local path and reload DB
oduflow reload-template default --source s3://mybucket/prod/ [--quiet]
oduflow reload-template default --source /backups/prod-latest/
```

### Start the MCP server

```bash
oduflow --transport http
# or: oduflow -t http
```

For HTTP mode, the server starts on `http://0.0.0.0:8000`. MCP endpoint: `http://<host>:8000/mcp`; send `Authorization: Bearer <auth_token>` using the value from `oduflow.toml`. The Web Dashboard is at `http://<host>:8000/`; sign in as `admin` with `ui_password`.

## MCP Client Configuration

### Cursor / Windsurf

`.cursor/mcp.json` or `.windsurf/mcp.json`:

```json
{
  "mcpServers": {
    "oduflow": {
      "type": "http",
      "url": "https://<your-oduflow-host>/mcp",
      "headers": {
        "Authorization": "Bearer <your-token>"
      }
    }
  }
}
```

### Claude Desktop / Amp

Same JSON format in `claude_desktop_config.json` or `.amp/settings.json`.

### Claude.ai (self-hosted OAuth)

For OAuth-based MCP clients like Claude.ai Remote MCP, Oduflow runs its own OAuth 2.1 Authorization Server (no external IdP). In **traefik mode** it is enabled automatically and runs on each team's own hostname (issuer derived per-request) — no `oauth_base_url` needed. In **port mode**, set `[oauth].oauth_base_url` to this instance's public URL. In Claude.ai add a custom MCP at `https://<team-hostname>/mcp` and enter `Client ID = team_<id>` (e.g. `team_1`, non-secret) and `Client Secret = the team's auth_token`. The OAuth flow issues an independent expiring access token; the configured `auth_token` also works directly as a plain Bearer token.

## Core MCP Tools

- `create_environment` — provision a new Odoo environment for a branch (optional `env_vars` injects container environment variables)
- `delete_environment` — tear down an environment
- `start_environment` / `stop_environment` / `restart_environment` — lifecycle control
- Idle environments auto-stop after 48h without work (`[lifecycle].auto_stop_hours`). Auto-delete of long-stopped environments is opt-in and off by default (`auto_delete_hours = 0`; set a positive value to enable — destructive; protected environments are exempt). Container-level tools (pull_and_apply, shell/tests/installs/file ops) wake a stopped environment automatically and note it in the response
- `update_environment` — re-create the container preserving DB and filestore (optional `odoo_image` switches image, `env_vars` replaces container environment variables)
- `install_odoo_modules` — install Odoo modules
- `upgrade_odoo_modules` — upgrade Odoo modules
- `run_odoo_tests` — run Odoo tests for specific modules
- `pull_and_apply` — pull latest code and auto-install/upgrade/restart as needed
- `get_environment_logs` — retrieve container logs
- `run_odoo_command` — execute shell commands inside the Odoo container
- `run_odoo_shell` — execute Python code in the Odoo shell with full ORM access
- `read_file_in_odoo` — read a text file or list a directory inside the container (supports line ranges)
- `write_file_in_odoo` — write a text file inside the container (CSV imports, scripts, configs)
- `search_in_odoo` — search for a pattern (fixed-string grep) in files inside the container
- `http_request_to_odoo` — make an HTTP request to the running Odoo instance (controllers, JSON-RPC, REST)
- `list_installed_modules` — list Odoo modules and their states with name/state filtering
- `run_db_query` — execute SQL queries against the environment's PostgreSQL database
- `reset_admin_password` — reset the admin user password (default: "test")
- `connect_as_user` — mint a passwordless Odoo login session for a user and return the `session_id` cookie + URL (Playwright-ready; skips the login form, supports any role incl. portal)
- `read_output` — read from a cached tool output by ID (paginate, grep, errors, tail)
- `list_environments` / `get_environment_info` — inspect environments
- `create_service` / `delete_service` / `restart_service` / `update_service` / `list_services` / `get_service_info` / `get_service_logs` / `run_service_command` — manage auxiliary services
  - In Traefik TLS mode every service implicitly receives the exact `oduflow-traefik-acme:/etc/traefik:ro` mount; do not pass or override that system volume
- `create_volume` / `list_volumes` / `inspect_volume` / `delete_volume` — manage Docker volumes
- `read_file_in_volume` / `write_file_in_volume` / `search_in_volume` / `delete_file_in_volume` — manage files inside Docker volumes
- `list_service_presets` / `restore_service` / `delete_service_preset` — manage service presets
- `save_as_template` / `delete_template` / `list_templates` — template management
- `import_template_from_odoo` — import a template from a running Odoo instance; optional `without_filestore` imports database-only
- `refresh_template` — re-apply a template's filestore to live overlay environments (preserves env changes by default; `reset_env_changes=True` is destructive)
- `attach_filestore` — attach/replace a template filestore from a local dir, archive, `rsync://`, or SSH rsync source; preserves env changes by default
- `setup_repo_auth` — cache git credentials for private repositories
- `add_extra_repo` / `list_extra_repos` / `update_extra_repo` / `delete_extra_repo` — manage extra addons repositories
- `get_agent_instructions` — get AI agent instructions for using Oduflow
- `get_odoo_development_guide` — get Odoo development standards guide for a specific version (15–19)

## Coding Agent (hosting)

An opt-in, per-team hosting feature: Oduflow runs one coding-agent container per team (`oduist/oduflow-coder`, Claude Code + OpenAI Codex) and exposes two dashboard surfaces — **Agent CLI** (the agent's TUI in the browser) and **Agent Chat** (a browser ACP chat with per-environment conversation history). The agent edits its own git checkout, `git push`es, and drives the environment through the Oduflow MCP server with a scoped per-environment token. A built-in Agent Browser MCP and Chromium provide browser automation to both agents, with one persistent profile per environment. Codex runs installed MCP methods without interactive approval prompts. It is off by default; enable it per team with `agent_enabled` and set provider credentials under `[team.X.agent_env]`. The agent UI is hidden for live-mount (`local_path`) environments.

## Database Sanitization

Template-based environments are neutralized by default, then run team-level
sanitization scripts followed by project scripts from
`.oduflow/odoo_sanitize/`. Both SQL and Python scripts are supported.

## Typical Agent Workflow

1. Call `list_environments` to check if an environment for the branch exists
2. If not, call `create_environment` with `branch`, `template_name`, `repo_url`, and `odoo_image`
3. Write code, `git push`, then call `pull_and_apply` (auto-detects what to do)
4. Use `install_odoo_modules` / `run_odoo_tests` / `get_environment_logs` to verify
5. Call `delete_environment` when the task is done

## Links

- Repository: <https://github.com/oduist/oduflow>
- Documentation: <https://docs.oduflow.dev>
- License: BUSL-1.1 (Business Source License 1.1) — free for non-commercial use; commercial use requires a paid license; converts to MPL 2.0 four years after publication
- Website: <https://oduflow.dev>
