Skip to content

Postgres Token State

@caracalai/tokenstate-postgres provides PostgresBackend, a class that persists MCP token state to a Postgres table. It is a storage backend for MCP session token records — not a RevocationStore and not a TokenCache. Use it when you need durable, queryable token state across restarts or replicas.

Terminal window
npm install @caracalai/tokenstate-postgres pg
npm install --save-dev @types/pg
import { PostgresBackend } from '@caracalai/tokenstate-postgres';
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const backend = new PostgresBackend(pool);

The constructor accepts a pg.Pool instance. PostgresBackend does not manage the pool lifecycle — create and close the pool in your application startup/shutdown code.


PostgresBackend stores token state in the mcp_token_state table. Call migrate() once at startup to create the table if it does not exist.

CREATE TABLE IF NOT EXISTS mcp_token_state (
zone_id TEXT NOT NULL,
sub TEXT NOT NULL,
scope TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (zone_id, sub)
);

The primary key is (zone_id, sub) — one active token state row per subject per zone.


async migrate(): Promise<void>

Executes the DDL to create mcp_token_state. Safe to call on every startup — the IF NOT EXISTS clause makes it idempotent. Throws if the database connection fails.

async upsert(
zoneId: string,
sub: string,
scope: string,
expiresAt: Date,
): Promise<void>

Inserts or updates the token state row for (zoneId, sub). Sets updated_at to the current database time. Use this when a new mandate is issued for a subject.

async get(zoneId: string, sub: string): Promise<TokenState | null>

Returns the TokenState for (zoneId, sub), or null if no row exists.

interface TokenState {
zoneId: string;
sub: string;
scope: string;
expiresAt: Date;
updatedAt: Date;
}
async delete(zoneId: string, sub: string): Promise<void>

Deletes the token state row for (zoneId, sub). No-op if the row does not exist.


import { PostgresBackend } from '@caracalai/tokenstate-postgres';
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const backend = new PostgresBackend(pool);
// Run once at startup
await backend.migrate();
// Store token state after issuing a mandate
await backend.upsert(
claims.zoneId,
claims.sub,
claims.scope,
new Date(Date.now() + expiresIn * 1000),
);
// Read back during a session check
const state = await backend.get(claims.zoneId, claims.sub);
if (!state || state.expiresAt < new Date()) {
// Token not found or expired
}
// Delete on logout or revocation
await backend.delete(claims.zoneId, claims.sub);

PostgresBackend does not implement RevocationStore. It stores token state (scope, expiry) for your application’s session management — it does not participate in the mandate verification pipeline directly. Mandate verification remains the responsibility of @caracalai/transport-mcp or @caracalai/mcp-express.

For revocation store functionality, use @caracalai/revocation (in-memory) or @caracalai/revocation-redis (production). See the Redis connector reference.