"""End-to-end paid Skavio MCP example (official MCP Python SDK). Runs the full paid sequence against the production Streamable HTTP endpoint: list tools -> read catalogue -> wallet -> upload -> estimate -> submit -> poll -> download signed result -> read settled accounting. Requires: pip install mcp anyio Env: SKAVIO_MCP_CREDENTIAL, SKAVIO_MCP_INPUT (a local file to process). Tenant, role and company come only from the credential. """ import asyncio import os import sys from mcp import ClientSession from mcp.client.streamable_http import create_mcp_http_client, streamable_http_client ENDPOINT = os.environ.get( "SKAVIO_MCP_ENDPOINT", "https://www.skavio.eu/processing/mcp" ) async def call(session, name, arguments): result = await session.call_tool(name, arguments) if result.isError: raise SystemExit(f"{name} failed: {result.content}") return result.structuredContent async def main(): credential = os.environ["SKAVIO_MCP_CREDENTIAL"] source = os.environ["SKAVIO_MCP_INPUT"] headers = {"Authorization": f"Bearer {credential}"} async with ( create_mcp_http_client(headers=headers, timeout=30) as http, streamable_http_client(ENDPOINT, http_client=http) as (read, write, _), ClientSession(read, write) as session, ): await session.initialize() tools = await session.list_tools() print("tools:", ", ".join(sorted(t.name for t in tools.tools))) catalog = await call(session, "skavio_paid_catalog", {}) print( "pricing:", catalog.get("pricing_version"), "margin band:", catalog.get("margin_band"), catalog.get("currency"), "per credit", catalog.get("usd_per_credit"), ) wallet = await call(session, "skavio_wallet", {}) print("wallet:", wallet) # skavio_upload_bytes accepts small inline content (<= 48 KiB). # For larger files use skavio_upload with a filename in the # operator-configured input directory. import base64 from pathlib import Path data = await asyncio.to_thread(Path(source).read_bytes) upload = await call( session, "skavio_upload_bytes", { "filename": os.path.basename(source), "data_base64": base64.b64encode(data).decode(), "idempotency_key": "upload-" + os.path.basename(source), }, ) upload_id = upload["uploads"][0]["id"] print("uploaded:", upload_id) spec = {"operation": "pdf_to_text", "upload_ids": [upload_id]} quote = await call(session, "skavio_estimate", {"spec": spec}) print("quote:", quote["estimated_credits"], "credits") # The reservation is the immutable hard cap and is persisted before # dispatch, so a retry with the same key can never double-charge. spec["max_credits"] = int(quote["estimated_credits"]) key = "example-" + os.path.basename(source) job = await call( session, "skavio_submit_job", {"spec": spec, "idempotency_key": key}, ) job_id = job["id"] print("job:", job_id, job["status"]) for _ in range(60): job = await call(session, "skavio_get_job", {"job_id": job_id}) if job["status"] in ("completed", "failed"): break await asyncio.sleep(5) print("final:", job["status"], "charged", job["charged_credits"]) if job["status"] == "completed": result_id = job["result"]["files"][0]["id"] delivery = await call( session, "skavio_download_result", {"job_id": job_id, "result_id": result_id}, ) print("signed:", delivery["signed"], "sha256:", delivery["sha256"]) usage = await call(session, "skavio_adapter_usage", {}) print("accounting:", usage) if __name__ == "__main__": try: asyncio.run(main()) except KeyError as exc: sys.exit(f"set environment variable {exc}") except BaseException as exc: # includes ExceptionGroup from the SDK task group text = "".join( str(part) for part in getattr(exc, "exceptions", [exc]) ) if "401" in text or "Unauthorized" in text: sys.exit("401 Unauthorized: check SKAVIO_MCP_CREDENTIAL.") raise