← All posts

Connect Claude to Odoo: one URL, no configuration

The entire setup is one paste. Claude’s settings, add a custom connector, give it https://mcp.odooconsole.com/mcp, done. No API key, no client ID, no per-instance URL. The first time you watch it work it looks like something must be missing.

Nothing is missing. The configuration you’d expect to do by hand is specified protocol behaviour, and the client does all of it in the couple of seconds after you click Add. I traced the whole thing with curl below, partly because it’s a nice piece of protocol design and partly so you can see there’s no magic in it.

The part you see

Settings → Connectors → Add custom connector. Name it whatever you want. The URL is the same one for every OdooConsole instance:

Claude's Add custom connector dialog with the OdooConsole MCP URL pasted into the remote server field
That's the whole form. The OAuth fields underneath stay empty.

Click Add and Claude sends you to a consent screen on auth.odooconsole.com. This screen is where you pick which of your instances the connector can reach — that choice lives in the grant, not in Claude’s settings, which is why the URL never needs to change. Approve, and Claude can work that instance’s ERP: search records, draft invoices, check stock, install modules, whatever the scopes you approved allow.

Cursor, VS Code and ChatGPT go through the identical steps. None of this is Claude-specific.

The part you don’t

So how does a client that only knows a URL figure out where to log in? It asks, and gets told. Watch:

$ curl -si https://mcp.odooconsole.com/mcp -X POST \
    -H 'content-type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"initialize"}'

HTTP/2 401
www-authenticate: Bearer realm="https://mcp.odooconsole.com",
  resource_metadata="https://mcp.odooconsole.com/.well-known/oauth-protected-resource"

A 401, but with directions. The WWW-Authenticate header points at a metadata document — this is RFC 9728, which MCP’s auth spec builds on. Fetch it and you get the map:

$ curl -s https://mcp.odooconsole.com/.well-known/oauth-protected-resource
{
  "resource": "https://mcp.odooconsole.com",
  "authorization_servers": ["https://auth.odooconsole.com"],
  "scopes_supported": ["odoo:read", "odoo:write",
                       "instances:read", "instances:write"],
  "bearer_methods_supported": ["header"],
  "resource_documentation": "https://www.odooconsole.com/docs/mcp"
}

Now the client knows who the authorization server is. It registers itself there on the fly (RFC 7591 — that’s why there was no client ID to paste), runs a normal authorization-code flow with PKCE, and lands you on the consent screen. Four steps, all discovered, none configured.

Claude's dialog has optional Client ID and Secret fields for servers that can't do dynamic registration. Ours can. A pasted secret would just be one more thing to leak.

Two kinds of grant, on purpose

The consent screen offers two different things, and the difference matters more than it looks:

Grant Scopes Reaches
One instance odoo:read, odoo:write That instance’s records, models and views
The account instances:read, instances:write Create, restart, inspect, delete instances. No route to any instance’s data

These are separate credentials from separate consent flows. The reason is prompt injection: sooner or later an agent reads text that someone else wrote into your ERP — an invoice description, a delivery note. The token doing that reading carries no provisioning scopes, so hostile text in a record can’t talk its way to delete_instance. I wrote up the full reasoning in the tenancy post; the short version is that scopes on a single credential aren’t much of a boundary when a language model is the one picking tools.

Adding a second instance later means authorising a second connector. You change nothing in Claude. Same URL.

Try it

The free tier covers the whole loop: create an instance, paste the URL into Claude, ask it what’s in your ERP. If anything surprises you, the MCP reference documents every tool and every error — including that 401 up top, which is the only error a healthy connection should ever show you.

OdooConsole gives you a dedicated Odoo instance with an MCP endpoint your agent can reach — and nothing else can.

Create an instance More posts