chat-export-server
Enables listing, reading, exporting, creating, deleting, and asking questions in GitHub Copilot web chats through the user's logged-in browser session.
Enables listing, reading, exporting, creating, deleting, and asking questions in Perplexity chats through the user's logged-in browser session.
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 ( | same |
X Grok | same |
Claude | same |
GitHub Copilot web | same |
Qwen ( | same |
DeepSeek ( | same |
Perplexity ( | 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)
Extension — load chat-export-extension unpacked; enable bridge in popup; open provider tabs logged in.
Server —
npm install && npm run build,chat-export-server key new,chat-export-server start→http://127.0.0.1:8765.MCP client — point to
http://127.0.0.1:8765/mcpwithAuthorization: Bearer <key>; runbrowsers_bindthenchats_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 PATHRequires Node 22.13+ (node:sqlite).
Script | Purpose |
| compile to |
|
|
|
|
|
|
| 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 /healthData 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 outputchat-export-server key rotate
chat-export-server key list
chat-export-server statusConfiguration
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 |
| Data directory (default |
| Bind address ( |
| HTTP port (default |
| Extra |
|
|
| Dashboard login (overrides |
| Optional preset |
|
|
|
|
| DB driver override ( |
| DB URL when not using default SQLite path |
| Set |
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/healthPublished 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 downOptional 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 |
| MCP and CLI ( |
| Chrome extension WebSocket ( |
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 |
| Connected Chrome profiles |
| Enablement matrix |
| Read and write exports |
| UI drive (delete needs |
| Send prompt; wait or job queue |
| Async pipeline |
| 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-dashboardgit 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_deleteand similar needallowDestructiveon extension and server plusconfirm: 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.
Related repositories
Repo | Role |
Content scripts + WS client | |
chat-export-server (this) | MCP hub, jobs, cache |
Operator UI submodule | |
Install, security, FAQ |
Support the project
Tip (fiat): DonationAlerts — svwergio_mel
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 toolsbrowsers_bindAIdempotent
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_listARead-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_statusARead-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_deleteADestructiveIdempotent
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_readARead-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_outlineARead-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_readARead-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_deleteADestructive
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_getARead-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_listARead-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_listARead-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_listARead-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_statusARead-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_listARead-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_chatsARead-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_deleteADestructive
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_listARead-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
Scored across 27 tools
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.
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.
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.
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
Related MCP Connectors
Real-time YouTube transcripts, comments, channels & video search MCP. Local npx or hosted OAuth.
Hosted X (Twitter) MCP server for Claude, ChatGPT and Cursor, with each action switched per account.
Bot-free macOS Zoom/Meet/Teams call transcripts, Apple Vision screen OCR, and live SQLite MCP.
1Share one project context across ChatGPT, Claude, Telegram and any MCP client.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceSyncs AI conversations from multiple platforms to local Markdown files for your second brain, and provides browser automation tools via MCP for agentic workflows.AGPL 3.0
- FlicenseNot gradedqualityBmaintenanceLets any MCP-capable coding agent drive your real, logged-in Chrome through a local MCP server and MV3 extension.5-
- AlicenseNot gradedqualityBmaintenanceMCP 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
- FlicenseNot gradedqualityAmaintenanceEnables 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.-