# MCP server security overview

How the Cortena MCP server authenticates users, scopes tools, and logs writes.

Companion to Cortena trust documentation for customer IT and information-security reviewers assessing whether to enable the Cortena MCP connector in an AI assistant (Claude, ChatGPT, Dust, Cursor, Perplexity, and similar).

**Setup how-to:** [MCP server](https://help.cortena.ai/guides/advanced/mcp-server). The MCP server is in **beta**.

## 1. What it is

| | |
| --- | --- |
| **Endpoint** | `https://app.cortena.ai/api/mcp` |
| **Protocol** | Model Context Protocol over HTTPS (Streamable HTTP, JSON-RPC) |
| **Authentication** | OAuth 2.0 (authorization code + PKCE for multi-user connectors; client credentials for single-user clients), or a legacy Settings bearer token |
| **Hosting** | Same Cortena application infrastructure as `app.cortena.ai` (EU-hosted; see [EU hosting](https://help.cortena.ai/compliance/eu-hosting)) |
| **Data scope** | The connecting user's own Cortena tenants and permissions. No other customer's data is reachable |

It is not a separate product or data store. It is a narrower interface to the same Cortena APIs and data the user already sees in the web application, with an additional connector ceiling (tenants, access mode, optional tool allow-list).

Token endpoint: `https://app.cortena.ai/api/mcp/oauth/token`. Clients are managed under **Settings → MCP access (beta)** (`https://app.cortena.ai/?settings=mcp-access`).

### Dynamic Client Registration (DCR)

Cortena supports **OAuth 2.0 Dynamic Client Registration**. Assistants that support DCR (for example **Claude**, and Dust Automatic OAuth) only need the MCP URL:

**`https://app.cortena.ai/api/mcp`**

The assistant registers itself, obtains Client ID and Secret automatically, and completes Cortena sign-in plus connector setup in the browser. You do not paste OAuth keys by hand. Auto-registered clients still appear under **Settings → MCP access (beta)** and can be revoked there at any time.

For Claude, prefer the listed connector: **[Cortena FinOps in the Claude directory](https://claude.ai/directory/cortena-finops)** (Connect → sign in to Cortena).

Manual OAuth clients (Client ID + Secret from Settings) remain available for assistants that do not use DCR.

## 2. Who can connect

Only people who already hold a Cortena user account. There is no public self-registration on this interface.

Access requires:

1. A Cortena user identity (the same login used for the web app).
2. The **MCP access** tenant permission on each tenant the connector may use (granted by a tenant admin under **Settings → User permissions**).
3. An active MCP client (created in Settings, or auto-registered via DCR) whose **allowed tenants** include that tenant.

Effective runtime access is always:

```
connector ceiling ∩ user tenant membership ∩ MCP access permission ∩ per-tool Cortena permissions
```

A connection can succeed with an empty tenant overlap. In that case the assistant sees no tenant-scoped data; `cortena_user_list_tenants` explains whether to fix permissions, the connector allow-list, or reconnect.

For organization connectors (for example Claude), an admin sets the **organization ceiling** once (access mode + allowed tenants). Each additional user still signs in with their own Cortena account and, if they are not the connector owner, must approve **Allow MCP connector access**. The access gate is the person, not the application.

## 3. Token handling

| Control | Implementation |
| --- | --- |
| **Access token** | Bearer `cortena_mcp_at_…`, default lifetime **14 days** |
| **Refresh token** | Issued on authorization-code connections, default lifetime **30 days**, rotated on use |
| **MCP HTTP session** | In-memory session id, default **24 hours**; recovered automatically while the access token remains valid |
| **Transport** | HTTPS only |
| **Revocation** | **Settings → MCP access → Revoke** marks the client inactive; the next request fails authentication |

Permissions are not fixed forever at sign-in. On every call Cortena re-resolves the credential, the connecting user, allowed tenants, access mode, and tool allow-list. Removing **MCP access**, removing tenant membership, or revoking the client takes effect on the next request.

## 4. Access modes: read only vs read + write

Each MCP client has an **access mode** set at creation or org connector setup:

| Access mode | What the assistant can do |
| --- | --- |
| **Read only** | List and read tools only. Write tools are not registered and cannot be called. |
| **Read + write** | Read tools plus prepare/commit workflow actions, payables upload, and support ticket writes (still gated by Cortena permissions). |

Optional **Advanced: tools & resources** can further restrict which tools and documentation resources a client exposes. Omitting a custom list means "all tools allowed for that access mode."

### Read tools (both modes, unless custom-restricted)

| Area | Examples |
| --- | --- |
| **Identity & tenants** | List effective tenants, get permissions, tenant metadata |
| **Invoices** | List/search, get one invoice, export summary, list filters, deep links |
| **Workflows** | Catalog, node types, per-invoice workflow status |
| **Pulse** | Dashboard metrics (same feature and permission gates as the web app) |
| **DATEV trial balance** | Status and paginated sums-and-balances rows already synced into Cortena |
| **Accounting postings** | Dimensions, status, list, aggregate, transaction drill-down for synced ERP history (requires **View accounting postings**) |
| **Support** | List and get tenant support tickets (requires **View support tickets**) |

### Write tools (read + write only)

Invoice and workflow mutations never apply in one silent step:

1. **Prepare** (`cortena_invoice_approve`, `cortena_invoice_review`, `cortena_invoice_update_service_accrual`, or `cortena_change_prepare`) returns a plain-language `humanSummary` and a `confirmationToken`.
2. **You confirm** explicitly in the chat.
3. **Commit** (`cortena_change_commit` with `userConfirmed: true`) applies the change through the same Cortena APIs as the web UI.

Prepared changes expire after **15 minutes**. Supported prepare operations today: approve, complete review, reject (with reason), and update a line's service accrual From/To dates (when the accrual booking proposal feature is enabled and the user has **Edit invoice line details**).

**Write exceptions** (no prepare/commit step, because the user action is already explicit):

- **Upload payables** (`cortena_invoices_upload`): same ingest pipeline as manual upload in the UI. Requires **Add invoices**.
- **Support ticket** create, reply, close, reopen, and link: require the matching support permissions.

Having a read + write token without the underlying Cortena permission still fails (for example upload without **Add invoices**).

## 5. Connected accounting systems (what MCP can and cannot touch)

MCP talks to **Cortena**. It does not open a live write channel into your accounting system.

| Connection | Direction in Cortena | Via MCP |
| --- | --- | --- |
| **DATEV Datenservice Export Rechnungswesen** | **Read-only inbound** sync into Cortena (fiscal years, chart of accounts, posting history, sums & balances / trial balance) | Assistants can **read** already-synced trial balance and posting history stored in Cortena. MCP cannot trigger a DATEV sync job and cannot upload or push data into DATEV through Datenservice Export. |
| **DATEV Buchungsdatenservice** (EXTF / documents) | **Outbound** export of booking-ready batches from Cortena to DATEV | **Not available as an MCP tool.** "Send to DATEV" remains in the Cortena web application (and its audit trail) only. |
| **Twinfield / Exact Online** posting history | Synced into Cortena for reporting | MCP can read synced postings when the user has **View accounting postings**. MCP does not post or export to those systems. |
| **Payables upload** | Files land in Cortena (S3 → extraction → invoice workspace) | Allowed only with read + write + **Add invoices**. This does **not** send invoices into DATEV or another ledger. |

**Bottom line for DATEV Datenservice Export Rechnungswesen:** it is a read-only API connection from DATEV into Cortena. Connecting an assistant does not give that assistant (or Cortena via MCP) any ability to write, upload, or push bookings into DATEV through that interface.

## 6. Access model

Access mirrors the user's existing Cortena permissions. Connecting an assistant does not grant new invoice, workflow, or accounting rights.

| Layer | Controls |
| --- | --- |
| **MCP client ceiling** | Allowed tenants, `read_only` / `read_write`, optional tool allow-list |
| **MCP access** | Whether the user may use MCP on a tenant at all |
| **App permissions** | Same checks as the web app (`Add invoices`, `View accounting postings`, workflow eligibility, support permissions, and so on) |
| **Workflow eligibility** | Approve/review only when the user can complete the current step in the UI |

Cross-customer access is not possible. Every tenant-scoped call is constrained server-side to the intersection above.

The effective control points for administrators are: who holds a Cortena account, who has **MCP access**, which tenants and access mode each connector allows, and when to **Revoke** a client.

## 7. Where the data goes once an assistant is connected

This concerns the customer's own AI vendor relationship rather than Cortena's, and we recommend assessing it explicitly:

Data retrieved from Cortena is transferred into the connected assistant. Its provider receives that data under the customer's own agreement and data processing terms with that provider. Cortena is neither controller nor processor for that leg and cannot impose retention, storage, or training terms on it.

The customer's administrator controls enablement in the assistant's organization settings and decides which users may connect. Cortena's own sub-processors are documented under [Sub-processors](https://help.cortena.ai/compliance/sub-processors).

## 8. Logging and offboarding

### Credential lifecycle

Cortena records significant MCP access events attributable to an individual user (not a shared service account), including:

- Credential / client **created**
- User **connect** (authorization-code sign-in)
- Client **revoked** (disconnect)

Payloads include user identity, credential id, OAuth client id, label, access mode, and allowed tenants.

### Every write action

Writes go through the same business APIs as the web application and appear in the invoice **Activity** trail ([Audit log](https://help.cortena.ai/guides/invoices/audit-log)):

| Action | Audit behaviour |
| --- | --- |
| Approve / review / reject / service accrual (after commit) | Invoice activity entry for the real user, with initiator channel **mcp** and a reference to the MCP credential |
| Payables upload | Same import / processing trail as manual upload; upload origin defaults to **mcp** |
| Support ticket writes | Same support ticket history as the web UI |

Prepared-but-not-committed changes are stored only as pending confirmation records (15-minute expiry) and do not mutate invoices until commit.

Per-client usage counters (last used, calls per tool) are stored on the MCP client for operational visibility. They are not a substitute for the invoice Activity trail.

### Offboarding

| Action | Effect |
| --- | --- |
| **Revoke** the MCP client in Settings | Client becomes inactive; tokens fail on the next request; refresh against that client fails |
| Remove **MCP access** | Tenant drops out of the eligible set; tenant-scoped tools return forbidden |
| Remove the user from the tenant | Membership check fails on the next call |
| Rotate Client ID / Secret after a leak | Treat as revoke + recreate |

## 9. Explicit limitations (beta)

Not available through the user MCP server today:

- Banking connections and bank transaction tools
- Treasury / payment batch execution
- Accounting **export** triggers (DATEV Buchungsdatenservice, Exact, Twinfield, and similar "send to ledger" actions)
- Most invoice field edits beyond workflow steps and service accrual dates
- Bulk multi-invoice commits
- User or tenant administration, and connecting or disconnecting integrations
- Triggering DATEV Datenservice Export sync jobs
- Any push or upload into DATEV via Datenservice Export Rechnungswesen

## 10. Contact

- Product / security questions: [compliance@cortena.ai](mailto:compliance@cortena.ai)
- Day-to-day support: [support@cortena.ai](mailto:support@cortena.ai)

For connector setup steps, see [MCP server](https://help.cortena.ai/guides/advanced/mcp-server).
