Content
# MCP Server — Odoo SaaS
Servidor MCP (Model Context Protocol) **genérico** para conectar assistentes de IA a **qualquer instância Odoo SaaS** via a API REST nativa JSON-2 (`/json/2`, Odoo 19+). Para trocar de instância, basta trocar duas variáveis de ambiente — sem código específico por cliente e sem módulo extra instalado no Odoo.
Vale lembrar que este MCP faz o mapeamento de campos novos e funcionalidades especificas da base.
```
Usuário → Claude → MCP Server (este repo) → Odoo SaaS (JSON-2 API)
```
O servidor expõe o Odoo como um conjunto de **tools** e **resources** para o LLM. Em linguagem natural, o modelo pode buscar, criar, atualizar, excluir registros e invocar métodos de workflow — encadeando operações sozinho.
---
## Diferenciais
### 1. API correta para Odoo 19+
A maioria dos servidores MCP para Odoo usa **XML-RPC**, protocolo legado que a Odoo **removeu do SaaS na 19.1** (e removerá do Odoo.sh/on-prem no Odoo 20). Este servidor usa a **JSON-2** (`POST {url}/json/2/{model}/{method}` com `Authorization: Bearer <API_KEY>`), o padrão oficial atual — sem `authenticate` separado, sem db/uid, sem XML.
### 2. Tools por operação (verbo), não por model/endpoint
Em vez de gerar uma tool para cada model ou endpoint, as tools são separadas pela **operação ORM** (o verbo) e o `model` é apenas um **parâmetro**. O eixo de separação é a **ação**, não o objeto.
### 3. Schema lido em runtime, nunca assumido
O servidor **não** embute lista de campos/models no código. Quando precisa do schema, **pergunta à base** (`odoo_fields_get`, `odoo_list_models`, resource `odoo://model/{model}`). Funciona em qualquer base — com módulos e campos custom (`x_studio_*`) diferentes — e nunca fica desatualizado.
### 4. Guard read-only
`ODOO_MCP_READONLY=true` bloqueia todas as operações de escrita (create, write, unlink, call_method) no nível do servidor — útil para demos ou integrações somente leitura.
### 5. Zero módulo extra + conexão preguiçosa
Não requer nenhum módulo adicional no Odoo (usa a API key padrão de usuário). E não toca a base no startup: conecta na **1ª chamada de tool** (cliente lazy).
---
## Tools
### Leitura
| Tool | Descrição |
| --- | --- |
| `odoo_search_read(model, domain, fields, limit, offset, order)` | Busca + lê numa só chamada |
| `odoo_search_count(model, domain)` | Conta registros do domain |
| `odoo_read(model, ids, fields)` | Lê campos por id |
| `odoo_fields_get(model, attributes)` | Schema dos campos |
| `odoo_list_models(name_filter, limit)` | Lista models (de `ir.model`) |
### Escrita (bloqueadas se `ODOO_MCP_READONLY=true`)
| Tool | Descrição |
| --- | --- |
| `odoo_create(model, values)` | Cria registro, retorna id |
| `odoo_write(model, ids, values)` | Atualiza registros |
| `odoo_unlink(model, ids)` | Exclui registros (**irreversível**) |
| `odoo_call_method(model, method, ids, params)` | Chama método arbitrário (ex.: `action_confirm`) |
`odoo_call_method`: `ids` são os registros-alvo; `params` são os demais argumentos nomeados do método. Ex.: `odoo_call_method("sale.order", "action_confirm", ids=[42])`.
## Resources
| URI | Descrição |
| --- | --- |
| `odoo://models` | Lista (markdown) de todos os models |
| `odoo://model/{model}` | Schema (markdown) de um model |
## Domain (filtros)
Sintaxe Odoo: lista de tuplas `[campo, operador, valor]`, com operadores lógicos `&` (E, default), `|` (OU), `!` (NÃO) em notação polonesa.
```python
[["customer_rank", ">", 0], ["country_id.code", "=", "BR"]]
["|", ["state", "=", "draft"], ["state", "=", "sale"]]
```
---
## Configuração rápida
Variáveis de ambiente:
```env
ODOO_URL=https://suaempresa.odoo.com
ODOO_API_KEY=sua_chave_de_api
ODOO_MCP_READONLY=false # opcional, default false
```
A API key é gerada no Odoo em: **Preferências → Segurança da conta → Nova chave de API**.
Bloco mínimo para Claude Desktop (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"odoo": {
"command": "python",
"args": ["C:\\caminho\\para\\odoo_mcp\\server.py"],
"env": {
"ODOO_URL": "https://suaempresa.odoo.com",
"ODOO_API_KEY": "sua_chave_de_api"
}
}
}
}
```
> Passo a passo completo (venv, chave de API, Claude Code via `.mcp.json`, smoke test, primeiro uso): ver **[`SETUP.md`](SETUP.md)**.
---
## MCP vs. acesso direto à API
Uma alternativa ao MCP seria consultar o `/doc` da instância Odoo (que lista todos os models e campos) e montar chamadas REST manualmente. O MCP vai além:
| | /doc + REST manual | MCP Server (este repo) |
|---|---|---|
| Descoberta de schema | Você lê, você interpreta | LLM chama `odoo_fields_get` autonomamente |
| Execução | Você monta o request, executa, lê resposta | LLM executa a tool diretamente |
| Encadeamento | Você decide o próximo passo após cada resposta | LLM encadeia operações sem intervenção |
| Auth / headers | Você inclui em cada chamada | Invisível — resolvido uma vez no `OdooClient` |
| Erros | Você interpreta e decide | LLM interpreta e corrige o caminho |
**Exemplo concreto:** criar dois contatos numa empresa inexistente.
- **Via /doc manual:** abrir /doc → achar `res.partner` → ler campos → criar empresa → verificar ID → criar contato 1 → criar contato 2. Você media cada passo.
- **Via MCP:** o LLM busca a empresa, constata que não existe, cria, e associa os dois contatos — tudo em sequência autônoma, sem você tocar num `curl`.
O valor não é só sintaxe: é **agência** — o LLM age em vez de apenas consultar.
---
## Estrutura
```
odoo_mcp/
server.py # FastMCP: 9 tools + 2 resources
odoo_client.py # cliente REST /json/2 (Bearer, erros)
config.py # env vars (URL + API key)
requirements.txt
.env.example
tools/generate_docs.py
docs/
README.md # índice da documentação dos models
*.md # docs por model (res.partner, sale.order, …)
generated/ # snapshots de schema por instância (.gitignore)
.mcp.json.example # modelo de config para Claude Code
```
A documentação dos models de negócio core está em **[`docs/README.md`](docs/README.md)**.
---
## Segurança
- Credenciais via variáveis de ambiente — nunca no código.
- A API key herda as permissões do usuário Odoo associado; use um usuário dedicado com escopo mínimo em produção.
- `ODOO_MCP_READONLY=true` para ambientes somente leitura.
- `odoo_unlink` exclui permanentemente — confirme os ids antes.
- HTTPS obrigatório em produção (padrão do Odoo SaaS).
- `.env` e `.mcp.json` estão no `.gitignore` — nunca versione a chave.