Dibbla Docs Get started Guides Workflows Changelog
Open Console
Beyond apps

Custom functions

Writing the functions a graph can call — the SDK's two builders, the gRPC transport underneath, and the import rule that stops external modules dead.

The built-in functions cover agents, templating, HTTP calls and data access. Everything else is yours to write — a function is just a typed handler on a worker you run.

The builder you will use

type In struct {
  Region string `json:"region"`
  Limit  int    `json:"limit"`
}
type Out struct {
  Summary string `json:"summary"`
}

fn := sdk.NewSimpleFunction[In, Out](
  "summarise_incidents",          // name
  "1.0.0",                        // version
  "Summarise open incidents for a region.",
).WithHandler(func(in In) (Out, error) {
  // your code
  return Out{Summary: "…"}, nil
})

server.RegisterFunction(fn)

The input and output structs are the contract: their JSON field names become the node’s input and output ports, and their Go types become the types the validator checks. This is why dibbla functions get can print a schema you never wrote by hand.

Use SimpleFunction — the advanced builder is not reachable from your module

The advanced Function[In, Out] handler signature exposes types from the SDK’s internal/ packages, and Go’s internal/ rule forbids importing those from any module other than sdk-go itself. If you find a tutorial using it, it was written from inside the SDK repository. NewSimpleFunction is the external surface.

The transport

Workers talk to the platform over gRPC: connect, announce what you provide, then stay connected and serve dispatches. You never write gRPC yourself — the SDK does it — but two of its properties leak into your life:

  • A function exists only while its worker is connected. Stop the process and its nodes stop resolving; the workflow does not fail at author time, it fails at run time with an unknown-function error.
  • The listener is cluster-internal. Reaching it from a laptop means a port forward; from a deployed app it is direct.
$SERVER_NAME=my-worker
$GRPC_SERVER_ADDRESS=127.0.0.1:50051   # never rely on the default
$GRPC_USE_TLS=false                    # true against a real endpoint
$SERVER_API_TOKEN=…
The default address is production

GRPC_SERVER_ADDRESS defaults to grpc.dibbla.com:443. A worker started without it — in a test, in CI, on your laptop — registers against production and begins serving real dispatches. Set it explicitly everywhere.

Making it callable

Once registered, the function appears in the catalogue and can be wired like any built-in:

The function catalogue in the node builder, searchable by name

In YAML it is addressed by the (server, function) pair:

  - id: summarise_incidents
    type: function
    function: summarise_incidents
    server: my-worker
    inputs:
      region: eu-north
      limit: 25
    outputs: [summary]

Functions as agent tools

A function can also be given to an agent as a tool, which lets the model decide when to call it. One rule prevents the mistake everyone makes once:

One node, one role

A node may be an agent’s tool or a data input to that agent — never both. Wiring both closes a cycle (the implicit tool-connection edge plus your data edge) and the run is rejected with CYCLE_DETECTED before it starts. To inject data into an agent’s context, template it into the system message instead, and keep that node out of the tools: list.

The full SDK surface — server options, job handlers, the logger API, OAuth on behalf of a user — is in the Go SDK reference.