Metadata-Version: 2.4
Name: centurian-sdk
Version: 0.1.0b8
Summary: Centurian Python SDK — thin MCP wrapper for AI agents. Auto-registers, instruments tool calls, attributes cost. Implements ADR-017 SDK perf primitives: local rule cache + per-action OPA decision cache + durable sqlite-backed event queue + async batched MCP calls. Owner: Connect agent (T12 W3-W5).
Author-email: Centurian <engineering@centurian.ai>
License: Proprietary
Project-URL: Homepage, https://centurian.ai
Project-URL: Documentation, https://centurian.ai/docs/quickstart
Project-URL: Repository, https://github.com/omniviewai/Centurian
Keywords: centurian,mcp,agent,observability,compliance
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Provides-Extra: http
Requires-Dist: httpx>=0.27.0; extra == "http"

# centurian-sdk (Python)

Thin MCP wrapper for AI agents. See `docs/prd/v4.1/Centurian_Solo_Developer_Experience.md`.

## Status

Version `0.1.0b8` ships **real network calls** to the Centurian control plane. The previous `0.1.0b7` shipped a stub MCP transport (security audit C1) — anyone on `<= 0.1.0b7` should upgrade.

## Install

```bash
pip install centurian-sdk
# optional: enable httpx transport (otherwise stdlib urllib is used)
pip install 'centurian-sdk[http]'
```

For local-monorepo development:

```bash
pip install -e ./packages/sdk-python
```

## Quickstart

```python
from centurian import Agent

agent = Agent(
    name="my-agent",
    purpose="Answer questions about my docs",
    # token is the provisioning token from your /signup flow
    # (also auto-read from CENTURIAN_TOKEN env)
)

@agent.tool
def search_docs(query: str) -> str:
    return run_my_search(query)

agent.run()
```

`agent.run()` does this over the network:

1. POST `/api/mcp/tools/call` with `tools/call` JSON-RPC and tool `centurian.register_agent`.
2. GET `/api/mcp/resources/org_global_rules_manifest?org_id=...` to seed the local rule cache.
3. Start the durable sqlite-backed event queue. Tool invocations enqueue trajectory + tool-call events; the queue flushes via `centurian.report_*_bulk` JSON-RPC calls.

## Auth

| Call | Auth |
|------|------|
| `register_agent` | `Authorization: Bearer <provisioning_token>` (passed via `token` constructor arg or `CENTURIAN_TOKEN` env var) |
| All spine-ingest tools (`report_*`, `submit_*`) | `signing_credential` field inside the tool arguments — extracted from the agent identity returned by `register_agent` |

## Action evaluation

`agent.evaluate_action(verb, attributes)` consults the local rule cache and returns an `ActionDecision`:

| Effect | `decision.allowed` | When |
|--------|---------------------|------|
| `allow` | `True` | Rule explicitly allows or no rule matches and manifest is fresh |
| `deny` | `False` | Rule explicitly denies |
| `escalate` | `False` | Synchronous human-in-the-loop gate required |
| `miss` | `False` | Rule manifest stale or absent — treat as deny (Shadow #21 fix). Re-prime via `agent.run()` or `mcp.prime_rule_cache()`. |

Use `agent.assert_action_allowed(...)` to raise `ActionRefusedError` instead of branching.

## Configuration

Reads `CENTURIAN_TOKEN`, `CENTURIAN_MCP_URL`, `CENTURIAN_ORG_ID`, `CENTURIAN_OWNER_USER_ID`, `CENTURIAN_QUEUE_PATH` from env.

## Errors

- `McpRpcError(code, message, data)` — JSON-RPC error envelope from the server (4xx).
- `McpTransportError(message, status=None)` — network failure after retries exhausted (5xx, DNS, TLS).
- `ActionRefusedError(decision)` — local rule cache refused the action.

## Known beta limitations (`v0.1.0b8`)

These are documented gaps tracked for the `0.1.0` GA release. Production usage on `--pre` should be aware:

### W3-C1c — `register_agent` provisioning-token binding is surface-only

The HTTP endpoint at `/api/mcp/tools/call` requires an `Authorization: Bearer <provisioning_token>` header for `register_agent`, but only validates that the token is ≥16 characters. The handler trusts the `org_id` and `owner_user_id` claims supplied in the call arguments; it does NOT verify that the bearer token is bound to those claims server-side. **Implication:** anyone with any 16+ char string can register an agent under any org/owner they claim. The registered agent's signing credential IS bound at the row level, so subsequent calls authenticate normally — but the registration itself is a trust gap.

**Mitigation in beta:** treat your provisioning tokens as confidential; only obtain them through the documented `/signup` flow; rotate via `centurian.rotate_credential` if compromised. **GA fix planned:** introspectable `provisioning_tokens` table that binds each token to a specific `(org_id, owner_user_id, expires_at)` and rejects mismatches.

### #13 — `validateSigningCredential` runs scrypt on every event ingest

Each `report_trajectory_step`, `report_tool_call`, etc. validates the `signing_credential` via scrypt (~30ms per call). High-throughput callers should batch via the bulk variants (`report_trajectory_steps_bulk`, `report_tool_calls_bulk`) which amortize the auth cost across up to 1,000 events per round trip. The SDK's `BatchClient` does this automatically; manual MCP callers should follow the pattern. **GA fix planned:** auth-cache layer keying validated credentials by `(prefix, secret_hash)` for 60s LRU.

### #17 — No nonce/JTI replay defense on signing credentials

Captured `cnt_live_*` credentials are valid for their full TTL (default 90 days) until rotated. There is no nonce/JTI/timestamp on signed requests. **Mitigation in beta:** rotate credentials proactively if you suspect interception; restrict your SDK runtime environment so credentials cannot be exfiltrated. **GA fix planned:** SDK signs requests with timestamp + nonce; server rejects nonces seen within rotation window or timestamps with >5min skew.

### Other deferred items

- KMS-managed signing keys for tier-3 audit reports — not yet shipped; tier-3 audit emit throws in production until the host registers a real signer resolver.
- Per-org rate limiting at the MCP HTTP route — not yet implemented; free-tier $5/mo + 10K-step caps at the cost layer mitigate.

## License

Proprietary. © Centurian.
