MCP server
SolidInvoice ships with a built-in Model Context Protocol (MCP) server. Once you connect an AI agent to it, the agent can read your invoices, quotes, clients, and payments โ and, if you grant write access, create and update them on your behalf.
Every connection is bound to a single company at consent time and authenticated with OAuth 2.1. An agent connected to Company A can never see Company B's data, even if you belong to both.
Endpoint URLโ
The MCP server is always served at the path /_mcp on your SolidInvoice instance.
| Deployment | URL |
|---|---|
| Cloud SaaS | https://solidinvoice.app/_mcp |
| Self-hosted | https://<your-solidinvoice-host>/_mcp |
The transport is Streamable HTTP (the standard remote-MCP transport), not stdio. Any agent that supports remote MCP servers with OAuth can connect โ no proxy or wrapper script is required.
A self-hosted instance must be reachable over HTTPS for OAuth to work. The flow involves browser redirects, so localhost works for local testing but a private LAN address generally will not โ use a tunnel (Cloudflare Tunnel, ngrok) if you need to expose a dev instance to a hosted agent.
How authorization worksโ
You don't paste API keys anywhere. The first time an agent connects, it walks you through a standard OAuth consent flow:
- The agent discovers SolidInvoice's OAuth endpoints from
/.well-known/oauth-authorization-serverand registers itself dynamically. - Your browser opens to the SolidInvoice consent page. Sign in if you aren't already.
- On the consent screen you choose:
- Which company the agent will act on (if you belong to more than one).
- Which scopes to grant โ
mcp:readis always enabled;mcp:writeis opt-in and only shown if the agent asked for it.
- After you approve, the agent receives an access token (valid for 24 hours) and a refresh token (valid for 90 days, rotated on each use). Tokens are bound to the company and scopes you chose; nothing about that binding can be changed later without re-consenting.
You can review and revoke any connected agent at any time from Profile โ Connected apps (/profile/connected-apps).
Connecting your agentโ
The exact steps depend on the agent. The URL is always your /_mcp endpoint; everything else is handled by OAuth.
- Claude Desktop
- Claude Code
- Cursor
- Codex CLI
- Goose
- MCP Inspector
- Other agents
In Claude Desktop, open Settings โ Connectors โ Add custom connector and enter:
- Name:
SolidInvoice - URL:
https://solidinvoice.app/_mcp(or your self-hosted URL)
Claude opens a browser window to complete OAuth. Approve the consent screen and the connector is ready to use in any chat.
From a terminal:
claude mcp add --transport http solidinvoice https://solidinvoice.app/_mcp
For a self-hosted instance, swap the URL. The first tool call in a session triggers the OAuth flow in your browser.
To make the connection available across all your projects, add --scope user:
claude mcp add --scope user --transport http solidinvoice https://solidinvoice.app/_mcp
Open Settings โ MCP โ Add new MCP server and add:
{
"mcpServers": {
"solidinvoice": {
"url": "https://solidinvoice.app/_mcp"
}
}
}
Cursor will prompt you to authorize in the browser on first use.
Add the server to ~/.codex/config.toml:
[mcp_servers.solidinvoice]
url = "https://solidinvoice.app/_mcp"
Restart Codex. The first request triggers browser-based OAuth.
In Goose, add an extension of type Remote (HTTP) pointing at your /_mcp URL. Goose handles dynamic registration and OAuth automatically.
The official inspector is the quickest way to test a connection without configuring a full agent:
npx @modelcontextprotocol/inspector https://solidinvoice.app/_mcp
It walks through dynamic client registration, opens the consent page in your browser, then drops you into a UI where you can list and call every tool the server exposes.
Any MCP client that supports the Streamable HTTP transport with OAuth 2.1 (authorization code + PKCE) and Dynamic Client Registration (RFC 7591) can connect with no extra setup:
- Server URL:
https://solidinvoice.app/_mcp(or your self-hosted URL) - Auth: discovered automatically from
/.well-known/oauth-authorization-server - Scopes: request
mcp:readand (if the agent needs to make changes)mcp:write
If your client cannot perform DCR or OAuth, it cannot use this server โ there is intentionally no static API-key fallback.
After connecting, ask the agent something like "List my unpaid invoices" or "Show this month's revenue" to verify everything works.
Picking a companyโ
If you belong to multiple companies, the consent screen shows a company picker. The choice is locked into the access token โ to point the same agent at a different company, revoke it from Profile โ Connected apps and reconnect.
You can connect the same agent to multiple companies by registering it more than once under different names โ for example solidinvoice-acme and solidinvoice-personal. Each registration gets its own consent and its own token, so the agent can act against either company by picking the matching connection.
Available toolsโ
The server exposes a single, consistent toolset across every agent. Tools requiring mcp:write will only succeed if you granted write access at consent time.
Read tools (mcp:read)โ
| Tool | What it does |
|---|---|
list_resource | List records of any resource (invoice, quote, client, contact, payment, tax, recurring invoice) with filters and pagination. |
get_resource | Fetch a single record by ID. |
list_invoices_by_status | List invoices filtered by status (draft, pending, paid, overdue, etc.). |
list_overdue_invoices | List overdue invoices, optionally for a specific client. |
list_quotes_by_status | List quotes filtered by status. |
list_recurring_invoices | List recurring invoices, optionally filtered by status and/or client. |
list_payment_methods | List configured payment methods for the active company. |
list_tax_rates | List tax rates configured for the active company. |
list_workflow_transitions | List workflow transitions currently enabled on a record. |
get_company_info | Return id, name, and default currency for the active company. |
get_client_summary | Totals invoiced, outstanding balance, total paid, and counts for a client. |
get_dashboard_stats | Outstanding/overdue totals per currency, invoice counts by status, client count. |
get_invoice_status_distribution | Count of invoices grouped by status. |
get_revenue_by_period | Revenue from captured payments between two dates, grouped by day/week/month. |
get_total_outstanding | Total outstanding invoiced amounts per currency, optionally filtered by client. |
get_total_overdue | Total overdue invoiced amounts per currency, optionally filtered by client. |
Write tools (mcp:write)โ
| Tool | What it does |
|---|---|
create_resource | Create a flat resource โ client, contact, or tax. |
update_resource | Update scalar fields on an existing client, contact, or tax. |
delete_resource | Delete a client, contact, or tax. |
create_invoice | Create an invoice for a client with line items, optional discount, due date, and contacts. |
create_quote | Create a quote for a client with line items, optional discount, due date, and contacts. |
create_recurring_invoice | Create a recurring invoice on a daily, weekly, monthly, or yearly cadence. |
clone_invoice | Clone an existing invoice into a new draft with the same line items. |
clone_quote | Clone an existing quote into a new draft with the same line items. |
convert_quote_to_invoice | Convert a quote into a new invoice, copying client, line items, and totals. |
apply_invoice_transition | Apply a workflow transition (accept, cancel, overdue, pay, reopen, archive) to an invoice. |
apply_quote_transition | Apply a workflow transition (send, accept, decline, cancel, reopen, archive) to a quote. |
apply_recurring_transition | Apply a workflow transition to a recurring invoice. |
record_payment | Record an offline payment against an invoice and transition it to paid. |
add_contact | Add a contact (email + optional name) to an existing client. |
send_invoice_reminder | Email a manual reminder for an invoice to its contacts. |
The agent always operates as the user who consented, on the company chosen at consent time. Any company/company_id an agent tries to pass in a write call is ignored โ the active company is enforced server-side.
Managing connected appsโ
Visit /profile/connected-apps while signed in to:
- See every agent that has been authorized, the company it is bound to, and the scopes it holds.
- Revoke an agent. Revocation immediately invalidates its access and refresh tokens; the next request from that agent will fail with
401 invalid_tokenand the user will need to re-consent.
Revoking an agent does not delete its registration, so the same agent can be reconnected later without going through dynamic registration again.
Self-hosted: enabling the serverโ
The MCP server is bundled with SolidInvoice 3.0 and enabled by default. The only one-time prerequisites are:
- OAuth signing keys. Run
bin/console mcp:keys:generateonce; keys are written to$SOLIDINVOICE_CONFIG_DIR/oauth/and survive redeployments. The single-binary launcher (solidinvoice run) does this automatically on first start. - Database migrations. Run
bin/console doctrine:migrations:migrateto create the OAuth and token tables.
For multi-node deployments, switch the MCP session store off the default on-disk backend so sessions survive across nodes:
SOLIDINVOICE_MCP_SESSION_STORE=cache
SOLIDINVOICE_MCP_SESSION_CACHE_POOL=cache.app
| Store | When to use |
|---|---|
file | Default. Single-node deployments. |
memory | Local development only โ sessions reset on every worker restart. |
cache | Multi-node deployments. Point SOLIDINVOICE_MCP_SESSION_CACHE_POOL at a Redis pool. |
framework | Re-uses your application's Symfony session handler. |
Troubleshootingโ
The browser opens but consent fails with "company required"
You belong to multiple companies and didn't pick one. Re-trigger the connection in the agent and select a company on the consent screen.
Tools that change data return "insufficient scope"
The agent only requested โ or you only granted โ mcp:read. Revoke the connection from Profile โ Connected apps and reconnect, making sure the agent requests mcp:write and you tick the write checkbox on the consent screen.
Requests started returning 401 invalid_token
The token expired or was revoked. Most agents refresh automatically; if not, reconnect from the agent's MCP settings. Check Profile โ Connected apps to confirm the agent hasn't been revoked.
Self-hosted: OAuth redirect fails
SolidInvoice must be reachable from the agent's browser over HTTPS at the same hostname the agent was configured with. Mixed http/https or hostname mismatches between the configured URL and the redirect target will break the flow.