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.

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

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.14
File Size Uploaded
qoder_agent_sdk-1.0.14.tar.gz 138.2 kB Details

Built distributions (wheels)

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

Total release size: 356.8 MB

Release files / qoder_agent_sdk-1.0.14.tar.gz

Download URL qoder_agent_sdk-1.0.14.tar.gz
Size 138.2 kB
Tags Source
SHA-256 checksum
How to use checksums
8152204e337425d662530056e7edac12701c8b2b10832afe2fb6e14858c3f63c
BLAKE2b-256 checksum
How to use checksums
3ac42f04e570d6e1435cf0641edea7257fde129a63931d374200f8293c89570f
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.14-py3-none-win_amd64.whl

Download URL qoder_agent_sdk-1.0.14-py3-none-win_amd64.whl
Size 64.9 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
eed0f487ed9ffad769b4c186651f441d7650dc03ecad7c1a26fd88e16686318c
BLAKE2b-256 checksum
How to use checksums
8c99be119a6271d22ad85b828329f524288477f8fea4a3ab1ea82a6d7f341575
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.14-py3-none-musllinux_1_2_x86_64.whl

Download URL qoder_agent_sdk-1.0.14-py3-none-musllinux_1_2_x86_64.whl
Size 51.4 MB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
1813a1d21d1be449b46c2ce913fffdcfc4242fc749ab0322c70d84970a9a7a54
BLAKE2b-256 checksum
How to use checksums
fbf40ed5ee016c3fcf739d79bd26159d8dd7680268c8b588555233f4292a2f2b
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.14-py3-none-musllinux_1_2_aarch64.whl

Download URL qoder_agent_sdk-1.0.14-py3-none-musllinux_1_2_aarch64.whl
Size 50.7 MB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
6b1c6bc9eb89400b3cdc774578b8f00d44db037e4a01caa5a1cd28b0ed6d26ca
BLAKE2b-256 checksum
How to use checksums
a31105f408975bba90a28f270d649290ec79c5d26908cc474eadfd91dfe17c88
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.14-py3-none-manylinux_2_17_x86_64.whl

Download URL qoder_agent_sdk-1.0.14-py3-none-manylinux_2_17_x86_64.whl
Size 52.2 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
e92de14b5faf764cb0b7747f7af8abc43030e126a008301e3fc8bc68b0ffa037
BLAKE2b-256 checksum
How to use checksums
6f81da4d4349af71bd5b33ef04e03a0fd80a6674b80c2f2beb411a1ce7cb77c8
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.14-py3-none-manylinux_2_17_aarch64.whl

Download URL qoder_agent_sdk-1.0.14-py3-none-manylinux_2_17_aarch64.whl
Size 52.0 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
89743a8c2a593bef8fb9d20980122c6144bf3e6caf6d57d2a3d4c581e93a96e8
BLAKE2b-256 checksum
How to use checksums
eb54753fdd8ebbc0817c618a8cb8e62779cf5da80d25abcda3760c6cec55b183
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.14-py3-none-macosx_11_0_x86_64.whl

Download URL qoder_agent_sdk-1.0.14-py3-none-macosx_11_0_x86_64.whl
Size 44.7 MB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
eba057057b362fb226e0a2b35252fb44a97c3a3bf5c5b1ce36994cbd8b48c3b5
BLAKE2b-256 checksum
How to use checksums
6cb70ecafb2cb1d949d35f5c319efa57acf3175e20de4bdaca0a306c159ca892
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.14-py3-none-macosx_11_0_arm64.whl

Download URL qoder_agent_sdk-1.0.14-py3-none-macosx_11_0_arm64.whl
Size 40.7 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
c5ceddb4cbfe8d21bdbd76747b5a7025edc4dcf2b5e9b10046cc139f44fb9cfe
BLAKE2b-256 checksum
How to use checksums
dc8f7b38b799c5fc3404afec987b9aa08b29ec7600d53bff68b55463127432f8
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

1.0.15

8 release files

This release

1.0.14 This release

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