setup-python-tools — Claude Code skill: Pyodide in chat
Wires client-side Python tool execution through Pyodide into an app that already has useAtlasChat. Useful when the agent ships tool code and the browser should run it.
npx skills add cognitedata/builder-skills --skill setup-python-tools -a claude-codeWhat setup-python-tools does
The skill walks through the setup: read package.json and the component calling useAtlasChat, install [email protected], add the usePyodideRuntime hook, pass pythonRuntime into useAtlasChat. No PythonToolConfig entries are needed. CDF runPythonCode tools arrive as toolConfirmation plus clientTool, and the library fetches the code on its own when the agent asks for it.
The Pyodide version has to match the CDN artifacts. Install a different one and loading breaks. First load pulls roughly 30MB and takes 30-60 seconds; after that the browser cache serves it in under 2 seconds. While initialization runs, the skill says to disable the whole input area rather than just the send button, and to show a compact pill above the input with the current stage and percent.
You need src/atlas-agent/ from the integrate-atlas-chat skill and the @sinclair/typebox package; python.ts, pyodide.ts, pyodide-react.ts and pyodide-runtime.ts get copied from integrate-atlas-chat/code/. If the app runs through the Atlas / EOS sidebar, skip this skill — Python tools there come from the agent config.
What it helps with
- run Python tool code inside the browser
- attach Pyodide to an existing
useAtlasChatchat - show runtime loading progress above the input
- block message sending until Python is ready
- keep the
pyodideversion aligned with the CDN artifacts
Example prompts for the agent
- set up python tools for the my-agent agent
- wire usePyodideRuntime into the component that calls useAtlasChat
- add a Pyodide loading indicator above the chat input
How to install setup-python-tools
1Skills CLI (Vercel)
npx skills add cognitedata/builder-skills --skill setup-python-tools -a claude-code2Manually: put the files in place
~/.claude/skills/setup-python-tools/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/setup-python-tools/
1Skills CLI (Vercel)
npx skills add cognitedata/builder-skills --skill setup-python-tools -a codex2Manually: put the files in place
.agents/skills/setup-python-tools/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add cognitedata/builder-skills --skill setup-python-tools -a cursor2Manually: put the files in place
.cursor/skills/setup-python-tools/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install cognitedata/builder-skills setup-python-tools2Manually: put the files in place
.github/skills/setup-python-tools/SKILL.md1Skills CLI (Vercel)
npx skills add cognitedata/builder-skills --skill setup-python-tools -a gemini-cli2Platform CLI
gemini skills install https://github.com/cognitedata/builder-skills --path skills/setup-python-toolsManually: put the files in place
.devin/skills/setup-python-tools/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add cognitedata/builder-skills --skill setup-python-tools -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about setup-python-tools
How do I install setup-python-tools in Claude?
Run npx skills add cognitedata/builder-skills --skill setup-python-tools -a claude-code. The skill lands in ~/.claude/skills/setup-python-tools/SKILL.md.
What does setup-python-tools do?
It adds client-side Python tool execution via Pyodide to an app using useAtlasChat: installs [email protected], wires usePyodideRuntime and passes the runtime to the chat.
Which Pyodide version is required?
Exactly [email protected]. It must match the artifacts loaded from the CDN, otherwise initialization fails with errors.
Source text
SKILL.md · Apache-2.0
--- name: setup-python-tools description: "Pyodide Python tools for an already-approved in-app useAtlasChat UI. EOS sidebar Python tools run from agent CDF config — do not vendor Pyodide. Triggers: Pyodide, pythonRuntime, usePyodideRuntime, runPythonCode." allowed-tools: Read, Glob, Grep, Edit, Write, Bash metadata: argument-hint: "[tool-names or agent-external-id]" --- # Set Up Python Tool Execution Add client-side Pyodide execution for **$ARGUMENTS**. Skip this if the app uses the Atlas / EOS sidebar (`integrate-fusion-agent`). **Prerequisite:** `src/atlas-agent/` from `integrate-atlas-chat`, plus `@sinclair/typebox`. Copy `python.ts`, `pyodide.ts`, `pyodide-react.ts`, `pyodide-runtime.ts` from `integrate-atlas-chat/code/` into `src/atlas-agent/`. CDF `runPythonCode` tools arrive as `toolConfirmation` + `clientTool`. Wire `usePyodideRuntime` and pass `pythonRuntime` to `useAtlasChat` — no `PythonToolConfig` entries. Pyodide is ~30MB, cached after first load. --- ## Step 1 — Understand the app Read these files before touching anything: - `package.json` — detect package manager and existing deps - The component that calls `useAtlasChat` — understand current tools/config --- ## Step 2 — Install Pyodide Install **exactly** `[email protected]` using the app's package manager. This version must match the CDN artifacts loaded at runtime — installing a different version will cause errors. - pnpm → `pnpm add [email protected]` - npm → `npm install [email protected]` - yarn → `yarn add [email protected]` > `@sinclair/typebox` should already be installed from `integrate-atlas-chat`. Add it if missing. --- ## Step 3 — Set up usePyodideRuntime In the component that calls `useAtlasChat`, add the Pyodide runtime hook: ```tsx import { loadPyodide } from "pyodide"; import { usePyodideRuntime } from "./atlas-agent/pyodide-react"; import { useAtlasChat } from "./atlas-agent/react"; function MyChat() { const { sdk, isLoading } = useDune(); // Initialize Python runtime (loads Pyodide, installs packages, sets up Cognite SDK) const { runtime: pythonRuntime, loading: pythonLoading, progress: pythonProgress, error: pythonError, isReady: pythonReady, } = usePyodideRuntime({ loadPyodide, client: isLoading ? null : sdk, requirements: ["pandas", "numpy"], // optional — additional packages }); // ... useAtlasChat below } ``` ### Hook API reference | Return field | Type | Description | |---|---|---| | `runtime` | `PythonRuntime \| undefined` | The initialized runtime, or undefined if not ready | | `loading` | `boolean` | True while Pyodide is loading / initializing | | `error` | `string \| null` | Error message if initialization failed | | `progress` | `{ stage: string; percent: number }` | Current init progress for UI display | | `isReady` | `boolean` | Convenience: `!loading && !error && runtime !== undefined` | ### Loading state UI Place the loading indicator **above the chat input**, not in the message list. Keep it compact — a pill/badge showing stage text and percent. Show an error badge separately. First load is ~30-60s (downloads ~30MB); subsequent loads are <2s from browser cache. ```tsx {/* Loading — shown above the input while Pyodide initializes */} {pythonLoading && ( <div className="flex items-center gap-2 rounded-lg border bg-muted/50 px-3 py-2 text-sm text-muted-foreground"> {/* Optional: <IconBrandPython /> from @tabler/icons-react */} <span>{pythonProgress.stage || "Initializing Python..."}</span> {pythonProgress.percent > 0 && pythonProgress.percent < 100 && ( <span className="text-xs opacity-70">({pythonProgress.percent}%)</span> )} </div> )} {/* Error — shown if init fails (after loading finishes) */} {pythonError && !pythonLoading && ( <div className="flex items-center gap-2 rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive"> <span>Python runtime failed to load</span> </div> )} ``` --- ## Step 4 — Wire into useAtlasChat Pass the runtime to `useAtlasChat`. That's all — no tool configs needed: ```tsx const { messages, send, isStreaming, progress, error, reset, abort } = useAtlasChat({ client: isLoading ? null : sdk, agentExternalId: "my-agent", tools: [renderTimeSeries], // regular client tools (declared to agent), if any pythonRuntime, // from usePyodideRuntime — enables Python tool execution }); ``` **Note**: Python tools are NOT declared to the agent via `tools`. The agent already knows about them from its CDF config. The library fetches the code automatically when needed. --- ## Step 5 — Disable input while Python loads The user shouldn't send messages before the runtime is ready. Disable the **entire input area** (not just the send button) so the state is unambiguous: ```tsx <ChatInput onSend={handleSend} disabled={isStreaming || pythonLoading} // ... /> ``` If you have a home page with suggestion chips, disable those too: ```tsx <ChatHomePage onSuggestionClick={handleSuggestionClick} disabled={pythonLoading} /> ``` --- ## Done The app can now execute Python tools client-side via Pyodide. When the agent calls a Python tool, the library automatically fetches its code from the agent config, runs it in the browser, and returns the result to the agent.
© cognitedata · Apache-2.0 · cognitedata/builder-skills