# nerve-email

Python SDK for the [Nerve](https://github.com/neuralmail/nerve-cloud) email MCP server.

## Install

```bash
pip install nerve-email
```

## Hosted deployment

- `NerveClient` talks to the MCP runtime. Use `base_url="https://nerve-runtime.fly.dev"`.
- `NerveAdmin` talks to the public tenant REST API. Use `base_url="https://nerve.email"`.
- The SDK appends `/mcp` for MCP requests automatically.
- `https://nerve.email/v1/*` proxies to the control plane for hosted deployments.

## Quick start

```python
from nerve_email import NerveClient

async with NerveClient(base_url="https://nerve-runtime.fly.dev", api_key="nrv_live_...") as client:
    # Check server health
    if await client.health_check():
        threads = await client.list_threads(inbox_id="inbox_123")
        thread = await client.get_thread(thread_id="thread_456")
        results = await client.search_inbox(inbox_id="inbox_123", query="appointment")

    # Draft and send replies
    draft = await client.draft_reply(thread_id="thread_456", goal="Confirm the appointment")
    await client.send_reply(thread_id="thread_456", body_or_draft_id=draft["draft_id"])

    # Compose new email
    await client.compose_email(
        inbox_id="inbox_123",
        to="patient@example.com",
        subject="Appointment confirmation",
        body="Your appointment is confirmed for Monday at 10am.",
    )
```

## Admin client

For domain and inbox management (control plane):

```python
from nerve_email import NerveAdmin

async with NerveAdmin(base_url="https://nerve.email", api_key="admin_key") as admin:
    domain = await admin.add_domain(org_id="org_123", domain="clientclinic.com")
    dns = await admin.get_dns_records(org_id="org_123", domain_id=domain["domain"]["id"])
    await admin.verify_domain(org_id="org_123", domain_id=domain["domain"]["id"])

    inbox = await admin.create_inbox(
        org_id="org_123",
        address="support@clientclinic.com",
        domain_id=domain["domain"]["id"],
    )
```

## Tool definitions

Framework-agnostic tool definitions for LLM function calling:

```python
from nerve_email.tools import get_tool_definitions

# Claude/Anthropic format
tools = get_tool_definitions(format="claude", prefix="email_")

# OpenAI format
tools = get_tool_definitions(format="openai", prefix="email_")
```

## Error handling

```python
from nerve_email import NerveClient, NerveRateLimitError, NerveQuotaError

async with NerveClient(base_url=url, api_key=key) as client:
    try:
        threads = await client.list_threads(inbox_id="inbox_123")
    except NerveRateLimitError as e:
        print(f"Rate limited, retry after {e.retry_after}s")
    except NerveQuotaError:
        print("Quota exceeded")
```

## License

MIT
