Usage guide
Back up your own account
Grok Bots, with attachments, and regular Grok chats. One command, two directories, on the computer where Chrome is already logged in.
Fully local, no API key. Progress lines from the exporter are in Chinese. Markdown source: docs/guide.md.
Contents
1. Before you start
- macOS, Windows, or Linux. macOS uses the Keychain. Windows can use DPAPI. Linux is best-effort. Any platform can pass
--cookie. - Google Chrome, logged in at grok.com as the account you want to back up. Safari, Firefox, Arc, and Chromium are not read.
- That login lives in the Chrome profile Default, Profile 1, or Profile 2. Other profiles are ignored.
- Python 3.9 or newer (
python3 --version). - The account is yours.
--mode bot (the default) exports Grok Bots: an agent with an id, a roster entry, and a transcript. --mode chat exports regular grok.com conversations. --mode all exports both, into two directories.
Sign in before you run anything. The tool reuses that session. It does not open a login window. macOS will ask once for Keychain access; that step is under Troubleshooting.
2. Install
git clone https://github.com/LofiSu/grok-export-tool.git
cd grok-export-tool
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txt
requirements.txt installs websocket-client and cryptography. Both modes decrypt Chrome’s cookie database first, so both need them. Chat requests themselves use the Python standard library.
Keep the virtualenv active in the terminal where you run the exporter. The MCP server has no third-party imports, so its command can be system python3.
python3 grok-bot-export.py --help
You should see --mode {bot,chat,all}, --skip-attachments / --messages-only, and --workers.
3. Export
Run these from the repository root, with the virtualenv active. The first successful cookie read triggers a macOS dialog for the Keychain item Chrome Safe Storage. Choose Allow.
Grok Bots, full archive
Messages, events, voice-call records, and attachment files.
python3 grok-bot-export.py
# or
./export.sh
Default directory: ~/Downloads/grok-bot-export-YYYYMMDD/.
Grok Bots, messages only
The transcript is still complete. Attachment bytes are not downloaded. messages.jsonl still records localFile when the transcript names a file, so a later full run knows where to put it.
python3 grok-bot-export.py --messages-only
# --skip-attachments is the same flag
# ./export-messages.sh wraps it
Regular Grok chats
python3 grok-bot-export.py --mode chat
# or
./export-chat.sh
Default directory: ~/Downloads/grok-chat-export-YYYYMMDD/. This writes conversation text and raw response JSON. It does not download attachment files. --messages-only has no effect here.
Both
python3 grok-bot-export.py --mode all
With no directory argument, Bots go to ~/Downloads/grok-bot-export-YYYYMMDD/ and chats go to ~/Downloads/grok-chat-export-YYYYMMDD/. There is no export-all.sh. --messages-only together with --mode all skips Bot attachments and still writes chats.
Choose a directory
python3 grok-bot-export.py ~/Backups/grok-bots
python3 grok-bot-export.py --messages-only ~/Backups/grok-bots
python3 grok-bot-export.py --mode chat ~/Backups/grok-chats
python3 grok-bot-export.py --mode all ~/Backups/grok-bots
The last command writes Bots to ~/Backups/grok-bots and chats to ~/Backups/grok-bots-chat. The -chat suffix is added only when --mode all and a directory are both set. A chat-only run uses the directory you passed, with no suffix.
Attachment workers
Bot downloads use 6 threads unless you change it. --workers must be from 1 to 32. Each chunk is 4 MB, which is the request size the exporter sends.
python3 grok-bot-export.py --workers 4
A file that already exists and is non-empty is left in place. An empty file is downloaded again. Re-running a full Bot export retries failures.
Each mode walks the whole list the session returns. There is no flag for a single Bot or a single chat. Afterward, open one folder, or ask the MCP server for one Bot by name.
4. Where the files go
Defaults sit under ~/Downloads/, outside the git repository. An export can contain full transcripts and files, and it should not be committed.
A cookie failure exits before any directory is created. If the Bot WebSocket fails after cookies succeed, the Bot export may already have created an empty output directory.
5. Reading the archive
Grok Bots
grok-bot-export-YYYYMMDD/
AGENTS.md short map for an agent
_index.md one link per Bot
roster.json the roster, under "agents"
01_<name>/
meta.json roster record for that Bot
messages.jsonl one JSON object per line
conversation.md the same transcript, for reading
transcript.json decoded entries
attachments/ downloaded files
Folder numbers are at least two digits. The name is sanitized: path characters are replaced, and it is trimmed to 60 characters. Open _index.md, then a conversation.md. For scripts or an agent, prefer messages.jsonl.
| Field | Meaning |
|---|---|
seq | Order from the transcript |
type | message, send-message, user-attachment, event, voice-call, or another entry kind |
role | user or assistant on message entries. send-message is stored as assistant. user-attachment is stored as user |
content | Text. Empty for a bare user attachment |
timestamp, timestampMs | Local time string, and the original millisecond value |
attachment | File name and path fields when this entry has a file |
localFile | Path relative to the Bot folder, such as attachments/0007_name |
event, call | Payload for those entry kinds |
entryId, fromAgent, replyTo, messageType, raw | Present when the transcript has them |
meta.json is the roster object. name, agentId, status, and lastTurnAt are the fields the MCP server displays.
Regular chats
grok-chat-export-YYYYMMDD/
_index.md
conversations.jsonl every message, one object per line
conversations/
0001_<title>.md readable transcript
0001_<title>.json id, title, times, raw responses
Numbers are at least four digits. Titles are sanitized the same way as Bot names. When the API omits a title, the file uses 未命名对话.
| Field | Meaning |
|---|---|
conversationId | grok.com conversation id |
title | Conversation title |
role | user when the sender is human, otherwise assistant |
sender | Sender string from the API |
content | Message text. When the body is not a string, the JSON text of that body |
timestamp, timestampMs | Local time string, and createTime |
The per-conversation JSON keeps the raw messages array plus createTime and modifyTime. A chat export has no AGENTS.md, and the MCP server does not index it.
6. MCP server for OpenCode
The server is grok-bot-mcp.py. Point it at the Bot folder from the previous section, the one that contains roster.json and numbered Bot directories.
python3 /absolute/path/to/grok-export-tool/grok-bot-mcp.py \
/absolute/path/to/grok-bot-export-YYYYMMDD
It speaks JSON-RPC on stdin and stdout and waits. The agent client starts it; you do not leave that line running yourself. The script expands ~ in the directory argument. Prefer an absolute path, because some clients do not run the command through a shell.
If you leave the directory out, the server selects the newest ~/Downloads/grok-bot-export-* by modification time. It does not look at grok-chat-export-*. Pass the Bot path explicitly.
OpenCode
The current schema at https://opencode.ai/config.json uses mcp.servers. Edit ~/.config/opencode/opencode.json, or create opencode.json in a project.
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"grok-bot": {
"type": "local",
"command": [
"python3",
"/absolute/path/to/grok-export-tool/grok-bot-mcp.py",
"/absolute/path/to/grok-bot-export-YYYYMMDD"
],
"enabled": true
}
}
}
}
Replace both paths. System python3 is enough, because the MCP file does not import websocket or cryptography. To use the exporter’s virtualenv instead, put .venv/bin/python first in command.
Start a new OpenCode session so it reloads config. Ask it to use the grok-bot tools: list the bots, then open one by the name you see.
| Tool | Arguments | Behavior |
|---|---|---|
list_bots | none | Name, agentId, message count, last active time. The wording is Chinese |
get_bot | bot (required), limit (default 200) | Matches a folder prefix such as 01, then a substring of the name or agentId |
search | query (required), context (default 2) | Case-sensitive substring of content. Up to 10 hits, with neighboring messages |
If OpenCode rejects servers
Some older builds want the server directly under mcp. The current schema marks that shape deprecated. Use it only when the servers form is rejected:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"grok-bot": {
"type": "local",
"command": [
"python3",
"/absolute/path/to/grok-export-tool/grok-bot-mcp.py",
"/absolute/path/to/grok-bot-export-YYYYMMDD"
]
}
}
}
OpenCode skill
The skill file is .claude/skills/grok-export/SKILL.md. Claude Code and OpenCode read .claude/skills/ in the project. The skill name is grok-export. Ask, in ordinary language, to export or to look something up in the Bot archive.
Other agents
Any stdio MCP client can launch the same program.
Claude Code, project scope, is .mcp.json at the project root. Approve it when Claude Code asks. User scope lives in ~/.claude.json. The mcpServers object is the same either way:
{
"mcpServers": {
"grok-bot": {
"type": "stdio",
"command": "python3",
"args": [
"/absolute/path/to/grok-export-tool/grok-bot-mcp.py",
"/absolute/path/to/grok-bot-export-YYYYMMDD"
]
}
}
}
Codex reads ~/.codex/config.toml, and .codex/config.toml in a trusted project:
[mcp_servers.grok-bot]
command = "python3"
args = [
"/absolute/path/to/grok-export-tool/grok-bot-mcp.py",
"/absolute/path/to/grok-bot-export-YYYYMMDD",
]
Cursor uses .cursor/mcp.json or ~/.cursor/mcp.json:
{
"mcpServers": {
"grok-bot": {
"command": "python3",
"args": [
"/absolute/path/to/grok-export-tool/grok-bot-mcp.py",
"/absolute/path/to/grok-bot-export-YYYYMMDD"
]
}
}
}
If a key is rejected, that client’s MCP page is the authority. The process to launch does not change.
list_bots looks for subfolders that contain meta.json. A chat directory has none, and the reply says, in Chinese, that no export was found.
7. Troubleshooting
Keychain prompt
The exporter runs security find-generic-password for the item Chrome Safe Storage, account Chrome. Choose Allow, or Always Allow so later runs stay quiet. The password stays on the machine and decrypts Chrome’s cookie database. Do not paste it into an issue, a chat, or a commit. SECURITY.md lists what a report must leave out.
If you choose Deny, the script exits with 未能从 Chrome 解密出 grok.com 登录态. In Keychain Access, allow the terminal app you are using, then run the export again. A headless SSH session may be unable to answer the dialog; run it once in a graphical session.
Chrome is not logged in
The same exit line covers three causes:
- grok.com is not logged in inside Google Chrome.
- The login is in a profile other than Default, Profile 1, or Profile 2. Sign in inside one of those three. This tool does not scan any further profile.
- Keychain access was denied, so the cookies could not be decrypted.
The tool needs both cookie names sso and sso-rw. If none of the three profile databases exist, the log also includes 未找到 Chrome 的 Default、Profile 1 或 Profile 2 Cookies 文件. Log in, confirm the profile, allow Keychain, and run again. Quitting Chrome is necessary only when the cookie database itself cannot be copied.
Empty Bot roster
The log line 找到 0 个 bot means the WebSocket roster returned no agents. The directory is still written: AGENTS.md, _index.md, and roster.json.
Regular chats show up under --mode chat or --mode all, in the chat directory. An account that only uses the main grok.com chat will have an empty Bot roster. A login in another Chrome profile is invisible unless that profile is Default, Profile 1, or Profile 2.
Empty chat list
找到 0 个普通会话 means the chat list was empty or the list request failed. Look for 会话列表请求失败(HTTP …) just above it. HTTP 429 and HTTP 5xx are retried up to five times. A warning 拉取失败 on one title means that conversation was saved with whatever messages had already arrived. Other conversations in the same run are unaffected.
Cookie database error
If Python prints a traceback while copying Chrome’s Cookies file, quit Chrome completely, including background processes, and run the export again. The tool copies the database to a temp file, reads it, and deletes the copy. It does not print cookie values.
Attachments missing
--messages-only skips downloads on purpose. On a full run, the summary line reports how many attachment jobs succeeded. A file is kept only when its download finishes. Re-run the full Bot export to retry files that are missing. Files larger than 2 GB are skipped. Chat mode never writes an attachments/ folder.
MCP says there are no bots
Pass the Bot directory, not the repository root and not the chat directory. The folder should contain subfolders with meta.json. Restart the agent client after changing the path.
The run dies on the WebSocket or mid-list
grok.com can change these interfaces without notice. Pull the latest main and try again. If it still fails, open an issue with the error text and the commit you ran. Leave out cookies, the Keychain password, agent ids, and transcript text. Security reports go through SECURITY.md, not a public issue.
8. Safety
- Back up an account you own.
- Keep
~/Downloads/grok-bot-export-*and~/Downloads/grok-chat-export-*private. - Do not commit exports, Chrome’s cookie database, or Keychain material.
grok-bot-mcp.pydoes not open a network connection. The exporter does open connections to grok.com, using your existing session, to read history.- The tool does not send a new chat message and does not delete bots or conversations.
- The exporter sets umask 077 and then marks the export tree so directories are mode 0700 and files are mode 0600. Logs do not include cookie values.
- The project is not affiliated with xAI. Follow grok.com’s terms. Interfaces can break.
Home · 中文教程. Contact and GitHub Sponsors are in the footer, and also on GitHub, X / @su_lofi, and at lofisuchat@gmail.com.