Metadata-Version: 2.4
Name: alter-sdk
Version: 0.2.0
Summary: Alter Vault Python SDK - OAuth token management with policy enforcement
Keywords: oauth,tokens,security,policy,vault
Author: Alter Team
Author-email: founders@alterai.dev
Requires-Python: >=3.11,<4.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
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[http2] (>=0.25.0,<0.26.0)
Requires-Dist: pydantic (>=2.5.0,<3.0.0)
Project-URL: Homepage, https://alterai.dev
Description-Content-Type: text/markdown

# Alter SDK for Python

Official Python SDK for [Alter Vault](https://alterai.dev) - OAuth token management with policy enforcement.

## Features

- **Zero Token Exposure**: Tokens are never exposed to developers - injected automatically
- **Single Entry Point**: One method (`vault.request()`) for all provider APIs
- **Type-Safe Enums**: `Provider` and `HttpMethod` enums with autocomplete
- **URL Templating**: Path parameter substitution with automatic URL encoding
- **Automatic Audit Logging**: All API calls logged as fire-and-forget background tasks
- **Real-time Policy Enforcement**: Every token request checked against current policies
- **Automatic Token Refresh**: Tokens refreshed transparently by the backend
- **Dual HTTP Client Security**: Separate clients prevent credential leakage to providers
- **Actor Tracking**: First-class support for AI agent and MCP server observability

## Installation

```bash
pip install alter-sdk
```

## Quick Start

```python
import asyncio
from alter_sdk import AlterVault, Provider, HttpMethod

async def main():
    vault = AlterVault(api_key="alter_key_...")

    # Make API request - token injected automatically, never exposed
    response = await vault.request(
        Provider.GOOGLE,
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
        user={"user_id": "alice"},
        query_params={"maxResults": "10"},
    )
    events = response.json()
    print(events)

    await vault.close()

asyncio.run(main())
```

## Usage

### Simple GET Request

```python
response = await vault.request(
    Provider.GOOGLE,
    HttpMethod.GET,
    "https://www.googleapis.com/calendar/v3/calendars/primary/events",
    user={"user_id": "alice"},
)
```

### POST with JSON Body

```python
response = await vault.request(
    Provider.SALESFORCE,
    HttpMethod.POST,
    "https://api.example.com/v1/items",
    user={"user_id": "alice"},
    json={"name": "New Item", "price": 99.99},
    reason="Creating new item",
)
```

### URL Path Templating

```python
response = await vault.request(
    Provider.SALESFORCE,
    HttpMethod.PUT,
    "https://api.example.com/v1/items/{item_id}",
    user={"user_id": "alice"},
    path_params={"item_id": "123"},
    json={"price": 89.99},
)
```

### Query Parameters and Extra Headers

```python
response = await vault.request(
    Provider.GOOGLE,
    HttpMethod.GET,
    "https://api.example.com/v1/items",
    user={"user_id": "alice"},
    query_params={"fields": "name,price", "limit": "10"},
    extra_headers={"Notion-Version": "2022-06-28"},
)
```

### Context Manager

```python
async with AlterVault(api_key="alter_key_...") as vault:
    response = await vault.request(
        Provider.GOOGLE,
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
        user={"user_id": "alice"},
    )
# Automatically closed
```

### AI Agent Actor Tracking

```python
vault = AlterVault(
    api_key="alter_key_...",
    actor_type="ai_agent",
    actor_identifier="email-assistant-v2",
    actor_name="Email Assistant",
    actor_version="2.0.0",
    framework="langgraph",
)

response = await vault.request(
    Provider.GOOGLE,
    HttpMethod.GET,
    "https://www.googleapis.com/calendar/v3/calendars/primary/events",
    user={"user_id": "alice"},
    run_id="550e8400-e29b-41d4-a716-446655440000",
    thread_id="thread-xyz",
)
```

## Configuration

```python
vault = AlterVault(
    api_key="alter_key_...",              # Required: Alter Vault API key
    base_url="https://api.alter.com",     # Optional: Custom API URL
    enable_audit_logging=True,            # Optional: Enable audit logs (default: True)
    timeout=30.0,                         # Optional: HTTP timeout in seconds
    # Actor tracking (optional)
    actor_type="ai_agent",               # "ai_agent" or "mcp_server"
    actor_identifier="my-agent",         # Unique identifier
    actor_name="My Agent",               # Human-readable name
    actor_version="1.0.0",               # Version string
    framework="langgraph",               # AI framework
    client_type="cursor",                # MCP client type
)
```

## Error Handling

```python
from alter_sdk import AlterVault, Provider, HttpMethod
from alter_sdk.exceptions import (
    AlterSDKError,              # Base exception for all SDK errors
    PolicyViolationError,       # Policy denied access (403)
    ConnectionNotFoundError,    # No OAuth connection found (404)
    TokenExpiredError,          # Token refresh failed (400/502)
    TokenRetrievalError,        # Other backend errors
    NetworkError,               # Backend or provider unreachable
    ProviderAPIError,           # Provider API returned error (4xx/5xx)
)

try:
    response = await vault.request(
        Provider.GOOGLE,
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
        user={"user_id": "alice"},
    )
except PolicyViolationError as e:
    print(f"Policy denied: {e.message}")
    print(f"Policy error: {e.policy_error}")  # Detailed policy failure reason
except ConnectionNotFoundError:
    print("No OAuth connection - user needs to authenticate")
except TokenExpiredError as e:
    print(f"Token expired for connection: {e.connection_id}")
except NetworkError as e:
    print(f"Network issue: {e.message}")
except ProviderAPIError as e:
    print(f"Provider error {e.status_code}: {e.response_body}")
```

## Supported Providers

```python
from alter_sdk import Provider

Provider.GOOGLE       # "google"
Provider.GITHUB       # "github"
Provider.SLACK        # "slack"
Provider.MICROSOFT    # "microsoft"
Provider.SALESFORCE   # "salesforce"
Provider.SENTRY       # "sentry"

# Strings also work for any provider
await vault.request("notion", HttpMethod.GET, url, user=user)
```

## Architecture

See [ALTER_PYTHON_SDK_ARCHITECTURE.md](./ALTER_PYTHON_SDK_ARCHITECTURE.md) for details on:
- Dual HTTP client security model
- Zero token exposure design
- No SDK-side caching (real-time policy enforcement)
- Actor tracking and audit logging

## Development

```bash
poetry install
poetry run pytest tests/ -v
poetry run ruff check alter_sdk/ tests/
poetry run ruff format alter_sdk/ tests/
```

## Requirements

- Python 3.11+
- httpx (with HTTP/2 support)
- pydantic

## License

MIT License

