Connecting a client
Connect Claude Code, Claude Desktop, the MCP Inspector or any MCP client to the Factuarea MCP server — with OAuth 2.1 or an API key, and in test mode.
The Factuarea MCP server speaks Streamable HTTP at
https://mcp.factuarea.com. Any MCP-compatible client can connect using one of
the two supported credentials:
- OAuth 2.1 — the client registers itself and the user authorizes it through a consent screen. Best for end-user tools.
- API key — you pass a
fact_live_/fact_test_Bearer token directly. Best for your own automations.
The canonical endpoint is the subdomain root, https://mcp.factuarea.com. The
previous path form https://mcp.factuarea.com/mcp keeps working as a
compatibility alias.
Using Claude Code? The recommended setup is the official factuarea-mcp
plugin — two commands and you're connected over OAuth, with a guidance skill
bundled in. See the dedicated Claude Code plugin
guide. The manual steps below are for other clients or headless setups.
Claude Code
The smoothest path is the Claude Code plugin — it registers the server and bundles a guidance skill in one install. If you prefer to wire the server by hand, Claude Code also supports remote MCP servers over HTTP with built-in OAuth.
With OAuth
Add the server
claude mcp add --transport http factuarea https://mcp.factuarea.comAuthenticate
Inside Claude Code, run the slash command:
/mcpPick factuarea, choose Authenticate, and your browser opens the consent screen. Select the company to grant access to, the environment (live or test) and the scopes you want to allow, then confirm. Claude Code stores the resulting token and refreshes it automatically.
Use it
Ask Claude to do something — "list my overdue invoices in test mode" — and it discovers and calls the matching tools.
With an API key
If you'd rather use your own key (no consent flow), pass it as an
Authorization header:
claude mcp add --transport http factuarea https://mcp.factuarea.com \
--header "Authorization: Bearer fact_test_xxxxxxxxxxxxxxxxxxxxxxxx"The key's scopes determine which tools appear in tools/list. A key with *
sees all 232 tools; a narrower key sees only the tools its scopes cover.
Claude Desktop
Claude Desktop connects to remote servers
through its configuration file. Add an entry under mcpServers:
{
"mcpServers": {
"factuarea": {
"type": "http",
"url": "https://mcp.factuarea.com"
}
}
}On the next launch, Claude Desktop discovers the server and walks you through
the OAuth consent flow in your browser. To use an API key instead, add a
headers object:
{
"mcpServers": {
"factuarea": {
"type": "http",
"url": "https://mcp.factuarea.com",
"headers": {
"Authorization": "Bearer fact_test_xxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}
}The config file lives at
~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or
%APPDATA%\Claude\claude_desktop_config.json (Windows). Restart the app after
editing it.
MCP Inspector
The MCP Inspector is the quickest way to explore the catalog and call tools by hand while you build.
Launch it
npx @modelcontextprotocol/inspectorConnect
Set Transport to Streamable HTTP and URL to
https://mcp.factuarea.com. For OAuth, the Inspector runs the authorization
flow for you. For an API key, add an Authorization: Bearer fact_test_…
header under Authentication.
Explore
Open Tools → List Tools to see every tool your credential can reach, inspect its input schema, and run it with sample arguments.
Any MCP client
The server follows the MCP spec, so any compliant client works. The essentials:
- Endpoint —
https://mcp.factuarea.com(the path form…/mcpis a compatibility alias) - Transport — Streamable HTTP
- Auth —
Authorization: Bearer <token>, where the token is either an API key (fact_live_/fact_test_) or an OAuth 2.1 access token - Discovery — on a
401, the server returns aWWW-Authenticateheader pointing at its Protected Resource Metadata (RFC 9728) so clients can find the authorization server automatically
Tool discovery is paginated; the server returns the full catalog (up to 200
tools) in a single tools/list response by default, and honors nextCursor if
your client paginates.
Authenticate
The MCP server accepts two kinds of credential on the same
https://mcp.factuarea.com endpoint, for two different audiences. Both arrive as
Authorization: Bearer <token>; the server tells them apart by the token's
shape (fact_* → API key, anything else → OAuth access token).
Channel policy
This is the single most important rule of the MCP surface:
| Channel | Who | How scopes are chosen | Reachable tools |
|---|---|---|---|
| API key | The account owner automating their own company | You pick the scopes when you create the key — up to the super-scope * | 232 (everything) |
| OAuth 2.1 | A third-party app acting on a user's behalf | The user grants scopes on the consent screen, from a curated catalog | 218 |
The model mirrors GitHub: a personal access token (API key) is the owner's own credential and may hold any permission, while an OAuth app is external and is limited to a vetted set of scopes the user explicitly approves.
The 14 tools an OAuth app can never reach (only an API key can) are the most sensitive fiscal and privacy operations:
- VeriFactu writes (
verifactu:write) — 8 tools: register/retry/subsanar VeriFactu records and events, upload/activate/revoke FNMT certificates, update VeriFactu settings. These touch AEAT compliance and are owner-only. - GDPR erasure (
delivery_notes:gdpr_forget) — 1 tool: erase signature-audit PII (Art. 17). Privileged, admin-only. - FacturaE (FACe) (
facturae:read/facturae:write) — 5 tools: the FACe B2G operations. Their scopes are not in the OAuth consent catalog yet, so they are API-key only for now.
Everything else — all 218 read/write/transition/send tools — is available to both channels. See Scopes & permissions for the catalog.
API keys
An API key is an opaque Bearer token bound to your company, created in the developer dashboard at Settings → Developers → API Keys. The format and rules are identical to the REST API:
fact_live_<24 alphanumeric characters> → production company
fact_test_<24 alphanumeric characters> → isolated sandbox companyThe prefix is the source of truth for the environment — see Test mode. The secret is shown only once at creation; the backend stores only a bcrypt hash. Pass it to your MCP client as:
Authorization: Bearer fact_test_xxxxxxxxxxxxxxxxxxxxxxxxFor the full key lifecycle — creation, scopes, rotation with grace period,
revocation, IP allowlist, expires_at — see the canonical
Authentication guide. Keys are shared across the REST
and MCP surfaces.
OAuth 2.1
For third-party apps, Factuarea is a full OAuth 2.1 Authorization Server. It supports Dynamic Client Registration, the authorization-code flow with PKCE, and refresh-token rotation. No pre-registration or manual app approval is required — a client registers itself and the user authorizes it. (Interactive clients like Claude Code and the MCP Inspector drive this whole flow for you; the steps below are for building your own client.)
Discovery
Clients discover the server's capabilities through standard metadata endpoints
(no /api prefix):
| Endpoint | RFC | Purpose |
|---|---|---|
/.well-known/oauth-authorization-server | 8414 | Authorization Server Metadata — lists the authorize/token/register/introspect/revoke endpoints, supported scopes, code_challenge_methods_supported: ["S256"]. |
/.well-known/oauth-protected-resource | 9728 | Protected Resource Metadata — declares the MCP resource and which authorization server issues valid tokens. |
When an unauthenticated request hits the endpoint, the server responds 401
with a WWW-Authenticate: Bearer ..., resource_metadata="<url>" header so RFC
9728 clients can find the authorization server without guessing.
1. Dynamic Client Registration (RFC 7591)
A client registers itself by POSTing its metadata; the server returns a
client_id (and a client_secret for confidential clients):
curl -X POST https://mcp.factuarea.com/api/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "My Invoicing Assistant",
"redirect_uris": ["https://myapp.example.com/callback"],
"token_endpoint_auth_method": "none"
}'Public clients (browser/native apps) register with
token_endpoint_auth_method: "none" and rely on PKCE; confidential clients use
client_secret_basic. Registration is rate-limited to 60 per minute per IP.
2. Authorization with PKCE
Send the user to the authorize endpoint with a PKCE challenge
(code_challenge_method=S256 is the only method accepted):
GET https://mcp.factuarea.com/api/oauth/authorize
?response_type=code
&client_id=<client_id>
&redirect_uri=https://myapp.example.com/callback
&scope=factuarea.read invoices.write
&state=<opaque>
&code_challenge=<base64url(sha256(verifier))>
&code_challenge_method=S256This renders the consent screen, where the user:
- Picks the company to grant access to (a user may belong to several).
- Picks the environment — live (the real company) or test (an isolated sandbox), Stripe-style. Test is opt-in; absent ⇒ live.
- Reviews and selects the scopes to grant. Sensitive scopes are flagged and not pre-checked.
On approval the server redirects back with a single-use code (and your
state). Sensitive operations are filtered out of the catalog the user can
approve — see the channel policy.
3. Token exchange
Exchange the code for an access token, sending the PKCE verifier:
curl -X POST https://mcp.factuarea.com/api/oauth/token \
-d grant_type=authorization_code \
-d code=<code> \
-d redirect_uri=https://myapp.example.com/callback \
-d client_id=<client_id> \
-d code_verifier=<verifier>{
"access_token": "<opaque>",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "<opaque>",
"scope": "profile.read clients.read invoices.read invoices.write"
}The token persists the expanded, fine-grained scopes (macros like
factuarea.read are expanded at issue time). Use the access token as the Bearer
credential. The token endpoint is rate-limited to 60 per minute per (client,
IP) and requires client authentication (HTTP Basic for confidential clients,
client_id in the body for public ones).
4. Refresh-token rotation
Refresh tokens rotate by family: each refresh issues a new access token and a new refresh token, and invalidates the one you used.
curl -X POST https://mcp.factuarea.com/api/oauth/token \
-d grant_type=refresh_token \
-d refresh_token=<refresh_token> \
-d client_id=<client_id>If a refresh token is replayed (used after rotation — the classic sign of a leak), the server detects the reuse, revokes the entire token family and raises a security alert. Always store and use the latest refresh token only.
Revocation & introspection
| Endpoint | RFC | Purpose |
|---|---|---|
POST /api/oauth/revoke | 7009 | Revoke an access or refresh token. |
POST /api/oauth/introspect | 7662 | Check whether a token is active and read its scopes/metadata. |
Both require client authentication. Users can also review and revoke connected apps from the Factuarea dashboard, and a company admin who loses access has their tokens revoked automatically on the next call.
Test mode
The MCP server runs against the same two environments as the REST API — live (your real company) and test (an isolated sandbox) — so you can build and validate an agent integration without touching production data, the AEAT, or your clients' inboxes.
How you select test mode depends on the channel:
| Channel | How to use test mode |
|---|---|
| API key | Authenticate with a fact_test_ key. The prefix is the source of truth — a fact_test_ token always operates on the sandbox. |
| OAuth 2.1 | On the consent screen, pick the Test environment (Stripe-style). Absent ⇒ live. The issued token is bound to that environment. |
A test credential operates on a dedicated sandbox company — a technical twin of your real company, provisioned automatically and inheriting its plan, so module/plan gating behaves faithfully. Isolation is structural (test and live data live in separate companies), and external effects are switched off:
| Effect | In live | In test |
|---|---|---|
| VeriFactu | The Alta record is created and transmitted to the AEAT. | Created locally, but never transmitted to the AEAT. |
| Document emails reach real recipients. | Not delivered to real recipients. | |
| Webhooks | Subscribed events are delivered to your endpoints. | Recorded with livemode: false, but not delivered. |
| FACe (FacturaE) | Submissions are presented to the real FACe web service. | Simulated — no SOAP call leaves Factuarea; the registry number is synthetic (FACE-SANDBOX-*). |
Everything else behaves exactly as in production, and the full set of 232 tools
is available in both environments (subject to your scopes and plan). When your
flow works end-to-end, switch to live: create a fact_live_ key, or re-run the
consent flow and select the live environment.
This is the same sandbox mechanism as the REST API. See the canonical Test mode & sandbox guide for how the sandbox company is provisioned and queried.