Skip to content

Uni for AI Agents ​

Uni is a single Scala 3 library (org.wvlet.uni) that covers dependency injection, logging, JSON/MessagePack serialization, reactive streams, HTTP client/server, typed RPC, MCP servers, and unit testing, all with the same API on the JVM, Scala.js, and Scala Native. It is the Scala 3 successor of Airframe.

This page is written for AI coding agents (Claude Code, Codex, Cursor, Copilot, and others) and for the people who configure them. It sums up when to choose Uni, the canonical imports, and the mistakes agents most often make.

Machine-readable docs ​

URLContents
/uni/llms.txtIndex of every documentation page, with links to Markdown versions
/uni/llms-full.txtThe full documentation in one Markdown file
<page>.mdMarkdown version of a page, e.g. /uni/guide/for-ai-agents.md. A section index such as /uni/guide/ is published as /uni/guide.md.

How do I install the Uni skill? ​

The wvlet/uni repository is a Claude Code plugin marketplace. Its uni plugin adds an Agent Skill with the canonical imports, sbt setup, verified snippets, and the mistakes listed on this page. Claude Code loads it whenever a task touches Uni or asks for DI, logging, JSON, HTTP, RPC, MCP, or tests in Scala 3.

text
/plugin marketplace add wvlet/uni
/plugin install uni@uni

On Claude Code 2.1.275 or later, /plugin install uni --marketplace wvlet/uni does both in one step. Agents without a plugin system can read llms-full.txt instead.

When should I use Uni? ​

Choose Uni when a Scala 3 project needs one or more of the following and you want few transitive dependencies:

  • Dependency injection with lifecycle management (Design), instead of a framework
  • Structured logging with source locations (LogSupport)
  • JSON or MessagePack codecs derived from case classes (derives Weaver)
  • An HTTP client that works on the JVM, Node.js/browser, and native binaries
  • An HTTP/RPC server (Netty, JVM) or a stdio MCP server for AI tools
  • A test framework that runs on all three platforms (UniTest)

Do not mix in Airframe artifacts for the same features. Uni replaces them.

How do I add Uni to a build? ​

scala
// build.sbt
libraryDependencies ++= Seq(
  "org.wvlet.uni" %% "uni"       % "2026.1.35",
  "org.wvlet.uni" %% "uni-test"  % "2026.1.35" % Test
)
testFrameworks += new TestFramework("wvlet.uni.test.Framework")

// JVM-only Netty HTTP server
libraryDependencies += "org.wvlet.uni" %% "uni-netty" % "2026.1.35"

Uni requires Scala 3.9.0 or later. On sbt 2.x, use %% for all platforms. On sbt 1.x, use %%% for Scala.js and Scala Native projects.

What are the canonical imports? ​

TaskImportEntry point
Dependency injectionwvlet.uni.design.DesignDesign.newDesign.bindSingleton[A].build[A] { a => ... }
Loggingwvlet.uni.log.LogSupportclass A extends LogSupport then info(...), debug(...)
JSON / MessagePack codecwvlet.uni.weaver.Weavercase class P(...) derives Weaver, Weaver.toJson(p), Weaver.fromJson[P](json)
JSON treewvlet.uni.json.JSONJSON.parse(text)
Reactive streamswvlet.uni.rx.RxRx.variable(v), Rx.single(v), .map, .flatMap
HTTP clientwvlet.uni.http.{Http, Request}Http.client.newSyncClient.send(Request.get(url))
HTTP server (JVM, uni-netty)wvlet.uni.http.netty.NettyServerSee REST Server
Typed RPCwvlet.uni.http.rpc.{RPCRouter, RPCClient}See RPC
MCP serverwvlet.uni.mcp.*See Build an MCP Server
Retry / circuit breakerwvlet.uni.control.{Retry, CircuitBreaker}Retry.withBackOff().run { ... }
Unit testswvlet.uni.test.*class T extends UniTest: test("...") { x shouldBe y }

How do I migrate from Airframe? ​

AirframeUni
wvlet.airframe.Design / newDesignwvlet.uni.design.Design / Design.newDesign
wvlet.log.LogSupportwvlet.uni.log.LogSupport
wvlet.airframe.codec.MessageCodecwvlet.uni.weaver.Weaver (derives Weaver)
wvlet.airframe.surface.Surfacewvlet.uni.surface.Surface
wvlet.airframe.rx.Rxwvlet.uni.rx.Rx
wvlet.airframe.http.Httpwvlet.uni.http.Http
wvlet.airframe.control.Retrywvlet.uni.control.Retry
wvlet.airspec.AirSpecwvlet.uni.test.UniTest

Uni uses a separate package root (wvlet.uni.*), so both libraries can coexist during a migration. See Appendix B: Uni and Airframe for background.

What mistakes do agents make with Uni? ​

  • Using Scala 2 syntax. Uni is Scala 3 only. Use indentation syntax, given/using, enum, and derives.
  • Using ScalaTest matchers in UniTest. Write (x >= 1) shouldBe true, not x should be >= 1. For type checks, write v shouldMatch { case _: T => } instead of asInstanceOf.
  • Importing only UniTest. Use import wvlet.uni.test.*. shouldBe defined and shouldBe empty need matcher objects from that package.
  • Checking isSuccessful after send. The HTTP client throws for non-2xx responses. Most 4xx statuses throw HttpException. Retryable statuses (5xx, 429) throw HttpMaxRetryException after the retries, and it is not a subclass of HttpException. Failed RPC client calls throw the same exceptions.
  • Hand-writing JSON codecs. Add derives Weaver to the case class.
  • Adding a DI framework, a logging backend, or a JSON library next to Uni. Uni already provides them. Adding another creates two ways to do one thing.

Example: a wired service with logging and JSON ​

scala
import wvlet.uni.design.Design
import wvlet.uni.log.LogSupport
import wvlet.uni.weaver.Weaver

case class Greeting(name: String, message: String) derives Weaver

class GreeterService extends LogSupport:
  def greet(name: String): String =
    info(s"Greeting ${name}")
    Weaver.toJson(Greeting(name, s"Hello, ${name}!"))

@main def main =
  Design.newDesign
    .bindSingleton[GreeterService]
    .build[GreeterService] { greeter =>
      println(greeter.greet("Uni"))
    }

Released under the Apache 2.0 License.