Connect

LogRack is a shared pile of logs — tasks, ideas, thoughts, memories, context, notes — that you and any LLM write to and read from. Connect your AI tools here.

Getting Started

1. Go to API Keys and create one key per LLM client, named after it — claude-code, cursor, chatgpt. The key name becomes the source.client provenance on every log that client writes, so you always know who left what.

2. Grant logs:write, logs:read, and racks:read to a typical client.

3. Connect via MCP (below). The tools teach the model when to write; a good habit to encourage in your prompts: "call get_context or catch_up at session start, and log decisions, tasks, and a session summary before you finish."

Connect via MCP

MCP is how LLMs reach the pile. The fastest route is the hosted server at https://mcp.lograck.ai/mcp — nothing to install.

Sign in with OAuth

If your client supports remote MCP servers, give it the URL and nothing else. It will send you here to sign in, pick a workspace, and choose what the app may do — then it is connected. Manage or disconnect it later under Connected Apps.

MCP server URL
https://mcp.lograck.ai/mcp

Or bring an API key

Clients that let you set a header can skip OAuth. The key you send is the key your session writes with, so your logs land in your workspace under that key's name.

Claude Code

Terminal
claude mcp add --transport http lograck https://mcp.lograck.ai/mcp \
  --header "Authorization: Bearer lrk_your_api_key"

Cursor

.cursor/mcp.json
{
  "mcpServers": {
    "lograck": {
      "url": "https://mcp.lograck.ai/mcp",
      "headers": { "Authorization": "Bearer lrk_your_cursor_key" }
    }
  }
}

Claude Desktop

Claude Desktop launches local commands, so reach the hosted server through mcp-remote:

claude_desktop_config.json
{
  "mcpServers": {
    "lograck": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote", "https://mcp.lograck.ai/mcp",
        "--header", "Authorization: Bearer lrk_your_desktop_key"
      ]
    }
  }
}

ChatGPT and other connectors

Add a connector pointing at https://mcp.lograck.ai/mcp. Sign in when it asks, or give it the header Authorization: Bearer lrk_... instead. All LogRack tools are discovered automatically.

Run the bridge yourself

The @lograck/ai-mcp-bridge package is the same MCP server, run locally over stdio. Build it once with yarn build in packages/ai-mcp-bridge.

Claude Code

Terminal
claude mcp add lograck \
  --env LOGRACK_URL=https://your-domain \
  --env LOGRACK_API_KEY=lrk_your_api_key \
  -- node /path/to/lograck/packages/ai-mcp-bridge/dist/index.js

Claude Desktop

Add to claude_desktop_config.json:

claude_desktop_config.json
{
  "mcpServers": {
    "lograck": {
      "command": "node",
      "args": ["/path/to/lograck/packages/ai-mcp-bridge/dist/index.js"],
      "env": {
        "LOGRACK_URL": "https://your-domain",
        "LOGRACK_API_KEY": "lrk_your_api_key"
      }
    }
  }
}

Cursor

Add to .cursor/mcp.json (use a separate key named cursor):

.cursor/mcp.json
{
  "mcpServers": {
    "lograck": {
      "command": "node",
      "args": ["/path/to/lograck/packages/ai-mcp-bridge/dist/index.js"],
      "env": {
        "LOGRACK_URL": "https://your-domain",
        "LOGRACK_API_KEY": "lrk_your_cursor_key"
      }
    }
  }
}

Self-hosted remote server

Prefer your own host instead of ours? Run the bridge in SSE mode and point connectors at it — callers authenticate the same way:

Remote deployment
# Run your own bridge with SSE transport (see packages/ai-mcp-bridge).
# No key on the server — every caller sends its own.
LOGRACK_MCP_TRANSPORT=sse \
LOGRACK_MCP_PORT=8080 \
LOGRACK_URL=https://your-domain \
node dist/index.js

# Clients then point at https://your-mcp-host/mcp and send:
#   Authorization: Bearer lrk_their_own_key

MCP Tools

ToolWhat it does
log_writeDrop a log on the pile (task, idea, thought, memory, context, note). Supports racks, tags, pinning, TTL, and supersession.
log_searchBrowse/search by kind, tags, rack, status, writer, or full text.
log_getOne log by ID, with its supersession chain.
log_updateMark a task done, pin/unpin, retag.
log_deletePermanently delete a log (prefer supersession for edits).
get_contextToken-budgeted context pack: pinned → matches → recent. Call at session start.
catch_upEverything written since your cursor — the agent-to-agent handoff primitive.
list_racks / create_rackManage per-project piles (log_write auto-creates racks by name).

REST API

Write a log

POST/api/logs
Scope: logs:write
cURL
curl -X POST https://your-domain/api/logs \
  -H "Authorization: Bearer lrk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "task",
    "title": "Ship the racks page",
    "body": "Portal needs a racks browser.",
    "tags": ["ui"],
    "rack": "lograck-pivot"
  }'
Response (201)
{
  "success": true,
  "data": {
    "acceptedCount": 1,
    "rejectedCount": 0,
    "logs": [
      {
        "id": "log_abc123",
        "kind": "task",
        "title": "Ship the racks page",
        "body": "Portal needs a racks browser.",
        "tags": ["ui"],
        "status": "open",
        "pinned": false,
        "rackId": "rack_xyz",
        "source": { "client": "claude-code" },
        "supersededById": null,
        "timestamp": "2026-07-29T14:30:00.000Z",
        "ingestedAt": "2026-07-29T14:30:00.120Z",
        "expiresAt": null
      }
    ],
    "rackId": "rack_xyz",
    "rackCreated": true
  }
}

Batch writes: send { "logs": [ ... ] } (max 100). Ephemeral logs: add ttlSeconds or expiresAt.

Supersede an older log
# Bodies are immutable — update content by superseding the old log
curl -X POST https://your-domain/api/logs \
  -H "Authorization: Bearer lrk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "memory",
    "body": "User prefers yarn (not npm) in all projects.",
    "supersedes": "log_old456"
  }'

Search the pile

GET/api/logs
Scope: logs:read
cURL
curl "https://your-domain/api/logs?kind=task&status=open&rack=lograck-pivot" \
  -H "Authorization: Bearer lrk_your_api_key"
ParameterTypeRequiredDefaultDescription
kindstringNoFilter by kind (task, idea, thought, memory, context, note). Can be repeated.
tagsstringNoComma-separated tags; a log must have all of them.
statusstringNoTask status filter: "open" or "done".
pinnedbooleanNoOnly pinned (true) or unpinned (false) logs.
sourcestringNoFilter by the client that wrote the log (e.g. "claude-code", "portal").
qstringNoFull-text search over title + body (websearch syntax). Max: 200 chars.
rack / rackIdstringNoFilter by rack name or ID.
includeSupersededbooleanNofalseInclude logs that were replaced by newer versions.
limitnumberNo100Maximum number of results. Max: 200.
cursorstringNoOpaque cursor for pagination. Use nextCursor from the previous response.

Context pack

GET/api/context

A ready-to-paste markdown bundle of the most relevant logs under a token budget: pinned first, then matches for q, then recent. Every log carries a provenance line and the header frames the content as data, not instructions.

cURL
curl "https://your-domain/api/context?rack=lograck-pivot&maxTokens=4000" \
  -H "Authorization: Bearer lrk_your_api_key"
Response
{
  "success": true,
  "data": {
    "pack": "# LogRack context pack\n...provenance-labeled markdown...",
    "tokenEstimate": 3800,
    "logCount": 24,
    "truncated": false,
    "generatedAt": "2026-07-29T14:31:00.000Z"
  }
}

Catch up (delta sync)

GET/api/sync
cURL
# First call (no cursor): start tracking, returns a fresh cursor
curl "https://your-domain/api/sync" \
  -H "Authorization: Bearer lrk_your_api_key"

# Later: everything written since the cursor, oldest first
curl "https://your-domain/api/sync?cursor=eyJ0cyI6..." \
  -H "Authorization: Bearer lrk_your_api_key"

Update / delete

PATCH/api/logs/:id
cURL
# Mark a task done / pin a log (only status, pinned, tags are mutable)
curl -X PATCH https://your-domain/api/logs/log_abc123 \
  -H "Authorization: Bearer lrk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "status": "done" }'

DELETE /api/logs/:id removes a log permanently. Racks live under /api/racks.

TypeScript SDK

@lograck/api (published on npm) wraps the whole API with typed namespaces: logs, context, sync, racks, and more.

TypeScript
// yarn add @lograck/api
import { LogRackClient } from '@lograck/api';

const client = new LogRackClient({
  apiKey: 'lrk_your_api_key',
  baseURL: 'https://your-domain',
});

// Write to the pile
await client.logs.write(
  { kind: 'idea', body: 'What if racks had shared templates?' },
  { rack: 'lograck-pivot' }
);

// Catch up on what other agents wrote
const delta = await client.sync.catchUp({ cursor: savedCursor });
for (const log of delta.logs) {
  console.log(`${log.source.client}: [${log.kind}] ${log.body}`);
}

Concepts

ConceptMeaning
kindWhat a log is: task (something to do), idea, thought (reasoning worth keeping), memory (durable fact), context (session state — usually with a TTL), or note.
supersedeBodies are immutable. To update content, write a new log with supersedes: oldId; the old one drops out of default reads but stays in the chain.
racksPer-project piles. Logs without a rack live in the loose pile. Racks are auto-created when you write with a new rack name.
pinnedPinned logs surface first in context packs.
sourceProvenance on every log: which client wrote it (from the API key name or "portal"), plus optional model and session identifiers.
expiryEphemeral logs carry ttlSeconds or expiresAt and are cleaned up once past due. Retention policies age out everything else (pinned exempt).

Authentication & Scopes

All programmatic requests use an API key in the Authorization header with the Bearer scheme. Keys are workspace-scoped and hashed at rest.

ScopeGrants
logs:writeWrite logs (POST /api/logs), update status/pin/tags, delete
logs:readSearch logs, context packs, catch-up sync, stats
racks:read / racks:writeList/read and create/update/delete racks
keys:read / keys:writeList, create, and revoke API keys (admin)

Rate Limiting

Writes are rate limited per API key; over-limit requests get 429 Too Many Requests with Retry-After and X-RateLimit-* headers.