Skip to content

Commit dcf4bc8

Browse files
committed
feat: support TAP trust advertisement
1 parent efaf6ee commit dcf4bc8

5 files changed

Lines changed: 26 additions & 7 deletions

File tree

‎README.md‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ async def main() -> None:
8585
inspection = await service.inspect()
8686
print(inspection.document.name)
8787
print([operation.name.value for operation in inspection.document.operations])
88+
protocols = inspection.document.protocols
89+
print([protocol.name.value for protocol in protocols.trust] if protocols else [])
8890

8991
page = await service.search_offerings(
9092
OfferingSearchRequest(query="plant"),
@@ -200,7 +202,7 @@ a fragment reference such as `#node`.
200202
`Service.handle()`, and copy the returned status, headers, and body into the framework response.
201203

202204
```python
203-
from offering_protocol.core import Collection, Offering
205+
from offering_protocol.core import Collection, Offering, Protocol, TrustProtocol
204206
from offering_protocol.service import ServiceBuilder, StaticCatalog, StaticCatalogOptions
205207

206208
catalog = StaticCatalog(
@@ -226,6 +228,7 @@ service = (
226228
endpoint_base="/odp",
227229
)
228230
.keywords(["houseplants", "indoor-plants"])
231+
.protocols([], [], [TrustProtocol(name=Protocol.TAP)])
229232
.website_url("https://example.com")
230233
.build(catalog)
231234
)
@@ -245,9 +248,9 @@ See [examples/README.md](./examples/README.md) for a runnable Service and Agent.
245248
## Protocol composition
246249

247250
ODP discovers what a Service offers and how an Agent can act on an Offering. A Service document and
248-
its Actions can advertise AEP enrollment and MPP or x402 payment requirements, but ODP does not
249-
create credentials, invoke Actions, or submit payments. Applications compose the appropriate
250-
enrollment and payment client around an Action resolved through ODP.
251+
its Actions can advertise enrollment, payment, and trust protocols, but ODP does not create
252+
credentials, invoke Actions, submit payments, or implement trust protocols. Applications compose
253+
the appropriate protocol clients around an Action resolved through ODP.
251254

252255
## Errors and validation
253256

‎src/offering_protocol/service/service.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
ServiceDocument,
3232
ServiceOpenApi,
3333
ServiceProtocols,
34+
TrustProtocol,
3435
is_local_resource_identifier,
3536
parse_collection,
3637
parse_collection_page,
@@ -155,13 +156,18 @@ def payment_origins(self, values: list[str]) -> ServiceBuilder:
155156
return self._updated(payment_origins=values)
156157

157158
def protocols(
158-
self, enrollment: list[EnrollmentProtocol], payments: list[PaymentProtocol]
159+
self,
160+
enrollment: list[EnrollmentProtocol],
161+
payments: list[PaymentProtocol],
162+
trust: list[TrustProtocol] | None = None,
159163
) -> ServiceBuilder:
160164
values: dict[str, object] = {}
161165
if enrollment:
162166
values["enrollment"] = enrollment
163167
if payments:
164168
values["payments"] = payments
169+
if trust:
170+
values["trust"] = trust
165171
return self._updated(protocols=ServiceProtocols.model_validate(values))
166172

167173
def search_capabilities(self, value: SearchCapabilities) -> ServiceBuilder:

‎tests/helpers.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ def response(
4141
"operations":[
4242
{"authentication":"not-required","name":"get-offering"},
4343
{"authentication":"not-required","name":"list-offerings"}
44-
]
44+
],
45+
"protocols":{"trust":[{"name":"tap"}]}
4546
}"""
4647

4748
OFFERING = """{

‎tests/test_agent.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
MissingPlacement,
5353
OfferingSearchRequest,
5454
OpenApiActionTarget,
55+
Protocol,
5556
SearchCapabilities,
5657
SortCapabilitySource,
5758
SortDefinition,
@@ -99,6 +100,8 @@ async def test_inspects_support_before_fetching_and_caches_document() -> None:
99100
first = await client.inspect()
100101
second = await client.inspect()
101102
assert first.freshness is Freshness.FETCHED
103+
assert first.document.protocols is not None
104+
assert first.document.protocols.trust[0].name is Protocol.TAP
102105
assert second.freshness is Freshness.FRESH
103106
assert (await client.get_offering("rubber-plant")).name == "Rubber Plant"
104107
assert transport.requests[-1].url.endswith("/odp/offerings/rubber-plant?representation=full")

‎tests/test_service.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
PaymentProtocol,
1919
Protocol,
2020
Representation,
21+
TrustProtocol,
2122
)
2223
from offering_protocol.service import (
2324
MEDIA_TYPE,
@@ -76,7 +77,11 @@ def _service(catalog: Catalog | None = None) -> Service:
7677
.status_url("/status")
7778
.payment_origins(["https://demo.inflowpay.ai"])
7879
.operation_authentication(Operation.GET_OFFERING, AuthenticationRequirement.REQUIRED)
79-
.protocols([EnrollmentProtocol(name=Protocol.AEP)], [])
80+
.protocols(
81+
[EnrollmentProtocol(name=Protocol.AEP)],
82+
[],
83+
[TrustProtocol(name=Protocol.TAP)],
84+
)
8085
.build(catalog or _catalog())
8186
)
8287

@@ -87,6 +92,7 @@ async def test_serves_document_offerings_and_collections() -> None:
8792
document = await service.handle(Request("GET", "/.well-known/odp"))
8893
assert document.status == 200
8994
assert json.loads(document.body)["name"] == "Indica Flowers"
95+
assert json.loads(document.body)["protocols"]["trust"] == [{"name": "tap"}]
9096
assert service.document.operations[0].authentication is AuthenticationRequirement.REQUIRED
9197
offerings = await service.handle(
9298
Request("GET", "/odp/offerings", headers={"Accept": MEDIA_TYPE}, query="limit=1")

0 commit comments

Comments
 (0)