Docs
/
EN DE

Authentication and permissions

The MCP server uses OAuth 2.1, as the MCP specification describes. A person signs in to neoo in the browser, chooses one company and what the assistant may do there, and the client receives tokens for that one connection. MCP clients such as Claude handle all of this on their own; this page is for anyone building or debugging a client.

The flow

  1. The client sends an MCP request without a token. The server answers 401 with a WWW-Authenticate header pointing to its protected-resource metadata:

    WWW-Authenticate: Bearer resource_metadata="https://<api host>/.well-known/oauth-protected-resource/mcp/acme", scope="accounting:read accounting:write …"
    
  2. The client reads the protected-resource metadata (RFC 9728), then the authorization-server metadata at https://<api host>/.well-known/oauth-authorization-server (RFC 8414). They list every endpoint and scope.

  3. The client sends the browser to the authorization endpoint with PKCE (code_challenge_method=S256) and the resource parameter set to the server URL (RFC 8707). Both are required; a request without resource is refused with invalid_target.

  4. The person signs in to neoo (with their second factor) and decides on the consent page — see Consent below.

  5. The client exchanges the code at the token endpoint and sends the access token as Authorization: Bearer … on every MCP request.

Endpoint
GET /.well-known/oauth-protected-resource/mcp/{dataset} Protected-resource metadata of the server URL
GET /.well-known/oauth-authorization-server Authorization-server metadata
GET /oauth/authorize Authorization endpoint — response_type=code only
POST /oauth/token Grants authorization_code and refresh_token
POST /oauth/revoke Token revocation (RFC 7009)
POST /oauth/register Dynamic client registration (RFC 7591)

All clients are public clients: there are no client secrets (token_endpoint_auth_method is none), and PKCE protects the code exchange.

Register your client

Your client identifies itself in one of two ways:

  • Client ID Metadata Document (preferred). Use an https URL as your client_id, serving a JSON document with client_id (the same URL), client_name and redirect_uris (up to 20). neoo fetches it when the person connects and caches it for up to 12 hours. The consent page shows the document’s domain as Verified domain.
  • Dynamic client registration. POST /oauth/register with redirect_uris and a client_name returns a client_id. Registration is limited to 10 per hour per IP address. A client registered this way is shown as Verified domain only when all its redirect URIs are https on one host; otherwise the person sees an Unverified app warning.

Redirect URIs must match exactly what was registered. Allowed are https URIs, http on localhost, 127.0.0.1 or [::1] (the port may change between requests, for native apps), and custom app schemes. A client whose name suggests a well-known assistant but whose domain doesn’t belong to it gets a prominent warning on the consent page.

On the consent page the person sees who is asking, where they’ll be sent back to, and chooses:

  • Dataset — the company the server URL belongs to. It is shown for checking and can’t be changed. The person must be owner or admin there, and AI connections must be switched on.
  • Access per area — No access, Read or Read and change.

Whatever scopes the client asks for, the person decides what is granted. The server’s challenge asks for every scope, so all areas are offered; reading is preselected for everything except the bank.

Scopes

Each area has a read scope and, where changes are possible, a write scope. A write scope always includes reading.

Area Read scope Write scope Covers
Accounting accounting:read accounting:write Reports (trial balance, income statement, balance sheet, VAT), chart of accounts, tax codes, fiscal years, journal entries and ledger lines. Write: post and change journal entries.
Invoices and bills invoices:read invoices:write Customer invoices and vendor bills, aging, services, workstations, invoice PDFs. Write: create and change invoices and bills, record payments, e-mail invoices and reminders.
Contacts contacts:read contacts:write Customers and vendors. Write: create and update them.
Bank bank:read — Imported bank transactions, including IBANs and counterparties.
Documents documents:read documents:write Attached documents and the AI inbox. Write: upload documents to the AI inbox.

A scope is the upper limit, not a grant on its own: what a request may do is the intersection of the connection’s scopes and the permissions of the person’s role in the company. Only owners and admins can connect, so in practice the scopes decide.

tools/list only lists the tools the connection can use. Calling a tool outside the granted scopes returns 403 with an insufficient_scope challenge naming the missing scope. To get more access, the person disconnects and connects again with more areas ticked.

Tokens and how long a connection lasts

Access token Valid for 1 hour.
Refresh token Rotates on every use; the connection stays alive as long as it is used at least every 90 days. Presenting an already used refresh token ends the whole connection.
Authorization code Valid for 60 seconds, single use.

Tokens are tied to the server URL they were issued for. A token for one company’s URL is refused at another company’s URL.

A connection ends — and its tokens stop working immediately — when:

  • the person disconnects it under My Profile → AI connections, or someone disconnects it under System → Management → AI connections
  • an owner or admin switches AI connections off for the company
  • it was not used for 90 days
  • the person is no longer owner or admin of the company, loses access to it, or their account is deactivated

An ended connection is never restored, even when the access comes back; the person connects again.

Errors

Status Meaning
401 with error="invalid_token" The token expired or the connection ended. Refresh, or sign in again.
403 with error="insufficient_scope" The tool needs a scope the connection wasn’t granted.
429 More than 120 requests in a minute on this connection. Wait for Retry-After seconds.

Errors of the token endpoint follow OAuth (invalid_grant, invalid_target, …) with a short error_description.