Skip to main content

Python SDK for Uno — the ClawdChat agent tool gateway (2000+ tools). Sync & async clients, OpenAI/Anthropic adapters.

Project description

Uno SDK for Python

Search and call 2000+ real-world tools from Python in two lines. Powered by ClawdChat.

PyPI Python License

Install

pip install uno-sdk                    # core
pip install uno-sdk[openai]            # + OpenAI adapter
pip install uno-sdk[anthropic]         # + Anthropic adapter
pip install uno-sdk[all]               # everything

Note on naming: the PyPI distribution is uno-sdk and the import name is uno_sdk. The bare uno PyPI slot is held by an unrelated Python 2-era package (2014, no longer installable) — uno_sdk keeps our namespace clean.

Quick Start

from uno_sdk import Uno

uno = Uno(api_key="uk-xxx")

# Search tools
tools = uno.search("send email")
print(tools[0].name, tools[0].description)

# Call a tool
result = uno.call("email.send_email", {
    "to": "alice@example.com",
    "subject": "Hello",
    "body": "Hi from Uno!"
})
print(result.data)

OpenAI Integration

from uno_sdk import Uno
from uno_sdk.adapters import OpenAIAdapter
from openai import OpenAI

uno = Uno(api_key="uk-xxx")
openai_client = OpenAI()

# Get tools in OpenAI format
tools = uno.search("weather", adapter=OpenAIAdapter())

# Use with chat completions
response = openai_client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "What's the weather in Beijing?"}],
    tools=tools,
)

# Execute the tool call
if response.choices[0].message.tool_calls:
    tc = response.choices[0].message.tool_calls[0]
    import json
    slug = OpenAIAdapter.slug_from_function_name(tc.function.name)
    result = uno.call(slug, json.loads(tc.function.arguments))
    print(result.data)

Anthropic Integration

from uno_sdk import Uno
from uno_sdk.adapters import AnthropicAdapter
import anthropic

uno = Uno(api_key="uk-xxx")
client = anthropic.Anthropic()

tools = uno.search("search", adapter=AnthropicAdapter())
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    messages=[{"role": "user", "content": "Search for AI news"}],
    tools=tools,
    max_tokens=1024,
)

Async

from uno_sdk import AsyncUno

async with AsyncUno(api_key="uk-xxx") as uno:
    tools = await uno.search("translate")
    result = await uno.call("translate.text", {"text": "hello", "to": "zh"})

Resilience: retry, timeout, validation

call() gains optional keyword args for transient-error resilience and client-side argument validation. All opts are off by default — existing callers see no behaviour change.

from uno_sdk import Uno, RateLimitError, UpstreamTimeoutError

uno = Uno(api_key="uk-xxx")

# Retry up to 3 times on rate limit / upstream timeout / 5xx, with exp backoff.
result = uno.call(
    "tikhub-douyin.fetch_user_post_videos",
    {"sec_user_id": "MS4w...", "count": 35},
    retry=3,
    retry_delay=0.5,            # 0.5s, 1s, 2s
    timeout=30,                 # per-request timeout override
)

# Validate args against the tool's JSON Schema before sending.
# Does one extra search() to fetch the schema lazily; for hot paths
# prefer call_tool() with a pre-fetched Tool instance.
uno.call("weather.get_current", {"city": "Beijing"}, validate=True)

# call_tool() — skips the search, validates against a known Tool for free.
tool = uno.search("weather")[0]
uno.call_tool(tool, {"city": "Beijing"})  # validates by default

Batch concurrent calls (async only)

AsyncUno.call_batch fans out N tool calls concurrently with a semaphore to stay under the gateway rate limit. Results are returned in input order.

from uno_sdk import AsyncUno

async with AsyncUno(api_key="uk-xxx") as uno:
    # Fetch 30 video stat records in parallel, max 10 in flight at once.
    calls = [("tikhub-douyin.fetch_video_stats", {"aweme_id": aid})
             for aid in aweme_ids[:30]]
    results = await uno.call_batch(calls, max_concurrency=10, return_exceptions=True)
    for aid, r in zip(aweme_ids, results):
        if isinstance(r, Exception):
            print(f"{aid}: {r}")
        else:
            print(f"{aid}: {r.data}")

Long-running jobs: submit → poll (async only)

Tools like transcription / video generation take 30s+ and use a submit-then-poll pattern. AsyncUno.call_async wraps the whole flow — you don't need to know which tools are submit/result shaped.

from uno_sdk import AsyncUno

async with AsyncUno(api_key="uk-xxx") as uno:
    # One-shot: submit, then poll until done or 180s timeout.
    res = await uno.call_async(
        submit_tool="qingdou-video-text.qingdou_submit",
        submit_args={"urls": "https://v.douyin.com/xxx/"},
        result_tool="qingdou-video-text.qingdou_result",
        max_wait=180,              # total deadline
        wait_seconds_per_call=50,  # server-side wait per poll (under 60s MCP timeout)
        poll_interval=3,           # client-side gap when server returns pending
    )
    print(res.raw["items"][0]["content"])  # full transcript

The defaults (batch_id / status / done/pending) match qingdou and the gateway's submit/result convention. For tools with different field names, pass id_field / status_field / done_value / pending_values.

MCP (Claude Desktop / Cursor)

No SDK needed — connect directly:

{
  "mcpServers": {
    "uno": {
      "url": "https://clawdtools.uno/mcp"
    }
  }
}

OAuth login opens automatically in your browser.

Error Handling

from uno_sdk.exceptions import (
    AuthError, AuthRequiredError, QuotaError, RateLimitError,
    ToolNotFoundError, ToolDisabledError, InvalidArgumentsError,
    UpstreamTimeoutError, UpstreamCancelledError, ServerError,
)

try:
    result = uno.call("github.list_repos", {})
except AuthRequiredError as e:
    print(f"Please authorize: {e.auth_url}")
except RateLimitError as e:
    print(f"Rate limited, retry after {e.retry_after}s")
except QuotaError:
    print("Out of credits — visit https://clawdtools.uno/pricing")
except ToolNotFoundError:
    print("Tool not found — search first")
except InvalidArgumentsError as e:
    print(f"Bad arguments: {e}")
except UpstreamTimeoutError:
    print("Tool timed out at upstream — retry with `retry=3`")
except ServerError as e:
    print(f"Server error: {e}")

The RETRYABLE_ERRORS tuple lists the exceptions worth retrying:

from uno_sdk import RETRYABLE_ERRORS
# (RateLimitError, UpstreamTimeoutError, UpstreamCancelledError, ServerError)

API

Uno(api_key, base_url="https://clawdtools.uno", timeout=180)

Method Returns Description
search(query, *, limit=10, adapter=None) list[Tool] or list[dict] Search tools
call(tool, arguments={}, *, retry=0, retry_on=RETRYABLE_ERRORS, retry_delay=0.5, timeout=None, validate=False) CallResult Call a tool, with optional retry + validation
call_tool(tool, arguments={}, *, validate=True, retry=0, …) CallResult Call using a pre-fetched Tool — skips search, validates for free
me() dict Current user info

AsyncUno has the same methods, all async, plus:

Method Returns Description
await call_batch(calls, *, max_concurrency=10, return_exceptions=False, …) list[CallResult | UnoError] Concurrent batch with order preservation
await call_async(submit_tool, submit_args, result_tool, *, max_wait=180, …) CallResult Submit + poll long-running jobs to completion

Tool

Field Type Description
slug str Tool identifier (e.g. weather.get_current)
name str Display name
description str What the tool does
input_schema dict JSON Schema for arguments
auth_required bool Needs OAuth?
pricing_mode str free / per_call / per_token
credit_cost float Credits per call
stats dict Calls / rating / etc.
Method Returns Description
validate_args(arguments) list[str] Lightweight JSON-Schema validation. Empty list = valid.

CallResult

Field Type Description
data Any Tool response data
error str | None Error message if failed
meta dict Latency, credits used
raw dict Full response envelope (for extracting auth_url / recharge_url / etc.)
ok bool True if no error (property)

Companion: Uno CLI

Prefer a command-line flow? pip install uno-cli ships the uno command (search, call, multi-account OAuth, scope enforcement) — same credentials file, same gateway. See uno-cli.

Get an API Key

  1. Visit clawdtools.uno/login
  2. Log in with ClawdChat / Google / Phone
  3. Copy your API key from the Dashboard

License

MIT © ClawdChat.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

uno_sdk-1.1.0.tar.gz (19.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

uno_sdk-1.1.0-py3-none-any.whl (19.0 kB view details)

Uploaded Python 3

File details

Details for the file uno_sdk-1.1.0.tar.gz.

File metadata

  • Download URL: uno_sdk-1.1.0.tar.gz
  • Upload date:
  • Size: 19.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.15

File hashes

Hashes for uno_sdk-1.1.0.tar.gz
Algorithm Hash digest
SHA256 ab302f449937027952dd07cb3d5479cae43e622559b96ea6be911aeb905f3965
MD5 7df962ba2ed3a1581e9cf30a7c793a22
BLAKE2b-256 af065d34ca325d45fe1ee451bfaeb2293703d0019d82aed23c32ee5b3727f8bb

See more details on using hashes here.

File details

Details for the file uno_sdk-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: uno_sdk-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.15

File hashes

Hashes for uno_sdk-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 385143bcadb13897279fc1a110e66f3713059748acf8833328392b877b2114d0
MD5 f718bfbe4a87ef1a7e9e28d88edf87fb
BLAKE2b-256 c2038a38067a37cc1926e3dbb09f576192df1e4c65a14f7b5976b17ea35fcc17

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page