lime-agents-sdk — Cryptographic Passport for AI Agents (JWT + MCP OAuth)
lime-agents-sdk is the official Python agent SDK for LIME — an AI agent identity platform that issues cryptographic passports (signed JWTs) for autonomous workers. Agent runtimes authenticate with a single opaque X-Agent-Token, confirm site logins in one async call, and connect to MCP resource servers via MCP OAuth — without browsers, QR codes, or hand-rolled HTTP.
Use this package when you build agent workers (not site backends). Pair with lime-sites-sdk on the site side for login creation, SSE delivery, and passport verification.
📖 Python API (Read the Docs): lime-agents-sdk.readthedocs.io
📖 Platform HTTP docs: lime.pics/docs#guide-agentSdk
📦 This SDK: github.com/Mawyxx/lime-agents-sdk
🌐 Platform: https://lime.pics
Why lime-agents-sdk?
| Problem | SDK solution |
|---|---|
| Manual PoW + approve HTTP | await agent.login(request_id) — challenge fetch, SHA-256 PoW, approve, retries |
| Two auth lanes (LIME vs MCP) | X-Agent-Token for LIME APIs; short-lived MCP JWT for external MCP servers |
| MCP OAuth boilerplate | list_tools / call_tool take required target; auto-issue + per-domain MCP JWT cache; optional get_mcp_access_token(target) for the raw token |
| Fragile agent credentials | Env-based LIME_AGENT_TOKEN (Stripe-style), typed errors, py.typed |
Two JWT flows (do not mix them)
LIME uses two different JWT artifacts. This SDK covers the agent worker side only.
| Flow | Who gets the JWT | Audience / use | This SDK |
|---|---|---|---|
| Site login passport | Site backend (via SSE) | aud=lime-site-login — cryptographic passport for the logged-in session |
Agent calls login() only; site verifies JWT with lime-sites-sdk + Core JWKS |
| MCP access token | Agent worker (cached in SDK; not sent to site) | aud=mcp — Bearer token for external MCP resource servers |
MCP facade methods require target; auto-issue + per-domain cache; optional get_mcp_access_token(target) for the raw JWT |
The MCP JWT is signed with LIME Core keys (JWKS at GET /api/v1/core/.well-known/jwks.json). Default TTL is 300 seconds (5 minutes). The SDK caches it in your worker and performs lazy refresh on the next MCP call when the token is within ~30 seconds of expiry (mcp_token_refresh_skew, default 30) — there is no background refresh task. You send the JWT to remote MCP servers as Authorization: Bearer — not to the site backend. MCP JWTs are rejected on LIME HTTP APIs — only opaque X-Agent-Token works there.
Installation
pip install lime-agents-sdk
Latest from GitHub:
pip install git+https://github.com/Mawyxx/lime-agents-sdk.git
Requirements: Python 3.10+ · runtime deps: httpx, mcp
Quick start
Scenario A — Site login (headless agent authentication)
Story: A site backend starts a login request and hands request_id to your agent worker. The worker proves identity with PoW + approve. The site receives the signed agent passport JWT over SSE (handled by lime-sites-sdk). Your worker only runs the approve step.
import asyncio
import os
from lime_agents import LimeAgent, ApiError, PowTimeoutError
# LIME_AGENT_TOKEN=at_... (from the LIME owner portal — server-side secret only)
REQUEST_ID = "lr_abc123" # from your site backend / job queue
async def main() -> None:
# One LimeAgent per worker process (reuse across jobs)
agent = LimeAgent(agent_token=os.environ["LIME_AGENT_TOKEN"])
try:
result = await agent.login(REQUEST_ID)
print(result.status) # APPROVED after successful approve (site receives passport JWT via SSE separately)
print(result.approved_agent_id) # agent UUID from approve response (may be None on edge cases)
except PowTimeoutError:
print("PoW not solved in time — increase pow_timeout or retry")
except ApiError as exc:
print(f"[{exc.code}] {exc.message}")
finally:
await agent.aclose()
asyncio.run(main())
What login() does internally:
GET /api/v1/auth/requests/{request_id}— read PoW challenge (no auth)- Solve PoW in a thread pool (
asyncio.to_thread) POST /api/v1/modules/agent-login/requests/{request_id}/approvewithX-Agent-Token+{"pow_nonce": "..."}
Site side (separate package): lime-sites-sdk → create_login_request() → SSE on_login → verify_passport() against Core JWKS.
Scenario B — MCP tools (MCP OAuth + streamable HTTP client)
Story: Your agent calls tools on an external MCP resource server. LIME issues a short-lived MCP JWT (~5 min) from your X-Agent-Token. Pass a target (URL or hostname). The SDK extracts the domain, posts JSON {"domain"} to LIME OAuth, caches JWTs per domain, pools sessions per URL, and on MCP 401 invalidates that domain only then retries once.
import asyncio
import os
from lime_agents import LimeAgent, CallToolResult, Tool
MCP_ENDPOINT = "https://mcp.example.com/mcp" # full streamable HTTP path, not just the host
async def main() -> None:
async with LimeAgent(agent_token=os.environ["LIME_AGENT_TOKEN"]) as agent:
# MCP JWT (~300s TTL) is fetched automatically on first list_tools / call_tool
tools: list[Tool] = await agent.list_tools(MCP_ENDPOINT) # target=
print([t.name for t in tools])
result: CallToolResult = await agent.call_tool(
MCP_ENDPOINT,
tools[0].name,
{"text": "hello from LIME agent"},
)
if result.isError:
print("tool error:", result.content)
else:
print(result.content)
# Same agent, another MCP server — sessions cached per URL
# await agent.call_tool("https://other-mcp.example.com/mcp", "get_weather", {"city": "Berlin"})
asyncio.run(main())
Credential lanes (never swap headers):
| Lane | Header | Used for |
|---|---|---|
| LIME platform | X-Agent-Token |
login(), get_profile(), POST .../oauth/token |
| External MCP RS | Authorization: Bearer <mcp_jwt> |
list_tools, call_tool, resources, prompts |
OAuth issuance: POST /api/v1/modules/oauth/token — header only, empty body (MCP OAuth ADR). Resource servers verify the JWT via Core JWKS — use lime-mcp-server-sdk on the server side.
Features
- One-call site login —
await agent.login(request_id)wraps PoW fetch, solve, and approve withX-Agent-Token - MCP OAuth built-in — issue, cache, and lazy-refresh 5-minute MCP JWTs on the next
list_tools/call_toolwhen near expiry; no manual/oauth/tokenin app code - Typed MCP client —
list_tools,call_tool,read_resource,get_prompt, … withmcp.typesmodels re-exported fromlime_agents - Automatic Proof-of-Work — SHA-256 solver with configurable
pow_timeoutand transient retry policy - Production-ready LIME HTTP — httpx async client, exponential backoff on 408/429/5xx for platform calls (
login, profile, OAuth issuance); MCP calls retry on 401 after token refresh - Strict typing —
ApprovalResult,AgentProfile,McpAccessToken,py.typed, mypy-clean public API
Comparison with Official MCP SDK
The official mcp package (PyPI: mcp) is the protocol SDK: transports, ClientSession, JSON-RPC types, server tooling (FastMCP), and generic OAuth helpers. lime-agents-sdk depends on it and wraps the client path for LIME agent workers — site login, LIME OAuth token issuance, session pooling, and typed facade methods.
Choose mcp alone when you need full control over transports, non-LIME OAuth (authorization code + PKCE, dynamic client registration), MCP servers, or stdio/SSE transports. Choose lime-agents-sdk when your worker already has a LIME X-Agent-Token and you want MCP tool calls with minimal boilerplate.
Side-by-side
| Feature / Aspect | Official MCP SDK (mcp) |
LIME SDK (lime-agents-sdk) |
Benefit of LIME |
|---|---|---|---|
| Scope | Client + server protocol stack, multiple transports | LIME agent worker client (login, profile, MCP tools) | One package for LIME identity + MCP — no glue code |
| Typical MCP tool call | streamable_http_client → ClientSession → initialize() → list_tools() / call_tool() (~15 lines) |
await agent.list_tools(url) / await agent.call_tool(url, name, args) (~3 lines) |
~70% less code; returns list[Tool] (no .tools nesting) |
| LIME machine OAuth | Not built-in; you fetch JWT and set Authorization on httpx.AsyncClient |
POST /modules/oauth/token (empty body); JWT auto-attached on MCP calls |
Tokens fetched and injected automatically |
| Generic OAuth | OAuthClientProvider (PKCE), ClientCredentialsOAuthProvider, TokenStorage |
LIME token model only — not a general OAuth library | Use mcp for non-LIME RFC flows (by design) |
| Token caching & refresh | You implement TokenStorage; refresh via refresh_token when available; no background timer |
In-memory cache; lazy refresh within mcp_token_refresh_skew (30s); single-flight lock; no background timer |
Out of the box — no storage layer; no races or thundering herd |
| Session pooling & concurrency | You manage ClientSession per URL and concurrency yourself |
McpSessionPool per URL; serialize_mcp_per_url=True by default |
One session per URL; safe same-URL default; parallel across different URLs |
| 401 from MCP RS | Your error handling | Invalidate JWT, close transports, one retry → McpAuthenticationError |
Automatic recovery without boilerplate |
| HTTP retries (408/429/5xx) | Transport reconnection (MAX_RECONNECTION_ATTEMPTS=2); OAuth refresh on invalid token |
Exponential backoff on LIME platform HTTP; MCP: 401 + broken-session retry | Platform resilience + MCP auth self-heal |
| Site login (PoW + approve) | Not included | await agent.login(request_id) |
Headless site login in one call |
Minimal example — list tools + call
Official mcp (streamable HTTP + Bearer you obtained yourself):
import asyncio
import httpx
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
MCP_URL = "https://mcp.example.com/mcp"
ACCESS_TOKEN = "..." # you fetch and refresh this
async def main() -> None:
async with httpx.AsyncClient(
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"},
timeout=30.0,
) as http:
async with streamable_http_client(MCP_URL, http_client=http) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = (await session.list_tools()).tools
if tools:
await session.call_tool(tools[0].name, {"text": "hi"})
asyncio.run(main())
lime-agents-sdk (LIME OAuth + pool + facade):
import asyncio
from lime_agents import LimeAgent
MCP_URL = "https://mcp.example.com/mcp"
async def main() -> None:
async with LimeAgent() as agent: # LIME_AGENT_TOKEN from env
tools = await agent.list_tools(MCP_URL)
if tools:
await agent.call_tool(MCP_URL, tools[0].name, {"text": "hi"})
asyncio.run(main())
When to use which SDK
Use mcp directly if you are:
- building an MCP server (
FastMCP, stdio/SSE/streamable HTTP transports), - implementing custom OAuth (authorization code + PKCE, dynamic client registration),
- integrating with an OAuth provider other than LIME.
Use lime-agents-sdk if you already have a LIME agent with X-Agent-Token and you want:
- ~5× less code for MCP tool calls (see examples above),
- automatic OAuth lifecycle — issue, cache, lazy refresh, and Bearer injection with no
TokenStorage, - session pooling and safe same-URL concurrency without writing lock/reconnect logic,
- 401 recovery and LIME platform retries built in,
- optional headless site login (
login()) in the same client.
lime-agents-sdk does not replace mcp for server authors or non-LIME OAuth — it composes mcp for the LIME machine-token model.
Details: MCP OAuth & pool (source) · Read the Docs
API reference (summary)
LimeAgent
| Method | Description |
|---|---|
await agent.login(request_id) |
Site login approve flow → ApprovalResult |
await agent.get_profile() |
GET /core/agents/me/profile → AgentProfile |
await agent.get_mcp_access_token() |
Optional: expose cached MCP OAuth JWT (~300s TTL); not required before MCP calls |
await agent.list_tools(server_url) |
MCP tools (typed Tool) |
await agent.call_tool(server_url, name, args) |
MCP tool invocation → CallToolResult |
await agent.list_resources(...) / read_resource(...) / list_prompts(...) / get_prompt(...) |
Full MCP facade |
async with agent.mcp_session(url) |
Low-level mcp.ClientSession with per-URL lock |
Constructor highlights: agent_token / LIME_AGENT_TOKEN, base_url / LIME_API_BASE (default https://lime.pics/api/v1), timeout, max_retries, mcp_token_refresh_skew (default 30, lazy refresh window), serialize_mcp_per_url (default True).
Context manager: async with LimeAgent() as agent: calls aclose() on exit. For long-running workers, create one instance at startup and reuse it.
Environment variables
| Variable | Required | Description |
|---|---|---|
LIME_AGENT_TOKEN |
Yes* | Agent secret (at_...) from the LIME portal |
LIME_API_BASE |
No | API root, e.g. https://lime.pics/api/v1 |
*Unless agent_token= is passed to the constructor.
Errors
All inherit from LimeError: AuthenticationError, PowTimeoutError, RateLimitError, ApiError, McpAuthenticationError, OAuthCapabilityError.
Related packages
| Package | Role |
|---|---|
lime-sites-sdk |
Site backend: create login, SSE events, verify site passport JWT |
lime-mcp-server-sdk |
MCP resource server: verify MCP Bearer JWT via Core JWKS |
Contributing
Issues and pull requests: github.com/Mawyxx/lime-agents-sdk
git clone https://github.com/Mawyxx/lime-agents-sdk.git
cd lime-agents-sdk
pip install -e ".[dev]"
ruff check src tests
mypy src/lime_agents
pytest --cov=lime_agents --cov-fail-under=100
CI runs on Python 3.10–3.13 with 100% line coverage on src/lime_agents.
License
MIT — see LICENSE.
Versioning
See CHANGELOG.md. 1.0.0 introduces Zero-Touch target (breaking).
Release files for lime-agents-sdk 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lime_agents_sdk-2.0.0.tar.gz | 42.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lime_agents_sdk-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 66.6 kB
Release files / lime_agents_sdk-2.0.0.tar.gz
| Download URL | lime_agents_sdk-2.0.0.tar.gz |
|---|---|
| Size | 42.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e59b67a994e3b17c0c16a4978f617351898efff48ba3a860ce166aec0217a7f8
|
|
BLAKE2b-256 checksum How to use checksums |
b97813ea87b79215617962e05e5642b6017e8d88c126ff7f0bb6c3899702f3ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / lime_agents_sdk-2.0.0-py3-none-any.whl
| Download URL | lime_agents_sdk-2.0.0-py3-none-any.whl |
|---|---|
| Size | 24.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e8baa8d25373a7d5bacd477e7404fea68d530379a01e4520185c472542fa816d
|
|
BLAKE2b-256 checksum How to use checksums |
0c3d18714ec380061e27fe02c77898fbad9698c9897a156f7886eb4617b1812e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|