The MCP server (htdocs/ai/server/mcp_server.php) loads a service user
from AI_MCP_USER_ID but does not expose it as the PHP global $user.
Several tool classes call 'global $user' to identify the caller --
they therefore see no authenticated user in HTTP MCP context (no PHP
web session exists) and return 'User not authenticated' even though
authentication via X-API-Key/Bearer has already succeeded.
This breaks roughly 40% of the MCP tools shipped by the module when
called from external clients (Claude Desktop Custom Connectors,
Claude Code CLI, MCP Inspector, custom scripts).
Two complementary fixes applied:
1. mcp_server.php: after successfully loading the service user,
assign it to $GLOBALS['user'] and the local $user variable. This
acts as a safety net for any tool (current or third-party) that
relies on the legacy 'global $user' pattern.
2. Three tool classes harmonize their constructor signatures with
the McpHandler contract -- new $className($db, $user, $conf):
- ToolCrudObjects (htdocs/ai/tools/crud_objects.class.php)
- ToolInvoices (htdocs/ai/tools/invoices.class.php)
- ToolReports (htdocs/ai/tools/reports.class.php)
These now accept $user via constructor (matching the pattern
already used in ToolThirdParty, ToolCategories, ToolProducts)
and store it as $this->user. Methods that previously called
'global $user' (execute, validateInvoice, payInvoice) now use
the injected $this->user, with a fallback to the global for
web-session contexts.
Tested with:
- create_other_document (proposal, supplier_invoice): works
- search_invoice, validate_invoice, pay_invoice: works
- get_sales_report, get_purchase_report: works
- AI Assistant web UI: unchanged, no regression
* Qual: Fix Phan notices, set PHP min to 7.2 to avoid some
* Qual: Fix Phan and PHPStan notices
* Update apstats.php
---------
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
The AI Assistant web UI (parse_intent.php) persists every request to
llx_ai_request_log via ai_log_request() in ai.lib.php. Administrators
can browse the resulting log via the existing Log Viewer
(htdocs/ai/admin/log_viewer.php).
The MCP HTTP server (mcp_server.php) does NOT call this helper. As a
result, external MCP client activity (Claude Desktop, Claude Code CLI,
MCP Inspector, custom scripts, etc.) is invisible to administrators
-- only dol_syslog() entries land in documents/dolibarr.log, which
is much harder to filter and audit.
This patch adds an mcp_log_request() helper alongside the existing
auth/dispatch logic in mcp_server.php. The helper:
- Persists every tools/call invocation to llx_ai_request_log
- Filters out lifecycle methods (initialize, ping,
notifications/initialized, tools/list, ...) to avoid log spam
- Logs with provider='mcp' so administrators can filter MCP traffic
in the Log Viewer
- Captures the tool name + arguments, success/error status, raw
request payload and raw response payload
- Charges the elapsed time per-call (also per-call inside batches)
The helper is gated by the same AI_LOG_REQUESTS toggle that already
controls the web UI logging -- no new admin setting introduced.
Tested with: search_products, create_other_document, search_invoice,
get_sales_report from Claude Desktop and Claude Code CLI -- each
call appears as a new row in the Log Viewer with the right tool name,
status, payloads, and elapsed time.
The MCP server currently accepts the API key via X-API-Key or
Authorization: Bearer headers only. This works for most MCP clients
(Claude Code CLI, MCP Inspector, mcp-proxy, custom scripts) which let
users configure arbitrary headers.
However, Claude Desktop's "Custom Connectors" feature (currently in
public beta) only exposes OAuth credential fields in its UI. There is
no way for end users to pass a custom X-API-Key or Authorization
header from that client today.
Without a fallback, Claude Desktop users cannot connect to a Dolibarr
MCP server at all (HTTP 401 on every request), even though all other
prerequisites are met.
This patch adds a query-string fallback: ?api_key=XXX or ?key=XXX.
The header-based methods remain preferred and are tried first; the
query string is checked only when no valid header was provided.
Security considerations:
- Query-string credentials appear in webserver access logs and
potentially in Referer headers. This is documented in an inline
comment alongside the code.
- Administrators relying on the fallback should restrict access at
the webserver level (IP allowlist, mTLS, etc.) and/or rotate
AI_MCP_API_KEY regularly.
- Header-based auth remains the recommended path and is unchanged.
Tested with:
- Claude Desktop Custom Connectors (beta): connection now succeeds
- Claude Code CLI with --header: existing header path unchanged
- curl with both header and query-string: both work