Install
One bind on the JVM, on Node, and on Scala Native. MCP, OAuth, and brotli are optional
artifacts on the same version. JSON is zio-json on core. JVM is JDK 21+. heddle and
heddle-mcp publish for all three runtimes (%%%). heddle-oauth is JVM and JS.
HeddleApp is JVM-only.
libraryDependencies += "rocks.earlyeffect" %% "heddle" % "0.4.0"
libraryDependencies += "rocks.earlyeffect" %% "heddle-mcp" % "0.4.0"
import heddle.* is the facade. SSE, WebSocket, and Datastar stay in their own packages.
A server in one file
This is already a service. You can stop here. The hub appears when you promote selected routes
to Endpoint / Api. That is the next page, not a rewrite of this one.
val routes = Routes(
Method.GET / "health" -> Handler.text("ok"),
Method.GET / "users" / int("id") -> { (id: Int) =>
ZIO.succeed(Response.text(id.toString))
},
)
val app = routes @@ (Middleware.requestId() ++ Middleware.cors())
To bind a port:
object Hello extends HeddleApp:
def routes = app
HeddleApp serves routes and exits 0 on Ctrl-C under sbt 2 (JVM).
Server.serve(app).provide(Server.Config.defaults) is the same bind without the trait, and
that is what JS and Native use.
Run the example hub
This repo's example is one process with HTTP, Swagger, MCP, an embedded OpenID provider, and a directory UI:
sbt example/run # http://localhost:8080/docs /preview POST /mcp
sbt "example/run -- --mcp-stdio" # same Api, stdio JSON-RPC
Seed user ada / ada. Machine client machine / secret.
Then walk Domain is data to see how that hub is assembled.