Claude Desktop Chat Export logo

Claude Desktop Chat Export

Community
vicnaum
claude-desktop-chat-export

Export a claude.ai (Claude Desktop / web) conversation and convert it to a Claude Code CLI JSONL session so it becomes resumable via `claude -r <sessionId>`. Use when the user wants to: (1) continue a claude.ai/Claude Desktop chat in Claude Code CLI, (2) import a web conversation into CC, (3) archive a claude.ai conversation locally with images and attachments, (4) unfreeze a claude.ai chat that hit its context limit, or (5) convert conversation.json from the claude.ai API to CC's JSONL envelope format. Triggers on mentions of exporting/importing Claude Desktop chats, resuming a web chat in CLI, conversation.json → JSONL, or claude.ai chat archival.

Overview

Publishervicnaum
Repositoryvics-agent-skills
Skill nameclaude-desktop-chat-export
Stars
53
Forks
5
Bundled files
3
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 3 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by vicnaum on GitHub. Read the source before you install it.

Installation

Install the Claude Desktop Chat Export AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/vicnaum/vics-agent-skills/tree/main/skills/claude-desktop-chat-export
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/vicnaum/vics-agent-skills.git /tmp/vics-agent-skills
mkdir -p .claude/skills
cp -r /tmp/vics-agent-skills/skills/claude-desktop-chat-export .claude/skills/claude-desktop-chat-export
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Claude Desktop Chat Export in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Claude Desktop Chat Export on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Claude Desktop Chat Export is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

claude-desktop-chat-export

claude.ai chats aren't resumable — once you hit context limit or close the tab, the conversation is read-only. Claude Code CLI stores every session as JSONL at ~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl and resumes via claude -r <sessionId>. This skill bridges the two: export a claude.ai conversation, convert it to CC's format, keep talking.

Same underlying model. Only the harness differs (system prompt, declared tools, output-length rules).

Workflow

Three stages: grab from the browser → organize → convert. The converter is scripts/convert_to_cli.py.

Two transport options for the "grab" stage:

  • DevTools console — downloads conversation.json and chat-images.zip to ~/Downloads via the browser's download mechanism. Works when the user runs the snippets manually.
  • MCP + local relay — works when driving the browser via the claude-in-chrome MCP. The MCP's javascript_tool return filters block cookie-like and base64 payloads, and extension-driven a.click() downloads don't land on disk. Solution: run scripts/relay_server.py locally, have the browser POST each blob to it. See MCP path below.

1. Grab the conversation JSON + images from claude.ai

Requires an authenticated browser session at claude.ai. Use DevTools console or the claude-in-chrome MCP.

First, identify the org and conversation UUIDs:

  • Org UUID: from /api/bootstrap → memberships[0].organization.uuid
  • Conversation UUID: from the chat URL

Run in the browser console (on claude.ai, already logged in):

js
const ORG = '<your-org-uuid>';
const CONV = '<conversation-uuid>';

// Fetch full conversation
fetch(`/api/organizations/${ORG}/chat_conversations/${CONV}?tree=True&rendering_mode=messages&render_all_tools=true`,
      {credentials:'include'})
  .then(r => r.blob())
  .then(b => { const a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = 'conversation.json'; a.click(); });

For images (API only serves webp previews — no originals):

js
// Load JSZip once:
await new Promise((res,rej)=>{ const s=document.createElement('script'); s.src='https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js'; s.onload=res; s.onerror=rej; document.head.appendChild(s); });

// Fetch + zip all images:
const convo = await (await fetch(`/api/organizations/${ORG}/chat_conversations/${CONV}?tree=True&rendering_mode=messages&render_all_tools=true`, {credentials:'include'})).json();
const images = [];
for (const m of convo.chat_messages) for (const f of (m.files||[])) if (f.file_kind==='image') images.push(f);
const zip = new JSZip();
for (const img of images) {
  const r = await fetch(`/api/${ORG}/files/${img.file_uuid}/preview`, {credentials:'include'});
  if (r.ok) zip.file(`${img.file_uuid}.webp`, await r.arrayBuffer());
}
const blob = await zip.generateAsync({type:'blob'});
const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'chat-images.zip'; a.click();

Chrome blocks a second auto-download from the same page — either run the two snippets in separate fresh tabs, or click "Allow" on the download-permission prompt.

MCP path (via relay)

When driving the browser via the claude-in-chrome MCP, use the bundled relay. Start it pointing at the target export directory:

bash
python3 <skill-dir>/scripts/relay_server.py <export-dir>/<new-uuid>/ &

It listens on http://127.0.0.1:8765 by default and writes each POST body to <out-dir>/<X-Filename>. Nested paths like files/<uuid>.webp are preserved; .. / absolute paths are rejected.

Then, via javascript_tool on a claude.ai tab, POST the conversation JSON and each image blob:

js
// Conversation JSON
(async () => {
  const ORG = '<org-uuid>';
  const CONV = '<conversation-uuid>';
  const r = await fetch(`/api/organizations/${ORG}/chat_conversations/${CONV}?tree=True&rendering_mode=messages&render_all_tools=true`, {credentials:'include'});
  const blob = await r.blob();
  const up = await fetch('http://127.0.0.1:8765/upload', {
    method:'POST',
    headers:{'Content-Type':'application/json','X-Filename':'conversation.json'},
    body: blob
  });
  return {size: blob.size, relay: up.status};
})()
js
// All image previews, written to files/<uuid>.webp
(async () => {
  const ORG = '<org-uuid>';
  const CONV = '<conversation-uuid>';
  const convo = await (await fetch(`/api/organizations/${ORG}/chat_conversations/${CONV}?tree=True&rendering_mode=messages&render_all_tools=true`, {credentials:'include'})).json();
  const imgs = [];
  for (const m of convo.chat_messages) for (const f of (m.files||[])) if (f.file_kind==='image') imgs.push(f.file_uuid);
  let ok=0, fail=0;
  for (const uuid of imgs) {
    try {
      const r = await fetch(`/api/${ORG}/files/${uuid}/preview`, {credentials:'include'});
      if (!r.ok) { fail++; continue; }
      const up = await fetch('http://127.0.0.1:8765/upload', {
        method:'POST',
        headers:{'Content-Type':'application/octet-stream','X-Filename':`files/${uuid}.webp`},
        body: await r.blob()
      });
      if (up.ok) ok++; else fail++;
    } catch(e) { fail++; }
  }
  return {total: imgs.length, saved: ok, failed: fail};
})()

Stop the relay (kill %1 / Ctrl+C) once both uploads are done. This bypasses both the MCP's cookie-data filter (only metadata, not page data, is returned from JS) and the extension-initiated-download issue.

2. Organize

mkdir -p <export-dir>/<conversation-uuid>/files
mv ~/Downloads/conversation.json <export-dir>/<conversation-uuid>/conversation.json
unzip ~/Downloads/chat-images.zip -d <export-dir>/<conversation-uuid>/files/

Result:

<export-dir>/<conversation-uuid>/
├── conversation.json      ← full claude.ai API response
└── files/
    └── <file_uuid>.webp   ← one per attached image

(With the MCP + relay path, files land directly in this layout — no unzip needed.)

3. Convert

bash
python3 <skill-dir>/scripts/convert_to_cli.py \
  <export-dir>/<conversation-uuid>/conversation.json \
  --cwd <project-path> \
  --session-id <conversation-uuid> \
  --slug <any-slug-string>

Output lands at ~/.claude/projects/<encoded-cwd>/<conversation-uuid>.jsonl. Resume:

bash
claude -r <conversation-uuid>

No external Python dependencies — stdlib only (Python 3.10+).

CLI flags

FlagDefaultPurpose
--cwd PATHos.getcwd()Which ~/.claude/projects/<encoded-cwd>/ folder the output lands in
--session-id UUIDfresh uuid4CC sessionId (pass the original claude.ai conversation UUID to keep identity)
--slug STRINGimported-chat-aiSlug stamped on every envelope. Must be consistent across messages
--files-dir PATH<conv_path>/../files/Where to look for images, named <file_uuid>.<ext>
--version VERSION2.1.114CC version stamped on envelopes
--git-branch NAMEmastergitBranch field
--keep-toolsoffKeep tool_use/tool_result blocks. Default flattens them to text (see limitations)
--flatten-thinkingoffPreserve thinking as <thinking>…</thinking> text blocks instead of dropping. Signature-free, so no resume failures. Strippable later via session-stripper strip-thinking (detects the wrappers automatically)
--out PATHdefault pathOverride output location
--dry-runoffReport summary but don't write

Defaults to know

Tools flattened by default. claude.ai tool names (view, web_fetch, bash_tool, …) collide with CC's declared tool names (Read, Bash, …). Resuming with raw tool blocks produces API errors. The default --keep-tools off flattens them to readable text so the model retains context of what was looked up. Only pass --keep-tools if you know what you're doing.

Thinking blocks dropped by default. Their cryptographic signatures are tied to the original API context and don't transfer, so keeping them as structural thinking blocks triggers "Invalid signature in thinking block" on resume. Pass --flatten-thinking to preserve thinking as plain <thinking>…</thinking> text blocks — no signatures, no resume failures, model reads it as ordinary context. Later, session-stripper strip-thinking detects these wrappers and can clear them without re-converting. Otherwise, the raw thinking text is still readable in conversation.json.

UUIDs regenerate on every converter run. Only sessionId is stable (via --session-id). If a CC session for this conversation is currently open, quit CC before re-running the converter — otherwise CC's in-memory state points to deleted UUIDs and the chain breaks silently.

For the full list of format-level gotchas, chain integrity requirements, and what does/doesn't survive the import, see references/format-notes.md.

When something goes wrong

  • "tool use concurrency issues" on resume: ran with --keep-tools but tool names don't match CC's declarations. Re-run without --keep-tools.
  • "Invalid signature in thinking block": thinking blocks leaked through. Re-run; converter drops them by default.
  • "Extra inputs are not permitted": tool_result had stray fields (uuid, citations, start_timestamp). Converter whitelists valid keys — re-run.
  • Chain broken silently / empty session list: CC was open during the previous conversion. Quit CC, re-run.
  • Images missing in resumed chat: check --files-dir points to a folder with <file_uuid>.webp files matching the JSON's files[].file_uuid.

See references/format-notes.md for the full gotcha list and the chain integrity invariants (parentUuid unbroken, slug identical, timestamps monotonic).

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Claude Desktop Chat Export AI skill do?

Export a claude.ai (Claude Desktop / web) conversation and convert it to a Claude Code CLI JSONL session so it becomes resumable via `claude -r <sessionId>`. Use when the user wants to: (1) continue a claude.ai/Claude Desktop chat in Claude Code CLI, (2) import a web conversation into CC, (3) archive a claude.ai conversation locally with images and attachments, (4) unfreeze a claude.ai chat that hit its context limit, or (5) convert conversation.json from the claude.ai API to CC's JSONL envelope format. Triggers on mentions of exporting/importing Claude Desktop chats, resuming a web chat in...

Why use Claude Desktop Chat Export on TypingMind?

Because you install it once and use it with any model. Claude Desktop Chat Export is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Claude Desktop Chat Export in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/vicnaum/vics-agent-skills/tree/main/skills/claude-desktop-chat-export. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Claude Desktop Chat Export?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Claude Desktop Chat Export?

As many as you like. As long as a model supports skills, you can use Claude Desktop Chat Export with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Claude Desktop Chat Export AI skill free?

It is published on GitHub by vicnaum. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇