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.

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.15

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.15
File Size Uploaded
qoder_agent_sdk-1.0.15.tar.gz 142.9 kB Details

Built distributions (wheels)

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

Total release size: 540.8 MB

Release files / qoder_agent_sdk-1.0.15.tar.gz

Download URL qoder_agent_sdk-1.0.15.tar.gz
Size 142.9 kB
Tags Source
SHA-256 checksum
How to use checksums
8e1f477b311d91ddadf00c829325eee69e61d6aeefe2378da858cd46264455d4
BLAKE2b-256 checksum
How to use checksums
608ffb7dec3a956067cdf2a7e0775fd4860c29860371450e1065add3d9fa54e5
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.15-py3-none-win_amd64.whl

Download URL qoder_agent_sdk-1.0.15-py3-none-win_amd64.whl
Size 97.9 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
054aa81fd00b3559dac970fcc7534a3a4616a12f2d4ad66b0a2c3dd165caaa29
BLAKE2b-256 checksum
How to use checksums
9ffa95991c1ab1281042b3a393d7bda7aeef754b52143beb6f8c534cd0eb6525
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.15-py3-none-musllinux_1_2_x86_64.whl

Download URL qoder_agent_sdk-1.0.15-py3-none-musllinux_1_2_x86_64.whl
Size 74.5 MB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
0863501ba356dcd42e4b3db00ea3ee657bd2df940c889d085fb1e6fac3ddf871
BLAKE2b-256 checksum
How to use checksums
a7f807ea535351ba2a8a19731673d7dfc493ffecad982c756f30ec24eaa014b0
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.15-py3-none-musllinux_1_2_aarch64.whl

Download URL qoder_agent_sdk-1.0.15-py3-none-musllinux_1_2_aarch64.whl
Size 74.1 MB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
50a59a016ba3ed32b7ef1823fd0fc344f82282066806538e43a47fed673ba20d
BLAKE2b-256 checksum
How to use checksums
8d164b2f3ca9527a34456687d1ef5c6c05a9d1f9c9cb135f34ad4a82a12874a0
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.15-py3-none-manylinux_2_17_x86_64.whl

Download URL qoder_agent_sdk-1.0.15-py3-none-manylinux_2_17_x86_64.whl
Size 77.8 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
dc74bd06198c68faff4107a795692cdac612302bf0524f8f03990bb4ef0af225
BLAKE2b-256 checksum
How to use checksums
1c89c3f0cd22611a8d8d757c0b71e0c18b75da6b319791469dff4da24f9fb233
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.15-py3-none-manylinux_2_17_aarch64.whl

Download URL qoder_agent_sdk-1.0.15-py3-none-manylinux_2_17_aarch64.whl
Size 77.4 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
ed25b6ab8a0ba19898018d7afc6442e93bdead7f3aeb8a51397fcbe3fa773783
BLAKE2b-256 checksum
How to use checksums
644ba20d11cc21ccf396aefdb0d5a5174b6a41de56e272a2bf2bf2acc87e8b44
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.15-py3-none-macosx_11_0_x86_64.whl

Download URL qoder_agent_sdk-1.0.15-py3-none-macosx_11_0_x86_64.whl
Size 71.8 MB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
d206c64da1af000c392a185818c4453187305529ffd8fe123da510b6bef293c8
BLAKE2b-256 checksum
How to use checksums
ef45815392cdd7a45f300c9719933d8dd4cb5b1de2fc7525ed772c508f3ebaaa
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.15-py3-none-macosx_11_0_arm64.whl

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

Release history Release notifications | RSS feed

This release

1.0.15 This release

8 release files

1.0.14

8 release files

1.0.13

8 release files

1.0.10

8 release files

1.0.9

8 release files

1.0.8

8 release files

1.0.5

8 release files

1.0.2

8 release files

1.0.1

8 release files

1.0.0

8 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