Metadata-Version: 2.5
Name: marcopolo-sdk
Version: 0.2.0
Summary: Python SDK for the Marcopolo API
Author: Immersa
License: Proprietary
Requires-Python: >=3.11
Requires-Dist: httpx<1,>=0.28
Requires-Dist: pydantic<3,>=2.11
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: datamodel-code-generator==0.74.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.11; extra == 'dev'
Description-Content-Type: text/markdown

# Marcopolo Python SDK

The Marcopolo SDK provides a typed, asynchronous Python interface to the
Marcopolo API. It intentionally exposes Marcopolo's product contract rather
than its MCP implementation details.

## Namespace user tokens

A partner backend exchanges its namespace key for a five-minute user token,
then passes that token to the normal `Marcopolo` client:

```python
from marcopolo import Marcopolo, MarcopoloNamespace


async with MarcopoloNamespace(api_key="mpk_...") as namespace:
    token = await namespace.issue_user_token("joe@acme.com")

async with Marcopolo(access_token=token.access_token) as client:
    connections = await client.connections.list()
```

The namespace key is accepted only by the token exchange endpoint. The caller
owns renewal of the returned token.

## Supported API

The SDK supports listing, retrieving, non-interactively creating,
updating, testing, and deleting private connections with a Marcopolo access
token:

```python
import asyncio

from marcopolo import Marcopolo


async def main() -> None:
    async with Marcopolo(access_token="...") as client:
        connections = await client.connections.list()
        for connection in connections:
            print(connection.name, connection.connection_type)

        connection_types = await client.connection_types.list(category="database")
        postgres = await client.connection_types.get("pg")
        print(postgres.display_name, postgres.setup_schema)

        connection = await client.connections.get(connections[0].name)
        print(connection.display_name)

        created = await client.connections.create(
            connection_type="pg",
            display_name="Warehouse",
            setup_method="manual",
            fields={"host": "db.internal", "dbname": "analytics", "password": "..."},
        )
        updated = await client.connections.update(
            created.name,
            display_name="Analytics Warehouse",
        )
        test_result = await client.connections.test(updated.name)
        print(test_result.status, test_result.message)
        await client.connections.delete(updated.name)


asyncio.run(main())
```

## Hosted connection setup (OAuth)

Connection types that authenticate interactively are set up through a hosted
setup session. The application starts a session with a `return_url` on an
origin registered for its namespace, sends the end user to the returned
provider authorization URL, and polls until the session is terminal:

```python
started = await client.connection_setup.start(
    connection_type="github",
    display_name="GitHub",
    return_url="https://app.partner.example/connections/done",
)
# Open started.authorization_url for the end user, then:
session = await client.connection_setup.get(started.setup_session_id)
if session.status == "ready":
    print(session.connection_name, "is ready to query")
```

Sessions expire 15 minutes after their last change. Namespace user tokens
live five minutes, so a backend that polls across a slow authorization simply
exchanges its namespace key for a fresh token and keeps the same
`setup_session_id`. Retrying `start` with the same `client_session_id` reuses
the in-flight session instead of creating a duplicate connection. Provider
access and refresh tokens never appear in any SDK response. See
[`marcopolo/docs/connection-oauth-setup.md`](../marcopolo/docs/connection-oauth-setup.md)
for the full flow.

Install this package for local development with:

```shell
python -m pip install -e "sdk[dev]"
```

The published developer guide, SDK reference, verification plan, and
[model/contract flow](docs/model-contract-flow.html) are maintained in
[`docs/`](docs/).

SDK resource models are generated from the public OpenAPI contract. From the
repository root, run `./scripts/contracts generate` after changing an API
schema. `./scripts/contracts check` verifies the committed OpenAPI contract and
materializes ignored TypeScript and Python sources for local checks. SDK builds
also generate their Python models through the Hatch build hook.

The SDK sends its immutable package version in
`X-Marcopolo-SDK-Version`. The service rejects versions below its configured
compatibility floor with `SDKVersionUnsupportedError`. Generated request
models reject unknown fields; generated response models ignore unknown
additive fields while continuing to validate all published fields strictly.

## Integration tests

Exercise the SDK against a running Marcopolo service — the local stack or a
deployed environment — through mproxy and the real backend:

```shell
MARCOPOLO_ACCESS_TOKEN=<token> \
  uv run --project sdk --extra dev pytest -m integration -q -s sdk/tests
```

`MARCOPOLO_BASE_URL` selects the target and defaults to `http://localhost:8000`
(the local stack). Set
`MARCOPOLO_INTEGRATION_CONNECTION` to a connection name when the test should
also verify that a specific seeded connection is visible. Set
`MARCOPOLO_INTEGRATION_TEST_CONNECTION` to explicitly run a provider connection
test; this is opt-in because not every visible connection type supports it.

The create integration test is opt-in because it mutates tenant state before
deleting its disposable connection through the SDK. Set
`MARCOPOLO_INTEGRATION_CREATE_TYPE`; optional JSON-object inputs are accepted in
`MARCOPOLO_INTEGRATION_CREATE_CONFIGURATION` and
`MARCOPOLO_INTEGRATION_CREATE_CREDENTIALS`.
