@@ -13,7 +13,7 @@ Services and navigating their Offerings.
1313ODP separates two levels of discovery:
1414
15151 . An Agent searches the canonical Directory for Services.
16- 2 . The Agent inspects a Service's live ODP document and navigates that Service's Collections and
16+ 2 . For a native ODP source, the Agent inspects the Service's live ODP document and navigates its Collections and
1717 Offerings.
1818
1919The Directory does not copy every Service catalog. Catalog searches go directly to each Service.
@@ -77,17 +77,36 @@ asyncio.run(main())
7777an explicit list must be nonempty and distinct. Filters apply to the owning Service's metadata.
7878Omit the query to browse. The default and maximum result limit are 100.
7979
80- A Collection is identified by its owning Service origin and case-sensitive Collection ID.
81- Inspect that Service's live ODP document, then call ` ServiceClient.get_collection() ` for current
82- details. The result's ` indexed_at ` describes Collection freshness; ` service.indexed_at ` describes
83- parent freshness. ` service.service_id ` is the Directory's Service identifier. A Service result can
80+ A Collection is identified by its owning ` service.service_id ` and case-sensitive Collection ID.
81+ Different OpenAPI documents can share an API origin without being the same Directory Service.
82+ When ` service.source.type == "odp" ` , inspect that Service's live ODP document, then call
83+ ` ServiceClient.get_collection() ` for current details. OpenAPI Collections are Directory presentation
84+ groups, not ODP operation targets. The result's ` indexed_at ` describes Collection freshness;
85+ ` service.indexed_at ` describes parent freshness. ` service.service_id ` is the Directory's Service identifier. A Service result can
8486have ` available_through ` platform attribution; a Collection's attribution is its owning ` service ` .
8587
8688Malformed known results are omitted and reported in ` issues ` with their original response index.
8789Unknown future types retain their full JSON in ` UnknownResult.raw ` ; do not treat them as Services
8890or execute their metadata. Additional fields are available through ` additional ` . Directory
8991metadata does not replace inspection of the Service's own document.
9092
93+ Mixed results use ` DirectoryIndexedService ` , separate from the native ` DirectoryService ` returned
94+ by ` search_services() ` . Each mixed Service requires ` service_id ` , ` service_origin ` , ` name ` ,
95+ ` indexed_at ` and ` source ` . Imported descriptions and languages are optional; missing lists become
96+ empty lists. Imported results do not expose native ODP operations. Native results retain ODP
97+ validation. Unverified execution fields such as ` http ` and ` payment_origins ` are not returned.
98+
99+ ` DirectorySource ` identifies the document used for discovery:
100+
101+ - ` type ` is ` "odp" ` , ` "openapi" ` , or an unknown future string. Unknown formats remain readable
102+ but must not be passed to ODP operations.
103+ - ` url ` is the exact document URL, including path and query. It can differ from the API origin;
104+ do not reconstruct it from ` service_origin ` .
105+ - ` x402_discovery ` records supporting fixed-path x402 discovery, not proof that an endpoint
106+ accepts payments. Advertised protocol evidence remains in ` protocols ` .
107+
108+ The client does not fetch or execute OpenAPI documents.
109+
91110Mixed search does not currently offer continuation. Missing ` next ` does ** not** mean every match
92111was returned. Refine the query or filters when needed. ` continue_search(next) ` follows one opaque
93112same-origin reference if the server supplies one; the SDK does not invent continuations. Facets
@@ -105,7 +124,32 @@ are candidate search queries, not resource identifiers.
105124
106125See the [ runnable canonical Directory example] ( examples/README.md#canonical-directory-discovery ) .
107126
108- ## Search only Services
127+ ### Filter by source
128+
129+ ``` python
130+ from offering_protocol.directory import (
131+ DirectoryClient,
132+ ResourceSearchRequest,
133+ ServiceFilters,
134+ SuggestionRequest,
135+ )
136+
137+
138+ async def discover_openapi () -> None :
139+ filters = ServiceFilters(sources = [" openapi" ])
140+ async with DirectoryClient() as directory:
141+ results = await directory.search(ResourceSearchRequest(query = " weather" , filters = filters))
142+ names = await directory.suggest(SuggestionRequest(prefix = " we" , filters = filters))
143+ print (results.items, names)
144+ ```
145+
146+ Omitting ` sources ` includes all formats. An explicit list must contain one or both distinct
147+ ` "odp" ` and ` "openapi" ` values. Sources are alternatives, combined with other filter categories
148+ using AND. Collections inherit their owning Service's source. Unsupported source filter values
149+ are rejected. Native ` search_services() ` accepts the filter but remains ODP-only: an OpenAPI-only
150+ filter returns no native matches.
151+
152+ ## Search only native ODP Services
109153
110154` DirectoryClient ` uses the one canonical production Directory. Pass ` Environment.SANDBOX ` when
111155working against InFlow's sandbox; the endpoint itself is not configurable.
@@ -143,6 +187,9 @@ and trust protocols. Use `suggest_services()` to discover Service-only keyword c
143187
144188### API migration
145189
190+ - Mixed results use ` DirectoryIndexedService ` with required source metadata. Missing sources are
191+ reported as item issues, not assumed to be ODP. Native Service-only models are unchanged.
192+ - Imported ` description ` and ` language ` can be ` None ` ; check the source before ODP navigation.
146193- Service-only ` search() ` calls become ` search_services() ` , and ` continue_search() ` calls become
147194 ` continue_search_services() ` .
148195- Aggregating ` search_services(request, options) ` calls become ` collect_services(request, options) ` .
@@ -385,8 +432,9 @@ Protocol models preserve additive members in `model.additional` and round-trip t
385432` model.to_dict() ` . Parsing remains strict for normative constraints and fields that prohibit unknown
386433members.
387434
388- Directory records retain additional metadata such as branding and MCP endpoints. These are discovery
389- hints, not authorization or authoritative routing data. The default Agent factory uses the record's
435+ Native Service-only records retain additional metadata such as branding and MCP endpoints. Mixed
436+ results omit unverified execution fields. Directory metadata is not authorization or authoritative
437+ routing data. The default Agent factory uses the native record's
390438` service_origin ` and retrieves that Service's own document before making catalog requests.
391439
392440Handle the narrowest error that the application can act upon and use the role's base error for the
0 commit comments