vivid-odoo
by abipsha
README.md
# Vivid Odoo MCP server
Gives Claude direct read/write access to the Vivid Windows Odoo CRM over the
Odoo external API, so the Vendo → CRM lead sync runs without a browser tab or
the Chrome extension.
Built because the current sync drives Odoo through a logged-in browser tab: it
stalls whenever the extension disconnects, it can't run unattended, and every
PDF attachment round-trips a ~500 KB base64 blob through the conversation.
## What it does differently
| | Browser route | This server |
|---|---|---|
| Needs Chrome extension connected | yes | no |
| Runs on a schedule unattended | no | yes |
| PDF attachment | base64 through the model context | fetched server-side, never touches the conversation |
| Lead lookup | page loads, 6–10 s renders | one RPC call |
| Credentials | none (rides your session) | Odoo API key, held server-side |
## Tools
**Guarded CRUD** — `odoo_search_read`, `odoo_read`, `odoo_write`, `odoo_create`,
`odoo_call`
**Workflow helpers** — these encode the conventions that are easy to get wrong,
so they don't have to be remembered at call time:
- `find_leads_by_vendo_id` — matches on the join key, includes archived records,
and flags each lead `needs_processing` (active **and** in `Leads`). Archived
leads come back with a warning, because a cancelled job is archived, not deleted.
- `find_duplicate_leads` — widens past the Vendo ID to name, email and street,
then groups by street so separate jobs for one household are distinguishable
from true duplicates.
- `merge_leads` — chunks around Odoo's five-record cap and folds the survivor
forward. Verifies which record actually survived rather than assuming.
- `set_lead_stage` — resolves the stage by name and reports which automations
fired.
- `attach_vendo_document` — fetches from Vendo storage and writes the binary,
filename companion and source URL together.
- `lookup_sales_team_for_canvasser`, `lookup_user`, `get_lead_history`
- `odoo_ping` — health check.
## Guardrails
These are enforced in code, not left to caller discipline:
- **Model allowlist.** Readable: `crm.lead`, `crm.stage`, `crm.tag`, `crm.team`,
`hr.employee`, `res.users`, `res.partner`, `mail.message`, `mail.activity`.
**Writable: `crm.lead` only.** This server can never edit an employee record
or a user account.
- **Method allowlist** on `odoo_call` — only the merge wizard. No `unlink`.
- **The `zip` guard.** A broken server action on `crm.lead` evaluates a
`res.city.zip` field that doesn't exist, so any write touching `zip` dies with
`ValueError: Invalid field res.city.zip`. The server refuses it with a message
pointing at `x_studio_zip_code`. *This is a real defect in your Odoo
automation and is worth fixing at the source rather than routing around
forever.*
- **Document host allowlist** — `attach_vendo_document` only fetches from
`storage.paradigmvendo.com`, verifies a `%PDF` header, and caps at 25 MB.
- **Binaries stripped from results** so a read never dumps a PDF into context.
- **Bearer auth** — when `MCP_BEARER_TOKEN` is set, unauthenticated requests get
a 401. The server logs a loud warning if you start it without one.
## Setup
### 1. Generate an Odoo API key
In Odoo: avatar → **Preferences** → **Account Security** → **New API Key**.
Create it on a **dedicated integration user**, not a personal login — one whose
CRM permissions are scoped to what the sync needs. Keys inherit the user's
rights, so a key on an admin account is an admin key. The key is shown once.
`ODOO_DB` is usually the subdomain (`myvivid`); confirm at
`https://myvivid.odoo.com/web/database/selector` if unsure.
### 2. Configure
```bash
cp .env.example .env # then fill it in
openssl rand -hex 32 # value for MCP_BEARER_TOKEN
```
Never commit `.env`. Use your host's secret store in production.
### 3. Deploy
Anywhere that gives you a public HTTPS URL and can reach `myvivid.odoo.com` —
Fly.io, Railway, Render, Cloud Run, or your own box behind a TLS proxy.
```bash
docker build -t vivid-odoo-mcp .
docker run -p 8080:8080 --env-file .env vivid-odoo-mcp
```
Or without Docker:
```bash
pip install -r requirements.txt
uvicorn server:app --host 0.0.0.0 --port 8080
```
Verify before connecting:
```bash
curl -s -X POST https://your-host/mcp \
-H "Authorization: Bearer $MCP_BEARER_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}'
```
You should get a handshake naming `vivid-odoo`. Without the header you should
get a 401 — check that too.
### 4. Connect it to Claude
claude.ai → **Settings → Connectors → Add custom connector**, MCP endpoint
`https://your-host/mcp`.
### 5. Point the skill at it
Once connected, `vendo-crm-lead-sync` should call these tools instead of running
`odoo()` in a browser tab. Vendo and Terros still need the browser — only the
Odoo half becomes headless. Worth updating the skill's "When the browser will
not cooperate" section accordingly: an Odoo outage is no longer a reason to
defer a run, but a Vendo or Terros outage still is.
## A note on auth
Bearer-token auth is the minimum bar. If your host supports it, put the server
behind OAuth or restrict inbound traffic by IP — a bearer token in a connector
config is a long-lived credential granting write access to your CRM pipeline.
Rotate the Odoo API key from Odoo's Account Security page if it's ever exposed;
revoking there kills the server's access immediately.
## Testing status
Verified in the build environment: all 14 tools register, the 8 guardrail cases
(model allowlist, write allowlist, zip guard, method allowlist, binary
stripping, document host allowlist, empty-search rejection, normal write) behave
correctly, and the full HTTP path works — 401 without a token, 401 with a wrong
token, MCP handshake with the right one. End-to-end RPC (auth, uid caching, lead
lookup, stage change) was exercised against a stub Odoo.
**Not yet tested against the live instance.** The build sandbox's egress policy
blocks `myvivid.odoo.com`, so the first real run needs `odoo_ping` against
production to confirm credentials, database name and field access.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues