zambo-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: zambo-sdk
3
+ Version: 0.1.0
4
+ Summary: Zambo Python client — 100+ MCP tools, verifiable receipt for every call, no API key
5
+ Author: Brennan Zambo
6
+ License: MIT
7
+ Project-URL: Homepage, https://zambo.dev
8
+ Project-URL: Documentation, https://zambo.dev/docs
9
+ Keywords: zambo,mcp,ai-tools,agents,receipts
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+
18
+ # zambo-sdk
19
+
20
+ Thin Python client for [Zambo](https://zambo.dev) — the trust layer for AI work. 100+ native MCP tools, a verifiable receipt for every call. Free 20 calls/tool/day, no account, no API key. Zero dependencies.
21
+
22
+ > **Name note:** the distribution name is pending final pick (`zambo-sdk`, `zambo-dev`, `zambo-api`, `pyzambo`, `zambodotdev`, `zambopy` are all free on PyPI). The import package is `zambo_sdk` regardless.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install zambo-sdk
28
+ ```
29
+
30
+ ## Quickstart
31
+
32
+ ```python
33
+ from zambo_sdk import Client, verify_receipt
34
+
35
+ z = Client()
36
+
37
+ # 1. Call a tool — plain JSON-RPC, no API key
38
+ result = z.call("live_price", {"symbol": "BTC"})
39
+ print(result.text)
40
+ print("receipt:", result.receipt_id)
41
+ print("audit: ", result.audit_url)
42
+
43
+ # 2. Route any goal through the universal entry point
44
+ job = z.universal("audit https://example.com for AI discoverability", session="demo-1")
45
+ print(job.text)
46
+
47
+ # 3. Search the full 100+ tool catalog
48
+ found = z.search("generate QR code")
49
+ print(found.text)
50
+
51
+ # 4. Verify a receipt client-side (recomputes the sha256 locally)
52
+ v = verify_receipt(result.receipt_id)
53
+ print("verified:", v.verified, v.checks)
54
+ ```
55
+
56
+ ## API
57
+
58
+ - `Client().call(tool, arguments)` → `CallResult` (`.text`, `.receipt_id`, `.audit_url`, `.receipt`)
59
+ - `Client().list_tools()` → list of tool descriptors
60
+ - `Client().search(query)` → search the full catalog via `capability_search`
61
+ - `Client().universal(goal, session=None)` → multi-step jobs via `zambo_universal`
62
+ - `get_receipt(receipt_id)` → full receipt JSON
63
+ - `verify_receipt(receipt_id)` → `Verification` (`.verified`, `.checks`, `.audit_url`)
64
+
65
+ `verify_receipt` checks three things: the receipt exists, the server marks it `verified`, and — recomputed locally so the server can't fake it — `sha256(canonical_bytes) == output_hash`, plus the presence of the anchor record.
66
+
67
+ ## Notes
68
+
69
+ - Endpoint: `https://zambo.dev/api/mcp` (JSON-RPC 2.0 over HTTPS, `streamable_http`-compatible).
70
+ - Receipts: `https://zambo.dev/api/receipt/<uuid>` (JSON), audit page `https://zambo.dev/run/<uuid>`.
71
+ - Need more than 20 calls/tool/day? The $1.49/24h Day Pass is at [zambo.dev](https://zambo.dev).
@@ -0,0 +1,54 @@
1
+ # zambo-sdk
2
+
3
+ Thin Python client for [Zambo](https://zambo.dev) — the trust layer for AI work. 100+ native MCP tools, a verifiable receipt for every call. Free 20 calls/tool/day, no account, no API key. Zero dependencies.
4
+
5
+ > **Name note:** the distribution name is pending final pick (`zambo-sdk`, `zambo-dev`, `zambo-api`, `pyzambo`, `zambodotdev`, `zambopy` are all free on PyPI). The import package is `zambo_sdk` regardless.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install zambo-sdk
11
+ ```
12
+
13
+ ## Quickstart
14
+
15
+ ```python
16
+ from zambo_sdk import Client, verify_receipt
17
+
18
+ z = Client()
19
+
20
+ # 1. Call a tool — plain JSON-RPC, no API key
21
+ result = z.call("live_price", {"symbol": "BTC"})
22
+ print(result.text)
23
+ print("receipt:", result.receipt_id)
24
+ print("audit: ", result.audit_url)
25
+
26
+ # 2. Route any goal through the universal entry point
27
+ job = z.universal("audit https://example.com for AI discoverability", session="demo-1")
28
+ print(job.text)
29
+
30
+ # 3. Search the full 100+ tool catalog
31
+ found = z.search("generate QR code")
32
+ print(found.text)
33
+
34
+ # 4. Verify a receipt client-side (recomputes the sha256 locally)
35
+ v = verify_receipt(result.receipt_id)
36
+ print("verified:", v.verified, v.checks)
37
+ ```
38
+
39
+ ## API
40
+
41
+ - `Client().call(tool, arguments)` → `CallResult` (`.text`, `.receipt_id`, `.audit_url`, `.receipt`)
42
+ - `Client().list_tools()` → list of tool descriptors
43
+ - `Client().search(query)` → search the full catalog via `capability_search`
44
+ - `Client().universal(goal, session=None)` → multi-step jobs via `zambo_universal`
45
+ - `get_receipt(receipt_id)` → full receipt JSON
46
+ - `verify_receipt(receipt_id)` → `Verification` (`.verified`, `.checks`, `.audit_url`)
47
+
48
+ `verify_receipt` checks three things: the receipt exists, the server marks it `verified`, and — recomputed locally so the server can't fake it — `sha256(canonical_bytes) == output_hash`, plus the presence of the anchor record.
49
+
50
+ ## Notes
51
+
52
+ - Endpoint: `https://zambo.dev/api/mcp` (JSON-RPC 2.0 over HTTPS, `streamable_http`-compatible).
53
+ - Receipts: `https://zambo.dev/api/receipt/<uuid>` (JSON), audit page `https://zambo.dev/run/<uuid>`.
54
+ - Need more than 20 calls/tool/day? The $1.49/24h Day Pass is at [zambo.dev](https://zambo.dev).
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "zambo-sdk" # Brennan-approved distribution name (2026-09-20). "zambo" is his CLI package.
7
+ version = "0.1.0"
8
+ description = "Zambo Python client — 100+ MCP tools, verifiable receipt for every call, no API key"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Brennan Zambo" }]
13
+ keywords = ["zambo", "mcp", "ai-tools", "agents", "receipts"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Intended Audience :: Developers",
19
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
20
+ ]
21
+ dependencies = []
22
+
23
+ [project.urls]
24
+ Homepage = "https://zambo.dev"
25
+ Documentation = "https://zambo.dev/docs"
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,30 @@
1
+ """zambo_sdk — thin Python client for zambo.dev.
2
+
3
+ Zambo (https://zambo.dev) is the trust layer for AI work: 100+ native MCP
4
+ tools over plain JSON-RPC, a verifiable receipt for every call. Free
5
+ 20 calls/tool/day, no account, no API key. $1.49/24h Day Pass.
6
+
7
+ This is the whole client: call tools, fetch receipts, verify receipts.
8
+ No dependencies beyond the standard library.
9
+ """
10
+
11
+ from zambo_sdk.client import (
12
+ ZAMBO_API_URL,
13
+ CallResult,
14
+ Client,
15
+ ReceiptNotFound,
16
+ Verification,
17
+ get_receipt,
18
+ verify_receipt,
19
+ )
20
+
21
+ __all__ = [
22
+ "ZAMBO_API_URL",
23
+ "CallResult",
24
+ "Client",
25
+ "ReceiptNotFound",
26
+ "Verification",
27
+ "get_receipt",
28
+ "verify_receipt",
29
+ ]
30
+ __version__ = "0.1.0"
@@ -0,0 +1,202 @@
1
+ """Thin, dependency-free client for the Zambo MCP endpoint.
2
+
3
+ Contract (verified live against https://zambo.dev/api/mcp):
4
+ - POST JSON-RPC 2.0, no auth, no session.
5
+ - ``tools/list`` -> {"tools": [...]}
6
+ - ``tools/call`` {"name", "arguments"} -> {"content": [...], "_receipt": {...}}
7
+ - Server answers JSON or SSE (``data: {...}`` lines); transport flakes are
8
+ retried, JSON-RPC errors are never retried.
9
+ - Receipt JSON: GET https://zambo.dev/api/receipt/<uuid> (Accept: application/json)
10
+ - Human audit page: https://zambo.dev/run/<uuid>
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import base64
16
+ import hashlib
17
+ import http.client as _http_client
18
+ import json
19
+ import time
20
+ import urllib.error
21
+ import urllib.request
22
+ from dataclasses import dataclass, field
23
+ from typing import Any, Dict, List, Optional
24
+
25
+ ZAMBO_API_URL = "https://zambo.dev/api/mcp"
26
+ ZAMBO_RECEIPT_API = "https://zambo.dev/api/receipt/{receipt_id}"
27
+ ZAMBO_AUDIT_URL = "https://zambo.dev/run/{receipt_id}"
28
+
29
+ _JSON_HEADERS = {
30
+ "Content-Type": "application/json",
31
+ "Accept": "application/json, text/event-stream",
32
+ }
33
+
34
+ _TRANSIENT = (
35
+ _http_client.IncompleteRead,
36
+ _http_client.RemoteDisconnected,
37
+ _http_client.BadStatusLine,
38
+ ConnectionResetError,
39
+ BrokenPipeError,
40
+ TimeoutError,
41
+ )
42
+
43
+
44
+ class ZamboError(Exception):
45
+ """A JSON-RPC error returned by the Zambo server."""
46
+
47
+
48
+ class ReceiptNotFound(ZamboError):
49
+ """No receipt exists for the given id."""
50
+
51
+
52
+ @dataclass
53
+ class CallResult:
54
+ """The outcome of one tool call."""
55
+
56
+ tool: str
57
+ text: str
58
+ receipt_id: Optional[str] = None
59
+ audit_url: Optional[str] = None
60
+ receipt: Dict[str, Any] = field(default_factory=dict)
61
+ raw: Dict[str, Any] = field(default_factory=dict)
62
+
63
+ @property
64
+ def ok(self) -> bool:
65
+ return self.receipt.get("status") == "success" if self.receipt else True
66
+
67
+
68
+ @dataclass
69
+ class Verification:
70
+ """Client-side verification of a Zambo receipt."""
71
+
72
+ receipt_id: str
73
+ verified: bool
74
+ checks: Dict[str, bool]
75
+ audit_url: str
76
+ receipt: Dict[str, Any] = field(default_factory=dict)
77
+
78
+
79
+ def _post_json_rpc(method: str, params: Dict[str, Any], attempts: int = 6) -> Dict[str, Any]:
80
+ body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params}).encode()
81
+ last: Optional[Exception] = None
82
+ for n in range(attempts):
83
+ try:
84
+ req = urllib.request.Request(ZAMBO_API_URL, data=body, headers=_JSON_HEADERS, method="POST")
85
+ with urllib.request.urlopen(req, timeout=60) as resp:
86
+ raw = resp.read().decode().strip()
87
+ break
88
+ except _TRANSIENT as e:
89
+ last = e
90
+ time.sleep(0.6 * (n + 1))
91
+ else:
92
+ raise ZamboError(f"Transport failed after {attempts} attempts: {type(last).__name__}: {last}")
93
+
94
+ if raw.startswith("{"):
95
+ payload = json.loads(raw)
96
+ else: # SSE: data: {...} lines
97
+ payload = None
98
+ for line in raw.splitlines():
99
+ if line.startswith("data:"):
100
+ try:
101
+ payload = json.loads(line[5:].strip())
102
+ break
103
+ except json.JSONDecodeError:
104
+ continue
105
+ if payload is None:
106
+ raise ZamboError(f"Unparseable response: {raw[:200]}")
107
+ if "error" in payload:
108
+ raise ZamboError(json.dumps(payload["error"]))
109
+ return payload["result"]
110
+
111
+
112
+ def _get_json(url: str, attempts: int = 4) -> Dict[str, Any]:
113
+ last: Optional[Exception] = None
114
+ for n in range(attempts):
115
+ try:
116
+ req = urllib.request.Request(url, headers={"Accept": "application/json"}, method="GET")
117
+ with urllib.request.urlopen(req, timeout=30) as resp:
118
+ return json.loads(resp.read().decode())
119
+ except urllib.error.HTTPError as e:
120
+ if e.code == 404:
121
+ raise ReceiptNotFound(f"No receipt found: {url}")
122
+ raise ZamboError(f"HTTP {e.code} fetching {url}")
123
+ except _TRANSIENT as e:
124
+ last = e
125
+ time.sleep(0.6 * (n + 1))
126
+ raise ZamboError(f"Transport failed after {attempts} attempts: {type(last).__name__}: {last}")
127
+
128
+
129
+ class Client:
130
+ """Minimal Zambo client. No API key, no account — free 20 calls/tool/day."""
131
+
132
+ def call(self, tool: str, arguments: Optional[Dict[str, Any]] = None) -> CallResult:
133
+ """Call a Zambo tool. Returns text + the verifiable receipt."""
134
+ result = _post_json_rpc("tools/call", {"name": tool, "arguments": arguments or {}})
135
+ text = "\n".join(
136
+ b.get("text", "") for b in result.get("content", []) if b.get("type") == "text"
137
+ )
138
+ receipt = result.get("_receipt", {}) or {}
139
+ rid = receipt.get("id")
140
+ return CallResult(
141
+ tool=tool,
142
+ text=text,
143
+ receipt_id=rid,
144
+ audit_url=ZAMBO_AUDIT_URL.format(receipt_id=rid) if rid else None,
145
+ receipt=receipt,
146
+ raw=result,
147
+ )
148
+
149
+ def list_tools(self) -> List[Dict[str, Any]]:
150
+ """List the tools on the main MCP surface."""
151
+ return _post_json_rpc("tools/list", {}).get("tools", [])
152
+
153
+ def search(self, query: str) -> CallResult:
154
+ """Search the full 100+ tool catalog."""
155
+ return self.call("capability_search", {"query": query})
156
+
157
+ def universal(self, goal: str, session: Optional[str] = None) -> CallResult:
158
+ """Route any goal through zambo_universal (the main entry point)."""
159
+ args: Dict[str, Any] = {"goal": goal}
160
+ if session:
161
+ args["session"] = session
162
+ return self.call("zambo_universal", args)
163
+
164
+
165
+ def get_receipt(receipt_id: str) -> Dict[str, Any]:
166
+ """Fetch the full receipt JSON for a receipt id."""
167
+ return _get_json(ZAMBO_RECEIPT_API.format(receipt_id=receipt_id))
168
+
169
+
170
+ def verify_receipt(receipt_id: str) -> Verification:
171
+ """Verify a Zambo receipt client-side.
172
+
173
+ Checks:
174
+ - the receipt exists and the server marks it ``verified``
175
+ - sha256 of the canonical bytes matches the claimed ``output_hash``
176
+ (recomputed locally — the server cannot fake this)
177
+ - an anchor record is present
178
+ """
179
+ receipt = get_receipt(receipt_id)
180
+ checks: Dict[str, bool] = {}
181
+
182
+ checks["exists"] = bool(receipt.get("id"))
183
+ checks["server_marks_verified"] = receipt.get("verification_status") == "verified"
184
+
185
+ claimed = receipt.get("output_hash", "")
186
+ canonical_b64 = receipt.get("canonical_bytes", "")
187
+ try:
188
+ digest = "sha256:" + hashlib.sha256(base64.b64decode(canonical_b64)).hexdigest()
189
+ checks["output_hash_matches"] = bool(claimed) and digest == claimed
190
+ except Exception:
191
+ checks["output_hash_matches"] = False
192
+
193
+ checks["anchor_present"] = bool((receipt.get("anchor") or {}).get("event_id"))
194
+
195
+ verified = all(checks.values())
196
+ return Verification(
197
+ receipt_id=receipt_id,
198
+ verified=verified,
199
+ checks=checks,
200
+ audit_url=ZAMBO_AUDIT_URL.format(receipt_id=receipt_id),
201
+ receipt=receipt,
202
+ )
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: zambo-sdk
3
+ Version: 0.1.0
4
+ Summary: Zambo Python client — 100+ MCP tools, verifiable receipt for every call, no API key
5
+ Author: Brennan Zambo
6
+ License: MIT
7
+ Project-URL: Homepage, https://zambo.dev
8
+ Project-URL: Documentation, https://zambo.dev/docs
9
+ Keywords: zambo,mcp,ai-tools,agents,receipts
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+
18
+ # zambo-sdk
19
+
20
+ Thin Python client for [Zambo](https://zambo.dev) — the trust layer for AI work. 100+ native MCP tools, a verifiable receipt for every call. Free 20 calls/tool/day, no account, no API key. Zero dependencies.
21
+
22
+ > **Name note:** the distribution name is pending final pick (`zambo-sdk`, `zambo-dev`, `zambo-api`, `pyzambo`, `zambodotdev`, `zambopy` are all free on PyPI). The import package is `zambo_sdk` regardless.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install zambo-sdk
28
+ ```
29
+
30
+ ## Quickstart
31
+
32
+ ```python
33
+ from zambo_sdk import Client, verify_receipt
34
+
35
+ z = Client()
36
+
37
+ # 1. Call a tool — plain JSON-RPC, no API key
38
+ result = z.call("live_price", {"symbol": "BTC"})
39
+ print(result.text)
40
+ print("receipt:", result.receipt_id)
41
+ print("audit: ", result.audit_url)
42
+
43
+ # 2. Route any goal through the universal entry point
44
+ job = z.universal("audit https://example.com for AI discoverability", session="demo-1")
45
+ print(job.text)
46
+
47
+ # 3. Search the full 100+ tool catalog
48
+ found = z.search("generate QR code")
49
+ print(found.text)
50
+
51
+ # 4. Verify a receipt client-side (recomputes the sha256 locally)
52
+ v = verify_receipt(result.receipt_id)
53
+ print("verified:", v.verified, v.checks)
54
+ ```
55
+
56
+ ## API
57
+
58
+ - `Client().call(tool, arguments)` → `CallResult` (`.text`, `.receipt_id`, `.audit_url`, `.receipt`)
59
+ - `Client().list_tools()` → list of tool descriptors
60
+ - `Client().search(query)` → search the full catalog via `capability_search`
61
+ - `Client().universal(goal, session=None)` → multi-step jobs via `zambo_universal`
62
+ - `get_receipt(receipt_id)` → full receipt JSON
63
+ - `verify_receipt(receipt_id)` → `Verification` (`.verified`, `.checks`, `.audit_url`)
64
+
65
+ `verify_receipt` checks three things: the receipt exists, the server marks it `verified`, and — recomputed locally so the server can't fake it — `sha256(canonical_bytes) == output_hash`, plus the presence of the anchor record.
66
+
67
+ ## Notes
68
+
69
+ - Endpoint: `https://zambo.dev/api/mcp` (JSON-RPC 2.0 over HTTPS, `streamable_http`-compatible).
70
+ - Receipts: `https://zambo.dev/api/receipt/<uuid>` (JSON), audit page `https://zambo.dev/run/<uuid>`.
71
+ - Need more than 20 calls/tool/day? The $1.49/24h Day Pass is at [zambo.dev](https://zambo.dev).
@@ -0,0 +1,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/zambo_sdk/__init__.py
4
+ src/zambo_sdk/client.py
5
+ src/zambo_sdk.egg-info/PKG-INFO
6
+ src/zambo_sdk.egg-info/SOURCES.txt
7
+ src/zambo_sdk.egg-info/dependency_links.txt
8
+ src/zambo_sdk.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ zambo_sdk