dolibarr-mcp-server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@dolibarr-mcp-serverWhat time entries did I submit this week?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
dolibarr-mcp-server
A remote, stateless Model Context Protocol server that authenticates each caller with that caller's own Dolibarr API key. It exposes 30 allowlisted tools for identity, time reporting, sales records, and leave requests while preserving Dolibarr's per-user permissions.
This is an independent community project. It is not affiliated with, endorsed by, or maintained by the Dolibarr project or its maintainers.
MVP status: suitable for evaluation and controlled deployments. The project intentionally has no local users, sessions, database, token cache, administrator key, or OAuth façade.
Features
official MCP Python SDK v2 and Streamable HTTP at
/mcp;one shared, stateless MCP server and connection-pooled Dolibarr client;
strict
Authorization: Bearer <DOLIBARR_API_KEY>parsing on every MCP HTTP request;per-request verification through
GET /api/index.php/users/infowithDOLAPIKEY;request-scoped safe identity and credential context; API keys are actively cleared and never retained or returned;
typed time-entry detail and aggregation by day, user, project, task, month, and ISO week;
API-only third-party and project-lead search with allowlisted typed projections;
typed leave-type lookup and leave-request search, detail, create, update, submit, approve, refuse, cancel, and reopen operations through official fixed API paths;
two-step preview-token confirmation for every create, update, and lifecycle transition;
explicit timeouts, connection limits, TLS verification, Host and Origin allowlists;
unauthenticated
/health/liveand/health/readyprobes;structured logs with correlation IDs and central credential redaction;
Python 3.12–3.14 CI, strict typing, branch coverage, packaging and container gates.
Related MCP server: HR System MCP Server
Architecture and security model
flowchart LR
C[MCP client] -->|Authorization: Bearer user key| A[ASGI transport]
A --> H[Host and Origin validation]
H --> B[Strict Bearer middleware]
B -->|DOLAPIKEY: user key| D[Dolibarr /users/info]
D -->|validated safe profile| X[request-scoped auth and credential contexts]
X --> M[MCPServer: 30 allowlisted tools]
M -->|DOLAPIKEY: same user key| R[Fixed Dolibarr REST operations]
R -->|allowlisted reporting, sales, and leave data| M
M --> P[Stateless mutation preview and stale-state token]The Bearer value is a Dolibarr API key, not an OAuth access token. The server does not publish
OAuth discovery metadata and does not pretend to be an authorization server. It validates the
key on every HTTP request, projects the upstream response to user_id, login, first_name, and
last_name, and drops the presented key when that request ends.
See architecture, security design, and
ADR 0001 plus
ADR 0002 and
ADR 0003 plus
ADR 0004 and
ADR 0005. The remaining accepted records
are listed in docs/adr.
Requirements
Python 3.12 or newer;
uv;
Dolibarr 23.0 or newer with its REST API, Projects module, and Leave/Holiday module enabled;
one Dolibarr API key per MCP user;
HTTPS and a rate-limiting reverse proxy for production.
Install and run with uv
uv sync --locked --all-groups
cp config.example.toml config.toml
# Edit dolibarr_base_url and the MCP allowlists in config.toml.
uv run dolibarr-mcpThe server listens on 127.0.0.1:8000 by default. dolibarr-mcp --help and
dolibarr-mcp --version do not require application configuration. Use
dolibarr-mcp --config path/to/config.toml to select another file explicitly.
Docker and Compose
docker build -t dolibarr-mcp-server:0.4.1 .
docker run --rm --read-only --tmpfs /tmp:rw,noexec,nosuid,size=16m \
-p 8000:8000 \
--mount type=bind,src="$PWD/config.toml",dst=/app/config.toml,readonly \
dolibarr-mcp-server:0.4.1For a container, set host = "0.0.0.0" and the external Host allowlist in config.toml before
running Docker or docker compose up --build. Compose mounts that file read-only. Neither the
image, Compose file, nor server configuration contains a user API key.
Configuration
| Default | Purpose |
| required | Fixed HTTPS Dolibarr base URL, including any subdirectory |
| unset | Optional private CA PEM path, relative to the config file or absolute |
|
| Uvicorn bind host |
|
| Uvicorn bind port |
|
|
|
| loopback list | Exact |
| empty list | Exact browser Origins; empty rejects requests carrying Origin |
|
| Connection timeout in seconds |
|
| Read timeout in seconds |
|
| Write timeout in seconds |
|
| Pool acquisition timeout in seconds |
|
| Maximum shared upstream connections |
|
| Maximum idle keep-alive connections |
| empty | Optional verified stage rows with ID, label, aliases, percentage, position, and activity |
|
| Permit HTTP only for literal loopback development URLs |
The server does not load .env or application settings from process environment variables.
There is deliberately no server-side DOLIBARR_API_KEY setting. A client may keep its own API key
in a secret environment variable and interpolate it into the request header shown below.
Configure an MCP client
Use a client that can attach a custom HTTP header. The exact client syntax varies, but the shape is:
{
"servers": {
"dolibarr": {
"type": "http",
"url": "https://mcp.example.invalid/mcp",
"headers": {
"Authorization": "Bearer ${DOLIBARR_API_KEY}"
}
}
}
}DOLIBARR_API_KEY in this example belongs to the client process, not the server. Never commit it.
A deliberately fictitious HTTP example:
curl --fail-with-body \
-H 'Authorization: Bearer FAKE_DOLIBARR_KEY_DO_NOT_USE' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
https://mcp.example.invalid/mcp \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"example","version":"1"}}}'After initialization, call dolibarr_whoami with no arguments. Its structured result is:
{
"user_id": 42,
"login": "example.user",
"first_name": "Example",
"last_name": "User"
}Tools
All dates use YYYY-MM-DD, are interpreted in UTC, and form an inclusive range. Durations always
include exact duration_seconds; duration_hours is a rounded display value. Positive identifiers
are fixed filters, never arbitrary Dolibarr paths or sqlfilters.
Tool | Required arguments | Result |
| none | The safe identity already verified for the request |
|
| Current user's time grouped by day, project, and task |
|
| Project totals grouped independently by user and task |
|
| Paged detailed entries for one task, including bounded notes |
|
| Totals by |
|
| Paged raw entries with optional |
Grouped tools accept limit from 1 to 500 and report truncated explicitly. Detailed tools accept
offset and limit from 1 to 500, and return entry_count, returned_entry_count, and has_more.
Aggregate tools never return notes. Only dolibarr_task_timespent and dolibarr_time_entries
return notes, truncated to 4000 characters.
Sales reads use only fixed official Dolibarr REST endpoints. A lead is a project whose
usage_opportunity flag is set; it is not inferred from the third party's prospect
classification. Incoming project descriptions are normalized to at most 65,535 UTF-8 bytes before
typed validation; excess content is truncated at a complete character boundary instead of
rejecting the entire project page. Search results still omit descriptions, lead details truncate
them to 4000 characters, and create/update inputs retain their 4000-character limit.
Read-only tool | Main arguments | Result |
| optional | Paged third parties matched on allowlisted fields |
|
| Allowlisted third-party details and bounded notes |
| optional | Active users safe to select for lead assignment |
| optional query, third party, stage, state, owner | Only projects with |
| optional stage-code or ID | Operator-configured plus observed stage ID/code pairs, explicitly incomplete |
|
| Lead data, sales stage, project state, and |
Leave reads also use fixed official endpoints and local filtering. Results include only typed request fields; Dolibarr decides which employees and requests the caller may access.
Read-only tool | Main arguments | Result |
| none | Active leave types and their balance behavior |
| optional employee, status, and overlapping date range | Paged leave-request summaries |
|
| One allowlisted leave request with bounded text |
Write tools always default to preview. The first call uses apply=false and returns changes,
warnings, and a 64-character confirmation_token. Repeat the same call with apply=true and that
token to write. The server re-reads the current API state and rejects a missing or stale token.
Confirmed write tool | Behavior |
| Create a third party with an explicit customer classification |
| Update only allowlisted company fields |
| Create a draft project with |
| Update lead facts without changing its control fields |
| Change the opportunity stage using exactly one |
| Replace internal |
| Validate a draft project or reopen a closed one |
| Set an open lead project's lifecycle state to closed with an explicit REST-semantics warning |
| Create a draft request; Dolibarr validates balance and overlap |
| Update allowlisted fields only while the request is draft |
| Move a draft request to submitted |
| Approve a submitted request with an explicit balance warning |
| Refuse a submitted request with a required reason |
| Cancel a submitted or approved request |
| Reopen a canceled request to submitted |
customer_status accepts neutral, customer, prospect, or customer_and_prospect. Create a
third party intended for a new lead with customer_status="prospect", then pass its returned ID to
dolibarr_lead_create. Lead creation leaves the project in draft. Opening and closing it are
separate confirmed calls.
Dolibarr 23.0.3 does not expose its complete opportunity-stage dictionary through REST. Configure
stages only after verifying their values in the target Dolibarr installation. Use the canonical
Dolibarr code as the table key and keep business shorthand in aliases, for example:
[dolibarr_lead_stage_catalog.LOST]
id = 7
label = "P3L - Lost"
aliases = ["P3L"]
percent = 0
position = 70
active = truedolibarr_lead_stage_list merges that trusted operator catalog with stages observed on accessible
leads and exposes configured metadata. dolibarr_lead_change_status accepts either the numeric
stage_id or a canonical code/alias in stage_code; configured resolution prefers the operator
catalog and otherwise requires exactly one observed match. A configured inactive stage is rejected
whether selected by code, alias, or ID. The list remains complete=false, so an empty result is
never presented as a complete Dolibarr dictionary.
Dolibarr 23.0.3 has no dedicated project-close REST action. The close tool therefore uses the
official fixed project update endpoint with the sole payload {"status": 2}, re-reads the lead,
and reports partial unless the closed state is visible. Every preview and result warns that this
path does not guarantee PROJECT_CLOSE triggers, closing-user/date metadata, or GUI-equivalent
semantics.
Leave-request status transitions are deliberately constrained:
draft -> submitted -> approved|refused, submitted|approved -> canceled, and
canceled -> submitted. Delete is not exposed. Approval never bypasses Dolibarr's permissions,
balance, overlap, or module policy; the preview calls this out before confirmation.
half_day_mode accepts:
full_days;start_afternoon_end_afternoon;start_morning_end_morning;start_afternoon_end_morning.
Health and operations
curl --fail http://127.0.0.1:8000/health/live
curl --fail http://127.0.0.1:8000/health/readyHealth probes never contact Dolibarr. Production deployments must terminate TLS at a trusted
reverse proxy or ingress, preserve an allowlisted Host, reject oversized requests, apply rate
limits, and avoid logging authorization headers. The application does not trust X-Forwarded-*
headers. CORS is not enabled; mcp_allowed_origins only validates an Origin if a browser sends one.
Invalid Dolibarr statuses, JSON, and typed payloads still produce the same generic MCP error. The
server also writes one WARNING event named upstream_response_invalid, correlated by the normal
request ID. It contains only a fixed failure category, HTTP method, status, fixed schema name, and
for schema failures at most 20 validation field locations and error types. It never includes the
upstream URL, path, query, headers, response body, rejected values, object IDs, user identity, or
exception text.
Development and quality gates
uv lock --check
uv sync --locked --all-groups
uv run ruff format --check .
uv run ruff check .
uv run mypy --strict src tests
uv run pytest
uv run pre-commit run --all-files
uv build
uv run twine check dist/*.whl dist/*.tar.gz
uv run pip-audit
uv run codespellSee development.md for the clean-wheel, workflow, and container checks.
MVP limitations
no resources or prompts, and no delete tools;
no OAuth discovery or token exchange;
clients must support a custom Bearer header;
one configured Dolibarr host per server deployment;
no automatic retry or in-process rate limiter;
no multi-tenant base URL supplied by a caller or model.
global reports use bounded fan-out because Dolibarr 23 has no global paginated time-entry API; requests fail explicitly beyond 1000 accessible tasks or 50,000 time lines.
sales search uses bounded API pagination and never accepts
sqlfilters;sales writes exclude extrafields, bank data, personal contacts, arbitrary payload fields, and returning a project to draft;
Dolibarr 23.0.3 has no official endpoint exposing the complete lead-stage dictionary; the stage tool merges the optional verified operator catalog with ID/code pairs observed on accessible leads and marks the result incomplete;
project closing uses a fixed generic update because Dolibarr 23.0.3 lacks a dedicated close REST action; close triggers and close audit metadata are not guaranteed and are warned before apply;
multi-call owner replacement is not transactional; a partial result returns the refreshed state.
leave searches process at most 10,000 accessible requests and never accept
sqlfilters;only drafts can be edited, status transitions use dedicated Dolibarr action endpoints, and no delete operation is exposed;
leave balance and negative-balance policy remain exclusively authoritative in Dolibarr.
Rotate and revoke user keys in Dolibarr. Report vulnerabilities privately as described in SECURITY.md; never place credentials or exploit details in a public issue.
Community
This server cannot be deployed
Maintenance
Related MCP Connectors
- odooOAuthcom.odooconsole
Odoo ERP for AI agents: hosted OAuth endpoint, gated writes, one endpoint for every instance.
Verified, pay-per-use API tools for AI agents through one authenticated connection.
OAuth 2.1 short-link tools for AI agents with scoped tokens, approvals, audit logs, and revocation.
ADHD-friendly tasks, notes & projects for LucidNest - 18 tools, scoped tokens, Streamable HTTP.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides a Model Context Protocol interface for the Dolibarr ERP/CRM, enabling AI agents to manage customers, products, invoices, and orders. It features specialized search tools and server-side filtering to efficiently interact with Dolibarr's REST API while minimizing token usage.1MIT
- FlicenseNot gradedqualityDmaintenanceEnables employee information lookup, directory listing, payroll access, and time-off request management with Okta token validation.-
- AlicenseNot gradedqualityBmaintenanceEnables LLM clients to securely read and write Frappe/ERPNext data under a real user's permissions, with audited calls and granular access control.AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to natively interact with Dolibarr ERP/CRM through its REST API, providing over 30 tools for managing customers, contacts, products, quotes, orders, invoices, stock, projects, contracts, interventions, and PDF generation, while supporting complex workflow orchestration and read-only security modes.MIT