Metadata-Version: 2.4
Name: odoo-mcp-uvx
Version: 1.0.1
Summary: Read-only MCP server for standard Odoo (SaaS/Cloud) via XML-RPC — no custom module required
Project-URL: Homepage, https://github.com/niduran-orion/odoo-mcp-uvx
Project-URL: Documentation, https://github.com/niduran-orion/odoo-mcp-uvx#readme
Project-URL: Repository, https://github.com/niduran-orion/odoo-mcp-uvx
Project-URL: Issues, https://github.com/niduran-orion/odoo-mcp-uvx/issues
Project-URL: Changelog, https://github.com/niduran-orion/odoo-mcp-uvx/blob/main/CHANGELOG.md
Author-email: Nicolas Duran <nicolas.duran@orion.global>
Maintainer-email: Nicolas Duran <nicolas.duran@orion.global>
License: MPL-2.0
License-File: LICENSE
Keywords: ai,anthropic,claude,erp,llm,mcp,model-context-protocol,odoo,xmlrpc
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: mcp<2,>=1.26.0
Requires-Dist: pydantic-settings>=2.0.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: python-dotenv>=1.0.0
Provides-Extra: dev
Requires-Dist: ruff>=0.15.2; extra == 'dev'
Requires-Dist: ty>=0.0.18; extra == 'dev'
Description-Content-Type: text/markdown

# odoo-mcp-uvx

[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)

A **read-only** MCP server that lets AI assistants (Claude, Cursor, Copilot, etc.) query your Odoo instance via the standard XML-RPC API — no custom Odoo module required. Works with any Odoo SaaS, Community, or Enterprise instance.

Access is determined entirely by the Odoo user account you configure: if your user can read a model in Odoo, the MCP server can read it; if not, Odoo will return an Access Denied error.

## Features

- Search and retrieve records from any accessible Odoo model
- Read individual records by ID
- Count records matching filters
- Inspect model field definitions
- List all models the user can access (via `ir.model`)
- Smart field selection — automatically picks the most relevant fields
- LLM-optimized hierarchical text output
- Multi-language support via `ODOO_LOCALE`
- No custom Odoo module needed — pure standard XML-RPC

## Quick Start

### 1. Install UV

The server runs on your local machine (where your AI client is). Install UV if you haven't already:

**macOS/Linux:**
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

**Windows:**
```powershell
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
```

Restart your terminal after installation.

### 2. Add to your MCP client

#### Claude Desktop

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "odoo": {
      "command": "uvx",
      "args": ["odoo-mcp-uvx"],
      "env": {
        "ODOO_URL": "https://mycompany.odoo.com",
        "ODOO_DB": "mycompany-prod-12345",
        "ODOO_USER": "ai-team@mycompany.com",
        "ODOO_PASSWORD": "your-password-or-api-key"
      }
    }
  }
}
```

#### Claude Code

```bash
claude mcp add odoo \
  --env ODOO_URL=https://mycompany.odoo.com \
  --env ODOO_DB=mycompany-prod-12345 \
  --env ODOO_USER=ai-team@mycompany.com \
  --env ODOO_PASSWORD=your-password \
  -- uvx odoo-mcp-uvx
```

Or add to `.mcp.json` in your project root:

```json
{
  "mcpServers": {
    "odoo": {
      "command": "uvx",
      "args": ["odoo-mcp-uvx"],
      "env": {
        "ODOO_URL": "https://mycompany.odoo.com",
        "ODOO_DB": "mycompany-prod-12345",
        "ODOO_USER": "ai-team@mycompany.com",
        "ODOO_PASSWORD": "your-password"
      }
    }
  }
}
```

#### Cursor

Add to `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "odoo": {
      "command": "uvx",
      "args": ["odoo-mcp-uvx"],
      "env": {
        "ODOO_URL": "https://mycompany.odoo.com",
        "ODOO_DB": "mycompany-prod-12345",
        "ODOO_USER": "ai-team@mycompany.com",
        "ODOO_PASSWORD": "your-password"
      }
    }
  }
}
```

#### VS Code (GitHub Copilot)

Add to `.vscode/mcp.json` in your workspace:

```json
{
  "servers": {
    "odoo": {
      "type": "stdio",
      "command": "uvx",
      "args": ["odoo-mcp-uvx"],
      "env": {
        "ODOO_URL": "https://mycompany.odoo.com",
        "ODOO_DB": "mycompany-prod-12345",
        "ODOO_USER": "ai-team@mycompany.com",
        "ODOO_PASSWORD": "your-password"
      }
    }
  }
}
```

> Note: VS Code uses `"servers"` as the root key, not `"mcpServers"`.

#### Windsurf

Add to `~/.codeium/windsurf/mcp_config.json`.

#### Zed

Add to `~/.config/zed/settings.json` under `"context_servers"`.

## Configuration

### Environment Variables

| Variable | Required | Description | Example |
|----------|----------|-------------|---------|
| `ODOO_URL` | Yes | Odoo instance URL **or** bare hostname | `https://myco.odoo.com` or `myco.odoo.com` |
| `ODOO_PORT` | No | Port number (used when `ODOO_URL` is a bare hostname) | `443` |
| `ODOO_PROTOCOL` | No | Protocol hint when `ODOO_URL` is a bare hostname | `jsonrpc+ssl`, `https`, `http` |
| `ODOO_DB` | Recommended | Database name | `myco-prod-28478563` |
| `ODOO_USER` | Yes* | Odoo username (email) | `ai-team@myco.com` |
| `ODOO_PASSWORD` | Yes* | Odoo password **or** API key | `eaqwywMXbpx9fqYDdBxr` |
| `ODOO_API_KEY` | Yes* | API key (alternative to password; requires `ODOO_USER`) | `0ef5b399e9...` |
| `ODOO_LOCALE` | No | Language for Odoo responses | `es_ES`, `fr_FR` |

*Either (`ODOO_USER` + `ODOO_PASSWORD`) or (`ODOO_USER` + `ODOO_API_KEY`) is required.

**Notes:**
- `ODOO_URL` can be a bare hostname like `myco.odoo.com` — the server will build a proper `https://` URL automatically using `ODOO_PORT` and `ODOO_PROTOCOL` as hints.
- If `ODOO_DB` is omitted, the server tries to auto-detect the database. Odoo SaaS instances usually have database listing disabled, so **always set `ODOO_DB`** for SaaS.
- The server also reads from a `.env` file in the working directory.

#### Example for Odoo SaaS (using separate host/port/protocol env vars)

```env
ODOO_URL=mycompany.odoo.com
ODOO_PORT=443
ODOO_PROTOCOL=jsonrpc+ssl
ODOO_DB=mycompany-prod-28478563
ODOO_USER=ai-team@mycompany.com
ODOO_PASSWORD=eaqwywMXbpx9fqYDdBxr
```

#### Advanced Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `ODOO_MCP_DEFAULT_LIMIT` | `10` | Default records returned per search |
| `ODOO_MCP_MAX_LIMIT` | `100` | Maximum record limit per request |
| `ODOO_MCP_MAX_SMART_FIELDS` | `15` | Max fields returned by smart field selection |
| `ODOO_MCP_LOG_LEVEL` | `INFO` | Log level (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |
| `ODOO_MCP_LOG_JSON` | `false` | Structured JSON log output |
| `ODOO_MCP_LOG_FILE` | — | Path for rotating log file |
| `ODOO_MCP_TRANSPORT` | `stdio` | Transport (`stdio` or `streamable-http`) |
| `ODOO_MCP_HOST` | `localhost` | Host for HTTP transport |
| `ODOO_MCP_PORT` | `8000` | Port for HTTP transport |

### Transport Options

#### stdio (default)
Standard input/output — used by Claude Desktop and most MCP clients.

```bash
uvx odoo-mcp-uvx
```

#### streamable-http
HTTP transport for remote or API-style access.

```bash
uvx odoo-mcp-uvx --transport streamable-http --host 0.0.0.0 --port 8000
```

The MCP endpoint will be at `http://localhost:8000/mcp/`.

## Available Tools

### `search_records`
Search for records matching a domain filter.

```json
{
  "model": "res.partner",
  "domain": [["is_company", "=", true], ["country_id.code", "=", "ES"]],
  "fields": ["name", "email", "phone"],
  "limit": 10
}
```

### `get_record`
Retrieve a specific record by ID.

```json
{
  "model": "res.partner",
  "record_id": 42,
  "fields": ["name", "email", "street", "city"]
}
```

### `count_records`
Count records matching a domain.

```json
{
  "model": "sale.order",
  "domain": [["state", "=", "sale"]]
}
```

### `get_fields`
Inspect field definitions for a model.

```json
{
  "model": "product.product"
}
```

### `list_models`
List all models the authenticated user can access.

```json
{}
```

### Smart Field Selection

Omitting the `fields` parameter (or passing `null`) triggers automatic smart field selection:

- Essential fields (`id`, `name`, `display_name`, `active`) are always included
- Business-relevant fields (state, amount, email, phone, partner, etc.) are prioritized
- Technical/expensive fields (binary, HTML blobs, computed non-stored) are excluded
- Default cap: 15 fields (adjustable via `ODOO_MCP_MAX_SMART_FIELDS`)

To get every field: `"fields": ["__all__"]`.

## Resources

Direct URI access to Odoo data:

| URI Pattern | Description |
|------------|-------------|
| `odoo://{model}/record/{id}` | Retrieve a specific record |
| `odoo://{model}/search` | Search records (first 10) |
| `odoo://{model}/count` | Count all records in a model |
| `odoo://{model}/fields` | Get field definitions |

**Examples:**
- `odoo://res.partner/record/1`
- `odoo://product.product/search`
- `odoo://res.partner/count`
- `odoo://product.product/fields`

## Usage Examples

Ask your AI assistant:

- "Show me all customers from Spain"
- "Find products with stock below 10 units"
- "List today's confirmed sales orders"
- "How many active employees do we have?"
- "Show contact details for the partner with ID 42"
- "What fields does the sale.order model have?"

## How It Works

```
AI Assistant (Claude, Cursor, Copilot, etc.)
        ↓  MCP Protocol (stdio or HTTP)
   odoo-mcp-uvx
        ↓  XML-RPC  (/xmlrpc/2/common, /xmlrpc/2/object)
   Odoo Instance (SaaS / Community / Enterprise)
```

Authentication uses the standard Odoo XML-RPC `authenticate()` call. All permission checks are enforced by Odoo itself — if your user cannot read a model, Odoo returns Access Denied.

## Security

- Always use HTTPS in production (`https://` URL or `ODOO_PORT=443`)
- Use a dedicated low-privilege Odoo user for the MCP connection
- The server is read-only; it never creates, updates, or deletes records
- API keys are preferred over passwords (generate one in Odoo under Settings > Users > your user > API Keys tab)

## Troubleshooting

**"spawn uvx ENOENT"** — UV is not installed or not in PATH. Install UV and restart your terminal. On macOS, launch Claude Desktop from Terminal: `open -a "Claude"`.

**Authentication failed** — Verify `ODOO_USER`, `ODOO_PASSWORD`/`ODOO_API_KEY`, and `ODOO_DB`. Make sure the user is active in Odoo.

**"Access Denied" on a model** — The Odoo user does not have read access to that model. Grant access via Odoo Settings > Users & Companies > Groups, or use a user with sufficient rights.

**Database auto-selection failed** — Odoo SaaS disables database listing. Always set `ODOO_DB` explicitly.

**SSL errors** — Add `"SSL_CERT_FILE": "/etc/ssl/cert.pem"` to your MCP environment config.

**Enable debug logging:**
```json
{ "env": { "ODOO_MCP_LOG_LEVEL": "DEBUG" } }
```

## Development

```bash
git clone https://github.com/niduran-orion/odoo-mcp-uvx.git
cd odoo-mcp-uvx
pip install -e ".[dev]"

# Run the server
python -m mcp_server_odoo

# Check version
python -m mcp_server_odoo --version

# Inspect with MCP Inspector
npx @modelcontextprotocol/inspector uvx odoo-mcp-uvx
```

## License

Mozilla Public License 2.0 — see [LICENSE](LICENSE).

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md).
