Reading claude.ai Artifacts headless
Read claude.ai Artifacts, including multi-file designs, from a headless `claude -p` session on a swarm worker
Claude Code can read a claude.ai Artifact from a claude -p session, including every file of a multi-file design (project/*.dc.html, project/canvas.json). The capability is off in -p mode by default and sits behind an internal env flag. This guide shows how to turn it on and how to download an artifact's files with the example script examples/claude-artifacts-read/claude-artifacts-read.sh.
The flags and the tool names below are internal and undocumented. Anthropic can change or remove them in any Claude Code release. Everything here was checked on Claude Code 2.1.314.
Requirements
| Need | Value |
|---|---|
| Claude Code | >= 2.1.309. 2.1.306 to 2.1.308 only expose a built-in Artifact tool that returns the rendered shell page, not the files. |
| Auth | First-party claude.ai OAuth: a claude setup-token token in CLAUDE_CODE_OAUTH_TOKEN, or a /login stored in CLAUDE_CONFIG_DIR. |
| Access | The account must already be able to open the artifact on claude.ai. |
| Script deps | jq and sha256sum. Both ship in the worker image. |
The flag
export CLAUDE_CODE_ARTIFACT=1With it set, claude -p connects a dynamic MCP server named claude-artifacts with 39 tools. Two of them read files:
| Tool | Input | Returns |
|---|---|---|
mcp__claude-artifacts__list_files | slug: the artifact URL or its UUID | slug (UUID), version, and files[] with path, size, content_type, sha256, of_type |
mcp__claude-artifacts__read_file | slug, path | The file text inside an <untrusted-document-…> tag, plus its metadata |
Check that it loaded from the init event:
CLAUDE_CODE_ARTIFACT=1 claude -p "reply ok" --output-format stream-json --verbose --max-turns 1 \
| jq -c 'select(.subtype == "init") | {version: .claude_code_version, mcp: .mcp_servers,
read: [.tools[] | select(test("claude-artifacts__(list|read)_file"))]}'Other CLAUDE_CODE_ARTIFACT_* names exist in the binary. A field report set CLAUDE_CODE_ARTIFACT_TOOLSET, _MULTI_FILE, _ASSETS and _TYPES to 1 as well. On 2.1.314 they did not change the tool list, and a full read worked without them. The script sets them anyway, since they are harmless.
Auth on a swarm worker
The worker's own CLAUDE_CODE_OAUTH_TOKEN (a claude setup-token token) works for the artifacts API. No separate /login is needed.
The agent's Bash tool does not inherit that token from the harness, so a script run from a task has no Claude auth by default. Pick one:
- Token: export a
claude setup-tokentoken asCLAUDE_CODE_OAUTH_TOKENin the shell that runs the script. Read it from a secret you control. Never put it in a prompt, a command line, or a file another agent reads. - Login: run
claudeonce interactively withCLAUDE_CONFIG_DIRpointing at a dedicated directory, then/login. Keep that directory out of git and out of shared paths, and set the sameCLAUDE_CONFIG_DIRwhen you run the script.
ANTHROPIC_API_KEY auth was not tested. The gating code also turns the capability off for Bedrock, Vertex and gateway routes, and when CLAUDE_CODE_ARTIFACT=0 is set explicitly.
Download an artifact
claude-artifacts-read <artifact-url> <outdir> [--all] [--max-bytes N]./examples/claude-artifacts-read/claude-artifacts-read.sh \
"https://claude.ai/artifact/<id>" ./designok: project/Main.dc.html
ok: project/canvas.jsonThe script:
- Runs one
claude -psession that callslist_filesand saves the result as<outdir>/manifest.json. - Picks the artifact's own text files.
--alladds the files the artifact type owns (of_type), such asSKILL.md,index.htmlandartifact-type/**. - Runs a second session that calls
read_fileonce per path. Paths the session missed get one more session. - Takes each file from the raw tool result, never from the model's reply, and checks it against the
sha256fromlist_filesbefore writing. A file that fails the check is not written, so an earlier copy at that path stays as it was.
The script writes only inside <outdir>. It refuses a path whose destination or parent directory is a symlink, and it renames each file into place, so a write never follows a link.
Both sessions run with --permission-mode dontAsk and allow only the one Artifact tool they need. Every other tool call is denied. Artifact text comes from other people, so treat it as data.
Use CLAUDE_BIN to pick a binary, for example a newer Claude Code than the one on PATH. Exit code 0 means every selected file was written and matched its hash.
Limits
- URL fragments:
#artboard-…only exists in the browser. The script drops it, and no tool can resolve it. - Text only: on an artifact made from a type,
read_filereturns text files only. Images and other binary files are listed in the manifest and skipped. - Size:
read_filerefuses a type-owned file above 262144 bytes. The script skips those and prints them. Results too big for the model context are saved by Claude Code undertool-results/, and the script reads that copy back. - Model in the loop: the tools only run through a model turn, so each run costs two short sessions. The model occasionally skips the call; the script retries.
- Session expiry: a
/loginsession expires and needs a new/login. Asetup-tokentoken lasts longer but can be revoked. - Undocumented: the flag, the MCP server name and the result shapes can change between Claude Code versions. Re-run the init check above after a bump.