Skip to main content
Glama

English | Русский · Support on DonationAlerts

chat-export-server

Local MCP server for the Chat Export Chrome extension. List, read, export, create, delete, and ask in your own chats on eight web providers (ChatGPT, Grok, X Grok, Claude, GitHub Copilot, Qwen, DeepSeek, Perplexity) — through the extension in your browser. Everything stays on 127.0.0.1: job queues, SQLite chat cache, audit log, optional dashboard UI.

Why this MCP (for awesome-list reviewers)

Most LLM MCP servers wrap a vendor HTTP API (keys, quotas, cloud data path). Chat Export is different: the agent talks MCP to a local process; that process drives the web UIs you are already logged into via the Chrome extension bridge. No OpenAI/Anthropic/xAI API keys for chat ops, no chat bodies leaving 127.0.0.1, and delete/create stay off until you opt in (allowDestructive + confirm). Pair with the extension zip; point Cursor (or any MCP client) at http://127.0.0.1:8765/mcp.

Related MCP server: Chromanche

The problem

IDE agents need structured access to web LLM sessions without pasting cookies or paying per-token cloud APIs. You want MCP tools, retries, and queues — but data must not leave your machine.

What you get

Platform

Automation

ChatGPT

list / get / export / create / delete / ask

Grok (grok.com)

same

X Grok

same

Claude

same

GitHub Copilot web

same

Qwen (chat.qwen.ai)

same

DeepSeek (chat.deepseek.com)

same

Perplexity (perplexity.ai)

same

flowchart TB
  EXT[Chrome extension] <-->|WS /ws ceb_| HUB[Browser hub]
  HUB --> Q[Job lanes per browser]
  Q --> MCP[MCP /mcp]
  MCP --> AGENT[Cursor etc. cem_/cek_]
  HUB --> API[Dashboard /dashboard/api]
  API --> UI[Dashboard static UI]
  Q --> DB[(SQLite cache + WAL)]

Quick start (3 steps)

  1. Extension — load chat-export-extension unpacked; enable bridge in popup; open provider tabs logged in.

  2. Server — npm install && npm run build, chat-export-server key new, chat-export-server start → http://127.0.0.1:8765.

  3. MCP client — point to http://127.0.0.1:8765/mcp with Authorization: Bearer <key>; run browsers_bind then chats_list / chats_export / chat_ask.

Wiki: MCP tools and profiles.

Install and build

npm install
npm run build   # tsc -> dist/
npm link        # optional: chat-export-server on PATH

Requires Node 22.13+ (node:sqlite).

Script

Purpose

npm run build

compile to dist/

npm run typecheck

tsc --noEmit

npm start

node dist/cli/index.js start

npm run dev

tsx watch src/cli/index.ts start

npm run mcp

stdio MCP attached

First run

chat-export-server key new    # print once; hash stored
chat-export-server start
# WS   /ws, /ext  — extension bridge
# MCP  /mcp       — Bearer key
# GET  /health

Data under ~/.chat-export (override CHAT_EXPORT_HOME):

~/.chat-export/
  config.json    # host, port, exportDir
  keys.json      # hashes only (chmod 600)
  audit.log      # MCP/RPC audit
  exports/       # chats_export output
chat-export-server key rotate
chat-export-server key list
chat-export-server status

Configuration

Environment variables

Copy .env.example to .env in the server directory (or set CHAT_EXPORT_ENV_FILE). Container and process env override config.json where noted.

Variable

Purpose

CHAT_EXPORT_HOME

Data directory (default ~/.chat-export; /data in Docker)

CHAT_EXPORT_HOST

Bind address (127.0.0.1 locally; use 0.0.0.0 in containers)

CHAT_EXPORT_PORT

HTTP port (default 8765)

CHAT_EXPORT_ALLOWED_HOSTS

Extra Host header names allowed (comma-separated)

CHAT_EXPORT_DASHBOARD_ENABLED

true / false

CHAT_EXPORT_ADMIN_USER / CHAT_EXPORT_ADMIN_PASS

Dashboard login (overrides config.json)

CHAT_EXPORT_MCP_TOKEN

Optional preset cem_… token hash on start

CHAT_EXPORT_BROWSER_TOKENS

browserId:ceb_…[:label] comma list for auto-enroll

CHAT_EXPORT_ALLOW_DESTRUCTIVE

true / false

CHAT_EXPORT_DB_DRIVER

DB driver override (sqlite default)

CHAT_EXPORT_DB_URL

DB URL when not using default SQLite path

CHAT_EXPORT_SKIP_MCP_CONFIG

Set 1 to skip auto-writing Cursor MCP config on key new

Docker and Colima

This repo ships a multi-stage Dockerfile (Node 22 Alpine, non-root user, CHAT_EXPORT_HOME=/data, listen 0.0.0.0:8765, /health check). Docker Desktop is not required — use Colima on macOS:

colima start
docker context use colima   # or: docker --context colima …
cp .env.example .env        # set CHAT_EXPORT_ADMIN_PASS at minimum
docker --context colima compose build
docker --context colima compose up -d
curl -sS http://127.0.0.1:8765/health

Published only on 127.0.0.1:8765. Persistent data: Compose volume chat-export-data → /data.

Stop when finished so a local chat-export-server daemon can reclaim the port:

docker --context colima compose down

Optional split stack (nginx serves dashboard static, proxies /dashboard/api, /mcp, /ws, /ext, /enroll, /health to the server): build from the maintainer tree chat-export-dashboard/Dockerfile and projects/Chat Export/docker-compose.yml (see dashboard repo).

config.json (under CHAT_EXPORT_HOME)

Created on first start. Example (secrets redacted):

{
  "host": "127.0.0.1",
  "port": 8765,
  "exportDir": "~/.chat-export/exports",
  "rpcTimeoutMs": 60000,
  "dashboard": {
    "enabled": true,
    "username": "admin",
    "password": "changeme"
  },
  "registeredBrowsers": [],
  "settings": {
    "platformsEnabled": ["chatgpt", "grok", "grok-x", "copilot", "claude"],
    "allowDestructive": false
  }
}

Change dashboard.password before exposing the host beyond loopback. Server listens on 127.0.0.1:8765 by default (/health, /mcp, /ws, /dashboard/).

Tokens

Prefix

Used by

cek_ / cem_

MCP and CLI (Authorization: Bearer … on /mcp)

ceb_

Chrome extension WebSocket (/ws); enroll via CLI or dashboard

Only hashes of MCP keys are stored in keys.json (mode 600). Plaintext is shown once at chat-export-server key new.

Detached daemon (maintainer workspace)

In the Chat Export maintainer tree, tmp-scripts/ensure-daemon.mjs can keep chat-export-server running on :8765, write ~/.chat-export/server.pid, and optionally sync Bearer cem_… into .cursor/mcp.json (--sync-mcp-json). Equivalent manual start: chat-export-server start.

Cursor MCP (~/.cursor/mcp.json or project .cursor/mcp.json)

HTTP (server already running):

{
  "mcpServers": {
    "chat-export": {
      "url": "http://127.0.0.1:8765/mcp",
      "headers": {
        "Authorization": "Bearer cem_YOUR_KEY_HERE"
      }
    }
  }
}

Profiles: expose the full tool surface (chats_*, jobs_*, chat_cache_*, projects_*) for IDE agents. A small read-focused subset (list/get/export, browsers, platforms) is documented on the wiki MCP tools page — match your client’s token budget. Open WebUI via mcpo can proxy the same HTTP endpoint if your setup uses an OpenAPI bridge on another port.

After MCP is configured, run browsers_bind so tools target your connected Chrome profile.

Extension bridge settings: chat-export-extension README.

Background jobs

Long work (chat_ask, export, bulk delete) uses per-browser lanes:

  • jobs_enqueue, jobs_status, jobs_list, jobs_cancel

Cursor MCP config

HTTP (server already running):

{
  "mcpServers": {
    "chat-export": {
      "url": "http://127.0.0.1:8765/mcp",
      "headers": {
        "Authorization": "Bearer cek_YOUR_KEY_HERE"
      }
    }
  }
}

stdio:

{
  "mcpServers": {
    "chat-export": {
      "command": "chat-export-server",
      "args": ["start", "--stdio"],
      "env": {
        "CHAT_EXPORT_HOME": "/Users/YOU/.chat-export"
      }
    }
  }
}

MCP tools (overview)

Tool

Purpose

browsers_list / browsers_bind / browsers_status

Connected Chrome profiles

platforms_list

Enablement matrix

chats_list / chats_get / chats_export

Read and write exports

chats_create / chats_delete

UI drive (delete needs confirm: true)

chat_ask

Send prompt; wait or job queue

jobs_*

Async pipeline

chat_cache_*

Temp SQLite cache for long threads

Full list evolves with releases; see wiki MCP Tools.

Layout

packages/protocol/   wire contract v1
src/core/            config, keys, audit, db, jobs, cache
src/hub/             WebSocket BrowserHub
src/mcp/             MCP HTTP + stdio
src/cli/             CLI entry
src/dashboard-api.ts /dashboard/api + dashboard/public
dashboard/           git submodule → chat-export-dashboard

git submodule update --init after clone.

Security

  • Loopback only by default (127.0.0.1). Change host only if you understand exposure.

  • Keys (cek_/cem_) grant full local API — never commit or publish.

  • Browser tokens (ceb_) are separate from MCP keys.

  • chats_delete and similar need allowDestructive on extension and server plus confirm: true.

  • Append-only audit for sensitive RPC. See SECURITY.md.

  • Personal data portability is the goal; you must comply with each provider ToS. Not legal advice.

Repo

Role

chat-export-extension

Content scripts + WS client

chat-export-server (this)

MCP hub, jobs, cache

chat-export-dashboard

Operator UI submodule

Wiki

Install, security, FAQ

Support the project

Tips are voluntary; they do not purchase support SLAs or private builds.

FAQ

  • No telemetry; no external servers for core operation.

  • Only your chats, from your browser session.

  • Shared wording: packages/faq.md.

Contributing

See extension CONTRIBUTING.md (protocol PLATFORMS, server MCP, config) and wiki Contributing.

License

See LICENSE.

Available Tools

27 tools
browsers_bindA
Idempotent

Set the active browserId for subsequent chat/project/job tools. Call after browsers_list; does not open tabs by itself. Prefer browsers_status to inspect the current binding without changing it.

Inputs: browserId (required).

browsers_listA
Read-onlyIdempotent

List Chrome profiles currently connected through the Chat Export extension bridge. Use before browsers_bind to pick a browserId; use browsers_status for the already-bound session only.

Inputs: no named parameters.

browsers_statusA
Read-onlyIdempotent

Show the currently bound browserId, connection state, and recent alerts. Prefer over browsers_list when you already bound a browser and only need health/permissions.

Inputs: no named parameters.

chat_askA

Send a prompt on a platform (new or existing chat) via the bound browser and wait for the assistant reply. Prefer over chats_create+manual wait; use jobs_enqueue kind=ask or wait:false for non-blocking. Requires browsers_bind; drives live provider UI (openWorld).

Inputs: prompt (required), platform (required), mode, wait, +8 more.

chat_cache_appendA

Append turns to an existing local SQLite cache copy and refresh FTS/totals. Prefer for incremental offline updates; use chat_cache_sync to rebuild from the live tab, chat_cache_update_block to edit one turn. Does not write to the provider UI.

Inputs: turns (required), chatId, cacheId, platform.

chat_cache_deleteA
DestructiveIdempotent

Delete a temp SQLite cached chat (metadata, blocks, FTS). Does not delete the provider chat — use chats_delete for that. Identify via cacheId or platform+chatId. Idempotent on missing rows.

Inputs: chatId, cacheId, platform.

chat_cache_readA
Read-onlyIdempotent

Read cached chat blocks from local SQLite by turn range, blockId, or FTS query without hitting the browser. Prefer after chat_cache_sync/chat_outline; use chat_read when the cache may be cold and a live fetch is OK. Identify the copy via cacheId or platform+chatId.

Inputs: query, chatId, blockId, cacheId, +4 more.

chat_cache_syncA

Fetch a chat from the bound browser and store blocks+FTS5 in the temp SQLite cache. Prefer before repeated chat_cache_read on long threads; use chat_outline/chat_read when you need live outline/slice without an explicit sync. Writes local cache only; requires browsers_bind for the host fetch.

Inputs: chatId (required), platform (required), force, branch, +1 more.

chat_cache_update_blockA

Update one cached block (bumps revision) or resync that turn from the live tab via resyncFromHost. Prefer for surgical cache edits; use chat_cache_append to add turns, chat_cache_sync to refresh the whole chat. Local cache only unless resyncFromHost needs the bound browser.

Inputs: turnIndex (required), role, chatId, cacheId, +3 more.

chat_outlineA
Read-onlyIdempotent

Build a turn index with short previews for a chat (auto-warms the SQLite cache for long threads). Prefer before chat_read to choose start/end indexes; use chats_get for the full payload, chat_cache_read when the cache is already warm. Empty/omitted chatId = open tab. Requires browsers_bind.

Inputs: platform (required), branch, chatId, previewChars.

chat_readA
Read-onlyIdempotent

Read a bounded slice of chat turns with a maxChars cap (uses/auto-fills the SQLite cache). Prefer after chat_outline for start/end indexes; use chats_get for the entire chat, chat_cache_read for offline cache-only access. Requires browsers_bind when the cache miss needs a host fetch.

Inputs: chatId (required), platform (required), end, start, +2 more.

chats_createA

Create a new chat on the provider UI in the bound browser and seed it with text. Prefer chat_ask when you also need to wait for the assistant reply; use chats_create only to open/seed a chat. Requires browsers_bind and allowCreate on the extension.

Inputs: text (required), platform (required), title.

chats_deleteA
Destructive

Permanently delete a chat on the provider UI. Destructive and audited: requires confirm:true plus allowDestructive on server and browser. Prefer chats_export first if you need a copy; chat_cache_delete only removes local cache.

Inputs: chatId (required), confirm (required), platform (required).

chats_exportA

Export one chat via extension builders and write files under exportDir (returns paths). Prefer for durable files; use chats_get for in-memory payload, jobs_enqueue kind=export/export_batch for bulk/async queues. Set async:true to return job_id instead of blocking. Requires browsers_bind; writes to disk.

Inputs: chatId (required), formats (required), platform (required), async, +2 more.

chats_getA
Read-onlyIdempotent

Return the full ExportPayload for one chat on the bound browser. Prefer for a complete dump of a known chatId; for turn index/previews use chat_outline, for a bounded slice use chat_read, for offline SQLite reuse use chat_cache_*. Empty chatId = currently open tab. Requires browsers_bind.

Inputs: chatId (required), platform (required), branch.

chats_listA
Read-onlyIdempotent

List chats for one platform in the bound browser (titles/ids, optional query). Prefer for discovery; use projects_chats for chats inside one project, chats_get for a full payload, chat_outline for turn indexes. Requires prior browsers_bind.

Inputs: platform (required), limit, query, cursor, +1 more.

containers_listA
Read-onlyIdempotent

Legacy alias identical to projects_list. Prefer projects_list for new agents; same args and result. Lists projects/folders on a platform via the bound browser.

Inputs: platform (required), limit, query, cursor.

jobs_cancelA

Cancel a queued job immediately, or request cancel for a running job. Prefer when stopping work started via jobs_enqueue/chat_ask wait:false. Does not roll back provider-side side effects already applied.

Inputs: job_id (required).

jobs_enqueueA

Enqueue a background job on the bound browser lane (ask, export, export_batch, delete, delete_batch) and return job_id immediately. Prefer for long/bulk work; use chat_ask/chats_export synchronously when you can wait. Poll with jobs_status; cancel with jobs_cancel. Requires browsers_bind; delete kinds need confirm + allowDestructive.

Inputs: kind (required), platform (required), max, mode, +16 more.

jobs_listA
Read-onlyIdempotent

List background jobs newest-first with optional status/kind/browser filters. Prefer over repeated jobs_status when surveying the queue; use jobs_status for one job_id detail.

Inputs: kind, limit, status, browserId.

jobs_statusA
Read-onlyIdempotent

Get status, progress, result, or error for a job_id from jobs_enqueue. Prefer for polling one job; use jobs_list to browse many. Local job store only.

Inputs: job_id (required).

platforms_listA
Read-onlyIdempotent

List supported providers and whether each is enabled on the server and the bound browser. Requires browsers_bind for browser-side enablement; use before chats_* when unsure which platforms are available.

Inputs: no named parameters.

projects_chatsA
Read-onlyIdempotent

List chats inside one project/container on the bound browser. Prefer over chats_list when scoped to a known containerId; use projects_list first to discover ids. Requires browsers_bind.

Inputs: platform (required), containerId (required), limit, cursor.

projects_createA

Create a project/folder on the provider UI. Prefer when organizing chats; does not create chats (use chats_create/chat_ask). Requires browsers_bind and allowProjects on the browser.

Inputs: name (required), platform (required), description, instructions.

projects_deleteA
Destructive

Permanently delete a project/folder on the provider. Destructive: requires confirm:true, allowProjects, and allowDestructive on the browser. Prefer projects_move_chat to rehome chats first. Does not delete local SQLite cache rows.

Inputs: confirm (required), platform (required), containerId (required), name.

projects_listA
Read-onlyIdempotent

List projects/folders (containers) on a platform via the bound browser. Prefer this over containers_list (identical legacy alias). Use projects_chats for chats inside one project, chats_list for all chats. Requires browsers_bind.

Inputs: platform (required), limit, query, cursor.

projects_move_chatA

Move a chat into a project, or pass containerId null to remove it from its project. Prefer over delete when reorganizing. Requires browsers_bind and allowProjects.

Inputs: chatId (required), platform (required), containerId.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4/5.0

    Scored across 27 tools

    Disambiguation3/5

    Descriptions do heavy lifting with explicit 'prefer X over Y' guidance, but four different read paths for a chat (chats_get, chat_outline, chat_read, chat_cache_read) plus chat_cache_sync/append/update_block create genuine boundaries an agent must reason through before every read. The explicit legacy duplicate containers_list == projects_list is a hard naming collision, and chats_create vs chat_ask also overlap.

    Naming Consistency4/5

    Nearly all tools follow namespace_action (browsers_*, projects_*, jobs_*, chats_*), which is predictable and readable. Minor deviations: the chat singleton tools (chat_outline, chat_read, chat_ask, chat_cache_*) use a different prefix than the plural chats_* group, and containers_list sits outside the projects_* namespace it belongs to.

    Tool Count3/5

    Twenty-seven tools across five real sub-domains (browsers, platforms, chats, cache, projects, jobs) is defensible given the scope, but it is above the comfortable range and forces the agent to triage before acting. At least one slot (containers_list) is pure dead weight, and the three browser tools plus six cache tools could be trimmed.

    Completeness4/5

    Coverage is unusually thorough: browser lifecycle, provider discovery, chat read/export/create/ask/delete, offline cache CRUD, project CRUD plus chat rehoming, and full async job management with cancel. Gaps are minor: no chats rename/update or metadata patch, no projects rename, and no job retry.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      MCP server that turns one authenticated ChatGPT browser page into a bounded MCP server, exposing tools for creating, listing, searching, exporting, sending, editing chats and downloading media via a CDP driver.
      MIT
    • F
      license
      Not graded
      quality
      A
      maintenance
      Enables web-based AI chat clients such as Claude, ChatGPT, and Grok to operate a user's local machine through a single OAuth-gated, whitelist-protected MCP endpoint exposing tools for file editing, shell commands, search, Git, SQLite, browser automation, DevTools, background tasks, localhost service fetching, clipboard, notifications, ports, and Postman control.
      -