Qoder Agent SDK for Python
Python SDK for building applications on top of Qoder Agent.
The SDK starts qodercn 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 qodercn-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 qodercn, 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 qodercn session |
The signed-in user | Interactive development on a workstation |
For a PAT, generate a token at qoder.cn/account/integrations, store it in a secret manager, and expose it through the default environment variable:
export QODERCN_PERSONAL_ACCESS_TOKEN=your-token
from qodercn_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 qodercn_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 qodercn. 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 qodercn_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 qodercn_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)
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 qodercn_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 qodercn_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,
)
Working Directory
Use cwd to run the agent in a specific project directory:
from pathlib import Path
from qodercn_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 qodercn_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 qodercn commits them locally. A later
process can restore the same session before qodercn starts:
qodercn commit -> SDK append(key, entries) -> external store
external store -> SDK load(key) -> temporary QODERCN_CONFIG_DIR -> qodercn resume
from qodercn_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. 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.
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 qodercn_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 qodercn_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 qodercn_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("qodercn 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"qodercn exited with code {exc.exit_code}")
except CLIJSONDecodeError as exc:
print(f"Could not parse qodercn 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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qodercn_agent_sdk-1.0.13.tar.gz.
File metadata
- Download URL: qodercn_agent_sdk-1.0.13.tar.gz
- Upload date:
- Size: 125.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da3d02e704d5d3937baa90be18efdadf0882a1d51da0617d3eff79f0d388a610
|
|
| MD5 |
4a90122131f270fc7b68cf3693cbdd2e
|
|
| BLAKE2b-256 |
e4b2df3a45c311bb44d8c5a48c0ea63e532fcaeef87a0bee36e5cf1c96a63135
|
File details
Details for the file qodercn_agent_sdk-1.0.13-py3-none-win_amd64.whl.
File metadata
- Download URL: qodercn_agent_sdk-1.0.13-py3-none-win_amd64.whl
- Upload date:
- Size: 63.3 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3fadc1c7e10c7cf85e43779a49e25063735fcbf5b4b008d0a8dcd06c8127f008
|
|
| MD5 |
ecfdf9e4d5a7c94e7882d3d23f812ba6
|
|
| BLAKE2b-256 |
812f81390c22ea93ab63c9588be2db97549a425735082c84cb9481686e9d1c7e
|
File details
Details for the file qodercn_agent_sdk-1.0.13-py3-none-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: qodercn_agent_sdk-1.0.13-py3-none-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 49.7 MB
- Tags: Python 3, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ef198eeae9f05a2c9c2e8d8b91c598ebf32c81d849babd4defa57a78bd7825a
|
|
| MD5 |
82793e5c26a44d8bdfa1fdac7f0f0bd8
|
|
| BLAKE2b-256 |
95d0906d09407296bc194beef0b8a3049bb20c2728e971f296e8617096601b58
|
File details
Details for the file qodercn_agent_sdk-1.0.13-py3-none-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: qodercn_agent_sdk-1.0.13-py3-none-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 49.1 MB
- Tags: Python 3, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db6a893b1767104f1d47c4c2cb7b9a68d9ff6c72d827f1635d1587dbd9936803
|
|
| MD5 |
274f5002045989d55fdc873bd4af575d
|
|
| BLAKE2b-256 |
060f8b3974da001b7c2cf0a03024f19373c5500918ebece9249487d1b25b1634
|
File details
Details for the file qodercn_agent_sdk-1.0.13-py3-none-manylinux_2_17_x86_64.whl.
File metadata
- Download URL: qodercn_agent_sdk-1.0.13-py3-none-manylinux_2_17_x86_64.whl
- Upload date:
- Size: 50.6 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cab6e78f8f73170bebcd20244cef3528b2d74d5b5b7edcc4fe3a9bd768e16fa2
|
|
| MD5 |
e0f5a28ba5ff2e7a725e7bfb53686dfe
|
|
| BLAKE2b-256 |
b6593b8047b12c74c7ca1a13e5b451908c42aef51ed0848f745b0944e718c7ae
|
File details
Details for the file qodercn_agent_sdk-1.0.13-py3-none-manylinux_2_17_aarch64.whl.
File metadata
- Download URL: qodercn_agent_sdk-1.0.13-py3-none-manylinux_2_17_aarch64.whl
- Upload date:
- Size: 50.4 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50feccd0c5d596659e086e9942f41712149666f242b67211f2e0d7f1cb3f1636
|
|
| MD5 |
bc5ad10b9df6080f9256a3f0f51d6bad
|
|
| BLAKE2b-256 |
fcf082778e4bca26f6d084c64c6c0bb62d09a8a7c587f3bbd6aa6ddf1e151905
|
File details
Details for the file qodercn_agent_sdk-1.0.13-py3-none-macosx_11_0_x86_64.whl.
File metadata
- Download URL: qodercn_agent_sdk-1.0.13-py3-none-macosx_11_0_x86_64.whl
- Upload date:
- Size: 43.0 MB
- Tags: Python 3, macOS 11.0+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ae5dbc306454389a25811a495b3c4fdab34d7e93d340210fb736401adcc9c30
|
|
| MD5 |
78473c5c049a9aa151b7aaaf7d7b4f87
|
|
| BLAKE2b-256 |
f0719c0f81a6fd8a66162fd18d0db34052fc120ccbb68eb2460f853fb8b9f842
|
File details
Details for the file qodercn_agent_sdk-1.0.13-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: qodercn_agent_sdk-1.0.13-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 39.0 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
820b8952b23db20e882070818b3e640e540abb51c7e1de4bb6c33e319ce12fdc
|
|
| MD5 |
f78b0dc7ce248c4279d1f6a9860efdb5
|
|
| BLAKE2b-256 |
fc6234b43ccd708b8939a6041416fb5159b77661c4b523445a78c02c074c09b6
|