Rekor — Data Layer for AI Agents

You have access to the rekor CLI — the builder interface for Rekor. Use it to set up schemas, configure collections, test in preview environments, and promote to production. Production agents use MCP tools to read/write documents; external systems integrate via REST API. The CLI is your tool for designing and managing the data model.

Agent Guidelines

  • Drive Rekor through the rekor CLI when you have shell access. If you also have mcp__rekor__ tools in your toolset, prefer the CLI for setup and configuration; reserve MCP tools for production read/write operations.
  • Only provide information from this skill, tool descriptions, or reference documentation. Do not invent URLs, paths, commands, or flags.
  • Schema work (collections, hooks, triggers, MCP Factory endpoints) only happens in preview databases. Always create or use a preview before changing schemas.
  • Promotion is human-only. When schema is ready, surface the exact rekor databases promote command and wait for the user to run it.
  • Tokens are shown only once on creation. Always tell the user to copy and store it before doing anything else.
  • Never auto-commit. Show the user git diff (when working with schema files) and wait for approval.

---

First-Time Setup Walkthrough

When the user is starting from scratch, walk them through these steps. Skip any that are already done — check with rekor databases list before assuming.

1. Install the CLI

npm install -g rekor-cli

Verify with rekor --version. The CLI is published to npm as rekor-cli; the binary is rekor.

2. Authenticate

If the user has a Rekor account:

rekor login

Opens a browser to complete OAuth. For headless or CI use, pass an existing API key:

rekor login --token rec_xxx

If the user does not have an account yet, direct them to [rekor.pro](https://rekor.pro) — free plan includes 1,000 operations per month, no credit card required.

3. Create a database

Databases are top-level data containers. One per app, domain, or tenant.

rekor databases create <id> --name "<Display Name>" [--tags <comma-separated>]

Set REKOR_DATABASE=<id> in the user's shell to avoid passing --database on every command.

4. Create a preview environment

Schema changes (collections, hooks, triggers, MCP Factory endpoints) are blocked in production databases. Create a preview to do schema work:

rekor databases create-preview <id> --name "<preview-slug>"

The resulting preview database ID is <id>--<preview-slug>. Use that as --database for all schema commands.

5. Define the first collection

A collection is a JSON Schema that defines a document type. Define it in the preview database:

rekor collections upsert <slug> --database <id>--<preview-slug> \

--name "<Display Name>" \

--schema '{"type":"object","properties":{...},"required":[...]}'

For schemas longer than a line, write them to a file and pass --schema @path/to/schema.json.

6. Test in preview, then ask the user to promote

Write some test documents to the preview, query them, iterate until the schema is right:

rekor documents upsert <slug> --database <id>--<preview-slug> --data '{...}'

rekor sql "SELECT FROM documents WHERE database_id = {database_id:String} AND collection = '<slug>' AND deleted = false" --database <id>--<preview-slug>

When ready, surface the promotion command for the user to run (you cannot promote yourself):

rekor databases promote <id> --from <id>--<preview-slug>

Recommend --dry-run first to preview the diff.

7. Connect production agents

Two ways to give production agents access:

Scoped API token — for direct REST/CLI access:
rekor tokens create --name "<agent-name>" \

--grants '[{"scope":{"databases":["<id>"],"environments":["production"]},"permissions":["read:documents","write:documents"]}]'

The raw token (rec_...) is shown once. Copy it immediately.

MCP Factory endpoint — for purpose-built MCP tools per agent (recommended for LLM agents):

See the [MCP Factory](#mcp-factory-custom-mcp-endpoints) section below.

---

Core Concepts

  • Collection: A schema (JSON Schema) that defines a document type. No migrations — create at runtime.
  • Document: A JSON document conforming to a collection's schema.
  • Relationship: A typed, directed link between two documents with optional metadata.

Quick Start

1. Create a collection

rekor collections upsert invoices --database my-ws \

--name "Invoices" \

--schema '{"type":"object","properties":{"customer":{"type":"string"},"amount":{"type":"number"},"status":{"type":"string","enum":["draft","issued","paid"]}},"required":["customer","amount"]}'

2. Create a document

rekor documents upsert invoices --database my-ws \

--data '{"customer":"Acme Corp","amount":5000,"status":"draft"}'

With external ID for idempotent upsert:

rekor documents upsert invoices --database my-ws \

--data '{"customer":"Acme Corp","amount":5000}' \

--external-id inv_123 --external-source billing

3. Query documents

rekor sql "SELECT data.invoice_number.:String as num, data.status.:String as status, arraySum(CAST(data.line_items[].amount, 'Array(Float64)')) as total FROM documents WHERE database_id = {database_id:String} AND collection = 'invoices' AND deleted = false ORDER BY total DESC" --database my-ws

4. Link documents

rekor relationships upsert --database my-ws \

--source invoices/rec_abc --target customers/rec_xyz \

--type belongs_to

5. Traverse relationships

rekor query-relationships invoices rec_abc --database my-ws \

--type belongs_to --direction outgoing

---

Full Command Reference

> Destructive delete commands prompt for confirmation in an interactive terminal. Pass -y/--yes to skip the prompt (it is also auto-skipped in non-interactive/CI contexts).

Databases

rekor databases list [--tag <tag>]

rekor databases get <id>

rekor databases create <id> --name <name> [--description <desc>] [--tags <comma-separated>]

rekor databases tag <id> --tags <comma-separated>

rekor databases delete <id>

Tags let you group databases (e.g., client:acme,billing). Filter with --tag.

Collections

rekor collections list --database <ws>

rekor collections get <id> --database <ws>

rekor collections upsert <id> --database <ws> --name <name> --schema <json|@file>

rekor collections delete <id> --database <ws>

Documents

rekor documents upsert <collection> --database <ws> --data <json|@file> [--id <uuid>] [--external-id <id>] [--external-source <src>]

rekor documents get <collection> <id> --database <ws>

rekor documents delete <collection> <id> --database <ws>

SQL Query

Execute read-only SQL queries directly against database data. Supports filtering, aggregation, JOINs, CTEs, and array operations.

rekor sql "<query>" --database <ws> [--param key=value ...] [--file query.sql]
Tables: documents, relationships, collections, databases, operations_log Important: Always include database_id = {database_id:String} and deleted = false. Queries always see the latest version of each row — the server handles deduplication. Accessing JSON fields: Use data.field.:Type subcolumn syntax for the native JSON type. Use CAST(data.field, 'Type') when type-safe conversion is needed (e.g., integers stored as Int64 vs Float64). Examples:
# Simple query

rekor sql "SELECT data.invoice_number.:String as num, data.status.:String as status FROM documents WHERE database_id = {database_id:String} AND collection = 'invoices' AND deleted = false" --database my-ws

Aggregation

rekor sql "SELECT data.status.:String as status, count() as cnt FROM documents WHERE database_id = {database_id:String} AND collection = 'invoices' AND deleted = false GROUP BY status" --database my-ws

Array aggregation (sum embedded line items)

rekor sql "SELECT data.invoice_number.:String as num, arraySum(CAST(data.line_items[].amount, 'Array(Float64)')) as total FROM documents WHERE database_id = {database_id:String} AND collection = 'invoices' AND deleted = false" --database my-ws

Explode array elements with ARRAY JOIN

rekor sql "SELECT item.description.:String as item, sum(CAST(item.amount, 'Float64')) as revenue FROM documents ARRAY JOIN data.line_items[] as item WHERE database_id = {database_id:String} AND collection = 'invoices' AND deleted = false GROUP BY item ORDER BY revenue DESC" --database my-ws

CTE joining documents with relationships

rekor sql "WITH inv AS (SELECT id, data.invoice_number.:String as num, arraySum(CAST(data.line_items[].amount, 'Array(Float64)')) as total FROM documents WHERE database_id = {database_id:String} AND collection = 'invoices' AND deleted = false), pay AS (SELECT target_id, sum(CAST(data.allocated, 'Float64')) as paid FROM relationships WHERE database_id = {database_id:String} AND rel_type = 'payment_for' AND deleted = false GROUP BY target_id) SELECT inv.num, inv.total, coalesce(pay.paid, 0) as paid, inv.total - coalesce(pay.paid, 0) as balance FROM inv LEFT JOIN pay ON pay.target_id = inv.id ORDER BY balance DESC" --database my-ws

With parameters

rekor sql "SELECT FROM documents WHERE database_id = {database_id:String} AND data.status.:String = {status:String} AND deleted = false" --database my-ws --param status=issued

Relationships

rekor relationships upsert --database <ws> --source <col/id> --target <col/id> --type <type> [--id <id>] [--data <json>]

rekor relationships get <id> --database <ws>

rekor relationships delete <id> --database <ws>

rekor query-relationships <collection> <id> --database <ws> [--type <type>] [--direction outgoing|incoming|both]

Hooks (inbound webhooks)

External systems push data into Rekor via hooks. Each hook provides a unique ingest URL.

rekor hooks create --database <ws> --name <name> --collection <collection>

rekor hooks list --database <ws>

rekor hooks get <id> --database <ws>

rekor hooks delete <id> --database <ws>

Hooks can only be created/deleted in preview databases. Promote to production when ready.

Triggers (outbound webhooks)

Triggers fire automatically when documents change, notifying external systems via HTTP POST.

rekor triggers create --database <ws> --name <name> --collection <collection> --url <url> --events '["create","update","delete"]'

rekor triggers list --database <ws>

rekor triggers get <id> --database <ws>

rekor triggers delete <id> --database <ws>

Triggers are HMAC-signed (X-Rekor-Signature) and fire asynchronously. By default, writes from hooks don't re-fire triggers (skip_hook_writes: true). Triggers can only be created/deleted in preview databases.

Batch (atomic)

Execute up to 1,000 operations atomically — all succeed or all fail:

rekor batch --database <ws> --operations '[

{"type":"upsert_document","collection":"invoices","data":{"customer":"A","amount":100}},

{"type":"upsert_document","collection":"invoices","data":{"customer":"B","amount":200}},

{"type":"upsert_relationship","rel_type":"related_to","source_collection":"invoices","source_id":"id1","target_collection":"invoices","target_id":"id2"}

]'

Operation types: upsert_document, delete_document, upsert_relationship, delete_relationship, upsert_collection, delete_collection

Provider Adapters

Import tool definitions from any LLM provider as collections, or export collections as tool definitions.

Import (creates collections from tool definitions):
# OpenAI

rekor providers import openai --database <ws> --tools '[{"type":"function","function":{"name":"create_invoice","parameters":{"type":"object","properties":{"customer":{"type":"string"},"amount":{"type":"number"}},"required":["customer"]}}}]'

Anthropic

rekor providers import anthropic --database <ws> --tools '[{"name":"create_invoice","input_schema":{"type":"object","properties":{"customer":{"type":"string"}}}}]'

MCP

rekor providers import mcp --database <ws> --tools '[{"name":"create_invoice","inputSchema":{"type":"object","properties":{"customer":{"type":"string"}}}}]'

From file

rekor providers import openai --database <ws> --tools @tools.json

Export (get collections as tool definitions):
rekor providers export openai --database <ws>

rekor providers export anthropic --database <ws> --collections invoices,customers

rekor providers export mcp --database <ws> --output tools.json

Import tool call (create a document from provider-native format):
# OpenAI tool call → document

rekor providers import-call openai invoices --database <ws> \

--data '{"arguments":{"customer":"Acme","amount":5000}}' \

--external-id call_abc123 --external-source openai

Anthropic tool call → document

rekor providers import-call anthropic invoices --database <ws> \

--data '{"input":{"customer":"Acme","amount":5000}}'

Supported providers: openai, anthropic, google, mcp

MCP Factory (custom MCP endpoints)

Create purpose-built MCP servers from your database collections. Each endpoint serves domain-specific tools — agents see create_invoice, list_payments, not generic Rekor operations.

# Create a curated MCP endpoint

rekor endpoints upsert invoicing-agent --database my-ws \

--name "Invoicing Agent" \

--tool "invoices:get,list" \

--tool "payments:create,get,list" \

--relationship "invoice_payment:list" \

--batch "invoices:create,update" --batch "payments:create" --batch "invoice_payment:create" \

--sql-query

Get the MCP connection URL

rekor endpoints url invoicing-agent

→ https://mcp.rekor.pro/e/invoicing-agent/mcp

List all endpoints

rekor endpoints list --database my-ws

Get endpoint config (with resolved schemas)

rekor endpoints get invoicing-agent --database my-ws --resolved

Delete an endpoint

rekor endpoints delete invoicing-agent --database my-ws

Tool spec format: collection:op1,op2 (operations: create, get, list, update, delete) Relationship spec format: rel_type:op1,op2 (operations: create, list, delete) Batch spec format: collection_or_rel:op1,op2 Custom tool names and descriptions: Use --config with full JSON for advanced control:
rekor endpoints upsert invoicing-agent --database my-ws --config '{

"name": "Invoicing Agent",

"tools": [

{

"collection": "invoices",

"operations": ["get", "list"],

"name_override": "search_invoices",

"description_override": "Search invoices by customer, status, or date range"

},

{

"collection": "payments",

"operations": ["create", "get", "list"],

"description_override": "Manage payment documents for invoices"

}

],

"relationships": [

{

"rel_type": "invoice_payment",

"operations": ["create", "list"],

"name_override": "assign_payment",

"description_override": "Link a payment to an invoice"

}

],

"sql_query": true

}'

Or from a file: --config @endpoint.json

Connect agents to the endpoint URL with a token scoped to exactly one database. The agent sees only the tools you configured — fully domain-specific, no Rekor concepts.

Endpoints can only be created/modified in preview databases. Promote to production when ready.

API Tokens

Create scoped tokens for agents, integrations, and CI/CD. Tokens can be restricted to specific databases, collections, environments, and permissions. Tokens are hashed before storage — the raw value is shown only once on creation.

# Create a full-access token

rekor tokens create --name "my-key" --grants '[{"scope":{"databases":[""]},"permissions":[""]}]'

Create a scoped token (read-only on one database)

rekor tokens create --name "client-a-reader" \

--grants '[{"scope":{"databases":["client-a"],"environments":["production"]},"permissions":["read:documents","read:collections"]}]'

Create a token with expiration

rekor tokens create --name "temp-key" \

--grants '[{"scope":{"databases":[""]},"permissions":[""]}]' \

--expires-at 2026-12-31T23:59:59Z

List tokens (shows status, last_used_at, expires_at)

rekor tokens list

Revoke a token

rekor tokens revoke <token_id>

Permissions: read:documents, write:documents, read:collections, write:collections, read:relationships, write:relationships, read:attachments, write:attachments, read:hooks, write:hooks, read:triggers, write:triggers, read:endpoints, write:endpoints, read:databases, write:databases, or for all. Scope fields: databases (required), collections (optional — omit for all), environments (optional — production, preview, or omit for both).

Tokens enforce a privilege ceiling — you can only create tokens with equal or narrower scope than your own.

Account & Diagnostics

rekor whoami                 # Show the authenticated identity

rekor status # Auth, connectivity, and CLI version diagnostics

rekor update # Update the CLI to the latest published version

The CLI checks for a newer published version in the background and prints a one-line notice when an update is available. Disable the check with REKOR_NO_UPDATE_CHECK=1 (also disabled when NO_UPDATE_NOTIFIER or CI is set).

---

Output Format

Add --output json for machine-readable JSON output (default is table).

rekor documents get invoices rec_abc --database my-ws --output json

Data Input

Any --data, --schema, --tools, or --operations flag accepts:

  • Inline JSON: '{"key":"value"}'
  • File reference: @path/to/file.json

Environments

Databases are either production or preview. As an agent, you can:

  • Read from any database (production or preview)
  • Write documents and relationships to any database
  • Modify schemas (collections, triggers, hooks) only in preview databases

To modify schemas, create a preview database first:

rekor databases create-preview my-database --name "add-invoices"

Then work in the preview database:

rekor collections upsert invoices --database my-database--add-invoices \

--schema '{"type":"object","properties":{"amount":{"type":"number"}}}'

When you're done, ask a human operator to promote your changes:

# Human runs: rekor databases promote my-database --from my-database--add-invoices
Promotion is a human-only operation. You cannot promote directly. Always work in preview for schema changes.

Best Practices

  • Use external IDs for idempotent upserts — retry-safe, no duplicates.
  • One database per context — keeps data isolated (per test run, per agent, per environment).
  • Preview for schema changes — modify collections, triggers, hooks in a preview database. Ask a human to promote.
  • Batch for atomicity — when multiple writes must succeed or fail together.
  • Relationships over nested data — link documents instead of embedding. Enables traversal and flexible queries.
  • Schema first — define the collection schema before writing documents. Validation catches bad data early.
  • Query delay after writes — single-document reads (documents get, relationships get) reflect writes immediately; sql and query-relationships may take a moment to catch up. If you need to query right after writing, wait briefly or use direct gets.
  • Set token expiration — use --expires-at for short-lived tokens. Revoke unused tokens promptly.
  • Save tokens immediately — the raw token is shown only once on creation. Store it securely before closing the terminal.