Skip to main content

Qoder Agent SDK for Python

Python SDK for building applications on top of Qoder Agent.

The SDK starts qodercli for you, streams agent messages back to Python, and lets your application configure tools, permissions, working directories, MCP servers, hooks, and interactive sessions.

Installation

pip install qoder-agent-sdk

Prerequisites:

  • Python 3.10+
  • A Qoder account or another authentication method supported by your host application

CLI Behavior

Published platform wheels include a bundled qodercli, so a separate CLI installation is not required for normal SDK use. If you prefer to use a system-wide CLI or a pinned local build, pass QoderAgentOptions(cli_path=...).

Authentication

Every SDK query needs an explicit authentication option.

Authentication method Identity Use case
Personal Access Token (PAT) A Qoder user Automation that needs the user's permissions and data
Service Account An organization workload Services and jobs that should not depend on a personal account
Local qodercli session The signed-in user Interactive development on a workstation

For a PAT, generate a token at qoder.com/account/integrations, store it in a secret manager, and expose it through the default environment variable:

export QODER_PERSONAL_ACCESS_TOKEN=your-token
from qoder_agent_sdk import QoderAgentOptions, access_token_from_env

options = QoderAgentOptions(auth=access_token_from_env())

For a Service Account, read the key from your secret manager and pass it directly to the SDK:

from qoder_agent_sdk import QoderAgentOptions, service_account

# Get the Service Account key from the host's secret manager adapter.
service_account_key = read_secret("qoder-service-account-key")
options = QoderAgentOptions(
    auth=service_account(service_account_key=service_account_key)
)

The SDK and CLI obtain and refresh short-lived Service Account tokens for this authentication method. A host can retain the Service Account key and use service_account(fetch_service_account_token=...) to obtain and refresh short-lived SATs for qodercli. See the host callback example for a complete Token exchange and query. To reuse a signed-in developer workstation, use qodercli_auth(). See the SDK authentication guide for complete setup instructions and security guidance.

Quick Start

import anyio
from qoder_agent_sdk import QoderAgentOptions, qodercli_auth, query


async def main() -> None:
    options = QoderAgentOptions(auth=qodercli_auth())

    async for message in query(
        prompt="What is 2 + 2?",
        options=options,
    ):
        print(message)


anyio.run(main)

Basic Usage

query() runs a single SDK query and returns an async iterator of response messages.

from qoder_agent_sdk import (
    AssistantMessage,
    QoderAgentOptions,
    TextBlock,
    qodercli_auth,
    query,
)

options = QoderAgentOptions(
    auth=qodercli_auth(),
    system_prompt="You are a helpful assistant.",
    max_turns=1,
)

async for message in query(prompt="Explain this repository", options=options):
    if isinstance(message, AssistantMessage):
        for block in message.content:
            if isinstance(block, TextBlock):
                print(block.text)

Code Security

Built-in code security is disabled by default in SDK sessions. Enable only the capabilities the integration needs:

options = QoderAgentOptions(
    auth=qodercli_auth(),
    cwd="/path/to/project",
    security_scan={
        "l1_static_check": True,
        "l2_lightweight_scan": True,
        "l3_deep_scan": False,
    },
)

Omitted switches remain disabled. The top-level option is authoritative for the SDK session. Enabling any switch also makes the native security skill available; project and file scans do not have a separate switch.

Tools and Permissions

Qoder Agent can use tools such as file reads, file edits, shell commands, and MCP tools. allowed_tools is an approval allowlist: listed tools are auto-approved, while unlisted tools continue through permission_mode and can_use_tool for a decision. It does not remove tools from the agent's available toolset. To block tools, use disallowed_tools.

from qoder_agent_sdk import QoderAgentOptions, qodercli_auth, query

options = QoderAgentOptions(
    auth=qodercli_auth(),
    allowed_tools=["Read", "Edit"],
    disallowed_tools=["Bash"],
    permission_mode="acceptEdits",
)

async for message in query(
    prompt="Update the README introduction.",
    options=options,
):
    print(message)

For application-specific approval flows, provide can_use_tool:

from qoder_agent_sdk import (
    PermissionResultAllow,
    PermissionResultDeny,
    QoderAgentOptions,
    ToolPermissionContext,
    qodercli_auth,
)


async def can_use_tool(
    tool_name: str,
    tool_input: dict,
    context: ToolPermissionContext,
):
    if tool_name == "Bash":
        return PermissionResultDeny(message="Shell commands are disabled here.")
    return PermissionResultAllow()


options = QoderAgentOptions(
    auth=qodercli_auth(),
    can_use_tool=can_use_tool,
)

Plan Mode

Plan Mode is independent from tool permissions. Set its initial state with plan_mode; the SDK applies it after the CLI handshake and before the first user message, while preserving the underlying permission_mode:

options = QoderAgentOptions(
    auth=qodercli_auth(),
    permission_mode="default",
    plan_mode=True,
)

For a live session, use await client.set_plan_mode(True | False) to switch the state and await client.get_plan_mode() to read the authoritative current state. These APIs require a CLI that advertises the plan_mode_v1 capability.

For compatibility with existing consumers, SystemMessage init data projects permissionMode as "plan" while Plan Mode is active. This does not replace the underlying tool permission mode; after leaving Plan Mode, the field reports that mode again. Use the Plan APIs and PlanModeChangedMessage as the authoritative Plan state.

Working Directory

Use cwd to run the agent in a specific project directory:

from pathlib import Path

from qoder_agent_sdk import QoderAgentOptions, qodercli_auth

options = QoderAgentOptions(
    auth=qodercli_auth(),
    cwd=Path("/path/to/project"),
)

Interactive Sessions

Use QoderSDKClient when you need a long-lived, bidirectional session instead of a single query() call.

from qoder_agent_sdk import QoderAgentOptions, QoderSDKClient, qodercli_auth

options = QoderAgentOptions(auth=qodercli_auth())

async with QoderSDKClient(options=options) as client:
    await client.query("Inspect this project and summarize the main modules.")

    async for message in client.receive_response():
        print(message)

QoderSDKClient is useful for chat interfaces, follow-up prompts, interrupts, runtime permission changes, MCP server management, and other workflows that need state across multiple turns.

Use message priority to steer a turn that is already running:

await client.query(
    "Stop the current direction and inspect the failing tests first.",
    priority="now",
)

priority="now" stops the current response and handles the message immediately. priority="next" is the default and uses the next suitable point. priority="later" waits until the current response finishes. should_query=False adds the message to the conversation without starting a response by itself; its processing time still follows priority.

Assign a session-unique message_uuid to messages that need tracking or cancellation, and do not reuse UUIDs within a session. await client.interrupt() stops the current response and returns None. await client.cancel_async_message(message_uuid) returns True when the queued message is cancelled and False when it can no longer be cancelled.

Manage background tasks from a connected QoderSDKClient:

await client.background_tasks(tool_use_id)  # ToolUseBlock.id; omit for bulk conversion
await client.stop_task(task_id)  # TaskStartedMessage.task_id

background_tasks() moves running foreground Bash / Agent executions into the background; it does not list tasks. A targeted call returns whether the execution moved; bulk True acknowledges the request without guaranteeing a transition. stop_task() returns None and leaves the main turn and other independent tasks running. Unknown IDs and repeated stops are safe no-ops. Both methods require the CLI capability background_tasks_v1 (otherwise UnsupportedCliCapabilityError) and are unavailable in Cloud Agent sessions.

interrupt() does not stop existing background tasks. To clean them up, continue consuming receive_messages() after the main ResultMessage, replace the roster on each BackgroundTasksChangedMessage, and stop each task separately. Bulk conversion can detach an Agent's Bash execution as an independent task.

Use AgentDefinition(background=True, ...) to launch delegated agents in the background. Merge TaskUpdatedMessage.patch into stored task state. Task events retain raw data; optional metadata depends on the CLI. Missing progress usage is None, and total_tokens can be absent within usage.

For event fields, launch result types, the Bash result guard, and examples, see Python SDK Reference.

External Session Storage

Use session_store when a host needs durable transcripts outside the local machine. The SDK mirrors entries after qodercli commits them locally. A later process can restore the same session before qodercli starts:

qodercli commit -> SDK append(key, entries) -> external store
external store -> SDK load(key) -> temporary QODER_CONFIG_DIR -> qodercli resume
from qoder_agent_sdk import (
    InMemorySessionStore,
    QoderAgentOptions,
    qodercli_auth,
    query,
)

session_store = InMemorySessionStore()

options = QoderAgentOptions(
    auth=qodercli_auth(),
    cwd="/path/to/project",
    session_store=session_store,
)

async for message in query(prompt="Inspect this project.", options=options):
    print(message)

resume_options = QoderAgentOptions(
    auth=qodercli_auth(),
    cwd="/path/to/project",
    resume="11111111-1111-4111-8111-111111111111",
    session_store=session_store,
)

Every store implements async append(key, entries) and load(key). Implement list_sessions(project_key) for continue_conversation=True and session listing, list_subkeys(key) to restore child-agent transcripts, and delete(key) for deletion. Entries are opaque JSON dictionaries and must remain in append order. A child transcript uses an opaque subpath such as subagents/agent-<id>; the key does not include the on-disk .jsonl extension.

When load() returns None or an empty list for an explicit resume, the SDK falls back to the same local session ID. Missing or empty child transcripts do not prevent restoration of the main session, and unsafe subpaths are ignored.

session_store_flush="batched" is the default. "eager" starts each append without waiting for the result boundary. Final append failures are emitted as non-fatal SDKMirrorErrorMessage values. load_timeout_ms defaults to 60,000 ms. Session storage cannot be combined with file checkpointing, a custom transport, or the Cloud Agent runtime. It requires the built-in subprocess transport.

The existing local session helpers remain synchronous. External stores use the async helpers list_sessions_from_store, get_session_info_from_store, get_session_messages_from_store, rename_session_via_store, tag_session_via_store, fork_session_via_store, and delete_session_via_store. Local and external child-agent transcripts are available through list_subagents / get_subagent_messages and list_subagents_from_store / get_subagent_messages_from_store. Use import_session_to_store to copy an existing local main transcript, child-agent transcripts, and metadata into a store.

Session message readers default to view="runtime", which returns the latest Compact Summary and messages written after it. Pass view="historical" to get_session_messages or get_session_messages_from_store to read the real active lineage across Compact boundaries; Compact Boundary and Summary records are hidden from that projection. The local and external fork_session helpers use the same historical lineage, so up_to_message_id may target a message before Compact.

For a running local session, QoderSDKClient.rewind() previews or executes a conversation, files, or both rewind when the bundled CLI advertises session_rewind_v1. To start a new process from a persisted historical node, set QoderAgentOptions.resume, resume_session_at, and optionally resume_drops_turn; combine them with fork_session=True to preserve the source session.

Production stores

The SDK exports the SessionStore protocol but does not ship a production-ready external storage implementation. Implement the protocol against shared storage operated by your application, then validate its append/load ordering, project isolation, subkey handling, and deletion behavior with run_session_store_conformance.

Memory

Configure native or custom memory with QoderAgentOptions(memory=...). Interactive clients expose flush_memory() and refresh_memory(). See Memory for configuration, generation callbacks, and lifecycle behavior.

Custom Tools

You can expose Python functions to Qoder Agent as in-process SDK MCP servers. This avoids managing a separate MCP subprocess for simple application-local tools.

from qoder_agent_sdk import (
    QoderAgentOptions,
    QoderSDKClient,
    create_sdk_mcp_server,
    qodercli_auth,
    tool,
)


@tool("greet", "Greet a user", {"name": str})
async def greet_user(args):
    return {
        "content": [
            {"type": "text", "text": f"Hello, {args['name']}!"}
        ]
    }


server = create_sdk_mcp_server(
    name="my-tools",
    version="1.0.0",
    tools=[greet_user],
)

options = QoderAgentOptions(
    auth=qodercli_auth(),
    mcp_servers={"tools": server},
    allowed_tools=["mcp__tools__greet"],
)

async with QoderSDKClient(options=options) as client:
    await client.query("Greet Alice.")
    async for message in client.receive_response():
        print(message)

Hooks

Hooks are deterministic Python callbacks invoked at specific points in the agent loop. They are useful for validation, policy checks, logging, and application-specific feedback.

from qoder_agent_sdk import HookMatcher, QoderAgentOptions, qodercli_auth


async def block_script(input_data, tool_use_id, context):
    if input_data["tool_name"] != "Bash":
        return {}

    command = input_data["tool_input"].get("command", "")
    if "./deploy.sh" in command:
        return {
            "hookSpecificOutput": {
                "hookEventName": "PreToolUse",
                "permissionDecision": "deny",
                "permissionDecisionReason": "Deployment scripts require review.",
            }
        }
    return {}


options = QoderAgentOptions(
    auth=qodercli_auth(),
    hooks={
        "PreToolUse": [
            HookMatcher(matcher="Bash", hooks=[block_script]),
        ],
    },
)

Error Handling

from qoder_agent_sdk import (
    CLIConnectionError,
    CLIJSONDecodeError,
    CLINotFoundError,
    ProcessError,
    QoderAgentOptions,
    QoderSDKError,
    qodercli_auth,
    query,
)

try:
    async for message in query(
        prompt="Hello Qoder",
        options=QoderAgentOptions(auth=qodercli_auth()),
    ):
        print(message)
except CLINotFoundError:
    print("qodercli was not found. Install a platform wheel or set cli_path.")
except CLIConnectionError as exc:
    print(f"Connection failed: {exc}")
except ProcessError as exc:
    print(f"qodercli exited with code {exc.exit_code}")
except CLIJSONDecodeError as exc:
    print(f"Could not parse qodercli output: {exc}")
except QoderSDKError as exc:
    print(f"SDK error: {exc}")

License and Terms

Copyright (c) 2026 Qoder

Use of this software is governed by the Qoder Product Service Terms:

https://qoder.com/product-service

By installing or using this package, you agree to those terms.

Metadata

Release files for qoder-agent-sdk 1.0.16

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

Source distribution (sdist)

Source distribution for qoder-agent-sdk 1.0.16
File Size Uploaded Provenance
qoder_agent_sdk-1.0.16.tar.gz 145.5 kB Without provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for qoder-agent-sdk 1.0.16
File
qoder_agent_sdk-1.0.16-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
qoder_agent_sdk-1.0.16-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details
qoder_agent_sdk-1.0.16-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details
qoder_agent_sdk-1.0.16-py3-none-manylinux_2_17_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
qoder_agent_sdk-1.0.16-py3-none-manylinux_2_17_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
qoder_agent_sdk-1.0.16-py3-none-macosx_11_0_x86_64.whl Python 3 none macOS 11.0+ x86-64 Details
qoder_agent_sdk-1.0.16-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details

Total release size: 545.0 MB

Release files / qoder_agent_sdk-1.0.16.tar.gz

Download URL qoder_agent_sdk-1.0.16.tar.gz
Size 145.5 kB
Tags Source
SHA-256 checksum
How to use checksums
534a0de6b972ec6f994681084d60362afdfb698729aaaa855193d2531ec2bf3f
BLAKE2b-256 checksum
How to use checksums
3157872a144167aa4488d3f09c0a666863b49b6e4046422eb2e55a75f7dad46e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.14

Release files / qoder_agent_sdk-1.0.16-py3-none-win_amd64.whl

Download URL qoder_agent_sdk-1.0.16-py3-none-win_amd64.whl
Size 98.2 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
4122b2c3ba07c771f9994f741ceee5d106c184bd7cf8e115cc106abc6c9da211
BLAKE2b-256 checksum
How to use checksums
54b9a7a57cb0ea364ff5a6f1ef1ff5cde431ca1a37e7c0753cac212e45739cdf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.14

Release files / qoder_agent_sdk-1.0.16-py3-none-musllinux_1_2_x86_64.whl

Download URL qoder_agent_sdk-1.0.16-py3-none-musllinux_1_2_x86_64.whl
Size 75.7 MB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
caa6e0e2eb814212052b971d8cd144e0d9b8d93ae26222b6983d3630ea7b5286
BLAKE2b-256 checksum
How to use checksums
9ca703e7c4993e478c8a65e8fe8adf80dca3b8995f6e3eb55b076f676e92def7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.14

Release files / qoder_agent_sdk-1.0.16-py3-none-musllinux_1_2_aarch64.whl

Download URL qoder_agent_sdk-1.0.16-py3-none-musllinux_1_2_aarch64.whl
Size 75.3 MB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
93ea5207869b210394d6594eeda3be7fe4958789f1b50ccc9f70c7a352e95191
BLAKE2b-256 checksum
How to use checksums
f4b85505d2f79b6d3c7b7949704d68d7a8f5372600d409dfc575756b4a0394cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.14

Release files / qoder_agent_sdk-1.0.16-py3-none-manylinux_2_17_x86_64.whl

Download URL qoder_agent_sdk-1.0.16-py3-none-manylinux_2_17_x86_64.whl
Size 78.1 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
ea9ab42537dcb75b5d5ec241ccab925501c90776a727eac01e963ab4bb15ad92
BLAKE2b-256 checksum
How to use checksums
12bcf696f76ef6a06637fb61aeb024ca887632d53615f7f9c112a6711525216b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.14

Release files / qoder_agent_sdk-1.0.16-py3-none-manylinux_2_17_aarch64.whl

Download URL qoder_agent_sdk-1.0.16-py3-none-manylinux_2_17_aarch64.whl
Size 77.8 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
13e262e0777f241c8390340d73384df910d6e711813432ef7f7192e2ade875c0
BLAKE2b-256 checksum
How to use checksums
8b3580ddb598b96f0397a8c69d2a46a92877eb3b04d6c355958a726a6861140d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.14

Release files / qoder_agent_sdk-1.0.16-py3-none-macosx_11_0_x86_64.whl

Download URL qoder_agent_sdk-1.0.16-py3-none-macosx_11_0_x86_64.whl
Size 72.2 MB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
656c1619d1a3c9206ad29e70f8e3b0edd700e2d8e36bfc31fd01291bdd4712a8
BLAKE2b-256 checksum
How to use checksums
a102bc556d00b8322ad13cdea9049d333b1c98c1fabd26a7260239ed3479b683
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.14

Release files / qoder_agent_sdk-1.0.16-py3-none-macosx_11_0_arm64.whl

Download URL qoder_agent_sdk-1.0.16-py3-none-macosx_11_0_arm64.whl
Size 67.6 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
33dbb74b5523086b9f90a2d605693799f1b297d7592f409f8d619e6ab59262fe
BLAKE2b-256 checksum
How to use checksums
0276bad318a35adbe09d7bf88d1f1c915d7fa4322afdd99f28fba4843ac0e6ec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.14

Security for qoder-agent-sdk 1.0.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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