Official Java software development kit for the Offering Discovery Protocol, the open protocol for discovering Services and navigating their Offerings.
ODP separates Service discovery from catalog discovery. An Agent searches the canonical directory for candidate Services, inspects each Service's live ODP document, and then navigates or searches that Service's Collections and Offerings. Full Offering details can describe structured attributes, price previews, images, and executable Actions without forcing every industry into one catalog schema.
Choose the module that matches the role your application implements:
Every application also selects one JSON provider: odp-json-jackson2 for applications using
Jackson 2, or odp-json-jackson3 for applications using Jackson 3. The role modules do not force a
second Jackson major version into the application.
All artifacts use Maven group org.offeringprotocol, require Java 17 or newer, and are available
from Maven Central without adding a repository.
Dependencies flow from role modules toward odp-core; odp-agent composes odp-directory.
odp-core does not depend on another ODP module, and odp-service does not depend on Agent or
directory behavior.
Import odp-bom once to keep every explicitly selected ODP module on a compatible release:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.offeringprotocol</groupId>
<artifactId>odp-bom</artifactId>
<version>0.2.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>For an Agent application:
<dependency>
<groupId>org.offeringprotocol</groupId>
<artifactId>odp-agent</artifactId>
</dependency>
<dependency>
<groupId>org.offeringprotocol</groupId>
<artifactId>odp-json-jackson2</artifactId>
</dependency>For a Service integration:
<dependency>
<groupId>org.offeringprotocol</groupId>
<artifactId>odp-service</artifactId>
</dependency>
<dependency>
<groupId>org.offeringprotocol</groupId>
<artifactId>odp-json-jackson2</artifactId>
</dependency>Gradle uses the same coordinates:
implementation(platform("org.offeringprotocol:odp-bom:0.2.1"))
implementation("org.offeringprotocol:odp-agent")
implementation("org.offeringprotocol:odp-json-jackson2")The BOM manages ODP module versions only. It does not add modules, select a Jackson generation, or manage Jackson itself. Applications select the role modules they use and exactly one JSON provider.
Consumers that prefer direct versions can omit the BOM and specify the same ODP release on each
dependency, for example org.offeringprotocol:odp-agent:0.2.1 and
org.offeringprotocol:odp-json-jackson2:0.2.1.
Replace odp-json-jackson2 with odp-json-jackson3 when the application uses Jackson 3. Exactly
one provider must be present at runtime; OdpJson discovers it through Java ServiceLoader.
Maven resolves the required Core and Directory modules transitively. Applications should not add
every ODP module to one project unless they actually implement multiple roles.
OdpAgent performs two-stage discovery: it searches the canonical directory and then searches the
live catalogs of matching Services. A Service failure becomes an IssueEvent without discarding
Offerings returned by other Services.
import org.offeringprotocol.odp.agent.OdpAgent;
import org.offeringprotocol.odp.directory.DirectoryClient;
DirectoryClient directory = DirectoryClient.create();
OdpAgent agent = new OdpAgent(directory);
for (OdpAgent.DiscoveryEvent event : agent.searchOfferings("plants", 10, 10)) {
if (event instanceof OdpAgent.OfferingEvent offering) {
System.out.printf("%s: %s%n", offering.service().name(), offering.offering().name());
} else if (event instanceof OdpAgent.IssueEvent issue) {
System.err.printf("%s: %s%n", issue.service().serviceOrigin(), issue.message());
}
}For a known Service, OdpServiceClient inspects /.well-known/odp, exposes the advertised
operations, and provides Collection and Offering list, search, get, and continuation methods. See
the Agent integration guide for direct navigation, sandbox selection,
localization, pagination, and authenticated transport composition.
The minimum Service integration publishes /.well-known/odp, lists Offerings, and retrieves one
Offering. StaticCatalog provides those operations for a small in-memory catalog.
import java.util.List;
import org.offeringprotocol.odp.core.OdpJson;
import org.offeringprotocol.odp.core.Offering;
import org.offeringprotocol.odp.service.OdpService;
import org.offeringprotocol.odp.service.StaticCatalog;
Offering offering = OdpJson.parseOffering("""
{
"odp_version": "1.0",
"id": "rubber-plant",
"name": "Rubber Plant",
"description": "A resilient indoor plant."
}
""");
OdpService service = OdpService.builder(
"Example Plant Store",
"Indoor plants selected for homes and offices.",
"en",
"/odp")
.keywords(List.of("plants", "indoor-plants"))
.endpoints(StaticCatalog.create(List.of(offering), List.of()))
.build();Adapt the framework's incoming request to OdpHttpRequest, pass it to service.handle(...), and
write the returned OdpHttpResponse. Large catalogs provide handlers backed by their own storage
and indexes instead of materializing the catalog in memory. See the
Service integration guide and the
runnable small Service.
ODP advertises enrollment, payment, and trust protocols, operation authentication requirements, and Offering Actions. It does not duplicate those protocols' credential, payment, or trust semantics.
The default Java Agent transport performs anonymous HTTP requests. Applications inject an
OdpTransport when catalog requests need AEP credentials, MPP, x402, or application-specific
network policy. The Service runtime advertises authentication requirements but expects the hosting
application to enforce authentication and payment before or around the ODP handler.
An Offering may describe an Action, but the Java SDK never invokes an Action implicitly. The application selects the Action and remains responsible for user approval, authentication, payment, and state-changing requests.
Applications own persistent caching, authentication context, authorization, catalog persistence, indexing, rate limiting, and Action execution. The clients enforce ODP document validation, same-origin redirect and continuation rules, response-size limits, and fixed production or sandbox directory selection.
OdpServiceClient fetches and validates its Service Document when the client is created and retains
that inspection for the client's lifetime. The Java SDK does not maintain a persistent cache or
refresh a live client automatically; applications choose when to reuse or recreate clients.
Run the small Service and Agent examples in separate terminals:
./scripts/run-small-service.sh
./scripts/run-agent-example.shThe Agent example explicitly uses a mock directory assembled from reachable Service origins. It then performs live Service inspection, Offering listing, and full Offering retrieval. See examples/README.md for the walkthrough and source map.
The Maven Wrapper provides the complete merge gate:
./mvnw verifyVerify the published module boundaries from an isolated consumer project with:
./scripts/verify-consumer.sh
./scripts/verify-gradle-consumer.shFormat Java sources with:
./mvnw spotless:applyGenerate Agent and Service conformance reports with:
ODP_SPECS_DIR=/path/to/odp-specs ./scripts/run-conformance.shRun the Java Agent against the Node.js reference Service with:
ODP_NODE_DIR=/path/to/odp-node ./scripts/run-node-interoperability.shThe shared harness writes release evidence to .conformance/reports/.
See DEVELOPMENT.md for repository conventions and
odp-specs for the normative draft, schemas,
examples, and test vectors.
See SECURITY.md for vulnerability reporting. The examples use illustrative in-memory catalogs and an explicitly labeled mock directory.
MIT.
