Skip to main content

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.

PyPI version Python versions License: MIT CI Documentation MCP compatible

📖 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:

  1. GET /api/v1/auth/requests/{request_id} — read PoW challenge (no auth)
  2. Solve PoW in a thread pool (asyncio.to_thread)
  3. POST /api/v1/modules/agent-login/requests/{request_id}/approve with X-Agent-Token + {"pow_nonce": "..."}

Site side (separate package): lime-sites-sdkcreate_login_request() → SSE on_loginverify_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/tokenheader 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 loginawait agent.login(request_id) wraps PoW fetch, solve, and approve with X-Agent-Token
  • MCP OAuth built-in — issue, cache, and lazy-refresh 5-minute MCP JWTs on the next list_tools / call_tool when near expiry; no manual /oauth/token in app code
  • Typed MCP clientlist_tools, call_tool, read_resource, get_prompt, … with mcp.types models re-exported from lime_agents
  • Automatic Proof-of-Work — SHA-256 solver with configurable pow_timeout and 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 typingApprovalResult, 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_clientClientSessioninitialize()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 retryMcpAuthenticationError 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/profileAgentProfile
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)

Source distribution for lime-agents-sdk 2.0.0
File Size Uploaded
lime_agents_sdk-2.0.0.tar.gz 42.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lime-agents-sdk 2.0.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

3.0.1

2 release files

3.0.0

2 release files

This release

2.0.0 This release

2 release files

1.0.0

2 release files

0.5.6

2 release files

0.5.5

2 release files

0.5.4

2 release files

0.5.3

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page