Metadata-Version: 2.4
Name: paperclip-sdk
Version: 0.6.0
Summary: Public Python SDK for calling remote functions, with sync and async clients.
Keywords: leasekey,sdk,rpc,paperclip
Requires-Python: >=3.10,<4.0
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: httpx (>=0.27,<1.0)
Project-URL: Homepage, https://leasekey.org
Description-Content-Type: text/markdown

# paperclip-sdk

Python SDK for calling server-hosted Paperclip tools from scripts, workers, web apps, and async services.

Use it when you want to keep business logic, pricing rules, private workflows, or customer-specific functionality on the server while calling it from Python through a small client.

## Install

```bash
poetry add paperclip-sdk
```

or

```bash
pip install paperclip-sdk
```

## Configure authentication

```bash
export LEASEKEY_API_KEY="your-api-key"
```

## Quickstart

### Sync

```python
from paperclip_sdk import Paperclip

with Paperclip() as paperclip:
    result = paperclip.tools.core.hello_world(name="Ada")
    print(result)
```

### Async

```python
import asyncio

from paperclip_sdk import AsyncPaperclip


async def main() -> None:
    async with AsyncPaperclip() as paperclip:
        result = await paperclip.tools.core.hello_world(name="Ada")
        print(result)


asyncio.run(main())
```

## Discover available tools

### Sync

```python
from paperclip_sdk import Paperclip

with Paperclip() as paperclip:
    print(paperclip.list_tools())
```

### Async

```python
import asyncio

from paperclip_sdk import AsyncPaperclip


async def main() -> None:
    async with AsyncPaperclip() as paperclip:
        print(await paperclip.list_tools())


asyncio.run(main())
```

## Real-world examples

The tool names below are illustrative. Replace them with the tools your Paperclip server exposes.

### Keep pricing logic on the server

```python
from decimal import Decimal

from paperclip_sdk import Paperclip


def quote_total(customer_id: str, sku: str, quantity: int) -> Decimal:
    with Paperclip() as paperclip:
        result = paperclip.tools.pricing.compute_quote(
            customer_id=customer_id,
            sku=sku,
            quantity=quantity,
        )

    return Decimal(result["total"])
```

### Use it inside an async API

```python
from fastapi import FastAPI

from paperclip_sdk import AsyncPaperclip

app = FastAPI()


@app.post("/tickets/{ticket_id}/summarize")
async def summarize_ticket(ticket_id: str) -> dict:
    async with AsyncPaperclip() as paperclip:
        return await paperclip.tools.support.summarize_ticket(
            ticket_id=ticket_id,
        )
```

### Build a remote plugin system

```python
from paperclip_sdk import Paperclip

with Paperclip() as paperclip:
    result = paperclip.tools.plugins.transform_payload(
        payload={
            "source": "stripe",
            "event": "invoice.paid",
        }
    )
    print(result)
```

### Reuse a tool handle

```python
from paperclip_sdk import Paperclip

with Paperclip() as paperclip:
    enrich_contact = paperclip.tools.crm.enrich_contact

    print(enrich_contact(email="ada@example.com"))
    print(enrich_contact(email="grace@example.com"))
```

### Reuse an async tool handle

```python
import asyncio

from paperclip_sdk import AsyncPaperclip


async def main() -> None:
    async with AsyncPaperclip() as paperclip:
        summarize = paperclip.tools.support.summarize_ticket

        print(await summarize(ticket_id="T-100"))
        print(await summarize(ticket_id="T-101"))


asyncio.run(main())
```

## Notes

- Tools are available under `paperclip.tools.<namespace>.<name>(...)`
- Registered tools can be discovered through `paperclip.list_tools()`
- The default base URL is `https://leasekey.org`, so passing `base_url` is optional
- The SDK automatically sends `User-Agent: paperclip-sdk/<version>`
- The SDK also sends `X-Leasekey-SDK-Version: <version>`
- Built-in retry behavior covers retryable transport failures and these HTTP statuses: `408`, `425`, `429`, `502`, `503`, `504`

