Python SDK for LIME 2.0 agent authentication. Zero-config, Proof-of-Work auto-solve, async-first.
Project description
LIME Agents SDK
Official Python SDK for LIME agent workers. Async-first client with one public login method: fetch Proof-of-Work challenge, solve SHA-256 PoW, submit approval with retries.
Installation
pip install lime-agents-sdk
Install the latest commit from GitHub:
pip install git+https://github.com/Mawyxx/lime-agents-sdk.git
Requirements: Python 3.10+
Quick start
Minimal
from lime_agents import LimeAgent
AGENT_TOKEN = "at_..."
async def login_to_site(request_id: str) -> str:
async with LimeAgent(agent_token=AGENT_TOKEN) as agent:
result = await agent.login(request_id)
return result.status
Production
from lime_agents import LimeAgent, PowTimeoutError, ApiError
AGENT_TOKEN = "at_..."
agent = LimeAgent(agent_token=AGENT_TOKEN)
async def on_login_required(request_id: str) -> str | None:
try:
result = await agent.login(request_id)
return result.status
except PowTimeoutError:
return (await agent.login(request_id)).status
except ApiError as exc:
print(f"[{exc.code}] {exc.message}")
return None
Authentication
The SDK authenticates agent HTTP calls with the X-Agent-Token header.
Resolution order:
- Constructor argument
agent_token="at_..." - Environment variable
LIME_AGENT_TOKEN
If neither is set (or the value is empty after trimming), construction raises AuthenticationError:
from lime_agents import LimeAgent, AuthenticationError
try:
agent = LimeAgent()
except AuthenticationError as exc:
print(exc.message)
Obtain the agent token once when registering an agent in the LIME owner portal. Store it as a server-side secret in your worker environment.
MCP client
LIME uses two credential lanes — never mix them:
| Lane | Header | Used for |
|---|---|---|
| LIME platform | X-Agent-Token |
login(), get_profile(), OAuth token issuance |
| External MCP RS | Authorization: Bearer <jwt> |
list_tools, call_tool, resources, prompts |
The SDK issues the MCP JWT automatically via POST /api/v1/modules/oauth/token (header only, empty body — ADR 0081). Requires agent capability oauth:issue.
import os
from lime_agents import LimeAgent
agent = LimeAgent(agent_token=os.getenv("LIME_AGENT_TOKEN"))
mcp_jwt = await agent.get_mcp_access_token()
# MCP methods — server_url first (one agent, many servers)
tools = await agent.list_tools("https://mcp.example.com")
result = await agent.call_tool("https://mcp.example.com", "echo", {"text": "Hello"})
resources = await agent.list_resources("https://mcp.example.com")
templates = await agent.list_resource_templates("https://mcp.example.com")
prompts = await agent.list_prompts("https://mcp.example.com")
data = await agent.read_resource("https://mcp.example.com", "file:///data.txt")
prompt = await agent.get_prompt("https://mcp.example.com", "greet", {"name": "Alice"})
await agent.set_logging_level("https://mcp.example.com", "info")
await agent.send_ping("https://mcp.example.com")
caps = await agent.get_server_capabilities("https://mcp.example.com")
await agent.send_progress_notification("https://mcp.example.com", "token123", 50.0, 100.0)
# Multiple servers — sessions cached per URL
await agent.call_tool("https://mcp2.example.com", "get_weather", {"city": "Moscow"})
await agent.aclose()
Errors:
| Exception | Meaning |
|---|---|
AuthenticationError |
Bad or missing LIME_AGENT_TOKEN |
OAuthCapabilityError |
Token valid but agent lacks oauth:issue |
McpAuthenticationError |
MCP JWT rejected by the resource server |
Integration pattern: Headless agent
Typical embedding: hold one LimeAgent per worker process and call login() when a login request arrives.
from lime_agents import LimeAgent
class TradingAgent:
def __init__(self, token: str):
self.lime = LimeAgent(agent_token=token)
async def on_login_required(self, request_id: str) -> str:
result = await self.lime.login(request_id)
return result.status
The site backend creates the request (POST /modules/agent-login/requests), delivers login_request_id to your worker, and long-polls events until status becomes DELIVERED. Your worker only runs the login step above.
Production deployment
For agent workers handling many login jobs, create one LimeAgent per worker process at startup. Do not use async with LimeAgent() inside each job handler — that tears down the HTTP client after every login request.
- Instantiate
LimeAgentonce when the worker starts. - Reuse the same instance for all
login()andget_profile()calls. - Optionally pass a shared
httpx.AsyncClientviahttp_clientfor connection pooling.
from lime_agents import LimeAgent
# Created once at worker startup
agent = LimeAgent()
# Reused in every task
async def handle_login(request_id: str) -> str:
result = await agent.login(request_id)
return result.status
Call agent.aclose() only on worker shutdown (or close your injected http_client yourself).
API reference
LimeAgent
Async client for agent-runtime operations (login confirmation, read profile).
Constructor
All arguments are keyword-only.
| Parameter | Type | Default | Description |
|---|---|---|---|
agent_token |
str | None |
None |
Agent secret. Falls back to LIME_AGENT_TOKEN. |
base_url |
str | None |
None |
API root including /api/v1. Falls back to LIME_API_BASE, then https://lime.pics/api/v1. |
timeout |
float |
30.0 |
Per-request HTTP timeout in seconds (httpx). |
max_retries |
int |
3 |
Maximum retries on transient network errors and HTTP 408/429/5xx. |
pow_timeout |
float |
10.0 |
Wall-clock budget in seconds for the PoW solver loop. |
mcp_token_refresh_skew |
float |
30.0 |
Refresh MCP JWT this many seconds before expires_in. |
mcp_read_timeout |
float |
300.0 |
MCP streamable HTTP read timeout in seconds. |
http_client |
httpx.AsyncClient | None |
None |
Inject a custom async HTTP client (tests, corporate proxy/TLS). When omitted, the SDK creates and owns a client. |
agent = LimeAgent(
agent_token="at_live_...",
base_url="https://lime.pics/api/v1",
timeout=60.0,
max_retries=5,
pow_timeout=15.0,
)
Context manager: async with LimeAgent() as agent: calls aclose() on exit. Call await agent.aclose() manually when not using a context manager.
async login(request_id: str) -> ApprovalResult
Confirms a site login request on behalf of the agent.
Internal steps:
GET /auth/requests/{request_id}(public, no auth) — readpow_challenge,pow_difficulty- Solve PoW: find
noncesuch thatint(SHA256(challenge + nonce), 16) < 2**(256 - difficulty) POST /modules/agent-login/requests/{request_id}/approvewithX-Agent-Tokenand body{"pow_nonce": "<nonce>"}
Parameters:
| Name | Type | Description |
|---|---|---|
request_id |
str |
Login request ID from the site backend (login_request_id from create). |
Returns: ApprovalResult with FSM status (typically DELIVERED after successful login confirmation).
from lime_agents import LimeAgent, PowTimeoutError, ApiError
async with LimeAgent() as agent:
try:
result = await agent.login("550e8400-e29b-41d4-a716-446655440000")
print(result.status, result.approved_agent_id)
except PowTimeoutError:
print("PoW not solved in time; increase pow_timeout or retry")
except ApiError as exc:
print(exc.code, exc.http_status, exc.message)
async get_profile() -> AgentProfile
Returns the authenticated agent's Core profile.
HTTP: GET /core/agents/me/profile with X-Agent-Token.
async with LimeAgent() as agent:
profile = await agent.get_profile()
print(profile.agent_id)
print(profile.owner_kyc_level)
print(profile.agent_reputation)
Types
ApprovalResult
Frozen dataclass returned by login().
| Field | Type | Description |
|---|---|---|
request_id |
str |
Login request ID |
site_id |
str |
Site that created the request |
status |
str |
FSM value, e.g. APPROVED, DELIVERED |
expires_at |
datetime |
Request expiry (timezone-aware when API sends offset) |
approved_agent_id |
str | None |
Agent that approved the request |
AgentProfile
Frozen dataclass returned by get_profile(). Matches GET /core/agents/me/profile response fields.
| Field | Type | Description |
|---|---|---|
agent_id |
str |
Agent identifier |
owner_id |
str |
Owning LIME user |
display_name |
str | None |
Public display name |
avatar_url |
str | None |
Avatar URL |
description |
str | None |
Public description |
owner_kyc_level |
int | None |
Owner KYC level synced from Foundation |
agent_reputation |
int | None |
Reputation score |
Error handling
All SDK exceptions inherit from LimeError. Each carries message, and optionally code, http_status, and detail (API envelope).
| Exception | When |
|---|---|
LimeError |
Base class; transport failures after retries, malformed JSON |
AuthenticationError |
Missing/empty token at construct; HTTP 401; MISSING_AGENT_TOKEN, INVALID_AGENT_TOKEN |
PowTimeoutError |
PoW solver exceeded pow_timeout |
RateLimitError |
HTTP 429 / RATE_LIMIT_EXCEEDED |
ApiError |
Other API errors (ok: false envelope) |
ApiError attributes: code, message, http_status, detail.
import asyncio
from lime_agents import (
LimeAgent,
LimeError,
AuthenticationError,
PowTimeoutError,
RateLimitError,
ApiError,
)
async def run() -> None:
try:
async with LimeAgent() as agent:
await agent.login("lr_abc123")
except AuthenticationError as exc:
print("auth:", exc.message)
except PowTimeoutError as exc:
print("pow:", exc.message)
except RateLimitError as exc:
print("rate limit:", exc.http_status)
except ApiError as exc:
print(f"api [{exc.http_status}] {exc.code}: {exc.message}")
except LimeError as exc:
print("sdk:", exc.message)
asyncio.run(run())
Non-retried HTTP statuses: 400, 401, 403, 404, 409 (e.g. INVALID_POW, SITE_LOGIN_CONFLICT).
Configuration
Environment variables
| Variable | Required | Description |
|---|---|---|
LIME_AGENT_TOKEN |
Yes (unless agent_token= passed) |
Agent secret (at_...) |
LIME_API_BASE |
No | API root, e.g. https://lime.pics/api/v1 |
Constructor tuning
| Use case | Suggestion |
|---|---|
| Slow network | Increase timeout (e.g. 60.0) |
| Flaky upstream | Increase max_retries (e.g. 5) |
| High PoW difficulty / slow CPU | Increase pow_timeout (e.g. 30.0) |
| Staging / self-hosted API | Set base_url or LIME_API_BASE |
Logging
HTTP and retry events are logged under the lime logger (not lime_agents):
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("lime").setLevel(logging.DEBUG)
At DEBUG, the client logs request method and URL. Tokens, pow_challenge, and pow_nonce are never logged.
Advanced usage
Custom httpx.AsyncClient
Inject a client for custom TLS, proxies, or tests. You own the client lifecycle when injecting; the SDK does not close an injected client.
import httpx
from lime_agents import LimeAgent
async def login_with_proxy() -> None:
client = httpx.AsyncClient(
timeout=60.0,
verify="/path/to/corporate-ca.pem",
proxy="http://proxy.corp.example:8080",
)
agent = LimeAgent(agent_token="at_...", http_client=client)
try:
await agent.login("lr_abc123")
finally:
await client.aclose()
Retries and timeouts
Retries use exponential backoff with jitter on connection errors, timeouts, and HTTP 408, 429, 500, 502, 503, 504. Each retry attempt is bounded by max_retries (default 3).
agent = LimeAgent(
agent_token="at_...",
max_retries=5,
timeout=45.0,
pow_timeout=20.0,
)
PoW debugging
PoW runs in a thread pool (asyncio.to_thread) so the event loop stays responsive. To observe HTTP flow (not nonce values):
import logging
logging.getLogger("lime").setLevel(logging.DEBUG)
If PowTimeoutError occurs, increase pow_timeout or verify pow_difficulty from GET /auth/requests/{id} (default 15 on production).
Development
pip install -e ".[dev]"
ruff check src tests
mypy src/lime_agents
pytest --cov=lime_agents --cov-fail-under=100
Live integration
pip install -e ".[dev]"
pytest --cov=lime_agents --cov-fail-under=100
# OAuth token against production/staging
LIME_INTEGRATION=1 LIME_AGENT_TOKEN=at_... pytest tests/integration/test_oauth_token.py -v
# MCP Bearer lane (mcp_test_server)
LIME_MCP_INTEGRATION=1 LIME_AGENT_TOKEN=at_... MCP_SERVER_URL=http://127.0.0.1:9000 pytest tests/integration/test_mcp_live.py -m mcp_integration -v
Full cycle (both SDKs): see lime-sait-sdk tests/integration/test_full_cycle_both_sdks.py — site LimeSite + agent LimeAgent against https://lime.pics/api/v1. From the LIME monorepo, run on the production VPS (SSE-safe): python scripts/_run_both_sdks_integration_remote.py.
Changelog
0.4.0
- MCP client included in default install (
mcpmoved to core dependencies) - Removed optional
[mcp]extra —pip install lime-agents-sdkis sufficient
0.3.0
- MCP client facade on
LimeAgent(optional[mcp]extra) get_mcp_access_token()— OAuth v3 token issuance- Session pool per
server_url, automatic JWT refresh, 401 retry oncall_tool py.typedmarker for PEP 561
Links
- SDK repository: github.com/Mawyxx/lime-agents-sdk
- LIME API docs: lime.pics/docs
- LIME platform: github.com/Mawyxx/Lime
License
MIT — see LICENSE.
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
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 lime_agents_sdk-0.4.0.tar.gz.
File metadata
- Download URL: lime_agents_sdk-0.4.0.tar.gz
- Upload date:
- Size: 25.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
938bc8544f8dab77a7d5f0259f78f37dd6758c33506c7e195da21a47a4985ff3
|
|
| MD5 |
4a2f2c9a3dba669f950bad605fbf9e38
|
|
| BLAKE2b-256 |
596ed0bc3863809d6aa8413894476b9d99657fe7657def5ce51d0a03acdaf53b
|
File details
Details for the file lime_agents_sdk-0.4.0-py3-none-any.whl.
File metadata
- Download URL: lime_agents_sdk-0.4.0-py3-none-any.whl
- Upload date:
- Size: 18.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e1d83e5668fa912cf4bc414b087c9105b9875cebbf2c719c3e5c444f98c4080a
|
|
| MD5 |
9e322e26e0c4f446295acde1acae5995
|
|
| BLAKE2b-256 |
17e63e10912396207f1b3c3e52c892e8d2f20d9d1d88568f038e18f71182e7c1
|