Two adapter tiers
An adapter is what a step's use line names, either a YAML binding the runner interprets or a program speaking NDJSON, with one manifest surface
What you'll learn
You need a vendor gtme doesn't ship, or you're reading someone else's adapter and want to know what it can do before you install it.
- what a binding can and can't express, and when an integration becomes a process adapter
- how the registry installs a binding pinned and verified
- why plan treats both tiers the same, and what simulate does with each
- what the built-in floor adapters are for
Here's a vendor the gtme binary doesn't ship. Search the registry, a public index of bindings on GitHub. This spends nothing and sends nothing:
gtme adapters search hubspot
The output is the following:
ID ROLE TIER INSTALL DESCRIPTION
hubspot/contact-search source verified gtme adapters add hubspot/contact-search Source HubSpot contacts via the CRM v3 Search API, filtered…
Install it with the line in the INSTALL column:
gtme adapters add hubspot/contact-search
The output is similar to the following:
fetched github.com/gtme-run/gtme-bindings/hubspot-contact-search@bcf671b9e1df177ed24e98b58e0c132176261c78 at bcf671b9e1df
hubspot/contact-search v1 — source (person)
calls: api.hubapi.com
demands: HUBSPOT_ACCESS_TOKEN
needs: none
provides: company_name, email, first_name, hubspot.contact_id, hubspot.lifecycle_stage, last_name, title
fixtures: ok — 1 response(s) on file, 2 record(s) extracted
...
add verified the adapter before installing it. calls: is every host it contacts, demands: is its credential, and needs: and provides: are the fields it reads and writes. fixtures: says its recorded vendor response ran offline and produced records. In the search output, TIER is the registry's trust level: verified means the registry's own tests run the fixtures. The adapter's tier is KIND, in the next listing.
What you just saw
An adapter is whatever a step's use: line names, and it comes in two tiers. Here's what's installed after also copying in the gtme repository's example process adapter, adapters/mock-enrich-py:
gtme adapters
The output is the following:
ID VERSION ROLE KIND SOURCE
hubspot/contact-search 1 source binding github.com/gtme-run/gtme-bindings/hubspot-contact-search@bcf671b9e1df177ed24e98b58e0c132176261c78 (bcf671b9e1df)
mock-enrich-py 1 enrich process installed by hand
SOURCE shows the commit the registry pinned for the ID. Both live in ~/.gtme/adapters/.
A binding is a YAML file the runner (gtme itself) interprets. Here's the head of yours, trimmed:
id: hubspot/contact-search
version: 1
role: source
entity_type: person
credentials: [HUBSPOT_ACCESS_TOKEN]
...
request:
method: POST
url: "{{config.base_url}}/crm/v3/objects/contacts/search"
body:
filterGroups:
- filters:
- propertyName: "{{config.known_property}}"
operator: HAS_PROPERTY
...
extract:
records: results
fields:
email: properties.email
first_name: properties.firstname
last_name: properties.lastname
title: properties.jobtitle
company_name: properties.company
hubspot.contact_id: id
hubspot.lifecycle_stage: properties.lifecyclestage
...
request: is a template filled from the step's with: values. extract.fields maps each response path to a canonical field or a hubspot. name. Auth, pagination, errors, retries, and cost are settings in the same file, and none of them can hold a formula or an if.
A process adapter is a program, here a folder holding manifest.json and run. Here's the manifest, trimmed:
{
"id": "mock-enrich-py",
"version": 1,
"role": "enrich",
"entity_type": "person",
"needs": {
...
"provides": {
...
"mock.score": {"type": "integer", "minimum": 0, "maximum": 100},
"mock.note": {"type": "string"}
...
run is an executable in any language that reads and writes records as NDJSON, newline-delimited JSON with one object per line.
Here's the test for your vendor. A documented HTTP API you call with a key is a binding. Anything needing a conditional, several calls per record, OAuth or request signing (login schemes a fixed template can't perform), or its own computation is a process adapter.
Both tiers present the same manifest surface: id, version, role, entity type, needs, provides, the with: keys a step may set, and credentials. gtme plan reads only that surface. Neither tier touches the ledger; the runner writes what they return.
So what?
One pipeline can use both. This file sources from the binding and scores with the process adapter:
name: tiers
version: 1
source:
use: hubspot/contact-search
with:
known_property: email
limit: 2
steps:
- id: score
use: mock-enrich-py
gtme plan tiers.yaml stops here, because plan checks declared credentials (setting one):
gtme: step "source": missing credential HUBSPOT_ACCESS_TOKEN (set it in the environment or run `gtme secret set HUBSPOT_ACCESS_TOKEN`)
Simulate needs no key. It serves a binding from its fixture, and runs an uncredentialed process adapter like this one for real:
gtme run tiers.yaml --simulate
The receipt at the end is similar to the following:
run 01M3FBF44H1KBCN64FH9TMV50G — done (SIMULATED — recorded responses only; nothing sent, nothing persisted)
step adapter in out empty cached filtered failed cost avoided
source hubspot/contact-search 0 2 - 0 - - $0 -
score mock-enrich-py 2 2 - 0 - - $0 -
total: $0 (estimated) spent
The source row is HubSpot's recorded response, and the score row is the Python program scoring it.
Hand your agent the search and add lines, because add installs nothing that fails verification. If your vendor isn't listed, hand it this, which prints the binding schema and a reference binding as JSON:
gtme help --bindings
That's it. That's how adapters work, and every vendor you add is one of these two.
Why it's this way
Most vendor APIs are plain HTTP, so most adapters are data. The decision record, ADR-022, runs every binding through one HTTP engine in the runner. A binding can't run code, so a stranger's binding is safe to read and add can list its hosts. SPEC calls the binding-or-program test the graduation rule (SPEC §10a).
Vendors live in a registry outside the binary. gtme adapters update moves a pin only when you ask, and every registry entry must ship fixtures (ADR-042).
The binary ships a floor: the csv/*, http/*, sql/*, ai/*, and group/* families (the last for record groups). It carries no vendor: every vendor adapter is a registry entry you install with gtme adapters add (ADR-059). Most are bindings; instantly/add-to-campaign is a Go process adapter the registry serves prebuilt and pinned by checksum, because its preflight checks are logic a binding may not hold (ADR-063). The floor is the crudest version of any integration on purpose, so anything is wireable today (ADR-023). Inline http/* config that repeats across pipelines should become a binding. We think that's the right trade: a pipeline runs on day one and improves when a binding replaces its floor step.
The binary also ships built-ins outside the floor: the participant adapters human/* and agent/*, demo/enrich, and text/compose.
What it gave up. A process adapter is code you install by hand, so read it first, and it can't travel inside a bundle.
Where it shows up
Three guides and a start page write adapters, and four reference pages list them.
- Add a vendor and Add a vendor with a binding write bindings.
- Write a process adapter and Publish to the registry cover the second tier and the index.
- Binding manifest, Wire protocol,
gtme adapters, and the Adapter catalog are the lookup pages.