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.
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-sdkand the import name isuno_sdk. The bareunoPyPI slot is held by an unrelated Python 2-era package (2014, no longer installable) —uno_sdkkeeps 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
- Visit clawdtools.uno/login
- Log in with ClawdChat / Google / Phone
- Copy your API key from the Dashboard
License
MIT © ClawdChat.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab302f449937027952dd07cb3d5479cae43e622559b96ea6be911aeb905f3965
|
|
| MD5 |
7df962ba2ed3a1581e9cf30a7c793a22
|
|
| BLAKE2b-256 |
af065d34ca325d45fe1ee451bfaeb2293703d0019d82aed23c32ee5b3727f8bb
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
385143bcadb13897279fc1a110e66f3713059748acf8833328392b877b2114d0
|
|
| MD5 |
f718bfbe4a87ef1a7e9e28d88edf87fb
|
|
| BLAKE2b-256 |
c2038a38067a37cc1926e3dbb09f576192df1e4c65a14f7b5976b17ea35fcc17
|