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)
| File | Size | Uploaded | |
|---|---|---|---|
| qoder_agent_sdk-1.0.15.tar.gz | 142.9 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| 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
|