Skip to main content
Glama
plgonzalezrx8

odoo-mcp

README.md
# odoo-mcp

Docker-first FastMCP server for Odoo 19 JSON-2 integrations.

`odoo-mcp` exposes Odoo as an MCP server over both HTTP and stdio. It uses the
Odoo 19 external JSON-2 API only:

```text
POST /json/2/<model>/<method>
Authorization: bearer <ODOO_API_KEY>
X-Odoo-Database: <optional database>
```

Legacy XML-RPC and JSON-RPC are intentionally out of scope.

## Status

This is an early implementation with a strong v1 foundation:

- FastMCP `3.2.4`
- Docker Compose first
- HTTP and stdio transports
- lazy Odoo credential loading so MCP discovery works before secrets are present
- guarded write operations with `confirm=True`
- generic Odoo tools
- comprehensive CRM tool pack
- resources and prompts for safe Odoo work
- pytest, ruff, mypy, and GitHub Actions CI

## Quick Start With Docker

Copy the environment template and edit the Odoo values:

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

Required for real Odoo calls:

```bash
ODOO_URL=https://your-odoo-host.example.com
ODOO_API_KEY=your-odoo-api-key
ODOO_DATABASE=your-database-if-needed
```

Start the HTTP server:

```bash
docker compose up --build
```

The default MCP endpoint is:

```text
http://localhost:8000/mcp
```

Check local process readiness:

```bash
docker compose exec odoo-mcp uv run --no-sync odoo-mcp healthcheck
```

## Local Development

Install dependencies:

```bash
uv sync
```

Run the full gate:

```bash
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run mypy src
```

Run HTTP locally:

```bash
uv run odoo-mcp http --host 0.0.0.0 --port 8000 --path /mcp
```

Run stdio locally:

```bash
uv run odoo-mcp stdio
```

Inspect sanitized config:

```bash
uv run odoo-mcp inspect-config
```

## MCP Client Config

Stdio example:

```json
{
  "mcpServers": {
    "odoo": {
      "command": "uv",
      "args": ["run", "odoo-mcp", "stdio"],
      "env": {
        "ODOO_URL": "https://your-odoo-host.example.com",
        "ODOO_API_KEY": "your-odoo-api-key",
        "ODOO_DATABASE": "your-database-if-needed"
      }
    }
  }
}
```

HTTP clients should connect to `/mcp`. If the server is exposed beyond a trusted
network, set `MCP_AUTH_MODE=static` or `MCP_AUTH_MODE=jwt`.

## Configuration

| Variable | Purpose |
| --- | --- |
| `ODOO_URL` | Base URL for Odoo 19 JSON-2 calls. |
| `ODOO_API_KEY` | Odoo API key sent as `Authorization: bearer ...`. |
| `ODOO_DATABASE` | Optional Odoo database header. |
| `ODOO_TIMEOUT_SECONDS` | HTTP timeout for Odoo calls. Defaults to `30`. |
| `ODOO_ALLOWED_GENERIC_METHODS` | Comma-separated allowlist for otherwise blocked generic methods. |
| `ODOO_CRM_OPTIONAL_FEATURES` | Comma-separated optional CRM features to expose. |
| `MCP_AUTH_MODE` | `none`, `static`, or `jwt`. Defaults to `none`. |
| `MCP_STATIC_TOKEN` | Bearer token for `MCP_AUTH_MODE=static`. |
| `JWT_JWKS_URI` | JWKS URL for `MCP_AUTH_MODE=jwt`. |
| `JWT_PUBLIC_KEY` | Public key alternative for JWT verification. |
| `JWT_ISSUER` | Optional expected JWT issuer. |
| `JWT_AUDIENCE` | Optional expected JWT audience. |
| `JWT_REQUIRED_SCOPES` | Optional comma-separated JWT scopes. |
| `MCP_HTTP_HOST` | HTTP bind host. Defaults to `0.0.0.0`. |
| `MCP_HTTP_PORT` | HTTP bind port. Defaults to `8000`. |
| `MCP_HTTP_PATH` | MCP endpoint path. Defaults to `/mcp`. |
| `MCP_LOG_LEVEL` | HTTP server log level. Defaults to `info`. |

## Tool Catalog

Generic Odoo tools:

- `odoo_search_read`
- `odoo_read`
- `odoo_create`
- `odoo_write`
- `odoo_unlink`
- `odoo_action`
- `odoo_call_method`
- `odoo_current_user`
- `odoo_model_fields`
- `odoo_list_models`

CRM tools:

- leads and opportunities: `crm_list_leads`, `crm_get_lead`, `crm_create_lead`,
  `crm_update_lead`, `crm_assign_lead`
- pipeline: `crm_list_pipeline_stages`, `crm_move_lead_to_stage`,
  `crm_pipeline_report`
- won/lost lifecycle: `crm_mark_won`, `crm_mark_lost`, `crm_restore_lead`,
  `crm_list_lost_reasons`
- conversion and merge: `crm_convert_lead_to_opportunity`,
  `crm_merge_opportunities`
- activities: `crm_schedule_activity`, `crm_mark_activity_done`,
  `crm_list_activities`, `crm_list_activity_types`, `crm_activity_report`
- teams and scoring: `crm_list_teams`, `crm_update_lead_score`
- optional features: `crm_enrich_lead`, `crm_list_scoring_rules`,
  `crm_recurring_revenue_report`

Optional CRM tools are registered only when their feature key is listed in
`ODOO_CRM_OPTIONAL_FEATURES`.

## Resources And Prompts

Resources:

- `odoo://server/info`
- `odoo://user/context`
- `odoo://model/{model}/fields`
- `odoo://crm/pipeline/summary`

Prompts:

- `odoo_safe_operation`
- `odoo_crm_pipeline_review`
- `odoo_record_change_plan`

## Safety Model

Odoo remains the final authorization layer through its access rights and record
rules. The MCP server adds local guardrails:

- mutating Odoo client calls require `confirm=True`
- generic dangerous methods such as `call_kw` and `execute_kw` are blocked unless
  explicitly allowlisted
- HTTP MCP auth is separate from the server-side Odoo API key
- `inspect-config` redacts secrets
- Odoo API errors redact configured secret values

Use `MCP_AUTH_MODE=static` for simple private HTTP deployments:

```bash
MCP_AUTH_MODE=static
MCP_STATIC_TOKEN=change-me
```

Use `MCP_AUTH_MODE=jwt` with `JWT_JWKS_URI` or `JWT_PUBLIC_KEY` for production
identity-provider-backed deployments.

## Extending

Add module packs under `src/odoo_mcp/tools/` and register them from
`src/odoo_mcp/server.py`. Prefer typed, curated tools for business workflows and
leave `odoo_call_method` as the explicit escape hatch.

When adding new Odoo workflows:

1. Write tests first with mocked Odoo JSON-2 behavior.
2. Prefer single-call Odoo methods like `search_read` because each JSON-2 call is
   its own transaction.
3. Require `confirm=True` for writes, actions, posting, validation, archive, and
   delete operations.
4. Add focused README entries for new tools and optional feature flags.

TDQS

A3.7/5.0

Scored across 1 tool

Disambiguation5/5

With only one tool, there is no possibility of confusion between tools. The purpose of 'healthcheck' is clear and distinct.

Naming Consistency5/5

A single tool cannot be inconsistent with itself. The name 'healthcheck' is a reasonable single-word descriptor.

Tool Count1/5

The server is named for Odoo, a full-featured ERP system, but provides only a single health check endpoint. This is an extreme mismatch between scope and tool count.

Completeness1/5

For an Odoo integration, a single healthcheck tool is severely incomplete, lacking any CRUD operations or domain-specific functionality.

Maintenance

ActivityInactive
ResponsivenessNo issues