Skip to main content
Glama
nestordiaz-one

ams-odoo-mcp-connector

README.md
# AMS Odoo MCP Connector

Python **MCP (Model Context Protocol)** server that gives an AI assistant
(such as Claude) access to an **Odoo** instance: **read** business data and -**only
with human approval**- **create** records. It is a standalone intermediary service
(not a module inside Odoo) and *instance-agnostic*: it works against any Odoo
18/19 by changing only configuration.

> Each person runs **their own copy** with **their own Odoo credentials**.
> No credentials travel in this repository.

For business context and design principles, see [`CLAUDE.md`](CLAUDE.md).

---

## 1. Requirements

- **Python 3.11+** (tested on 3.13)
- **git**
- An **Odoo 18/19** instance reachable from your machine and an **API user** (starting with read-only permissions is ideal).
- Optional: **Docker** (for the containerized connection checks).

---

## 2. Quickstart

```bash
git clone <REPO-URL> ams-odoo-mcp-connector
cd ams-odoo-mcp-connector
```

Create the virtual environment and install the package:

**Windows (PowerShell)**
```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e ".[dev]"
```

**macOS / Linux**
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```

---

## 3. Configure your credentials

Copy the template and edit it with **your** Odoo details:

```bash
cp .env.example .env
```

| Variable | Required | Description |
|----------|-----------|-------------|
| `ODOO_URL` | yes | Base URL of your Odoo instance. |
| `ODOO_DB` | yes | Database name. |
| `ODOO_USER` | yes | API user. |
| `ODOO_PASSWORD` | yes | Password / API key. |
| `ODOO_MCP_APPROVAL_SECRET` | for writes | Secret that signs the approval gate. Without it, record creation is disabled. |
| `ODOO_VERSION_OVERRIDE` | no | Pins the major version (e.g. `18`) and skips autodetection. |
| `ODOO_MCP_CONFIG_DIR` | no | Directory for the allowlists (defaults to `config`). |
| `ODOO_MCP_LOG_LEVEL` | no | Log level (to **stderr**). `INFO` by default. |

Generate your own `ODOO_MCP_APPROVAL_SECRET` (required to create records):

```bash
python -c "import secrets; print(secrets.token_urlsafe(48))"
```

> The `.env` is git-ignored: it is **yours and local**. Never commit it.

---

## 4. Verify that everything loads

```bash
ams-odoo-mcp-config        # prints your config with secrets masked
```

If you have connectivity to Odoo, run the **read-only connection check**:

```bash
python smoke_test.py       # login + version + a harmless search_read. Writes NOTHING.
```

Expected success output: `OK Handshake correct`.

---

## 5. Connect it to Claude

The assistant launches the server as a local process (stdio). It carries no credentials:
the server reads your `.env` on its own.

### Claude Desktop

Edit the configuration file:

- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
  *(in the Microsoft Store version, the real path is under
  `...\Packages\Claude_*\LocalCache\Roaming\Claude\claude_desktop_config.json`)*
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`

Add (or merge) this block, with **your** paths:

```json
{
  "mcpServers": {
    "ams-odoo": {
      "command": "RUTA-A-TU/.venv/Scripts/python.exe",
      "args": ["-m", "odoo_mcp.server"],
      "cwd": "RUTA-ABSOLUTA-A-ESTE-REPO"
    }
  }
}
```

*(on macOS/Linux the `command` is `RUTA-A-TU/.venv/bin/python`.)*

Quit Claude Desktop **completely** (tray icon -> Quit) and reopen it.

### Claude Code

```bash
cp .mcp.json.example .mcp.json
```

Edit `.mcp.json` with the path to your venv Python and to this repo. When you open the
project, Claude Code will ask you to approve the server.

---

## 6. Try it

In a Claude chat, ask for example:

- *"List the modules installed in Odoo."* -> uses `odoo_list_modules`.
- *"Find 3 sales orders with their customer."* -> uses `odoo_search_read`.
- *"Schedule an installation."* -> `odoo_create_record` will return a **proposal**
  for you to approve; **nothing is created** until you confirm.

---

## The three tools

| Tool | What it does | Type |
|------|----------|------|
| `odoo_search_read(model, domain?, fields?, limit?, offset?)` | Reads records from an allowed model, with bounded pagination. | Read |
| `odoo_list_modules()` | Lists the modules installed in the instance. | Read |
| `odoo_create_record(model, values, approval_token?)` | Creates a record **after human approval** (two-phase gate). | Write |

**Write gate:** the first call (without `approval_token`) returns a
PROPOSAL with a signed token and **creates nothing**; the second, with that token
approved by a human, is the only one that executes the `create`. The token is signed
(it cannot be forged), bound to the exact values, and expires in 5 minutes.

---

## Governance and security

- **Strict allowlists** in `config/`: only the listed models can be read/created.
  The write list starts scoped to `calendar.event`.
- **Human-in-the-loop** for all writes (gate + signed token).
- **Secrets only in configuration** (never in code, logs, or Docker image).
- **Never assume:** if it cannot confirm something in Odoo, it says so; a permissions
  problem is never reported as "the data does not exist".

---

## Architecture (`odoo_mcp` package, in `src/`)

| Module | Responsibility |
|--------|-----------------|
| `config.py` | Typed config from environment/`.env` with *fail-fast*; finds `.env` and `config/` without relying on the cwd. |
| `session_manager.py` | Stateless JSON-RPC transport, authentication, re-login, safe connection backoff. |
| `odoo_client.py` | High-level `execute_kw` with re-login -> retry cycle. |
| `discovery.py` | Instance version and installed modules; availability of a model. |
| `governance.py` | Allowlists, field validation, pagination cap, availability cache. |
| `errors.py` | Error hierarchy + `translate()` to actionable natural language. |
| `approval.py` | HMAC token for the write gate (propose->commit). |
| `tools.py` | Logic of the 3 tools (independent of the MCP runtime). |
| `server.py` | MCP wiring (stdio), lifespan, tool registration. |

---

## Development

```bash
pytest                    # 68 tests (use mocks; no Odoo required)
ruff check src tests      # lint
```

### Containerized connection checks (Docker, hardened)

Credentials are injected from your `.env`; they do not live in the image. The container
runs non-root, read-only rootfs, no capabilities, no privilege escalation.

```bash
docker compose build smoke
docker compose run --rm smoke                       # read-only: login + version + search_read
docker compose run --rm smoke python write_test.py  # controlled write: create -> verify -> delete
```

---

## Troubleshooting

| Symptom | Likely cause / fix |
|---------|--------------------------|
| `Missing required configuration variables` | The `.env` is missing or wrong. Check with `ams-odoo-mcp-config`. |
| `Odoo rejected the credentials` | Incorrect `ODOO_USER` / `ODOO_PASSWORD` / `ODOO_DB`, or no login permission. |
| `Could not communicate with the Odoo instance` | Network/VPN/firewall/URL: the instance is not reachable from your machine. |
| Claude Desktop shows "failed" | Check the `command`/`cwd` path in the JSON and fully restart Desktop. Logs: `%APPDATA%\Claude\logs\mcp-server-ams-odoo.log`. |
| Creation says "disabled" | `ODOO_MCP_APPROVAL_SECRET` is missing from your `.env`. |

---

(c) 2026 onePhase - Proprietary. See [`LICENSE`](LICENSE).

TDQS

A4.3/5.0

Scored across 3 tools

Disambiguation5/5

Each tool targets a distinct operation: reading records, listing modules, and creating records with an approval flow. There is no overlap in purpose or ambiguous boundaries between them.

Naming Consistency5/5

All tool names follow the consistent pattern 'odoo_' + verb_noun (search_read, list_modules, create_record). The naming is uniform and predictable, despite mixing verbs like 'search' and 'list'.

Tool Count4/5

With only 3 tools, the server is on the lean side but still within a reasonable range for a focused connector. Each tool provides a distinct function, though the count is minimal.

Completeness2/5

The tool surface lacks essential CRUD operations such as update and delete, which are common in Odoo data access. While read and create are covered, missing write operations and additional search capabilities create significant gaps for a general-purpose connector.

Maintenance

ActivityMaintained
ResponsivenessSyncing