Skip to content

Commit 1b557b2

Browse files
mchadesCopilot
andauthored
[#9] feat: add top-level API, README docs, and integration tests (#14)
### What changes were proposed in this pull request? Finalize the public API surface, documentation, and integration tests: - `src/adp_sdk/__init__.py`: Complete public exports + `stdio_client()` convenience async context manager - `README.md`: Quick Start guide, API overview, error handling documentation - `tests/test_integration.py`: End-to-end tests with embedded mock ADP server (full lifecycle: initialize → discover → describe → validate → execute) ### Why are the changes needed? Provides a polished developer experience with a convenient entry point (`stdio_client()`), comprehensive documentation, and end-to-end test coverage. Fix: #9 ### Does this PR introduce _any_ user-facing change? Yes. Adds `stdio_client()` convenience function and complete public API exports. Adds full README documentation. ### How was this patch tested? Integration tests launch a mock ADP server subprocess and exercise the full protocol lifecycle through the public API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 319ba71 commit 1b557b2

File tree

4 files changed

+379
-4
lines changed

4 files changed

+379
-4
lines changed

README.md

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# ADP Python SDK
22

3-
Python SDK for the Agentic Data Protocol (ADP) - a deterministic, policy-aware protocol that allows AI agents to access heterogeneous data systems safely and reproducibly via a unified intent interface.
3+
Python SDK for the **Agentic Data Protocol (ADP)** — a deterministic, policy-aware
4+
protocol that allows AI agents to access heterogeneous data systems safely and
5+
reproducibly via a unified intent interface.
6+
7+
This SDK provides a **client-side** API for connecting to any ADP-compliant server
8+
(such as [adp-hypervisor](https://github.com/agntcy/adp-hypervisor)) over **stdio
9+
transport**.
410

511
## Installation
612

@@ -12,10 +18,74 @@ uv add adp-sdk
1218
pip install adp-sdk
1319
```
1420

21+
## Quick Start
22+
23+
```python
24+
import asyncio
25+
from adp_sdk import stdio_client, QueryIntent, PredicateGroup
26+
27+
async def main():
28+
async with stdio_client("python", ["-m", "adp_hypervisor", "--config", "config.yaml"]) as session:
29+
# 1. Discover available resources
30+
discovery = await session.discover()
31+
for resource in discovery.resources:
32+
print(f" {resource.resource_id}: {resource.description}")
33+
34+
# 2. Describe a resource to get its usage contract
35+
contract = await session.describe("com.example:users", "QUERY")
36+
print(f"Fields: {[f.field_id for f in contract.usage_contract.fields]}")
37+
38+
# 3. Execute an intent
39+
intent = QueryIntent(
40+
intentClass="QUERY",
41+
predicates=PredicateGroup(op="AND", predicates=[]),
42+
)
43+
result = await session.execute("com.example:users", intent)
44+
print(f"Got {len(result.results)} rows")
45+
46+
asyncio.run(main())
47+
```
48+
49+
## API Overview
50+
51+
### `stdio_client` (convenience)
52+
53+
```python
54+
async with stdio_client(command, args=None, env=None, cwd=None) as session:
55+
...
56+
```
57+
58+
Launches an ADP server as a subprocess and returns an initialized `ClientSession`.
59+
60+
### `ClientSession`
61+
62+
| Method | Description |
63+
|--------|-------------|
64+
| `discover(filter?, cursor?)` | List available resources |
65+
| `describe(resource_id, intent_class, version?, cursor?)` | Get a resource's usage contract |
66+
| `validate(resource_id, intent)` | Check if an intent is valid |
67+
| `execute(resource_id, intent, cursor?)` | Run an intent against a resource |
68+
| `ping()` | Check server liveness |
69+
70+
### Error Handling
71+
72+
All server errors are mapped to typed exceptions:
73+
74+
```python
75+
from adp_sdk import ResourceNotFoundError, ValidationFailedError
76+
77+
try:
78+
result = await session.execute("com.example:missing", intent)
79+
except ResourceNotFoundError as e:
80+
print(f"Resource not found: {e}")
81+
except ValidationFailedError as e:
82+
print(f"Invalid intent: {e}")
83+
```
84+
1585
## Development
1686

1787
```bash
18-
# Install development dependencies
88+
# Install dependencies
1989
uv sync --all-extras
2090

2191
# Run tests

src/adp_sdk/__init__.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,122 @@
11
"""ADP SDK - Python SDK for the Agentic Data Protocol."""
22

3+
from collections.abc import AsyncIterator
4+
from contextlib import asynccontextmanager
5+
6+
from adp_sdk.client import ClientSession
7+
from adp_sdk.shared import (
8+
ADPError,
9+
ExecutionFailedError,
10+
InternalError,
11+
InvalidParamsError,
12+
InvalidRequestError,
13+
MethodNotFoundError,
14+
ParseError,
15+
ResourceNotFoundError,
16+
UnauthorizedError,
17+
ValidationFailedError,
18+
)
19+
from adp_sdk.transports import Transport
20+
from adp_sdk.transports.stdio import StdioClientTransport
321
from adp_sdk.types import LATEST_PROTOCOL_VERSION
22+
from adp_sdk.types.common import (
23+
Capabilities,
24+
FieldDefinition,
25+
IdentityPredicate,
26+
IntentClass,
27+
Predicate,
28+
PredicateGroup,
29+
Resource,
30+
UsageContract,
31+
ValidationIssue,
32+
)
33+
from adp_sdk.types.intents import (
34+
IngestIntent,
35+
Intent,
36+
LookupIntent,
37+
QueryIntent,
38+
ReviseIntent,
39+
)
40+
from adp_sdk.types.responses import (
41+
DescribeResult,
42+
DiscoverResult,
43+
ExecuteResult,
44+
InitializeResult,
45+
ValidateResult,
46+
)
447

548
__version__ = "0.1.0"
649

50+
51+
@asynccontextmanager
52+
async def stdio_client(
53+
command: str,
54+
args: list[str] | None = None,
55+
env: dict[str, str] | None = None,
56+
cwd: str | None = None,
57+
) -> AsyncIterator[ClientSession]:
58+
"""Create a :class:`ClientSession` connected via stdio transport.
59+
60+
Launches *command* as a subprocess and communicates over
61+
stdin/stdout using newline-delimited JSON.
62+
63+
Usage::
64+
65+
async with stdio_client("python", ["-m", "adp_hypervisor"]) as session:
66+
resources = await session.discover()
67+
68+
Args:
69+
command: The executable to run.
70+
args: Command-line arguments.
71+
env: Additional environment variables.
72+
cwd: Working directory for the subprocess.
73+
74+
Yields:
75+
An initialized :class:`ClientSession`.
76+
"""
77+
transport = StdioClientTransport(command, args=args, env=env, cwd=cwd)
78+
async with ClientSession(transport) as session:
79+
yield session
80+
81+
782
__all__ = [
883
"__version__",
984
"LATEST_PROTOCOL_VERSION",
85+
# Convenience
86+
"stdio_client",
87+
# Core classes
88+
"ClientSession",
89+
"Transport",
90+
"StdioClientTransport",
91+
# Protocol types
92+
"Capabilities",
93+
"DescribeResult",
94+
"DiscoverResult",
95+
"ExecuteResult",
96+
"FieldDefinition",
97+
"IdentityPredicate",
98+
"IngestIntent",
99+
"InitializeResult",
100+
"Intent",
101+
"IntentClass",
102+
"LookupIntent",
103+
"Predicate",
104+
"PredicateGroup",
105+
"QueryIntent",
106+
"Resource",
107+
"ReviseIntent",
108+
"UsageContract",
109+
"ValidateResult",
110+
"ValidationIssue",
111+
# Exceptions
112+
"ADPError",
113+
"ExecutionFailedError",
114+
"InternalError",
115+
"InvalidParamsError",
116+
"InvalidRequestError",
117+
"MethodNotFoundError",
118+
"ParseError",
119+
"ResourceNotFoundError",
120+
"UnauthorizedError",
121+
"ValidationFailedError",
10122
]

src/adp_sdk/client/session.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99
import itertools
1010
import logging
11+
from importlib.metadata import version as _pkg_version
1112
from types import TracebackType
1213
from typing import Any
1314

14-
from adp_sdk import __version__
1515
from adp_sdk.shared import (
1616
deserialize_json,
1717
get_exception_for_error_code,
@@ -45,6 +45,7 @@
4545
logger = logging.getLogger(__name__)
4646

4747
_SDK_NAME = "adp-sdk-python"
48+
_SDK_VERSION = _pkg_version("adp-sdk")
4849

4950

5051
class ClientSession:
@@ -70,7 +71,7 @@ def __init__(
7071
self._transport = transport
7172
self._client_info = client_info or Implementation(
7273
name=_SDK_NAME,
73-
version=__version__,
74+
version=_SDK_VERSION,
7475
)
7576
self._capabilities = capabilities or ClientCapabilities()
7677
self._id_counter = itertools.count(1)

0 commit comments

Comments
 (0)