Skip to content

Call a service with an adapter

This guide shows how to make an auto step do real work: read a file, call an API, post a message. At the end you have a step that reads a file through Realm’s built-in file adapter, a later step that uses what it read, and you know where the credentials go for a service that needs them.

You need a workflow you can run. Write your first workflow makes one.

Three things connect a step to the outside world:

  • An adapter is the code that talks to one kind of service. Realm ships several, such as filesystem and github.
  • A service is a name you give to an adapter inside one workflow, under services.
  • An auto step names the service with uses_service, picks an operation, and builds the operation’s input with input_map.

Add a services block to the workflow, and give the file adapter a name:

services:
files:
adapter: filesystem
trust: engine_delivered

Add a step that reads a file. The path comes from the run’s parameters:

steps:
read_note:
description: Read the note from disk.
execution: auto
uses_service: files
operation: read
input_map:
path: run.params.path

input_map maps each input of the operation to a value. run.params.path means the path parameter the run was started with.

The output of a step is available to later steps under context.resources.<step>. The read operation returns the file’s text in a field named content. Put it in the next step’s prompt:

steps:
# read_note: as above
summarise:
description: Summarise the note in one sentence.
execution: agent
depends_on: [read_note]
prompt: |
Summarise this note in one sentence.
{{ context.resources.read_note.content }}
input_schema:
type: object
additionalProperties: false
required: [summary]
properties:
summary:
type: string

The complete file:

id: notes
name: Summarise a note
version: 1
params_schema:
type: object
additionalProperties: false
required: [path]
properties:
path:
type: string
services:
files:
adapter: filesystem
trust: engine_delivered
steps:
read_note:
description: Read the note from disk.
execution: auto
uses_service: files
operation: read
input_map:
path: run.params.path
summarise:
description: Summarise the note in one sentence.
execution: agent
depends_on: [read_note]
prompt: |
Summarise this note in one sentence.
{{ context.resources.read_note.content }}
input_schema:
type: object
additionalProperties: false
required: [summary]
properties:
summary:
type: string

Register the workflow, then run it by hand with the full path of a text file:

Terminal window
realm workflow register ./
realm workflow run ./ --params '{"path":"/home/you/notes/note.txt"}'

At the first prompt, press Enter. Then type a summary:

→ [auto] read_note: Read the note from disk.
Mock output (service: files) — JSON (Enter for {}):
✓ → running | hash: 9016ad33... | 2ms
→ [agent] summarise: Summarise the note in one sentence.
Agent output JSON (Enter for {}): {"summary":"Realm 0.45 adds a replay page."}
✓ → completed | hash: 740968a5... | 0ms
Run complete. Phase: completed

The first prompt is labelled Mock output, but the service is really called. Whatever you type there is recorded as the step’s input and does not replace the service’s output, so pressing Enter is the right answer.

Check that the file was read. Print the record:

Terminal window
realm run inspect <run-id>

It prints, for the first step (the paths are shortened here):

1. read_note success 2ms hash: 9016ad33
Taken by: ops@server-1 (from the OS user, via run)
Input: {}
Resolved: {"path":"/home/you/notes/note.txt"}
Output: {"content":"Realm 0.45 ships a replay page.\nIt records two real runs.\n","path":"/home/you/notes/…

Notice Resolved, the input the adapter received after input_map was applied, and Output, the file’s text.

Run it again with a path that does not start at the root of the disk:

Terminal window
realm workflow run ./ --params '{"path":"note.txt"}'

It prints:

→ [auto] read_note: Read the note from disk.
Mock output (service: files) — JSON (Enter for {}):
✗ error: path must be absolute
Run complete. Phase: failed
Resume: realm run resume 4c8e2a17-93bd-4f05-b6e1-7a0d9c3f5e28 --from read_note

The file adapter accepts only full paths. The step failed, the run ended as failed, and the step that depended on it was skipped. The last line is the command that makes the failed step runnable again, once its cause is fixed (added after version 0.46.0). The record says so:

Phase: failed
Sealed by: step_failure
Cause: Step 'read_note' failed: path must be absolute
Failed: read_note
Skipped: summarise
summarise: trigger_rule_unsatisfiable: all_success, dep read_note failed

To keep a run alive when a service fails, give the workflow a step that handles the failure. See Handle failure.

The file adapter needs no setup. Most others need a token. Credentials never go in workflow.yaml. They go in two files beside it:

realm.yaml says how to build the adapter, and names the secret it needs:

version: 1
adapters:
github:
use: github
config: { auth: { token: '${secret:GITHUB_TOKEN}' } }

.env holds the secret’s value:

GITHUB_TOKEN=your-token-here

The workflow then names the adapter as before:

services:
github:
adapter: github
trust: engine_delivered

If realm.yaml is missing, a step that uses the adapter is blocked, and the run waits. Over MCP, start_run replies ok and names the step in warnings (Step '<step>' is blocked: …), and execute_step on the step is refused with:

Adapter 'github' for service 'github' is not registered. Declare this adapter under 'adapters:' in realm.yaml …

If the secret is missing, nothing starts, and Realm names it:

Deployment manifest secrets: 1 unresolved secret reference(s):
adapters.github.config.auth.token → ${secret:GITHUB_TOKEN}