Security¶
Warning
The MCP endpoint grants full access to your InvoiceNinja account — whatever the token's permissions allow.
- Every request to the MCP path must carry an API token in the
Authorizationheader (an optionalBearerprefix is stripped). Requests without one are rejected with401before reaching any tool. - The server never validates an API token itself; InvoiceNinja enforces validity when the forwarded call arrives.
- In
tokenmode the server is stateless and holds no secret. Inoauthandbothmodes it stores client registrations and the tokens it minted, encrypted at rest withMCP_OAUTH_SECRETunder/data/oauth. - In
bothmode a bearer without theinmcp_prefix is forwarded as a raw InvoiceNinja token, exactly as intokenmode. /healthis always unauthenticated (used by the container healthcheck).- The token is sent to InvoiceNinja as
X-API-TOKENon every call. Over plain HTTP it travels in cleartext, so keep traffic on a trusted network (LAN or Docker network) or put TLS in front. - By default any
Hostis accepted (DNS-rebinding protection off) so the server works behind a proxy or by LAN IP. SetMCP_ALLOWED_HOSTSto the host[:port] values you actually use to lock this down. - Use a token scoped to what the client needs — the token's permissions are the only access boundary.
OAuth login¶
- The login page sends your email and password straight to InvoiceNinja and never stores or logs them. It uses the resulting session token once to mint a token named
MCP: <client name>, then discards it without logging you out. - Each client gets its own token, revocable on its own. See Connecting clients for the archived-token caveat.
- Remote OAuth needs an HTTPS
MCP_BASE_URL(localhost excepted), so put TLS in front. Access tokens bound to another resource are rejected here (audience check). - Dynamic Client Registration lets anyone register a client and send you a genuine login link. The page shows the client name and redirect host, but there is no allowlist: only log in to connections you started.
- The plain-HTTP
POST /upload/{entity}/{id}route is gated inoauthandbothmodes byUploadAuthMiddleware, which verifies the bearer itself and forwards the minted token. Requests without a valid one get401. - Keep
MCP_OAUTH_SECRETprivate and the/datavolume backed up. Losing or changing the secret logs every client out.