Skip to main content

pyclaudecli

PyPI Python versions License: MIT Downloads

A Python library that wraps the Claude Code CLI (claude) so you can drive it from Python instead of shelling out by hand: one-shot prompts, JSON/streaming output, background agents, authentication, MCP servers, plugins, and the rest of the CLI's surface.

It's a thin wrapper, not a reimplementation — every call runs the real claude binary, so it always reflects whatever version, auth, and config you have installed locally.

Contents

Install

pip install pyclaudecli

Requires the claude CLI itself to be installed and on PATH (see the Claude Code docs).

Quickstart

from pyclaudecli import ClaudeCLI

claude = ClaudeCLI()

# One-shot prompt
print(claude.prompt("Summarize this repo's README.", model="haiku"))

Setup

from pyclaudecli import ClaudeCLI

# Defaults: binary="claude" on PATH, inherited cwd/env, no timeout
claude = ClaudeCLI()

# Pointing at a specific binary/project, with a default timeout for every call
claude = ClaudeCLI(
    "claude",
    cwd="/path/to/project",
    env={"ANTHROPIC_API_KEY": "sk-ant-..."},
    timeout=120,
)

Authentication

claude handles auth itself, so pyclaudecli just drives it. You have two options.

API key

Pass the key through the environment — either inherited from your shell or set per client:

from pyclaudecli import ClaudeCLI

claude = ClaudeCLI(env={"ANTHROPIC_API_KEY": "sk-ant-..."})

OAuth login (paste the code)

auth_login() runs claude auth login, prints the sign-in URL, waits for the CLI's "Paste code here" prompt, and writes your code back to it. With no arguments it reads the code from stdin, so this is the whole interactive flow:

from pyclaudecli import ClaudeCLI

claude = ClaudeCLI()

if not claude.auth_status().get("loggedIn"):
    # Prints the sign-in URL, then asks: "Paste the code from the browser here:"
    exit_code = claude.auth_login()
    print("login exit code:", exit_code)

print(claude.auth_status())   # {"loggedIn": True, "email": "you@example.com", ...}

To capture the URL yourself (open it in a browser, send it to a chat, log it) and paste the code back without stdin, use on_output plus code_provider:

import re

URL_RE = re.compile(r"https://\S+")
login_url = None

def capture(chunk: str) -> None:
    global login_url
    print(chunk, end="", flush=True)          # or log.info(chunk)
    if login_url is None:
        match = URL_RE.search(chunk)
        if match:
            login_url = match.group(0)

def supply_code(url: str) -> str:
    # `url` is the sign-in URL pyclaudecli found in the CLI's output.
    # Open it however you like, then return the code the browser shows.
    print(f"\nOpen this URL and approve the login:\n{url}\n")
    return input("Paste code here: ").strip()

claude.auth_login(
    on_output=capture,
    code_provider=supply_code,
    console=True,      # --console: force the URL/console flow instead of opening a browser
    timeout=300,       # how long to wait for the "Paste code here" prompt
)

Fully non-interactive — when the code already came from somewhere else (a queue, a browser-automation step, an operator pasting it into your own UI):

claude.auth_login(code="123456")

# Or fetch it programmatically from the printed sign-in URL
claude.auth_login(code_provider=lambda url: fetch_code_from_my_browser(url))

# Enterprise SSO, or pre-filling the account
claude.auth_login(sso=True)
claude.auth_login(email="you@example.com")

auth_login() returns the CLI's exit code (0 on success) and raises ClaudeTimeoutError if the login prompt never appears within timeout seconds.

Signing out, and long-lived tokens for CI:

claude.auth_logout()
claude.setup_token()   # interactive; sets up a long-lived auth token

Command-line usage

The package also installs a minimal pyclaudecli command (and python -m pyclaudecli) for quick one-off prompts — for anything beyond that, use ClaudeCLI directly.

pyclaudecli "What's 2+2?"                    # defaults to model "haiku"
pyclaudecli --model sonnet "Explain this diff"
pyclaudecli -m sonnet "Explain this diff"

python -m pyclaudecli "Hello, Claude!"

Errors

All CLI failures raise ClaudeCLIError (or ClaudeNotFoundError / ClaudeTimeoutError), carrying returncode, stdout, stderr, and cmd.

from pyclaudecli import ClaudeCLI, ClaudeCLIError, ClaudeNotFoundError, ClaudeTimeoutError

claude = ClaudeCLI()

try:
    claude.prompt("Do something", model="haiku", timeout=30)
except ClaudeTimeoutError as exc:
    print("timed out:", exc.cmd)
except ClaudeNotFoundError as exc:
    print("claude binary not on PATH:", exc)
except ClaudeCLIError as exc:
    print(exc.returncode, exc.stdout, exc.stderr)

build_flags

The helper ClaudeCLI uses internally to turn a {python_name: value} dict into CLI flags — handy if you're composing your own extra_flags or calling .run() directly. None/False are omitted, True becomes a bare flag, lists/tuples repeat the flag followed by each value, and anything else becomes the flag plus str(value).

from pyclaudecli import build_flags

build_flags({"model": "haiku", "verbose": True, "quiet": False, "add_dir": ["a", "b"]})
# ["--model", "haiku", "--verbose", "--add-dir", "a", "b"]

Every method, with an example

All examples assume:

from pyclaudecli import ClaudeCLI

claude = ClaudeCLI()

Raw / internal

# .run() — lowest-level call; returns a CommandResult(returncode, stdout, stderr)
result = claude.run(["--version"], check=False)
print(result.returncode, result.stdout)

Version / health

claude.version()   # "claude --version" -> "1.2.3"
claude.doctor()    # "claude doctor" -> health-check report
claude.update()    # "claude update" -> checks for and installs updates

claude.install()                       # "claude install" (latest default)
claude.install("stable")               # "claude install stable"
claude.install("1.2.3", force=True)    # "claude install 1.2.3 --force"

Prompting

# One-shot prompt -> plain text
claude.prompt("Summarize this repo's README.", model="haiku")

# Structured result (cost, session id, etc.)
result = claude.prompt_json("What's 2+2?", model="haiku")
print(result["result"], result["total_cost_usd"])

# Live streaming events
for event in claude.prompt_stream("Write a haiku about tests.", model="haiku"):
    print(event["type"])

# Full option surface
claude.prompt(
    "Refactor this function for clarity.",
    model="sonnet",
    output_format="json",
    system_prompt="You are a terse senior engineer.",
    append_system_prompt="Always answer in bullet points.",
    allowed_tools=["Read", "Edit"],
    disallowed_tools=["Bash"],
    add_dir=["../shared-lib"],
    permission_mode="acceptEdits",
    mcp_config=["./mcp.json"],
    settings="./claude-settings.json",
    resume="session-id-123",
    fork_session=True,
    effort="high",
    fallback_model="haiku",
    max_budget_usd=0.50,
    json_schema='{"type": "object"}',
    betas=["some-beta-flag"],
    no_session_persistence=True,
    dangerously_skip_permissions=False,
    restricted=True,
    input_text="piped stdin content",
    timeout=60,
    extra_flags={"worktree": True},  # anything without a named parameter
)

# continue_session=True appends --continue (keeps typing in the latest session)
claude.prompt("And now add tests for it.", continue_session=True)

Background sessions ("agents")

session_id = claude.start_background("Refactor the auth module", model="sonnet")

claude.list_agents()                          # all sessions, as a list of dicts
claude.list_agents(all=True, cwd="/repo")      # include finished ones, scoped to a project

claude.attach(session_id)                      # interactive; requires a real TTY
claude.logs(session_id)                        # raw terminal snapshot
claude.logs(session_id, strip_ansi=True)       # best-effort plain text

claude.stop(session_id)                        # stop, keep state
claude.respawn(session_id)                     # restart one stopped session
claude.respawn(all=True)                       # restart every stopped session
claude.rm(session_id)                          # delete a stopped session

Auth

claude.auth_status()                # {"loggedIn": True, "email": "...", ...}
claude.auth_status(as_json=False)   # human-readable text instead

# Interactive OAuth: prints the sign-in URL, then reads the pasted code from stdin
claude.auth_login()

# Non-interactive login: supply the code yourself
claude.auth_login(code="123456")

# Or fetch the code programmatically from the printed sign-in URL
claude.auth_login(code_provider=lambda url: fetch_code_from_my_browser(url))

# Route the printed sign-in URL/output somewhere other than stdout
claude.auth_login(on_output=lambda chunk: log.info(chunk), console=True, timeout=120)

claude.auth_login(sso=True)                      # enterprise SSO
claude.auth_login(email="you@example.com")       # pre-fill the account

claude.auth_logout()
claude.setup_token()   # interactive; sets up a long-lived auth token

MCP servers

claude.mcp_list()
claude.mcp_get("sentry")

claude.mcp_add("sentry", "https://mcp.sentry.dev/mcp", transport="http")
claude.mcp_add(
    "local-tool", "node", "server.js",
    transport="stdio", env=["API_KEY=abc"], scope="project",
)

claude.mcp_add_json("sentry", {"type": "http", "url": "https://mcp.sentry.dev/mcp"})
claude.mcp_add_from_claude_desktop(scope="user")

claude.mcp_remove("sentry", scope="project")
claude.mcp_login("sentry")             # interactive OAuth
claude.mcp_logout("sentry")
claude.mcp_reset_project_choices()

Plugins

claude.plugin_list()
claude.plugin_list(as_json=True)

claude.plugin_install("some-plugin", yes=True)
claude.plugin_install("some-plugin@my-marketplace", scope="user", as_json=True)

claude.plugin_uninstall("some-plugin", yes=True, prune=True)
claude.plugin_enable("some-plugin")
claude.plugin_disable("some-plugin")
claude.plugin_disable(all=True)        # disable every plugin

claude.plugin_update("some-plugin", yes=True)
claude.plugin_details("some-plugin")
claude.plugin_validate("./my-plugin", strict=True, as_json=True)
claude.plugin_prune(dry_run=True)

claude.plugin_marketplace_list()
claude.plugin_marketplace_add("https://github.com/org/marketplace-repo")
claude.plugin_marketplace_remove("marketplace-name")
claude.plugin_marketplace_update()             # update all
claude.plugin_marketplace_update("marketplace-name")

Project state

claude.project_purge()                 # purge state for the current project
claude.project_purge("/path/to/repo")  # or for a specific path

Auto mode

claude.auto_mode_config()      # effective classifier config, as a dict
claude.auto_mode_defaults()    # shipped default rules, as a dict
claude.auto_mode_reset()       # remove custom rules from user settings
claude.auto_mode_critique()    # AI feedback on your custom rules

Misc

claude.import_config("cursor", dry_run=True, yes=True)   # source: codex, gemini, or cursor

claude.ultrareview()                                 # review the current branch
claude.ultrareview("main")                            # review against a base branch
claude.ultrareview(482, as_json=True, post=True)       # review a PR, post results, get JSON

# Long-running server: returns a Popen handle, doesn't block
gateway = claude.start_gateway(config="./gateway.json")
...
gateway.terminate()

See ClaudeCLI's docstrings for the exact CLI flags behind each method — it covers every top-level claude command (auth, mcp, plugin, project, agents/background sessions, auto-mode, doctor, update, install, import, ultrareview, gateway) plus the main prompt flags. Anything not exposed as a named parameter can still be passed through via each method's extra_flags dict.

Development

git clone https://github.com/Suriya-Ravichandran/pyclaudecli.git
cd pyclaudecli
pip install -e .
pytest

License

MIT

Release files for pyclaudecli 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyclaudecli 1.0.2
File Size Uploaded
pyclaudecli-1.0.2.tar.gz 15.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyclaudecli 1.0.2
File Interpreter ABI Platform
pyclaudecli-1.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 32.3 kB

Release files / pyclaudecli-1.0.2.tar.gz

Download URL pyclaudecli-1.0.2.tar.gz
Size 15.9 kB
Tags Source
SHA-256 checksum
How to use checksums
1a2de3277e3297cdc5204d83fa77642bfc14eb2b12723a912f5b4140f2bbe916
BLAKE2b-256 checksum
How to use checksums
2869269e7c2a626c9ab5cf065443703ff8f2ca8c8c5b2d0426b25c990147921b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.14 {"installer":{"name":"uv","version":"0.12.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / pyclaudecli-1.0.2-py3-none-any.whl

Download URL pyclaudecli-1.0.2-py3-none-any.whl
Size 16.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
29d0a99b99e5bb6ef64f157264e71f23fb4fc24bb3a030e8db679733141b89d6
BLAKE2b-256 checksum
How to use checksums
65e3d8edd5ec6a26d5ca75aa0b63752f647d5e4f5e2ca516445260a4d2cbb8eb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.14 {"installer":{"name":"uv","version":"0.12.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

This release

1.0.2 This release

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page