lime-sites-sdk
Accept AI agent login on your site backend — create a request, get a Core-signed passport over SSE, verify locally with JWKS.
Canonical docs
| Surface | URL |
|---|---|
| Hello (agents) | https://lime.pics/agent |
| Auth | https://lime.pics/auth.md |
| Flows | https://lime.pics/.well-known/agent-flows |
| Site login guide | https://lime.pics/docs/guides/site-login/ |
| Binding guide | https://lime.pics/docs/guides/site-binding/ |
| Machine package | https://lime.pics/.well-known/agent-context/ |
| GitHub | https://github.com/Mawyxx/lime-site-sdk |
Prefer lime.pics + GitHub over stale RTD mirrors.
from lime_sites import LimeSite
site = LimeSite() # LIME_SITE_TOKEN — construct inside a running asyncio loop
@site.on_login
async def handle_login(request_id: str, passport: str | None) -> None:
if passport is None:
return # expired
verified = await site.verify_passport(passport, expected_request_id=request_id)
# issue YOUR session cookie from verified.claims
req = await site.create_login_request()
# show req.request_id on YOUR waiting screen → agent.login(request_id)
What the SDK handles: Site Token auth · login request · SSE passport delivery · JWKS verify · optional agent binding.
Docs: lime.pics/docs · auth.md · GitHub
Installation
pip install lime-sites-sdk
export LIME_SITE_TOKEN=st_... # from https://lime.pics — site portal
Requirements: Python 3.10+ · httpx · PyJWT · cryptography
Config: one secret — LIME_SITE_TOKEN (or site_token=). Never give Site Token to the agent.
Quick start (canonical) — FastAPI site login
from contextlib import asynccontextmanager
from fastapi import FastAPI
from lime_sites import InvalidPassportError, LimeSite
pending_logins: dict[str, object] = {}
site: LimeSite
@asynccontextmanager
async def lifespan(app: FastAPI):
global site
site = LimeSite() # one LimeSite per process / site token
@site.on_login
async def handle_login(request_id: str, passport: str | None) -> None:
if passport is None:
pending_logins.pop(request_id, None)
return
try:
verified = await site.verify_passport(
passport,
expected_request_id=request_id,
)
except InvalidPassportError:
pending_logins.pop(request_id, None)
return
pending_logins[request_id] = verified.claims # set YOUR session
yield
await site.aclose()
app = FastAPI(lifespan=lifespan)
@app.post("/login/start")
async def start_login() -> dict[str, str]:
req = await site.create_login_request()
return {"request_id": req.request_id}
Copy-paste: examples/fastapi-login/.
Agent worker (separate package): lime-agents-sdk → await agent.login(request_id).
Mental model
LimeSite
├── Site login (primary) create_login_request · on_login · verify_passport
├── Agent binding create_binding_request · verify_binding_passport
└── Lifecycle aclose()
| Credential | Header | Used for |
|---|---|---|
| Opaque Site Token | X-Site-Token |
Your backend ↔ LIME only |
| Site passport JWT | delivered on SSE | Session after verify_passport (aud=lime-site-login) |
Site login ≠ agent binding. MCP Bearer (aud=mcp) is not verified here — use lime-mcp-server-sdk.
Rules that prevent foot-guns
| Rule | Why |
|---|---|
One LimeSite per site token per process |
One SSE connection |
| Construct inside a running asyncio loop | Dispatcher uses create_task |
Keep @site.on_login handlers fast |
Events dispatch sequentially |
passport is None → expired |
Clear pending state |
Second scenario — Agent binding
IS: Bind a LIME agent_id to a signed-in human via Connect (aud=lime-binding). No SSE.
DO: create → redirect human to connect_url → callback ?binding_code= → exchange → verify_binding_passport.
NEVER: JWT in redirect URL (query or fragment).
req = await site.create_binding_request(redirect_uri="https://yoursite.example/bind/callback")
# persist req.binding_id ↔ user_id, redirect browser to req.connect_url
# callback: binding_code = query["binding_code"]
# POST /api/v1/modules/bindings/exchange with X-Site-Token + {"binding_code": ...}
# → data.passport, then:
# verified = await site.verify_binding_passport(passport)
# → claims["binding_id"] / claims["agent_id"] (sub)
Canonical wire: site-binding guide · flow agent_binding in agent-flows.
Example sketch: examples/binding/.
Minimal end-to-end loop
With both SDKs installed (lime-sites-sdk + lime-agents-sdk):
import asyncio
from lime_agents import LimeAgent
from lime_sites import LimeSite
async def main() -> None:
received = asyncio.Event()
box: dict[str, str] = {}
site = LimeSite()
@site.on_login
async def handle_login(request_id: str, passport: str | None) -> None:
if passport:
box["jwt"] = passport
received.set()
req = await site.create_login_request()
async with LimeAgent() as agent:
await agent.login(req.request_id)
await asyncio.wait_for(received.wait(), timeout=120)
verified = await site.verify_passport(box["jwt"], expected_request_id=req.request_id)
print(verified.claims)
await site.aclose()
asyncio.run(main())
Example: examples/minimal-loop/.
API surface (summary)
| Method | Description |
|---|---|
create_login_request() |
Start site login → request_id |
@site.on_login |
Handler (request_id, passport | None) |
verify_passport(jwt, …) |
JWKS verify aud=lime-site-login |
create_binding_request(…) |
Start Connect binding → binding_id + connect_url |
verify_binding_passport(jwt) |
JWKS verify aud=lime-binding (after ?binding_code= → exchange) |
aclose() |
Stop SSE / close client |
Env: LIME_SITE_TOKEN (required unless constructor), LIME_API_BASE (optional).
Related packages
| Package | Role |
|---|---|
lime-agents-sdk |
Agent worker: login(request_id) + MCP client |
lime-mcp-server-sdk |
MCP RS: verify aud=mcp Bearer |
Examples
| Path | Purpose |
|---|---|
examples/fastapi-login/ |
Canonical site login |
examples/minimal-loop/ |
Site + agent E2E sketch |
examples/binding/ |
Connect binding sketch |
Contributing
git clone https://github.com/Mawyxx/lime-site-sdk.git
cd lime-site-sdk
pip install -e ".[dev]"
ruff check src tests
mypy src/lime_sites
pytest --cov=lime_sites --cov-fail-under=100
License
MIT — see LICENSE.
Release files for lime-sites-sdk 2.0.4
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_sites_sdk-2.0.4.tar.gz | 32.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lime_sites_sdk-2.0.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 49.0 kB
Release files / lime_sites_sdk-2.0.4.tar.gz
| Download URL | lime_sites_sdk-2.0.4.tar.gz |
|---|---|
| Size | 32.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fac5d8fe5e41d035b25611423a1c2139158a7bf47fc6179ad976256918c67461
|
|
BLAKE2b-256 checksum How to use checksums |
63e9279dc6210698fec20385538c4ffede3596a732612d0bab588de5ae0663da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / lime_sites_sdk-2.0.4-py3-none-any.whl
| Download URL | lime_sites_sdk-2.0.4-py3-none-any.whl |
|---|---|
| Size | 17.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3f9a0800c416bf92d2b2b1a918953fb470d68838347560b5bf3a3027134e0ec1
|
|
BLAKE2b-256 checksum How to use checksums |
2e2aad26edd193b821e8753db25677d6e859fce6ab5b226928e6015810231df4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|