> ## Documentation Index
> Fetch the complete documentation index at: https://ngquct-docs-fix-500-query-results.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Tokens

> Token model, scopes, connection allowlists, expiry, revocation, and the activity log

**Read Only** is where the **Generate Token** sheet starts, and it is the right level for a client that only reads. Move to **Read & Write** when an agent has to change data, and to **Full Access** only for `DROP` and `TRUNCATE`, which nothing below it can reach. The plaintext is shown once at creation and never again; what persists is a salted SHA-256 hash in the login keychain.

## Permission levels

| Level              | Scopes                         | What it can do                                                                                        |
| ------------------ | ------------------------------ | ----------------------------------------------------------------------------------------------------- |
| Anonymous loopback | `tools:read`, `resources:read` | Read schema and run `SELECT`.                                                                         |
| **Read Only**      | `tools:read`, `resources:read` | The same, under an issued token.                                                                      |
| **Read & Write**   | plus `tools:write`             | `INSERT`, `UPDATE`, `DELETE`, connect and disconnect, switch database or schema, open and focus tabs. |
| **Full Access**    | plus `admin`                   | Also `confirm_destructive_operation`, the only tool that runs `DROP` or `TRUNCATE`.                   |

Anonymous loopback is the floor and cannot be raised: the scope check refuses `tools:write` and `admin` to a caller with no issued token, whatever it asks for.

`confirm_destructive_operation` also needs the connection's external access to be **Read & Write**, and no permission level skips the user's approval. [Scopes and gates](/external-api/mcp-tools#scopes-and-gates) names which tools sit on which scope.

## Require authentication

**Settings > Integrations > Authentication > Require authentication** is on by default. Turning it on for the first time with no tokens yet mints a **Read & Write** token named "Default token" and reveals the plaintext once.

With the toggle off, a request from your own machine with no `Authorization` header is accepted as the anonymous loopback caller in the table above. Presenting an `Authorization` header still goes through normal token validation, so an invalid token is rejected either way. The server is loopback only, with no remote mode.

## Connection allowlist

A token grants access to all connections or to a named set. A call that targets a connection outside the set is refused with `-33007` before any other check runs, and aggregate reads such as `search_query_history` and `list_recent_tabs` filter their results to the allowlist whether or not you passed a connection id.

## Combining with external access

The effective permission is the lower of the token's scope and the connection's **External Clients** level.

| Token            | Connection access | Effective                                                 |
| ---------------- | ----------------- | --------------------------------------------------------- |
| **Read Only**    | **Read & Write**  | read only                                                 |
| **Read & Write** | **Read Only**     | read only                                                 |
| **Full Access**  | **Read Only**     | read only, and `confirm_destructive_operation` is refused |
| **Full Access**  | **Read & Write**  | read and write                                            |
| any              | **Blocked**       | denied, and the connection is not even listed             |

## Creating a token

Pairing is the usual route: see [Pairing](/external-api/pairing). To mint one by hand, open **Settings > Integrations > Authentication**, click **Generate Token**, and pick a name, permission level, connection allowlist and expiry. The plaintext is shown once.

## Expiry

Optional, and worth setting for anything with write access. The sheet offers 30, 60 and 90 days, a custom date, or **Never**. An expired token stops authenticating: requests get `-33008` with HTTP `401` and `WWW-Authenticate: Bearer error="invalid_token", error_description="token expired"`.

## Rotating a token

Mint the replacement before you retire the old one; two tokens for one client are fine, and pairing the same client twice does exactly this. Point the client at the new token, confirm it works, then revoke the old one. Revoking first cuts the client off mid-call, because revocation takes effect immediately.

## Revocation

The token list in **Settings > Integrations > Authentication** shows prefix, name, permission level, allowlist, last use, and expiry. Each row offers:

* **Revoke**: marks the token inactive. It stays in the list and cannot be reactivated.
* **Delete…**: removes the row entirely.

Revoking cancels every request that token has in flight and clears the connection approvals the user had granted it, so a re-issued token has to ask again. The next call gets `-33009` with HTTP `401`.

**Disconnect** in the Connected Clients list revokes that client's token. It is not a soft disconnect: the client has to pair again or be given a new token.

## The stdio bridge token

Every server start mints a token named `__stdio_bridge__` and writes it into the handshake file for the bundled bridge. It is **Read & Write**, so an agent going through the bridge can read and write but cannot call `confirm_destructive_operation`.

It expires after an hour and is rotated 15 minutes before that, deleted when the server stops, and any stale copy is removed the next time the store loads. It is hidden from the Settings token list, though its name can appear in the activity log. For a narrower scope, skip the bridge and connect over [HTTP with a token you mint](/external-api/mcp-clients#http-transport).

## Stored fields

| Field                     | Purpose                                                                                 |
| ------------------------- | --------------------------------------------------------------------------------------- |
| `name`                    | Label in the token list. Pairing uses the client name.                                  |
| `prefix`                  | First 8 characters of the plaintext, so a token can be identified without revealing it. |
| `tokenHash`, `salt`       | SHA-256 of salt plus plaintext. The plaintext is never persisted.                       |
| `permissions`             | `readOnly`, `readWrite`, or `fullAccess`.                                               |
| `connectionAccess`        | All connections, or a named set of connection UUIDs.                                    |
| `expiresAt`               | Optional. Absent means never.                                                           |
| `isActive`                | `false` after revocation.                                                               |
| `createdAt`, `lastUsedAt` | Shown in the token list.                                                                |

The plaintext is `tp_` followed by 32 base64url-encoded random bytes. Tokens live in the login keychain under the `com.TablePro` service; a pre-existing `mcp-tokens.json` is migrated in on first load and deleted.

## Activity log

Every authentication, tool call, resource read, and query is recorded in `~/Library/Application Support/TablePro/mcp-audit.db` with the token id, never the plaintext. Open it with **Settings > Integrations > View Activity…**.

| Field      | Values                                                                                                                                                                                                                                               |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Timestamp  | when the call happened                                                                                                                                                                                                                               |
| Token      | the token behind the call, by name and id                                                                                                                                                                                                            |
| Category   | `auth`, `access`, `admin`, `query`, `tool`, `resource`                                                                                                                                                                                               |
| Action     | `auth.success`, `auth.anonymousLoopback`, `auth.failure`, `auth.rateLimited`, `pairing.exchange`, `pairing.redirectRejected`, `token.created`, `token.revoked`, `server.started`, `server.stopped`, `query.executed`, `tool.<name>`, `resource.read` |
| Connection | the connection the call targeted, or `-`                                                                                                                                                                                                             |
| Outcome    | `success`, `denied`, `error`, `rateLimited`                                                                                                                                                                                                          |

Statement text is not stored. A `query.executed` row carries `sqlDigest`, the SHA-256 of the statement, alongside its duration and row count, which is enough to match a row against a statement you already hold. Error text that is stored is redacted and truncated first.

Entries are chained: each row's hash covers the previous one, so a row cannot be removed or edited without breaking the chain. Entries older than 90 days are pruned when the store opens.

## Rate limits

| Limit                    | Value                         |
| ------------------------ | ----------------------------- |
| Requests                 | 240 per 60 seconds per caller |
| Concurrent requests      | 8 per caller                  |
| Failed auth attempts     | 5 per 60 seconds              |
| Auth lockout             | 5 minutes                     |
| Failed pairing exchanges | 5 per 5 minutes               |
| Pairing lockout          | 15 minutes                    |

Request limits are keyed by token id, or by client address for an anonymous caller, and `subscriptions/listen` is exempt. Auth failures count against both the client address and the token, so one misbehaving client cannot lock out another on the same loopback address, and a successful auth clears the bucket. Anything over a limit returns `-33010` with HTTP `429` and a `Retry-After` header. Transport limits such as the body cap are on [MCP Protocol](/external-api/mcp-protocol#transport-limits).

## What a token cannot do

| Capability                                          | Reachable |
| --------------------------------------------------- | --------- |
| Read connection passwords                           | no        |
| Read SSH keys                                       | no        |
| Read license data                                   | no        |
| Read or change app settings                         | no        |
| Read files outside TablePro's own support directory | no        |
| Change Safe Mode rules                              | no        |
| Create, change, or read other tokens                | no        |
| Create or edit connection records                   | no        |

Statements that reach the filesystem or run server-side code are refused before they reach the driver, on every tool. The reachable surface is the [tool catalog](/external-api/mcp-tools), the [resources](/external-api/mcp-resources), and the [prompts](/external-api/mcp-prompts). Nothing outside those lists is exposed.
