Metadata-Version: 2.4
Name: qoder-agent-sdk
Version: 1.0.0
Summary: Python SDK for Qoder Agent
Author: Qoder
License: 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.
License-File: LICENSE
Keywords: agent,ai,qoder,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: anyio>=4.0.0
Requires-Dist: mcp>=0.1.0
Requires-Dist: typing-extensions>=4.0.0; python_version < '3.11'
Provides-Extra: dev
Requires-Dist: anyio[trio]>=4.0.0; extra == 'dev'
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.20.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest-timeout>=2.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: python-dotenv>=1.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# 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

```bash
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.

To reuse the local `qodercli` login state:

```python
from qoder_agent_sdk import QoderAgentOptions, qodercli_auth

options = QoderAgentOptions(auth=qodercli_auth())
```

To authenticate with a personal access token:

Generate a Personal Access Token at
[qoder.com/account/integrations](https://qoder.com/account/integrations):

1. Sign in to your Qoder account.
2. Open the integrations page.
3. Create a new PAT, choosing the expiry and scopes you need.
4. Copy the token immediately. The value cannot be retrieved again after the
   page is closed.

Use separate tokens for local scripts, CI, and production services when
possible, so each environment can be revoked independently. Do not hard-code
tokens in source code.

```python
from qoder_agent_sdk import QoderAgentOptions, access_token_from_env

options = QoderAgentOptions(auth=access_token_from_env())
```

`access_token_from_env()` reads `QODER_PERSONAL_ACCESS_TOKEN` by default.

## Quick Start

```python
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.

```python
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)
```

## 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`.

```python
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`:

```python
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,
)
```

## Working Directory

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

```python
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.

```python
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.

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

```python
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.

```python
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

```python
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.
