TablaCognita

Overview

TablaCognita is a markdown editor where you and your AI write together in real-time. Your AI agent connects via the Model Context Protocol (MCP) and gets a full set of tools to read, edit, and manage your document — while you see every change live in the browser.

Key principle: Your documents never leave your browser. The server is a relay — it routes messages between your browser and your AI agent, but stores zero document content.

Quick Start

  1. Sign up at the login page (email/password or Google).
  2. Open the editor — you'll land on a blank document ready to write.
  3. Copy your API key from the Account panel (top-right button).
  4. Add TablaCognita as an MCP server in your AI client (see below).
  5. Start writing together — ask your AI to read, edit, or restructure your document.

Connecting an AI Agent

TablaCognita works with any MCP-capable AI client: Claude Desktop, Claude Code, Cursor, Windsurf, or custom clients.

Claude Desktop

Add this to your claude_desktop_config.json:

{
  "mcpServers": {
    "tablacognita": {
      "url": "https://app.tablacognita.com/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Claude Code (CLI)

Add via the CLI:

claude mcp add tablacognita \
  --transport http \
  --url https://app.tablacognita.com/mcp \
  --header "Authorization: Bearer YOUR_API_KEY"

Other MCP Clients

Use these connection details:

SettingValue
URLhttps://app.tablacognita.com/mcp
TransportStreamable HTTP
AuthenticationAuthorization: Bearer YOUR_API_KEY

Your API key is shown in the Account panel inside the editor. Click "Copy" to get it.

Editor Interface

The editor has two panes:

The divider between panes is draggable. The toolbar provides file operations and account access.

When an AI agent edits your document, you'll see changes appear in real-time. Full document replacements require your confirmation via a dialog.

Context menu: Right-click on selected text to access formatting transforms (uppercase, lowercase, title case) and Send to AI, which emits a user_selection notification to all connected agents with the selected text and cursor context.

File Operations

ButtonAction
NewStart a blank document
OpenOpen a local .md file (File System Access API or file input fallback)
SaveSave to a local file (Save As dialog or overwrite current)
Copy (editor)Copy raw markdown to clipboard
Copy (preview)Copy as rich text (HTML)
CloudOpen from or save to Google Drive, OneDrive, Dropbox, or Box (visible when configured)

Keyboard Shortcuts

ShortcutAction
Ctrl+S / Cmd+SSave file

Capability Discovery

get_capabilities

Get server capabilities, tier limits, and feature flags. Call this first to understand what is available.

// No parameters required
→ {
  tier: "free",
  agent_id: "agent_abc",
  limits: { max_agents: 1, max_editors: "unlimited", max_payload_bytes: 2097152, relay_timeout_ms: 90000 },
  current: { agents_connected: 1, editors_connected: 1 },
  features: { cloud_storage: true, snapshots: true, fuzzy_matching: true, section_locking: true, write_confirmation: true },
  notification_events: ["cursor_moved", "section_deleted", "section_renamed", "document_changed", "user_selection"]
}

Session Discovery

list_sessions

List all connected editor tabs with their session IDs and document names. Use this to discover which editor_session to target when multiple tabs are open.

// No parameters required
→ {
  sessions: [
    { editor_session_id: "editor_abc123", document_name: "README.md", connected_at: "..." },
    { editor_session_id: "editor_def456", document_name: "notes.md", connected_at: "..." }
  ],
  count: 2,
  hint: "Multiple tabs open. Pass editor_session to target a specific tab."
}

Session targeting: All tools accept an optional editor_session parameter. When only one tab is open, it auto-resolves. When multiple tabs are open, you must pass editor_session or you'll get an AMBIGUOUS_SESSION error with the session list.

Reading Tools

read_document

Read the full document content. Returns the complete markdown text, total line count, section count, unsaved changes flag, and a content_hash for optimistic concurrency.

// No parameters required
→ { content, total_lines, sections, has_unsaved_changes, content_hash }

get_structure

Get the section outline without full content. Each section includes its ID, heading, level, line range, dirty flag, and lock status. This is the cheapest way to orient in a long document.

// No parameters required
→ { sections: [{ id, heading, level, line_start, line_end, dirty, locked, locked_by }] }

get_section

Read a single section by heading text or section ID. Resets the dirty flag for that section (so you won't see it in get_dirty_regions until the user edits it again).

{ section: "Introduction" }     // by heading
{ section: "sec_1" }            // by ID
→ { id, heading, level, line_start, line_end, content, dirty, content_hash }

Editing Tools

replace_section

Replace the full content of a section. Automatically acquires and releases a lock during the operation.

{
  section: "Conclusion",
  content: "## Conclusion\n\nUpdated conclusion text.",
  keep_heading: false,   // optional: if true, only replace the body
  expected_hash: "abc12" // optional: fail if document changed since last read
}
→ { id, heading, lines_before, lines_after, new_section_ids, lock_held_ms, content_hash }

replace_text

Targeted find-and-replace with four-level fuzzy matching. Ideal for surgical edits within a section.

{
  search: "old text",
  replace: "new text",
  expected_hash: "abc12",  // optional: fail if document changed
  options: {
    fuzzy: true,           // enable fuzzy matching (default: true)
    markdown_aware: true,  // strip markdown when matching (default: true)
    section: "Introduction", // limit to a section (optional)
    occurrence: 1          // which match if multiple (optional)
  }
}
→ { matched, section_id, line, fuzzy_applied, markdown_stripped, content_hash }

insert_after

Insert content after the end of a named section.

{
  section: "Introduction",
  text: "\n## New Section\n\nContent here.",
  expected_hash: "abc12"  // optional
}
→ { inserted_at_line, new_section_ids, content_hash }

append

Append content to the end of the document.

{
  text: "\n## Appendix\n\nAdditional content.",
  expected_hash: "abc12"  // optional
}
→ { inserted_at_line, new_section_ids, content_hash }

write_document

Full document replacement. Requires user confirmation — the user sees a dialog with the new content and must click Accept. Times out after 60 seconds.

{
  content: "# New Document\n\nFresh start."
}
→ { accepted, lines_before, lines_after, diff_summary }

The user can reject the replacement. If they do, you'll receive a CONFIRMATION_DENIED error. If they don't respond within 60 seconds, you'll get CONFIRMATION_TIMEOUT.

Document Management

open_document

Open a new document. Pass no parameters for a blank document, or a URL to fetch markdown from the web.

{ }                                    // blank document
{ source: "https://example.com/doc.md" } // from URL
→ { total_lines, sections, source_type, source_ref }

Local file paths are not supported in hosted mode. Use a URL or ask the user to open a file via the editor's Open button.

snapshot

Create a named checkpoint stored in the browser's IndexedDB. Use this before major changes.

{
  label: "Before restructuring"
}
→ { storage: "local", snapshot_id, timestamp, lines }

get_revision_history

List all available snapshots.

→ { revisions: [{ id, label, source, timestamp, lines }] }

restore_snapshot

Restore a previous snapshot. An auto-save is created first so the current state isn't lost.

{
  snapshot_id: "snap_abc123"
}
→ { accepted, lines_restored, label, snapshot_timestamp }

Collision Avoidance

request_edit_lock

Explicitly lock a section for multi-step editing operations. Locks are per-agent — other agents can't edit a locked section, but the owning agent can.

{
  section: "Introduction",
  ttl: 30  // seconds, default 30, max 120
}
→ { id, heading, locked, ttl, locked_by, user_cursor_in_section }

release_lock

Release a held lock. Pass "all" to release all locks held by this agent.

{ section: "sec_1" }  // specific section
{ section: "all" }    // release all
→ { released: ["sec_1"] }

Auto-locking: replace_section, replace_text (section-scoped), and insert_after automatically acquire and release a lock for the duration of the edit. Auto-locks respect explicit locks held by other agents. You only need explicit locks for multi-step operations (read → modify → write).

Context & Polling

get_cursor_context

Get the user's cursor position, the section they're in, nearby text, and any selected text.

→ { section_id, section_heading, line, column, nearby_text, selection, idle_seconds }

get_dirty_regions

Get sections the user has changed since the agent last read them. Useful for knowing what to re-read.

→ { dirty_sections: [{ id, heading, line_start, line_end }], clean_sections: 5, has_unsaved_changes: true }

poll_context

Poll for queued events (cursor moves, section renames, deletions, user selections, document changes) plus current cursor state. Events are per-agent — each agent gets its own queue.

→ {
  events: [
    { event: "cursor_moved", data: { section_id, line }, event_id: 1, timestamp },
    { event: "section_deleted", data: { section_id, heading }, event_id: 2, timestamp },
    { event: "section_renamed", data: { section_id, old_heading, new_heading }, event_id: 3, timestamp },
    { event: "user_selection", data: { section_id, selected_text, instruction }, event_id: 4, timestamp },
    { event: "document_changed", data: { change_type, sections_affected }, event_id: 5, timestamp }
  ],
  events_dropped: 0,
  cursor: { section_id, line, idle_seconds },
  dirty_count: 2
}

Each event has a monotonically increasing event_id. The events_dropped field tells you how many events were evicted from the queue (max 50) since your last poll — use this to detect if you missed events and need to re-read the document.

How It Works

TablaCognita has three components:

  AI Agent                  Relay Server               Browser Editor
  ────────                  ────────────               ──────────────
  MCP tool call ──────────→ Route by user ────────────→ Execute on document
                ←────────── Return result  ←──────────── Send response
                            (stores nothing)            (source of truth)

Multi-Agent & Multi-Tab

Pro tier users can have multiple editor tabs and multiple AI agents connected simultaneously.

Multi-Tab

Each browser tab gets a unique editor_session_id from the relay on connect. When an agent makes a tool call:

Agents discover sessions with list_sessions, which returns session IDs, document names, and connection timestamps.

Multi-Agent

Multiple AI clients (e.g., Claude Desktop + Claude Code) can connect to the same user account simultaneously. Each agent gets:

Multi-tab is available on all tiers with no limit. Free tier is limited to 1 agent; Pro tier allows up to 10 agents.

Zero Data Hosting

The server stores only user accounts (email, password hash, tier, API key). It never sees, stores, or caches your document content. All document state lives in the browser's memory. Snapshots are stored in the browser's IndexedDB.

This means:

Section Model

TablaCognita parses your document into sections based on markdown headings (#, ##, etc). Each section gets a stable ID (e.g., sec_1) that persists across edits, so agents can reference sections reliably even as the document changes.

Sections include:

Content before the first heading is treated as a section with a null heading.

Fuzzy Matching

The replace_text tool uses a four-level matching strategy to handle the common case where an AI quotes text slightly differently from the document:

  1. Exact match — Literal string search
  2. Whitespace-normalized — Collapses runs of whitespace
  3. Markdown-stripped — Removes **, *, `, []() and other markdown formatting before matching
  4. Subsequence / best-effort — Finds the closest match by character overlap

The response tells you which level was used (fuzzy_applied, markdown_stripped).

Optimistic Concurrency

To prevent lost updates when the user and agent edit simultaneously, reading tools return a content_hash and writing tools accept an optional expected_hash:

  1. Read the document or section — note the content_hash in the response.
  2. Write your changes, passing the hash as expected_hash.
  3. If the document changed between read and write, the operation fails with CONTENT_CHANGED. Re-read and retry.

The expected_hash parameter is optional — omit it to skip the check (useful for simple, non-conflicting appends).

Tools that support expected_hash: replace_section, replace_text, insert_after, append. Tools that return content_hash: read_document, get_section, and all successful write operations.

Notifications

The browser sends real-time notifications to all connected agents when the user:

Cursor moves are debounced (100ms default). All other events fire immediately. Each agent has its own event queue (max 50 events). Use poll_context to drain your queue.

Authentication

TablaCognita supports two sign-in methods:

Browser sessions use signed tokens stored in localStorage (30-day expiry). MCP clients authenticate with API keys or via the OAuth 2.0 PKCE flow (used automatically by Claude Desktop).

Tiers & Limits

FeatureFreePro ($4.99/mo)
AI agent connections110
Editor tabsUnlimitedUnlimited
All MCP toolsYesYes
Snapshots (browser-local)YesYes
Cloud storage (Google Drive, OneDrive, Dropbox, Box)Yes
Export (PDF, HTML)Yes

API Key

Your API key is shown in the Account panel inside the editor. It starts with tc_ and is used to authenticate MCP clients.

To regenerate your API key (invalidating the old one), use the Account panel. After regeneration, update your MCP client configuration.

Billing

Pro tier ($4.99/mo) is managed via Stripe Checkout. Click Upgrade to Pro in the Account panel to start a subscription. You'll be redirected to Stripe's secure checkout page.

After subscribing, manage your subscription (update payment method, cancel) via the Manage Subscription button, which opens the Stripe Customer Portal.

When you cancel, your tier reverts to Free at the end of the billing period. Downgrading reduces your agent limit to 1 — existing connections beyond the limit will be rejected on the next connect.

Cloud Storage

Open and save documents directly to cloud storage providers. Supported providers:

Click the Cloud button in the toolbar to connect a provider, open a file, or save the current document. Each provider requires a one-time OAuth authorization in a popup window.

Zero-data preserved: Document content flows directly from your browser to the cloud provider's API. The TablaCognita server never sees your document content — it only proxies the OAuth token exchange (which requires a client secret).

Tokens are stored in your browser's localStorage and persist across sessions. The Cloud button only appears when at least one provider is configured by the server administrator.

Error Codes

When a tool call fails, the response includes an error object with a code field:

CodeMeaning
NO_EDITOR_CONNECTEDNo browser tab is open for this user. The user needs to open the editor.
SECTION_NOT_FOUNDNo section matches the given heading/ID. Check suggestions in the error for similar sections.
SECTION_LOCKEDAnother agent holds a lock on this section. Wait or ask the user.
FUZZY_MATCH_FAILEDThe search text wasn't found, even with fuzzy matching. Check candidates in the error.
FUZZY_MATCH_AMBIGUOUSMultiple matches found. Use the occurrence option or narrow with section.
CONFIRMATION_DENIEDUser rejected the write_document replacement.
CONFIRMATION_TIMEOUTUser didn't respond to write_document within 60 seconds.
SNAPSHOT_NOT_FOUNDThe given snapshot ID doesn't exist in browser storage.
RELAY_TIMEOUTThe browser didn't respond within 90 seconds. Tab may be inactive or closed.
TIER_LIMITAgent limit reached for your tier. Upgrade or disconnect an agent.
AMBIGUOUS_SESSIONMultiple editor tabs open and no editor_session specified. Use list_sessions to discover sessions. The error includes the session list.
AUTH_REQUIREDMissing or invalid API key / token.
CONTENT_CHANGEDDocument changed since your last read. Re-read and retry. Returned when expected_hash doesn't match.

Self-Hosting

The standalone (single-user) TablaCognita editor is open source and can be self-hosted:

git clone https://github.com/PStryder/TablaCognita.git
cd TablaCognita
npm install
npm start

The standalone version runs with no authentication and supports stdio MCP transport for local AI clients. See the GitHub repository for details.