Idiomatic Kotlin wrapper around com.anthropic:anthropic-java.
val client = AnthropicOkHttpClient.fromEnv()
val response = client.createMessage(
model = Model.CLAUDE_OPUS_4_7,
maxTokens = 1024,
messages = listOf(Message.user("Hello, Claude!")),
)
println(response.text)The official Java SDK is full-featured but verbose from Kotlin: builder chains, Optional<T>, CompletableFuture<T>, and Iterator show up in every public signature. This module wraps it with five design invariants:
| Invariant | Java SDK | anthropic-kotlin |
|---|---|---|
| Async surface | CompletableFuture<T> |
suspend fun T |
| Streams | StreamResponse<T> (closeable iterator) |
Flow<T> |
| Polymorphic types | Optional<X> + isX() + asX() |
sealed class + when-exhaustive |
| Optional fields | Optional<T> |
Kotlin T? |
| Construction | Foo.builder().bar(...).build() |
named + default parameters |
// Java SDK
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_4_7)
.maxTokens(1024)
.addUserMessage("hi")
.build();
Message m = client.messages().create(params);
String text = m.content().get(0).asText().text();
// anthropic-kotlin
val response = client.createMessage(
model = Model.CLAUDE_OPUS_4_7,
maxTokens = 1024,
messages = listOf(Message.user("hi")),
)
val text = response.textThe artifact version tracks the underlying com.anthropic:anthropic-java release.
dependencies {
implementation("io.github.rimdoo:anthropic-kotlin:2.30.0")
}mavenCentral() is in your build by default, so no repository changes needed.
export ANTHROPIC_API_KEY=sk-ant-...val client = AnthropicOkHttpClient.fromEnv()
val response = client.createMessage(
model = Model.CLAUDE_OPUS_4_7,
maxTokens = 1024,
messages = listOf(Message.user("What is Kotlin?")),
)
println(response.text)client.createMessage(
model = Model.CLAUDE_OPUS_4_7,
maxTokens = 1024,
messages = listOf(
Message.user("What's Kotlin?"),
Message.assistant("A JVM language with null-safety and coroutines."),
Message.user("How is it different from Java?"),
),
)client.streamMessage(
model = Model.CLAUDE_OPUS_4_7,
maxTokens = 1024,
messages = listOf(Message.user("Write a haiku.")),
).collect { event ->
when (event) {
is MessageStreamEvent.ContentBlockDelta -> print(event.delta.text)
is MessageStreamEvent.MessageStop -> println()
else -> {}
}
}client.createMessage(
model = Model.CLAUDE_OPUS_4_7,
maxTokens = 1024,
system = systemPrompt {
text("Very long static context...", cache = CacheControl.Ephemeral)
text("Today: ${LocalDate.now()}")
},
messages = listOf(Message.user("Question?")),
)val weather = Tool(
name = "get_weather",
description = "Look up current weather",
inputSchema = jsonSchema {
property("city", type = "string", required = true)
},
)
val response = client.createMessage(
model = Model.CLAUDE_OPUS_4_7,
maxTokens = 1024,
tools = listOf(weather),
toolChoice = ToolChoice.Auto,
messages = listOf(Message.user("Weather in Seoul?")),
)
val toolUse = response.content.filterIsInstance<ContentBlock.ToolUse>().firstOrNull()
if (toolUse != null) {
// Run the tool yourself, then send the result back:
val followup = client.createMessage(
model = Model.CLAUDE_OPUS_4_7,
maxTokens = 1024,
tools = listOf(weather),
messages = listOf(
Message.user("Weather in Seoul?"),
Message.assistant(content = response.content),
Message.toolResult(toolUseId = toolUse.id, content = "Sunny, 21°C"),
),
)
println(followup.text)
}client.createMessage(
model = Model.CLAUDE_OPUS_4_7,
maxTokens = 1024,
messages = listOf(
Message.user(
ContentBlock.Image.url("https://example.com/cat.jpg"),
ContentBlock.Text("What is this?"),
),
),
)val result = runCatching {
client.createMessage(/* ... */)
}
result.onFailure { e ->
when (e) {
is AnthropicException.RateLimit -> println("retry after ${e.retryAfter}")
is AnthropicException.Auth -> error("API key invalid")
is AnthropicException.BadRequest -> println(e.message)
else -> throw e
}
}val tokens = client.countMessageTokens(
model = Model.CLAUDE_OPUS_4_7,
messages = listOf(Message.user("Hello, world!")),
)
println(tokens.inputTokens)Full coverage of the beta Managed Agents API (Agent / Environment / Session / Vault / MemoryStore + every sub-service). Create an autonomous agent that can run tools inside a managed container, send it user events, and stream events back:
val agent = client.createAgent(
name = "Coding Assistant",
model = AgentModel.CLAUDE_OPUS_4_7,
system = "You write clean, well-documented code.",
tools = listOf(AgentTool.Toolset20260401), // bash + file ops + web search
)
val environment = client.createEnvironment(name = "quickstart-env")
val session = client.createSession(
agentId = agent.id,
environmentId = environment.id,
title = "Fibonacci task",
)
client.sendSessionEvents(
sessionId = session.id,
events = listOf(
UserEvent.Message(
text = "Generate the first 20 Fibonacci numbers and save to fibonacci.txt",
),
),
)
client.streamSessionEvents(session.id)
.takeWhile { event ->
when (event) {
is SessionStreamEvent.AgentMessage -> { print(event.text); true }
is SessionStreamEvent.AgentToolUse -> { println("\n[Using ${event.name}]"); true }
is SessionStreamEvent.SessionStatusIdle -> false
else -> true
}
}
.collect()See sample/src/main/kotlin/.../ManagedAgentsQuickstart.kt for the runnable version.
| Area | Functions |
|---|---|
| Messages | createMessage, streamMessage, countMessageTokens |
| Batches | createBatch, retrieveBatch, cancelBatch, deleteBatch, listBatches, batchResults |
| Models | listModels, retrieveModel |
| Completions (legacy) | createCompletion, streamCompletion |
| Beta Files | uploadFile, retrieveFileMetadata, listFiles, deleteFile |
| Managed Agents | createAgent + retrieve/update/archive/list, listAgentVersions |
| Managed Environments | full CRUD |
| Managed Sessions | full CRUD + events (list / send / stream) + threads (list / retrieve / archive / events / stream) + resources (list / retrieve / addFile / update / delete) |
| Managed Vaults | full CRUD + credentials (create with CredentialAuth.{McpOAuth, StaticBearer} / list / retrieve / archive / delete / mcpOAuthValidate) |
| Managed MemoryStores | full CRUD + memories (full CRUD) + memoryVersions (list / retrieve / redact) |
68 public extension functions on AnthropicClient, every one smoke-tested. See :sdk:test.
MessageStreamEvent, ContentBlock, ContentDelta, Tool, ToolChoice, CacheControl, ThinkingConfig, UserEvent, OutcomeRubric, CredentialAuth, NetworkPolicy, AgentTool, BatchResultStatus, SessionStreamEvent, AnthropicException — all when-exhaustive.
export ANTHROPIC_API_KEY=sk-ant-...
./gradlew :sample:run # Quickstart (Messages API)
./gradlew :sample:runStreaming # Flow<MessageStreamEvent>
./gradlew :sample:runToolUse # tool-use roundtrip
./gradlew :sample:runManagedAgents # full Managed Agents flowSee sample/README.md.
./gradlew :sdk:build # compile + assemble
./gradlew :sdk:test # 118 unit tests (~3s, no network)
./gradlew :sdk:publishToMavenLocal # ~/.m2/repository/io/github/rimdoo/...:sdk:test runs offline — every test stubs the Java SDK via mockk so the full Builder chain executes (catching missing-required-field bugs at unit-test time, not at runtime).
| Class | Purpose | Count |
|---|---|---|
MappersTest |
Every *.toRaw() / .toParam() / Message factory / Tool lazy build |
29 |
DslTest |
jsonSchema { } / systemPrompt { } builders |
7 |
ExceptionsTest |
AnthropicException subtype fields |
7 |
ClientApiSmokeTest |
Every public AnthropicClient.* extension via mockk-stubbed client |
75 |
anthropic-kotlin mirrors the underlying com.anthropic:anthropic-java version (currently 2.30.0). A new wrapper release follows each Java SDK release; only the gradle/libs.versions.toml anthropic entry needs to change, and the Maven version of the artifact tracks it via libs.versions.anthropic.get().
Apache 2.0 — same as the underlying Java SDK.
Built on top of the official Anthropic Java SDK. This project is not affiliated with Anthropic.