| name | odoo-hermes-environment-setup |
| description | Provision an AI agent for Odoo dev on a fresh host, reusably |
| version | 1.0.0 |
| author | Vinay Rana |
| category | infrastructure |
| odoo_versions | ["17","18","19"] |
| tags | ["hermes","setup","provisioning","docker-sandbox","mcp","reusable","onboarding","multi-tenant"] |
Odoo + Hermes Environment Setup (Reusable Playbook)
Provision a fresh host (VPS, workstation, or CI runner) with everything needed
for AI-agent-driven Odoo 17/18/19 custom module development, sourced from
infovpcs/odoo-agent-pro-kit. Works with Hermes profiles today; the same
steps generalize to any AI agent/IDE that reads a skills directory and can
run shell commands (Claude Code, Cursor, Codex, Copilot, etc.) — substitute
that agent's skill-loading mechanism for Hermes's skills/<category>/ copy
step and everything else applies unchanged.
Use this skill when a user says things like "set up Hermes/an agent for
Odoo dev on this server", "give me a repeatable Odoo agent environment for
client X", or "replicate the odoo-agent-pro-kit setup on a new box".
0. Before you start — decide the model
Ask (or infer from context) which Odoo execution model this environment uses.
Do not guess; the two models are architecturally incompatible on the same
profile without extra wiring (see step 6):
- Docker Sandbox (recommended, default): every dev session gets an
isolated, ephemeral Odoo + PostgreSQL via
sandboxctl/sbx, torn down
after. No persistent Odoo daemon. Matches this repo's Phase 0–7 design.
- Persistent Odoo instances: always-on Odoo 17/18/19 (like a classic
dev server),
odoo_mcp servers always running on fixed ports 8765/8766/8767.
This playbook defaults to Docker Sandbox. For persistent instances, skip
step 5 (Docker Sandbox validation) and instead start Odoo + plugin/odoo_mcp/ start_mcp_server.sh --all as long-running services (systemd/supervisor).
1. Host prerequisites
Verify before installing anything:
git --version && python3 --version && curl --version
Install base deps if missing (Ubuntu example — adapt package manager per OS):
sudo apt-get update -y
sudo apt-get install -y git curl build-essential python3 python3-venv python3-pip ca-certificates
For Docker Sandbox mode, also confirm:
which sbx && sbx diagnose
ls -l /dev/kvm
If sbx diagnostics fail or /dev/kvm is missing, this host cannot run
Docker Sandbox — fall back to the persistent-instance model or a different
host. Do not silently downgrade the plan.
2. Install Hermes
curl -LsSf https://astral.sh/uv/install.sh | sh
source "$HOME/.local/bin/env" 2>/dev/null || export PATH="$HOME/.local/bin:$PATH"
mkdir -p ~/.hermes
git clone --depth 1 https://github.com/NousResearch/hermes-agent.git ~/.hermes/hermes-agent
cd ~/.hermes/hermes-agent
yes "" | ./setup-hermes.sh
Verify:
hermes --version
hermes status
3. Create one profile per Odoo version (or per customer project)
Repeat per version, or use the version as a namespace within a
customer-specific profile name (e.g. acme-odoo19) for multi-tenant setups:
hermes profile create odoo17-dev --description "Odoo 17.0 custom module developer: coding standard, dependency context, backend/frontend testing, Docker Sandbox lifecycle for version 17."
hermes profile create odoo18-dev --description "Odoo 18.0 custom module developer: ... version 18."
hermes profile create odoo19-dev --description "Odoo 19.0 custom module developer: ... version 19."
Set the profile's default version (bridged to env for skills/tools; the
"not a recognized config key" warning is expected and harmless):
hermes -p odoo17-dev config set env.DEFAULT_ODOO_VERSION "17.0"
hermes -p odoo18-dev config set env.DEFAULT_ODOO_VERSION "18.0"
hermes -p odoo19-dev config set env.DEFAULT_ODOO_VERSION "19.0"
Each hermes profile create auto-seeds ~82 bundled skills and a shell
wrapper in ~/.local/bin/<profile-name> (e.g. odoo19-dev chat).
4. Get the odoo-agent-pro-kit source and load its skills
git clone https://github.com/infovpcs/odoo-agent-pro-kit.git ~/odoo-agent-pro-kit
As of 0.3.0, plugin/ ships a real native Hermes plugin manifest
(plugin/plugin.yaml + plugin/__init__.py, alongside the pre-existing
Claude-Code-style .claude-plugin/plugin.json — Hermes reads the former,
Claude Code the latter, from the same directory). plugin/plugin.yaml
declares manifest_version: 1 — the bundled Hermes v0.20.3 installer's
_SUPPORTED_MANIFEST_VERSION caps at 1 even though the plugin loader
already understands v2 fields (api_version, tags,
python_dependencies, config_schema, license, homepage all still
parse correctly under a manifest_version: 1 declaration) — declaring 2
made hermes plugins install hard-refuse with "requires manifest_version
2, but this installer only supports up to 1" on any Hermes version that
hasn't run hermes update past that installer-side cap. Use hermes plugins install per profile — this is now the preferred, verified path:
for p in odoo17-dev odoo18-dev odoo19-dev; do
hermes -p "$p" plugins install ~/odoo-agent-pro-kit/plugin --enable
done
Local absolute paths need the file:// scheme + #subdir fragment, not
a bare filesystem path. hermes plugins install <identifier> treats any
identifier without a recognized URL scheme as owner/repo[/subdir]
GitHub shorthand — a bare ~/odoo-agent-pro-kit/plugin or
/home/ubuntu/odoo-agent-pro-kit/plugin gets misread as
owner=home, repo=ubuntu (or similar) and tries to clone a bogus
https://github.com/<owner>/<repo>.git, failing with fatal: could not read Username for 'https://github.com': terminal prompts disabled. The
verified working form is:
hermes -p "$p" plugins install "file://$HOME/odoo-agent-pro-kit#plugin" --enable
i.e. file://<absolute-repo-root>#<subdir-within-repo>. This clones the
local repo (via git clone file://..., so the repo must have at least one
commit — a plain uncommitted working-tree edit is invisible to this path)
and installs only the plugin subdirectory.
Local-path installs skip the security scanner's git-provenance checks
but still run static content scanning, and this repo's own skills
content (subprocess/shell examples, sudo systemctl start postgresql,
allowed-tools frontmatter, etc.) reliably trips a "dangerous" verdict —
even --force does not override "dangerous" (only "caution"). Work
around it per install by setting scan_on_install: false in that
profile's ~/.hermes/profiles/<profile>/config.yaml before installing,
then setting it back to true immediately after:
CFG=~/.hermes/profiles/"$p"/config.yaml
sed -i 's/scan_on_install: true/scan_on_install: false/' "$CFG"
hermes -p "$p" plugins install "file://$HOME/odoo-agent-pro-kit#plugin" --enable < /dev/null
sed -i 's/scan_on_install: false/scan_on_install: true/' "$CFG"
Note --enable on a scanner-bypassed non-interactive install prints
"enabled" but installing fresh does NOT set plugins.enabled the same way
hermes plugins enable <name> does on an existing install — always
confirm afterward with hermes -p "$p" plugins list --plain --no-bundled | grep odoo-agent-pro-kit and expect the enabled state column, running
hermes -p "$p" plugins enable odoo-agent-pro-kit again if it still shows
not enabled.
This registers, per profile, in one step: 7 odoo_* model-discovery tools
(in-process — no separate MCP server/port needed for a Hermes session), the
/plan-analysis//start-coding//testing//fleet slash commands, an
on_session_start hook (Odoo workspace / sandbox session detection banner)
and on_session_end hook (closes pooled Odoo connections), and all 20
bundled skills under the odoo-agent-pro-kit: namespace (e.g.
skill_view("odoo-agent-pro-kit:CommandingSystem")).
Verify before trusting an install — run this from the repo clone, before or
after installing into any profile:
hermes plugins doctor ~/odoo-agent-pro-kit/plugin --ci
Expect OK: runtime discovery, manifest parsing, import, and registration passed and zero warnings (a python_dependencies entry without an upper
bound is a real warning to fix, not noise — pin it like the shipped manifest
does). The plugin registers 7 tools and 5 hooks as of 0.5.0 — pre_tool_call
and post_tool_call were added for the deterministic pipeline guardrails /
version-aware linter (alongside on_session_start, on_session_end,
post_api_request). Verified on the Oracle KVM host with Hermes 0.20.4:
hermes plugins doctor odoo-agent-pro-kit --ci reports 7 tool(s), 5 hook(s)
in all three profiles. Then confirm live in a profile:
hermes -p odoo19-dev plugins list | grep odoo-agent-pro-kit
Legacy fallback (pre-0.3.0 repos, or if native install is refused for
policy reasons): copy the skills directly into each profile as a category
folder — this still works and needs no plugin registration at all, but
loses the native tools/commands/hooks:
for p in odoo17-dev odoo18-dev odoo19-dev; do
DEST=~/.hermes/profiles/$p/skills/vpcs-odoo-project
mkdir -p "$DEST"
cp -r ~/odoo-agent-pro-kit/plugin/skills/* "$DEST/"
done
Symlink each profile's workspace to the pro-kit repo so skills that expect
sandbox/, odoo_local_setup/, etc. relative to CWD resolve correctly —
needed for both the native-plugin and legacy-copy paths:
for p in odoo17-dev odoo18-dev odoo19-dev; do
WS=~/.hermes/profiles/$p/workspace
rmdir "$WS" 2>/dev/null || true
[ -e "$WS" ] || ln -s ~/odoo-agent-pro-kit "$WS"
done
If using the legacy copy path, verify all 19 skills loaded and enabled:
hermes -p odoo19-dev skills list 2>&1 | grep vpcs-odoo-project
Expect: CommandingSystem, OdooTools17/18/19, Odoo{17,18,19}CodingStandard,
Odoo{17,18,19}ExistingDependencyContext, DockerSandboxOperations,
Odoo_Custom_Backend_Testing, Odoo_Custom_Frontend_Testing,
Odoo_Custom_App_Install_Update, OdooRestartUpgradeRules, PRD-Writing,
excalidraw-diagram-skill, Agent-browser-skill,
Odoo_Module_Documentation_Screenshot — 19 entries, all enabled.
For a different AI agent/IDE (not Hermes): point that agent's
skill/context loader at ~/odoo-agent-pro-kit/plugin/skills/ and
plugin/commands/ directly per its own convention (e.g. Claude Code reads
.claude-plugin/plugin.json natively — installing the whole plugin/ dir
as a Claude Code plugin works as-is there, alongside Hermes's own
plugin.yaml in the same directory).
5. Docker Sandbox: create, validate, and destroy one real session
Always do a real LIVE TEST after setup — never claim it works untested.
sandboxctl needs a private Docker daemon; it must run inside an sbx
microVM, not on the bare host (docker: command not found on the host is
expected and correct — it means you tried to run it in the wrong place).
sbx create --name <name> shell ~/odoo-agent-pro-kit
sbx exec <name> -- bash -lc "cd ~/odoo-agent-pro-kit && sandbox/bin/sandboxctl create --version 19 --module sandbox_fixture"
sbx exec <name> -- bash -lc "cd ~/odoo-agent-pro-kit && sandbox/bin/sandboxctl status <session-id>"
sbx exec <name> -- bash -lc "cd ~/odoo-agent-pro-kit && sandbox/bin/sandboxctl module <session-id> install sandbox_fixture"
Clean up disposable test sessions/sandboxes (the fixture is public, no real
work is lost):
sbx exec <name> -- bash -lc "cd ~/odoo-agent-pro-kit && sandbox/bin/sandboxctl destroy <session-id> --allow-unexported"
sbx rm --force <name>
destroy without --allow-unexported refuses on purpose (Phase 5 design:
require commit/push/export before destructive cleanup) — only pass
--allow-unexported for genuinely disposable test/fixture sessions, never
for a customer's real module work.
6. Wire odoo_mcp to a live sandbox session (chat-time Odoo RPC tools)
The pinned sandbox/compose/compose.yaml does NOT publish Odoo's port
outside the sandbox's private Docker network, and each sbx session runs
inside its own microVM's network namespace — so odoo_mcp (built to talk to
a host-reachable Odoo) cannot reach a sandboxed Odoo without extra wiring.
Do not edit the pinned sandbox/compose/compose.yaml — it is phase-gated
and validated; add an override instead.
This repo now ships that override at sandbox/mcp-sidecar/:
odoo_mcp_sidecar.Dockerfile — bakes plugin/odoo_mcp into an image
pinned to mcp[server]>=1.0.0,<2.0.0 (mcp 2.0.0 dropped the fastmcp
submodule the server imports; the build will fail with
ModuleNotFoundError: No module named 'mcp.server.fastmcp' if you forget
this pin — do not upgrade past 1.29.0 without also porting the server).
mcp.override.yaml — adds an mcp Compose service, restart: unless-stopped, in the SAME Compose project as db/odoo, connected to
Odoo via the internal service name http://odoo:8069.
mcp_up.sh <session-id> [port] — brings the sidecar up against an
existing session; auto-picks port 8765/8766/8767 by version unless
overridden.
Steps, run entirely inside the sbx exec shell after sandboxctl create:
sbx exec <name> -- bash sandbox/mcp-sidecar/mcp_up.sh <session-id>
Then, from OUTSIDE the microVM (on the sbx host):
sbx ports <name> --publish 8767:8767
Verify from the sbx host:
curl -sS -D - -o /dev/null http://127.0.0.1:8767/sse
Register in the matching profile's config.yaml so it's available inside a
hermes -p odoo19-dev chat session:
mcp_servers:
odoo19:
url: http://127.0.0.1:8767/sse
Pitfall — do NOT use a bare docker run for the MCP sidecar. sbx
microVMs auto-suspend when idle and cold-reboot on the next sbx exec. A
container started with ad-hoc docker run (outside the Compose project)
does not survive that reboot and exits nonzero (Exited (255)) with no
useful log — this was reproduced twice while building this skill. Only
containers registered as Compose services with restart: unless-stopped
in the session's own Compose project reliably come back, matching how
db/odoo already behave. Always use mcp_up.sh, never a raw docker run.
Pitfall — mcp package pin. pip install mcp alone resolves to the
latest (currently 2.0.0), which removed mcp.server.fastmcp. The pro-kit's
plugin/odoo_mcp/requirements.txt says mcp[server]>=1.0.0 with no upper
bound — this is stale and will break on a fresh install. Always pin
<2.0.0 (or update odoo_mcp_server.py to the new API and drop the pin)
until this is fixed upstream in the repo.
Repeat the whole step 6 flow per Odoo version, one session each, one sidecar
each on its own fixed port.
7. Persist your work
- Save a short setup-notes file per host (what's installed, what's pending,
exact commands used, verbatim test results) — do not rely on memory or
chat history surviving to the next session.
- If this is a customer/project-specific deployment, record the customer
name, host identity, and any deviations from this playbook (e.g. only 2
of 3 versions needed, custom addons paths, non-default ports).
- Do not commit secrets,
.env files, or runtime.env (mode 0600, contains
generated app + API credentials) anywhere.
8. Reusability checklist (verify before calling a setup "done")
Known gaps not yet solved by this playbook
- Repo-hosted install shorthand (
hermes plugins install infovpcs/odoo-agent-pro-kit/plugin) requires the plugin's git history to
actually be pushed and reachable; re-verify the GitHub-hosted shorthand
once 0.3.0 is tagged/released, since it clones fresh rather than using an
existing local checkout.
- (Resolved 2026-08-18) The
hermes plugins install local-path blocker is
closed: plugin/plugin.yaml now declares manifest_version: 1 (the
installer's _SUPPORTED_MANIFEST_VERSION cap, distinct from the loader's
v2 support), and local installs use file://<abs-repo-root>#plugin with
a per-install scan_on_install: false toggle — see step 4 above for the
full working commands. Installed and verified enabled on all 3 Oracle
VPS profiles (odoo17-dev/odoo18-dev/odoo19-dev) via hermes -p <profile> plugins list and hermes -p <profile> plugins doctor odoo-agent-pro-kit --ci. As of 0.5.0 the plugin registers 5 hooks
(on_session_start, on_session_end, post_api_request, pre_tool_call,
post_tool_call); re-verified on the Oracle KVM host with Hermes 0.20.4 —
doctor --ci reports 7 tool(s), 5 hook(s), registration OK, zero warnings,
in all three profiles. A direct
in-process call to odoo_get_version_info on odoo17-dev returned a
clean connection-refused error (no Odoo backend listening on
localhost:8069 on that host) rather than a registration/import error,
confirming the plugin is functionally wired, not just discoverable.
- Odoo knowledge-base repos are wired in as filesystem references (see
below), not through the native plugin's
ctx.register_skill() surface —
a future iteration could ship a searchable index as a plugin-bundled
skill instead of a raw grep target.
Knowledge-base sync (solved — do it this way)
Odoo knowledge-base repos (full official docs converted to Markdown, one
branch per version, git@github.com:infovpcs/Knowledge-Base.git branches
17.0/18.0/19.0) sync onto a target host and wire into profile skills
like this:
- Sync each version's working tree (no
.git, ~110-130MB per version) via
tar piped over SSH — do not rely on rsync between macOS (openrsync)
and a Linux host without a matching rsync binary installed; it silently
fails with "protocol version" / "unexpected end of file" errors:
tar --exclude='.git' -czf - -C <local-workspaces-dir> knowledge-<ver> | \
ssh -i <key> <user>@<host> \
"mkdir -p ~/odoo-knowledge-base && tar -xzf - -C ~/odoo-knowledge-base"
Lands at ~/odoo-knowledge-base/knowledge-<ver>/odoo<ver>-okf/ on the
target host (subdirs: contributing/, attachments/, developer/,
legal/, administration/, applications/).
- Wire it into each profile as a reference, not a copy: append a short
"Local Odoo Documentation Knowledge Base" section to the matching
Odoo<ver>ExistingDependencyContext/SKILL.md in every profile's
skills/vpcs-odoo-project/ directory, pointing at the synced path and
telling the agent to grep -rl it for offline/static API and framework
research, while still treating live MCP/shell dependency capture as the
source of truth for actual installed-module state.
- Verify with a real grep against the synced tree (not just a file-count
check) before declaring it wired, e.g.
grep -rl "compute" ~/odoo-knowledge-base/knowledge-19/odoo19-okf/developer/.