Skip to content

REST connector

The REST connector turns an HTTP API into a fixed set of tools. The agent can call only the tools you declare, on the methods and path prefixes you allow, and never sees the credential.

examples/connectors/orders.yaml
# The REST connector over examples/mock-orders (S16). Create it in the dev org:
# make mock-orders # in another terminal
# deedglass connector create --config examples/connectors/orders.yaml
# deedglass connector credentials set orders --from-env MOCK_ORDERS_TOKEN
# deedglass connector test orders
# Never register it in the Acme demo org: that org's "orders" is the MCP-wrapped Postgres server.
type: rest
name: orders
description: The mock orders API (examples/mock-orders).
config:
base_url: http://127.0.0.1:8090 # dev only (DEEDGLASS_ALLOW_PRIVATE_NETWORKS=1); prod needs https
auth: { kind: bearer } # bearer | header | basic | query | none
allowed_methods: [GET, PATCH]
allowed_path_prefixes: [/v1/orders]
timeout_ms: 10000 # max 60000
max_response_bytes: 4194304 # max 16 MiB
redirects: same_host # none | same_host (writes never follow one)
send_action_id: true
test: { method: GET, path: /v1/health }
tools:
- name: get
description: Read one order.
method: GET
path: /v1/orders/{id}
args: { id: { in: path, type: string, pattern: "^[0-9]{1,12}$", required: true } }
- name: update_status
description: Set an order's status.
method: PATCH
path: /v1/orders/{id}
idempotency_header: Idempotency-Key # value = the action id
args:
id: { in: path, type: string, pattern: "^[0-9]{1,12}$", required: true }
status: { in: body, type: string, enum: [pending, shipped, cancelled], required: true }
readback: # S19: after a write, read the order back (L1)
tool: get # a read tool of this connector (checked when saved)
args: { id: "{{args.id}}" } # whole-value templates only: {{args.<path>}} or {{result.body.<path>}}
expect: { "body.status": "{{args.status}}" } # exact, per-field comparison
consistency: strong # strong (1 attempt) | eventual (attempts × interval_ms)
attempts: 1 # 1..5; eventual defaults to 3
interval_ms: 300 # 100..2000
on_not_found: gap # gap | violation
postcondition: 'observation.status == 200 && observation.body.id == int(args.id)' # optional CEL, for checks expect can't express
  • Only allowed_methods on paths under allowed_path_prefixes. Path arguments are validated against their patterns and can’t add segments.
  • Production needs https. Every request goes through the egress door, which refuses private, link-local and metadata addresses unless a dev override is on.
  • Response bodies are capped (max_response_bytes, 16 MiB at most). Writes never follow a redirect.
  • With idempotency_header, each write sends the action id, so a retried action can’t apply twice.
  • A readback reads the record after a write and compares the fields you name: L1 Confirmed.

The full key list and every check are in the connectors overview.

  • It doesn’t discover an API. Every tool is declared.
  • L1 compares only the fields you name in expect.
  • An API that answers consistently but falsely can’t be caught by reading it back.