Metadata-Version: 2.4
Name: authsec-sdk
Version: 4.0.0
Summary: AuthSec SDK for MCP auth, services, CIBA, and SPIFFE integration
Author-email: AuthSec Team <a@authnull.com>
Keywords: authsec,mcp,oauth,rbac,spiffe,ciba,authentication,authorization
Requires-Python: >=3.10.11
Description-Content-Type: text/markdown
Requires-Dist: aiohttp>=3.9.0
Requires-Dist: fastapi>=0.110.0
Requires-Dist: grpcio>=1.60.0
Requires-Dist: protobuf<6.0.0,>=5.29.0
Requires-Dist: requests>=2.31.0
Requires-Dist: uvicorn>=0.27.0

# AuthSec Python SDK (`authsec_sdk`)

Add OAuth + RBAC protection to MCP tools with minimal code changes.

This SDK lets you:
- Protect MCP tools with `@protected_by_AuthSec(...)`
- Register normal unprotected MCP tools with `@mcp_tool(...)`
- Run an MCP server that delegates auth/tool listing to AuthSec SDK Manager
- Access downstream service credentials safely through `ServiceAccessSDK`

## What This SDK Does (and Why)

AuthSec is the **control plane**: users authenticate through AuthSec, show up in the AuthSec web app, and you assign them roles/permissions and conditional access policies.

This Python package is the **enforcement layer for MCP**:
- It provides a minimal HTTP MCP server (`run_mcp_server_with_oauth(...)`) so you do not need to implement MCP transport + OAuth bootstrap tools yourself.
- It delegates OAuth flows and authorization decisions to the hosted AuthSec SDK Manager.
- It enforces allow/deny for protected tools at **call time** (the security boundary), and can also hide tools in `tools/list` as a UX improvement (policy controlled by AuthSec).

Why this approach:
- B2B teams often have many internal tools. You configure identity + RBAC once in AuthSec, then every MCP tool can inherit the same enterprise policy without re-implementing Okta/SAML/SCIM in each server.
- The SDK stays thin: your business logic stays in your tool handlers; AuthSec decides who can call them.

## Package

- Name: `authsec-sdk`
- Import path: `authsec_sdk`
- Python: `>=3.10.11`

## Install

### One command on macOS

From the repo:

```bash
bash scripts/bootstrap-python-sdk-macos.sh --client-id YOUR_CLIENT_ID
```

Using GitHub raw:

```bash
curl -fsSL https://raw.githubusercontent.com/authsec-ai/sdk-authsec/main/scripts/bootstrap-python-sdk-macos.sh | bash -s -- --client-id YOUR_CLIENT_ID
```

Notes:
- Default install source is `pypi`.
- Use `--source https` or `--source ssh` if you want to install directly from GitHub.
- Add `--run` to start the sample server immediately after setup.

### Local development

```bash
cd packages/python-sdk
python3 -m pip install -e .
```

### From GitHub (advanced)

If you already cloned this repo, the shortest path is an editable install:

```bash
python3 -m pip install -e packages/python-sdk
```

If you need a one-command setup on macOS, use the bootstrap script above.

<details>
<summary>Direct pip install from GitHub monorepo (long command)</summary>

```bash
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install "git+ssh://git@github.com/authsec-ai/sdk-authsec.git@main#subdirectory=packages/python-sdk"
```

If you prefer HTTPS:

```bash
python3 -m pip install "git+https://github.com/authsec-ai/sdk-authsec.git@main#subdirectory=packages/python-sdk"
```

#### Windows PowerShell

```powershell
py -3 -m venv .venv
.venv\Scripts\Activate.ps1
py -3 -m pip install --upgrade pip
py -3 -m pip install "git+https://github.com/authsec-ai/sdk-authsec.git@main#subdirectory=packages/python-sdk"
```

If your org uses GitHub SSH on Windows:

```powershell
py -3 -m pip install "git+ssh://git@github.com/authsec-ai/sdk-authsec.git@main#subdirectory=packages/python-sdk"
```
</details>

### From package index

```bash
python3 -m pip install authsec-sdk
```

#### Windows PowerShell

```powershell
py -3 -m pip install authsec-sdk
```

### Verify install

#### macOS / Linux

```bash
python3 -c "import authsec_sdk; print(authsec_sdk.__version__)"
```

#### Windows PowerShell

```powershell
py -3 -c "import authsec_sdk; print(authsec_sdk.__version__)"
```

## Quick Start

Create `server.py`:

```python
from authsec_sdk import mcp_tool, protected_by_AuthSec, run_mcp_server_with_oauth


@mcp_tool(
    name="ping",
    description="Health check tool",
    inputSchema={
        "type": "object",
        "properties": {},
        "required": []
    }
)
async def ping(arguments: dict) -> list:
    return [{"type": "text", "text": "pong"}]


@protected_by_AuthSec(
    tool_name="delete_invoice",
    roles=["admin"],
    scopes=["write"],
    require_all=True,
    description="Delete invoice by id",
    inputSchema={
        "type": "object",
        "properties": {
            "invoice_id": {"type": "string"},
            "session_id": {"type": "string"}
        },
        "required": ["invoice_id"]
    }
)
async def delete_invoice(arguments: dict) -> list:
    invoice_id = arguments.get("invoice_id")
    user = (arguments.get("_user_info") or {}).get("email_id", "unknown-user")
    return [{"type": "text", "text": f"Deleted invoice {invoice_id} by {user}"}]


if __name__ == "__main__":
    import __main__

    run_mcp_server_with_oauth(
        user_module=__main__,
        client_id="YOUR_CLIENT_ID",
        app_name="my-python-mcp",
        host="127.0.0.1",
        port=3005,
    )
```

Run on macOS / Linux:

```bash
AUTHSEC_AUTH_SERVICE_URL="https://dev.api.authsec.dev/sdkmgr/mcp-auth" \
AUTHSEC_SERVICES_URL="https://dev.api.authsec.dev/sdkmgr/services" \
python3 server.py
```

Run on Windows PowerShell:

```powershell
$env:AUTHSEC_AUTH_SERVICE_URL = "https://dev.api.authsec.dev/sdkmgr/mcp-auth"
$env:AUTHSEC_SERVICES_URL = "https://dev.api.authsec.dev/sdkmgr/services"
py -3 .\server.py
```

Connect MCP Inspector:

```bash
npx @modelcontextprotocol/inspector http://127.0.0.1:3005
```

### Full setup by OS

#### macOS / Linux

1. Create and activate a virtual environment.
2. Install the SDK from GitHub or from a package index.
3. Create `server.py` with `from authsec_sdk import ...`.
4. Export `AUTHSEC_AUTH_SERVICE_URL` and `AUTHSEC_SERVICES_URL` if you want to override the defaults.
5. Run `python3 server.py`.
6. Open MCP Inspector against `http://127.0.0.1:3005`.

#### Windows PowerShell

1. Create and activate a virtual environment with `py -3 -m venv .venv` and `.venv\Scripts\Activate.ps1`.
2. Install the SDK from GitHub or from a package index.
3. Create `server.py` with `from authsec_sdk import ...`.
4. Set `$env:AUTHSEC_AUTH_SERVICE_URL` and `$env:AUTHSEC_SERVICES_URL` if you want to override the defaults.
5. Run `py -3 .\server.py`.
6. Open MCP Inspector against `http://127.0.0.1:3005`.

## How Protection Works

1. Server exposes OAuth tools from SDK Manager (`oauth_start`, `oauth_authenticate`, `oauth_status`, etc.).
2. User authenticates and gets/uses a session.
3. When a protected tool is called, SDK hits `protect-tool` upstream.
4. RBAC is evaluated from your tool declaration (`roles`, `groups`, `resources`, `scopes`, `permissions`).
5. On success, your handler receives:
- `arguments["session_id"]` (resolved session)
- `arguments["_user_info"]` (JWT/user claims)

Auth behavior note:
- Auth decisions are delegated upstream to SDK Manager.
- SDK may still expose OAuth tool schemas when upstream `tools/list` is unavailable so clients can continue auth bootstrap.

## Decorators

### `@protected_by_AuthSec(...)`

Use for tools that require auth/RBAC.

Key parameters:
- `tool_name`
- `roles`, `groups`, `resources`, `scopes`, `permissions`
- `require_all` (default `False`; if `True`, all specified categories must match)
- `description`
- `inputSchema`

### `@mcp_tool(...)`

Use for tools that should stay unprotected.

Key parameters:
- `name`
- `description`
- `inputSchema`

## Session-Aware Handler Pattern

If your protected function accepts a second `session` argument, SDK passes a simple session object:

```python
@protected_by_AuthSec("service_call", scopes=["read"])
async def service_call(arguments: dict, session) -> list:
    # session.session_id, session.user_id, session.tenant_id, session.access_token
    return [{"type": "text", "text": session.session_id}]
```

## Service Access SDK

Use `ServiceAccessSDK(session)` to fetch service credentials/tokens from SDK Manager services API:

```python
from authsec_sdk import ServiceAccessSDK

@protected_by_AuthSec("fetch_github_token", scopes=["read"])
async def fetch_github_token(arguments: dict, session) -> list:
    services = ServiceAccessSDK(session)
    token = await services.get_service_token("github")
    return [{"type": "text", "text": f"Token length: {len(token)}"}]
```

## Environment Variables

SDK runtime:
- `AUTHSEC_AUTH_SERVICE_URL` (optional, default points to dev)
- `AUTHSEC_SERVICES_URL` (optional, default points to dev)
- `AUTHSEC_TIMEOUT_SECONDS` (default `15`)
- `AUTHSEC_RETRIES` (default `2`)
- `AUTHSEC_TOOLS_LIST_TIMEOUT_SECONDS` (default `8`)

Note:
- Tool visibility policies like `AUTHSEC_ALWAYS_EXPOSE_PROTECTED_TOOLS` / `AUTHSEC_HIDE_UNAUTHORIZED_TOOLS` are controlled in SDK Manager service config, not this package.

## Troubleshooting

- `ModuleNotFoundError: No module named 'AuthSec_SDK'`
  - The correct import is `from authsec_sdk import ...`. The package name is `authsec-sdk`, but the Python import path is `authsec_sdk`.
- Local folder named `authsec_sdk` shadows the installed package
  - Rename or remove the local folder before running your app. `python path/to/script.py` puts that script directory first on `sys.path`.
- Apple Silicon Mac imports fail with `incompatible architecture`
  - Do not mix `arm64` and `x86_64` Python environments. Recreate the virtualenv using the same architecture you will run.
- `OAuth tool failed: ... released back to the pool`
  - This is an SDK Manager backend deployment/version issue, not client SDK usage.
- `verifyToken failed: 404`
  - Check SDK Manager `AUTH_MANAGER_URL` and ensure it targets your correct environment (`dev` vs `prod`).
- Protected tools denied unexpectedly
  - Confirm JWT claims contain expected roles/scopes/resources and match your decorator RBAC.

## Prompt Template (Copy/Paste)

Use this prompt with any coding LLM to wrap a Python MCP server with AuthSec:

```text
You are editing a Python MCP server codebase.

Goal:
1) Integrate authsec_sdk so selected tools are protected with OAuth + RBAC.
2) Keep public tools unprotected.
3) Start server via run_mcp_server_with_oauth.

Requirements:
- Use @protected_by_AuthSec for sensitive tools.
- Use @mcp_tool for unprotected tools.
- Pass a clear inputSchema for each tool.
- Ensure protected handlers read user info from arguments["_user_info"].
- Keep all OAuth/auth checks delegated upstream (no local bypass/fallback logic).
- Add startup instructions using AUTHSEC_AUTH_SERVICE_URL and AUTHSEC_SERVICES_URL.

Inputs:
- client_id: <YOUR_CLIENT_ID>
- app_name: <YOUR_APP_NAME>
- protected tools + RBAC:
  - <tool_name_1>: roles=[...], scopes=[...], require_all=<true/false>
  - <tool_name_2>: ...
- unprotected tools:
  - <tool_name_3>

Deliverables:
- Updated Python source files.
- A runnable command section.
- A short verification checklist using MCP Inspector.
```

## Publishing (Python)

### One-command publish (recommended)

From the repo root:

```bash
# TestPyPI first
bash scripts/publish-python-sdk.sh --test

# Then PyPI
bash scripts/publish-python-sdk.sh
```

Auth for `twine` (do not commit tokens):

```bash
export TWINE_USERNAME="__token__"
export TWINE_PASSWORD="pypi-REDACTED"
```

### 1) Bump version

Update version consistently:
- `packages/python-sdk/pyproject.toml` -> `[project].version`
- `packages/python-sdk/src/authsec_sdk/__init__.py` -> `__version__`

### 2) Build artifacts

```bash
cd packages/python-sdk
python3 -m pip install --upgrade build twine
python3 -m build
python3 -m twine check dist/*
```

### 3) Test publish (recommended)

```bash
python3 -m twine upload --repository testpypi dist/*
```

Install from TestPyPI in a clean venv and run smoke test.

### 4) Publish to PyPI

```bash
python3 -m twine upload dist/*
```

### 5) Post-publish check

```bash
python3 -m pip install --upgrade authsec-sdk
python3 -c "import authsec_sdk; print(authsec_sdk.__version__)"
```
