golem-add-http-endpoint-scala

star 1.6k

Exposing a Scala Golem agent over HTTP. Use when the user asks to add HTTP endpoints, mount an agent to a URL path, or expose agent methods as a REST API.

golemcloud By golemcloud schedule Updated 5/5/2026

name: golem-add-http-endpoint-scala description: "Exposing a Scala Golem agent over HTTP. Use when the user asks to add HTTP endpoints, mount an agent to a URL path, or expose agent methods as a REST API."

Adding HTTP Endpoints to a Scala Golem Agent

Overview

Golem agents can be exposed over HTTP using code-first route definitions. This involves:

  1. Adding a mount parameter to @agentDefinition
  2. Annotating methods with @endpoint
  3. Adding an httpApi deployment section to golem.yaml (load the golem-configure-api-domain skill)

Related Skills

Skill When to Load
golem-http-params-scala Path/query/header variable mapping, body mapping, supported types, response mapping
golem-make-http-request-scala Making outgoing HTTP requests from agent code, especially when calling other Golem agent endpoints (required for correct JSON body formatting)
golem-add-http-auth-scala Enabling authentication
golem-add-cors-scala Configuring CORS allowed origins
golem-configure-api-domain Setting up httpApi in golem.yaml, security schemes, domain deployments

Steps

  1. Add mount = "/path/{param}" to @agentDefinition(...)
  2. Add @endpoint(method = "GET", path = "/...") to trait methods
  3. Add httpApi deployment to golem.yaml (see golem-configure-api-domain skill)
  4. Build and deploy

Mount Path

The mount parameter on @agentDefinition defines the base HTTP path. Path variables in {braces} map to constructor parameters defined in the class Id(...):

import golem.runtime.annotations.{agentDefinition, endpoint}
import golem.BaseAgent
import scala.concurrent.Future

@agentDefinition(mount = "/api/tasks/{name}")
trait TaskAgent extends BaseAgent {
  class Id(val name: String)

  // methods...
}

Constructor Parameter Naming in Scala

The path variable names must exactly match the class Id parameter names — do not use kebab-case in path variables. Use the same camelCase (or single-word) identifier that appears in the class Id:

  • Single parameter: class Id(val name: String) → use {name} in path
  • Multiple parameters: class Id(val arg0: String, val arg1: Int) → use {arg0}, {arg1} in path
  • Custom Id class with @id: use the parameter names from the annotated class
// ✅ Correct — path variable matches parameter name exactly
@agentDefinition(mount = "/api/tasks/{taskName}")
trait TaskAgent extends BaseAgent {
  class Id(val taskName: String)
}

// ❌ Wrong — {task-name} does not match parameter name taskName
@agentDefinition(mount = "/api/tasks/{task-name}")
trait TaskAgent extends BaseAgent {
  class Id(val taskName: String)
}
// Named parameters with @id annotation
import golem.runtime.annotations.id

@agentDefinition(mount = "/api/catalog/{region}/{catalog}")
trait CatalogAgent extends BaseAgent {
  @id
  class CatalogParams(val region: String, val catalog: String)
}

Rules:

  • Path must start with /
  • Every constructor parameter must appear as a {variable} in the mount path
  • Every {variable} must exactly match a constructor parameter name (same casing)
  • Catch-all {*rest} variables are not allowed in mount paths

Endpoint Annotation

The @endpoint annotation marks a method as an HTTP endpoint. Specify the HTTP method and path:

@endpoint(method = "GET", path = "/items")
def listItems(): Future[List[Item]]

@endpoint(method = "POST", path = "/items")
def createItem(name: String, count: Int): Future[Item]

@endpoint(method = "PUT", path = "/items/{id}")
def updateItem(id: String, name: String): Future[Item]

@endpoint(method = "DELETE", path = "/items/{id}")
def deleteItem(id: String): Future[Unit]

Endpoint paths are relative to the mount path. Supported HTTP methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, CONNECT, TRACE, or any custom string.

For details on how path variables, query parameters, headers, and request bodies map to method parameters, load the golem-http-params-scala skill.

Phantom Agents

Set phantomAgent = true to create a new agent instance for each HTTP request, enabling fully parallel processing:

@agentDefinition(
  mount = "/webhook/{agent-type}/{value}",
  phantomAgent = true
)
trait WebhookHandler extends BaseAgent {
  class Id(val value: String)
  // Each HTTP request gets its own agent instance
}

Custom Types

All types used in endpoint parameters and return values must have a zio.blocks.schema.Schema instance:

import zio.blocks.schema.Schema

final case class Task(id: String, title: String, done: Boolean) derives Schema

For collections, use List[T] instead of Array[T]. Array does not have automatic Schema derivation support.

Return Type to HTTP Response Mapping

Golem maps method return types to HTTP status codes and response bodies according to the table below. This mapping is currently not configurable.

Scala Either[E, T] is mapped to the WIT result<T, E> type, with Right treated as Ok and Left as Err.

Return Type HTTP Status Response Body
Future[Unit] 204 No Content empty
Future[T] 200 OK JSON-serialized T
Future[Option[T]] 200 OK if Some, 404 Not Found if None JSON T or empty
Future[Either[E, T]] 200 OK if Right, 500 Internal Server Error if Left JSON T or JSON E
Future[Either[E, Unit]] 204 No Content if Right, 500 if Left empty or JSON E
Future[Either[Unit, T]] 200 OK if Right, 500 if Left JSON T or empty
Future[UnstructuredBinary] 200 OK Raw binary with Content-Type

Complete Example

import golem.runtime.annotations.{agentDefinition, agentImplementation, endpoint, header}
import golem.BaseAgent
import zio.blocks.schema.Schema
import scala.concurrent.Future

final case class Task(id: String, title: String, done: Boolean) derives Schema

@agentDefinition(mount = "/task-agents/{name}")
trait TaskAgent extends BaseAgent {
  class Id(val name: String)

  @endpoint(method = "GET", path = "/tasks")
  def getTasks(): Future[List[Task]]

  @endpoint(method = "POST", path = "/tasks")
  def createTask(title: String): Future[Task]

  @endpoint(method = "GET", path = "/tasks/{id}")
  def getTask(id: String): Future[Option[Task]]

  @endpoint(method = "POST", path = "/report")
  def submitReport(@header("X-Tenant") tenantId: String, data: String): Future[String]
}
import golem.runtime.annotations.agentImplementation
import scala.concurrent.Future

@agentImplementation()
final class TaskAgentImpl(private val name: String) extends TaskAgent {
  private var tasks: List[Task] = Nil

  override def getTasks(): Future[List[Task]] =
    Future.successful(tasks)

  override def createTask(title: String): Future[Task] = Future.successful {
    val task = Task(id = (tasks.length + 1).toString, title = title, done = false)
    tasks = tasks :+ task
    task
  }

  override def getTask(id: String): Future[Option[Task]] =
    Future.successful(tasks.find(_.id == id))

  override def submitReport(tenantId: String, data: String): Future[String] =
    Future.successful(s"Report from $tenantId: $data")
}
# golem.yaml (add to existing file)
httpApi:
  deployments:
    local:
    - domain: my-app.localhost:9006
      agents:
        TaskAgent: {}

Key Constraints

  • A mount path is required on @agentDefinition before any @endpoint annotations can be used
  • All constructor parameters (from class Id) must be provided via mount path variables
  • Path/query variable names must exactly match method parameter names
  • Header parameters use @header("Header-Name") annotation on individual method parameters
  • Catch-all path variables {*name} can only appear as the last path segment
  • The endpoint path must start with /
  • Custom types require a zio.blocks.schema.Schema instance (use derives Schema in Scala 3)
  • The scalacOptions += "-experimental" flag is required for macro annotations
Install via CLI
npx skills add https://github.com/golemcloud/golem --skill golem-add-http-endpoint-scala
Repository Details
star Stars 1,597
call_split Forks 201
navigation Branch main
article Path SKILL.md
More from Creator