Skip to main content

Python SDK for LIME 2.0 agent authentication. Zero-config, Proof-of-Work auto-solve, async-first.

Project description

LIME Agents SDK

PyPI version Python versions License: MIT CI

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:

  1. Constructor argument agent_token="at_..."
  2. 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 LimeAgent once when the worker starts.
  • Reuse the same instance for all login() and get_profile() calls.
  • Optionally pass a shared httpx.AsyncClient via http_client for 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:

  1. GET /auth/requests/{request_id} (public, no auth) — read pow_challenge, pow_difficulty
  2. Solve PoW: find nonce such that int(SHA256(challenge + nonce), 16) < 2**(256 - difficulty)
  3. POST /modules/agent-login/requests/{request_id}/approve with X-Agent-Token and 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 (mcp moved to core dependencies)
  • Removed optional [mcp] extra — pip install lime-agents-sdk is 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 on call_tool
  • py.typed marker for PEP 561

Links

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

lime_agents_sdk-0.4.0.tar.gz (25.3 kB view details)

Uploaded Source

Built Distribution

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

lime_agents_sdk-0.4.0-py3-none-any.whl (18.1 kB view details)

Uploaded Python 3

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

Hashes for lime_agents_sdk-0.4.0.tar.gz
Algorithm Hash digest
SHA256 938bc8544f8dab77a7d5f0259f78f37dd6758c33506c7e195da21a47a4985ff3
MD5 4a2f2c9a3dba669f950bad605fbf9e38
BLAKE2b-256 596ed0bc3863809d6aa8413894476b9d99657fe7657def5ce51d0a03acdaf53b

See more details on using hashes here.

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

Hashes for lime_agents_sdk-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e1d83e5668fa912cf4bc414b087c9105b9875cebbf2c719c3e5c444f98c4080a
MD5 9e322e26e0c4f446295acde1acae5995
BLAKE2b-256 17e63e10912396207f1b3c3e52c892e8d2f20d9d1d88568f038e18f71182e7c1

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