feat: Phase 2 Workstream 4 — Java SDK (ai.sentryagent:idp-sdk)
Java 17 SDK in sdk-java/: - AgentIdPClient composing AgentRegistryClient, CredentialClient, TokenClient, AuditClient — all 14 endpoints covered - Both sync methods and CompletableFuture<T> async counterparts on each client - Thread-safe TokenManager (synchronized) with 60s refresh buffer - AgentIdPException (extends RuntimeException) with Code/HTTPStatus/Details - Builder pattern for all request types; Jackson 2.17 for JSON - Zero external HTTP dependencies — java.net.http.HttpClient (Java 11+) - No-dep JDK HttpServer used for unit tests (no WireMock needed) - mvn verify: 49/49 tests passed | JaCoCo coverage gate: >80% ✓ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
190
sdk-java/README.md
Normal file
190
sdk-java/README.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# SentryAgent.ai AgentIdP — Java SDK
|
||||
|
||||
Official Java client for the [SentryAgent.ai AgentIdP](https://sentryagent.ai) — an open-source Identity Provider for AI agents built on OAuth 2.0 (RFC 6749) and aligned with the [AGNTCY](https://agntcy.org) open standard.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Java 17+
|
||||
- A running AgentIdP server
|
||||
|
||||
## Installation
|
||||
|
||||
### Maven
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>ai.sentryagent</groupId>
|
||||
<artifactId>idp-sdk</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```java
|
||||
import ai.sentryagent.idp.AgentIdPClient;
|
||||
import ai.sentryagent.idp.models.*;
|
||||
|
||||
AgentIdPClient client = new AgentIdPClient(
|
||||
"https://idp.example.com",
|
||||
"your-agent-client-id",
|
||||
"sk_live_..."
|
||||
);
|
||||
|
||||
// Register a new AI agent
|
||||
Agent agent = client.agents().registerAgent(
|
||||
RegisterAgentRequest.builder()
|
||||
.email("screener@example.com")
|
||||
.agentType("screener")
|
||||
.version("1.0.0")
|
||||
.capabilities(List.of("read", "classify"))
|
||||
.owner("platform-team")
|
||||
.deploymentEnv("production")
|
||||
.build()
|
||||
);
|
||||
System.out.println("Registered: " + agent.getAgentId());
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
OAuth 2.0 Client Credentials are managed automatically. Tokens are cached and refreshed 60 seconds before expiry. The `TokenManager` is thread-safe.
|
||||
|
||||
```java
|
||||
// Custom scope (optional — defaults to all four scopes)
|
||||
AgentIdPClient client = new AgentIdPClient(
|
||||
"https://idp.example.com",
|
||||
"my-client-id",
|
||||
"my-client-secret",
|
||||
"agents:read agents:write"
|
||||
);
|
||||
```
|
||||
|
||||
## Agent Registry
|
||||
|
||||
```java
|
||||
// Register
|
||||
Agent agent = client.agents().registerAgent(
|
||||
RegisterAgentRequest.builder()
|
||||
.email("...").agentType("screener").version("1.0.0")
|
||||
.capabilities(List.of("read")).owner("team").deploymentEnv("production")
|
||||
.build());
|
||||
|
||||
// List (with optional filters)
|
||||
PaginatedAgents agents = client.agents().listAgents(
|
||||
ListAgentsParams.builder().status("active").page(1).limit(20).build());
|
||||
|
||||
// Get by ID
|
||||
Agent agent = client.agents().getAgent("agent-uuid");
|
||||
|
||||
// Partial update
|
||||
Agent updated = client.agents().updateAgent("agent-uuid",
|
||||
UpdateAgentRequest.builder().version("2.0.0").build());
|
||||
|
||||
// Decommission (permanent)
|
||||
client.agents().decommissionAgent("agent-uuid");
|
||||
```
|
||||
|
||||
## Credential Management
|
||||
|
||||
```java
|
||||
// Generate (returns one-time ClientSecret)
|
||||
CredentialWithSecret cred = client.credentials().generateCredential("agent-uuid");
|
||||
System.out.println(cred.getClientSecret()); // store this — shown only once
|
||||
|
||||
// List
|
||||
PaginatedCredentials creds = client.credentials().listCredentials("agent-uuid", 1, 20);
|
||||
|
||||
// Rotate
|
||||
CredentialWithSecret newCred = client.credentials().rotateCredential("agent-uuid", "cred-uuid");
|
||||
|
||||
// Revoke
|
||||
Credential revoked = client.credentials().revokeCredential("agent-uuid", "cred-uuid");
|
||||
```
|
||||
|
||||
## Token Operations
|
||||
|
||||
```java
|
||||
// Introspect (RFC 7662)
|
||||
IntrospectResponse result = client.tokens().introspectToken("access-token-to-check");
|
||||
if (result.isActive()) {
|
||||
System.out.println("Token belongs to: " + result.getSub());
|
||||
}
|
||||
|
||||
// Revoke
|
||||
client.tokens().revokeToken("access-token-to-revoke");
|
||||
```
|
||||
|
||||
## Audit Log
|
||||
|
||||
```java
|
||||
// Query with filters
|
||||
PaginatedAuditEvents events = client.audit().queryAuditLog(
|
||||
QueryAuditParams.builder()
|
||||
.agentId("agent-uuid")
|
||||
.action("token.issued")
|
||||
.outcome("success")
|
||||
.fromDate("2026-01-01")
|
||||
.toDate("2026-01-31")
|
||||
.page(1).limit(50)
|
||||
.build());
|
||||
|
||||
// Get single event
|
||||
AuditEvent event = client.audit().getAuditEvent("event-uuid");
|
||||
```
|
||||
|
||||
## Async Methods
|
||||
|
||||
Every sync method has an async counterpart returning `CompletableFuture<T>`:
|
||||
|
||||
```java
|
||||
CompletableFuture<Agent> future = client.agents().getAgentAsync("uuid-1");
|
||||
future.thenAccept(agent -> System.out.println(agent.getAgentId()));
|
||||
|
||||
// Compose multiple async calls
|
||||
client.agents().getAgentAsync("uuid-1")
|
||||
.thenCompose(agent -> client.credentials().generateCredentialAsync(agent.getAgentId()))
|
||||
.thenAccept(cred -> System.out.println("New secret: " + cred.getClientSecret()));
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
All errors are thrown as `AgentIdPException` (extends `RuntimeException`):
|
||||
|
||||
```java
|
||||
try {
|
||||
Agent agent = client.agents().getAgent("unknown-id");
|
||||
} catch (AgentIdPException ex) {
|
||||
System.out.printf("code=%s status=%d%n", ex.getCode(), ex.getHttpStatus());
|
||||
// e.g. code=AgentNotFoundError status=404
|
||||
}
|
||||
```
|
||||
|
||||
| Method | Type | Description |
|
||||
|------------------|--------------------------|-------------------------------------------------|
|
||||
| `getCode()` | `String` | Machine-readable error code |
|
||||
| `getMessage()` | `String` | Human-readable description |
|
||||
| `getHttpStatus()`| `int` | HTTP status code (0 for network/build errors) |
|
||||
| `getDetails()` | `Map<String, Object>` | Optional structured context from the API |
|
||||
|
||||
## API Coverage
|
||||
|
||||
| Endpoint | Method | SDK Method |
|
||||
|--------------------------------------------------|--------|-----------------------------------------|
|
||||
| POST /api/v1/agents | POST | `agents().registerAgent()` |
|
||||
| GET /api/v1/agents | GET | `agents().listAgents()` |
|
||||
| GET /api/v1/agents/:id | GET | `agents().getAgent()` |
|
||||
| PATCH /api/v1/agents/:id | PATCH | `agents().updateAgent()` |
|
||||
| DELETE /api/v1/agents/:id | DELETE | `agents().decommissionAgent()` |
|
||||
| POST /api/v1/agents/:id/credentials | POST | `credentials().generateCredential()` |
|
||||
| GET /api/v1/agents/:id/credentials | GET | `credentials().listCredentials()` |
|
||||
| POST /api/v1/agents/:id/credentials/:cid/rotate | POST | `credentials().rotateCredential()` |
|
||||
| DELETE /api/v1/agents/:id/credentials/:cid | DELETE | `credentials().revokeCredential()` |
|
||||
| POST /api/v1/token | POST | (TokenManager — automatic) |
|
||||
| POST /api/v1/token/introspect | POST | `tokens().introspectToken()` |
|
||||
| POST /api/v1/token/revoke | POST | `tokens().revokeToken()` |
|
||||
| GET /api/v1/audit | GET | `audit().queryAuditLog()` |
|
||||
| GET /api/v1/audit/:id | GET | `audit().getAuditEvent()` |
|
||||
|
||||
## License
|
||||
|
||||
Apache 2.0 — see [LICENSE](../LICENSE).
|
||||
Reference in New Issue
Block a user