earl

Template Schema Reference

Complete field-by-field reference for earl template HCL files

Templates are version = 1 HCL files. Each file declares a provider namespace and one or more command blocks. This page documents every field, type, and default value.

Root Fields

FieldTypeRequiredDefaultDescription
versionintegerYes--Must be 1.
providerstringYes--Namespace for commands (e.g., github). Must not be empty.
categoriesstring[]No[]Provider-level categories applied to all commands in the file.
command "<name>"blockYes--One or more named command blocks. At least one is required.

Commands are identified by provider.command (e.g., github.search_issues).

command

command "search_issues" {
  title       = "Search Issues"
  summary     = "Search GitHub issues using a query string"
  description = "Detailed markdown description..."
  categories  = ["search"]

  annotations { ... }
  param "query" { ... }
  operation { ... }
  result { ... }
}
FieldTypeRequiredDefaultDescription
titlestringYes--Human-readable title. Must not be empty.
summarystringYes--One-line summary. Must not be empty.
descriptionstringYes--Detailed markdown description. Must not be empty.
categoriesstring[]No[]Command-level categories (merged with provider categories).
annotationsblockYes--Mode and secret declarations.
paramblock(s)No[]Repeatable typed input parameters.
operationblockYes--Protocol-specific execution definition.
resultblockYes--Output decoding, extraction, and rendering.

annotations

annotations {
  mode    = "read"
  secrets = ["github.token"]
}
FieldTypeRequiredDefaultDescription
mode"read" | "write"Yes--Safety mode. Write-mode commands require user confirmation.
secretsstring[]No[]Secret keys this command is allowed to access. Auth blocks that reference a secret must declare it here.

param

Parameters define typed inputs. Use the block syntax param "<name>" { ... } for each parameter.

param "query" {
  type        = "string"
  required    = true
  description = "GitHub search query"
}

param "limit" {
  type    = "integer"
  default = 10
}
FieldTypeRequiredDefaultDescription
namestringYes--Parameter name (inferred from the block label). Must be unique within a command.
typestringYes--One of: string, integer, number, boolean, null, array, object.
requiredbooleanNofalseWhether the argument must be provided.
defaultanyNo--Default value used when the argument is not provided.
descriptionstringNo--Human-readable description shown in help and MCP tool schemas.

Parameter Type Details

TypeCLI InputDescription
string--name valuePassed as-is.
integer--name 42Parsed as a signed 64-bit integer.
number--name 3.14Parsed as a 64-bit float.
boolean--name or --name trueBare --flag is treated as true.
null--name nullOnly the literal null is accepted.
array--name '[1,2,3]'Parsed as a JSON array.
object--name '{"k":"v"}'Parsed as a JSON object.

operation

The operation block defines what earl executes. The protocol field selects the operation shape.

ProtocolDescriptionFeature Flag
httpStandard HTTP requesthttp (default)
graphqlGraphQL over HTTP POSTgraphql (default)
grpcgRPC over HTTP/2grpc (default)
bashSandboxed Bash script executionbash (default)
sqlParameterized SQL query executionsql (default)

HTTP Operation

operation {
  protocol = "http"
  method   = "GET"
  url      = "https://api.example.com/items"
  path     = "/{{ args.item_id }}"
  query    = { q = "{{ args.query }}" }
  headers  = { Accept = "application/json" }
  cookies  = { session = "{{ args.sid }}" }

  auth { ... }
  body { ... }
  transport { ... }
}
FieldTypeRequiredDefaultDescription
protocolstringYes--Must be "http".
methodstringYes--HTTP method (GET, POST, PUT, DELETE, etc.). Must not be empty.
urlstringYes--Base URL for the request. Must not be empty. Supports template expressions.
pathstringNo--Path appended to url. Supports template expressions.
querymap<string, any>No--Query string parameters. Keys and values support template expressions.
headersmap<string, any>No--HTTP headers. Keys and values support template expressions.
cookiesmap<string, any>No--Cookies sent with the request. Keys and values support template expressions.
authblockNo--Authentication configuration. See auth.
bodyblockNo--Request body configuration. See body.
transportblockNo--Transport options (timeouts, retries, TLS). See transport.

GraphQL Operation

operation {
  protocol = "graphql"
  url      = "https://api.example.com/graphql"
  headers  = { Authorization = "Bearer {{ secrets.myapi.token }}" }

  graphql {
    query          = "query User($id: ID!) { user(id: $id) { login email } }"
    operation_name = "User"
    variables = {
      id = "{{ args.user_id }}"
    }
  }

  auth { ... }
  transport { ... }
}
FieldTypeRequiredDefaultDescription
protocolstringYes--Must be "graphql".
methodstringNo"POST"Must be POST when provided.
urlstringYes--GraphQL endpoint URL. Must not be empty.
pathstringNo--Path appended to url.
querymap<string, any>No--URL query parameters (not the GraphQL query).
headersmap<string, any>No--HTTP headers.
cookiesmap<string, any>No--Cookies.
authblockNo--Authentication. See auth.
graphqlblockYes--GraphQL query definition.
transportblockNo--Transport options. See transport.

graphql block

FieldTypeRequiredDefaultDescription
querystringYes--GraphQL query or mutation string. Must not be empty.
operation_namestringNo--GraphQL operation name (for documents with multiple operations).
variablesanyNo--Variables passed to the GraphQL operation. Supports template expressions.

gRPC Operation

operation {
  protocol = "grpc"
  url      = "http://127.0.0.1:50051"
  headers  = { x-request-id = "{{ args.request_id }}" }

  auth { ... }

  grpc {
    service             = "grpc.health.v1.Health"
    method              = "Check"
    body                = { service = "" }
    descriptor_set_file = "./descriptors/health.fds.bin"
  }

  transport { ... }
}
FieldTypeRequiredDefaultDescription
protocolstringYes--Must be "grpc".
urlstringYes--gRPC server URL. Must not be empty.
headersmap<string, any>No--gRPC metadata headers.
authblockNo--Authentication. For gRPC, api_key.location must be header. See auth.
grpcblockYes--gRPC call definition.
transportblockNo--Transport options. proxy_profile and tls.min_version are not supported for gRPC.

grpc block

FieldTypeRequiredDefaultDescription
servicestringYes--Fully qualified gRPC service name (e.g., grpc.health.v1.Health). Must not be empty.
methodstringYes--RPC method name (e.g., Check). Must not be empty.
bodyobject or arrayNo--Request message. An object for unary/server-streaming calls, or an array of objects for client-streaming.
descriptor_set_filestringNo--Path to a protobuf file descriptor set (.fds.bin). If omitted, earl uses server reflection. Must not be empty when provided.

Bash Operation

Requires the bash feature flag. Bash scripts run in a sandboxed environment. The global sandbox configuration in config.toml can further restrict execution.

operation {
  protocol = "bash"

  bash {
    script = "du -sh {{ args.path }}"
    env = {
      MY_VAR = "{{ args.value }}"
    }
    cwd = "/tmp"
    sandbox {
      network        = false
      writable_paths = ["output/"]
      max_time_ms    = 5000
      max_output_bytes = 1048576
    }
  }

  transport { ... }
}
FieldTypeRequiredDefaultDescription
protocolstringYes--Must be "bash".
bashblockYes--Bash execution definition.
transportblockNo--Transport options (only timeout_ms is typically relevant).

bash block

FieldTypeRequiredDefaultDescription
scriptstringYes--Bash script to execute. Supports template expressions. Must not be empty.
envmap<string, any>No--Environment variables passed to the script. Values support template expressions.
cwdstringNo--Working directory for the script.
sandboxblockNo--Sandbox restrictions.

bash.sandbox block

FieldTypeRequiredDefaultDescription
networkbooleanNo--Allow network access from the script.
writable_pathsstring[]No--Relative paths the script may write to. Must not contain absolute paths or .. segments.
max_time_msu64No--Maximum execution time in milliseconds.
max_output_bytesu64No--Maximum combined stdout+stderr size in bytes.

SQL Operation

Requires the sql feature flag. SQL queries use parameterized placeholders (?) instead of template expressions in the query string. Jinja2 expressions ({{ }}) are not allowed in sql.query.

operation {
  protocol = "sql"

  sql {
    connection_secret = "analytics.database_url"
    query             = "SELECT id, customer, total FROM orders ORDER BY created_at DESC LIMIT ?"
    params            = ["{{ args.limit }}"]
    sandbox {
      read_only  = true
      max_rows   = 100
      max_time_ms = 5000
    }
  }

  transport { ... }
}
FieldTypeRequiredDefaultDescription
protocolstringYes--Must be "sql".
sqlblockYes--SQL query definition.
transportblockNo--Transport options.

sql block

FieldTypeRequiredDefaultDescription
connection_secretstringYes--Secret key containing the database connection URL. Must not be empty. Must be declared in annotations.secrets.
querystringYes--SQL query to execute. Use ? for parameterized values. Must not be empty. Must not contain {{ or }}.
paramsany[]No--Positional parameters bound to ? placeholders in the query. Values support template expressions. Wrap Jinja expressions in quotes (e.g., ["{{ args.limit }}"]) so the HCL parser sees valid string tokens. Pure expressions like "{{ args.limit }}" are automatically parsed as JSON, so integers and other types are preserved.
sandboxblockNo--Sandbox restrictions.

sql.sandbox block

FieldTypeRequiredDefaultDescription
read_onlybooleanNo--Restrict to read-only queries. The sandbox.sql_force_read_only setting in config.toml can override this to true.
max_rowsu64No--Maximum number of rows to return. The sandbox.sql_max_rows setting in config.toml can cap this further.
max_time_msu64No--Maximum query execution time in milliseconds.

auth

The auth block configures authentication for HTTP, GraphQL, and gRPC operations. The kind field selects the authentication strategy. All secrets referenced in auth must be declared in annotations.secrets.

Sends an Authorization: Bearer <token> header.

auth {
  kind   = "bearer"
  secret = "github.token"
}
FieldTypeRequiredDescription
kindstringYesMust be "bearer".
secretstringYesSecret key containing the bearer token.

Sends an API key as a header, query parameter, or cookie.

auth {
  kind     = "api_key"
  location = "header"
  name     = "X-API-Key"
  secret   = "service.api_key"
}
FieldTypeRequiredDescription
kindstringYesMust be "api_key".
location"header" | "query" | "cookie"YesWhere to send the API key. For gRPC, must be "header".
namestringYesHeader name, query parameter name, or cookie name.
secretstringYesSecret key containing the API key value.

Sends an Authorization: Basic <base64(username:password)> header.

auth {
  kind            = "basic"
  username        = "{{ args.username }}"
  password_secret = "service.password"
}
FieldTypeRequiredDescription
kindstringYesMust be "basic".
usernamestringYesUsername. Supports template expressions.
password_secretstringYesSecret key containing the password.

Uses an OAuth2 profile configured in config.toml to obtain an access token. The token is sent as a Bearer header.

auth {
  kind    = "oauth2_profile"
  profile = "my_provider"
}
FieldTypeRequiredDescription
kindstringYesMust be "oauth2_profile".
profilestringYesName of the OAuth2 profile defined in configuration.

Explicitly disables authentication.

auth {
  kind = "none"
}
FieldTypeRequiredDescription
kindstringYesMust be "none".

body

The body block configures the request body for HTTP operations. The kind field selects the body format. Not available for gRPC, Bash, or SQL operations.

Sends a JSON request body with Content-Type: application/json.

body {
  kind = "json"
  value = {
    title = "{{ args.title }}"
    body  = "{{ args.body }}"
    labels = {{ args.labels }}
  }
}
FieldTypeRequiredDescription
kindstringYesMust be "json".
valueanyYesJSON value to send. Supports template expressions.

Sends URL-encoded form data with Content-Type: application/x-www-form-urlencoded.

body {
  kind = "form_urlencoded"
  fields = {
    grant_type = "client_credentials"
    scope      = "{{ args.scope }}"
  }
}
FieldTypeRequiredDescription
kindstringYesMust be "form_urlencoded".
fieldsmap<string, any>YesForm fields. Values support template expressions.

Sends multipart form data. Each part must specify exactly one of value, bytes_base64, or file_path.

body {
  kind = "multipart"
  parts = [
    {
      name  = "file"
      file_path    = "{{ args.file }}"
      content_type = "application/octet-stream"
      filename     = "upload.bin"
    },
    {
      name  = "description"
      value = "{{ args.description }}"
    }
  ]
}
FieldTypeRequiredDescription
kindstringYesMust be "multipart".
partsarrayYesArray of part objects. Must include at least one part.

Multipart Part Fields

FieldTypeRequiredDescription
namestringYesForm field name.
valuestringNoText value. Mutually exclusive with bytes_base64 and file_path.
bytes_base64stringNoBase64-encoded binary content. Mutually exclusive with value and file_path.
file_pathstringNoPath to a file to upload. Mutually exclusive with value and bytes_base64.
content_typestringNoMIME type for this part.
filenamestringNoFilename sent in the multipart Content-Disposition header.

Sends a raw text body.

body {
  kind         = "raw_text"
  value        = "{{ args.payload }}"
  content_type = "text/plain"
}
FieldTypeRequiredDescription
kindstringYesMust be "raw_text".
valuestringYesText content. Supports template expressions.
content_typestringNoMIME type override.

Sends raw binary data from a base64-encoded string.

body {
  kind         = "raw_bytes_base64"
  value        = "{{ args.encoded_data }}"
  content_type = "application/octet-stream"
}
FieldTypeRequiredDescription
kindstringYesMust be "raw_bytes_base64".
valuestringYesBase64-encoded binary content.
content_typestringNoMIME type override.

Streams a file as the request body.

body {
  kind         = "file_stream"
  path         = "{{ args.file_path }}"
  content_type = "application/octet-stream"
}
FieldTypeRequiredDescription
kindstringYesMust be "file_stream".
pathstringYesPath to the file. Must not be empty. Supports template expressions.
content_typestringNoMIME type override.

Explicitly sends no request body.

body {
  kind = "none"
}
FieldTypeRequiredDescription
kindstringYesMust be "none".

transport

Optional transport configuration for timeouts, retries, redirects, TLS, compression, and proxying. Available on all protocols.

transport {
  timeout_ms         = 30000
  max_response_bytes = 10485760
  compression        = true
  proxy_profile      = "corp"

  redirects {
    follow   = true
    max_hops = 5
  }

  retry {
    max_attempts    = 3
    backoff_ms      = 250
    retry_on_status = [429, 502, 503]
  }

  tls {
    min_version = "1.2"
  }
}

Top-level Transport Fields

FieldTypeRequiredDefaultDescription
timeout_msu64No--Request timeout in milliseconds. Must be greater than 0.
max_response_bytesu64No--Maximum response body size in bytes. Must be greater than 0.
compressionbooleanNo--Enable response decompression.
proxy_profilestringNo--Named proxy profile from config.toml network.proxy_profiles. Must not be empty when provided. Not supported for gRPC.

transport.redirects

FieldTypeRequiredDefaultDescription
followbooleanNotrueWhether to follow HTTP redirects.
max_hopsintegerNo5Maximum number of redirect hops.

transport.retry

FieldTypeRequiredDefaultDescription
max_attemptsintegerNo0Maximum number of retry attempts.
backoff_msu64No250Backoff between retries in milliseconds.
retry_on_statusinteger[]No[]Status codes that trigger a retry. For HTTP/GraphQL these are HTTP status codes; for gRPC these are gRPC status code numbers.

transport.tls

FieldTypeRequiredDefaultDescription
min_versionstringNo--Minimum TLS version. Accepted values: "1.0", "1.1", "1.2", "1.3". Not supported for gRPC.

result

The result block controls how the response is decoded, optionally extracted, and rendered to output.

result {
  decode       = "json"
  extract {
    json_pointer = "/items"
  }
  output       = "Found {{ result | length }} items"
  result_alias = "items"
}
FieldTypeRequiredDefaultDescription
decodestringNo"auto"Decode strategy for the response body.
extractblockNo--Optional extraction rule applied after decoding.
outputstringYes--Jinja2 template for the human-readable output. Must not be empty.
result_aliasstringNo--Additional variable name for result in the output template.

result.decode Values

ValueDescription
autoAutomatically detect the format from Content-Type headers.
jsonParse the response body as JSON.
textTreat the response body as plain text.
htmlTreat the response body as HTML (enables css_selector extraction).
xmlTreat the response body as XML (enables xpath extraction).
binaryTreat the response body as binary data.

result.extract Shapes

Exactly one extraction field must be provided in the extract block. Each shape requires a compatible decode mode.

Extracts a value from a JSON response using an RFC 6901 JSON Pointer.

extract {
  json_pointer = "/items/0/name"
}
FieldTypeRequiredDescription
json_pointerstringYesJSON Pointer path (e.g., /items/0). Requires decode = "json" or "auto" with JSON response.

Extracts text using a regular expression. If the regex has a capture group, the first group is returned; otherwise the full match.

extract {
  regex = "id=([a-z0-9-]+)"
}
FieldTypeRequiredDescription
regexstringYesRegular expression pattern. Requires decoded text, HTML, or XML body.

Extracts text content from HTML elements matching a CSS selector. Returns an array of matched text strings.

extract {
  css_selector = "h1.title"
}
FieldTypeRequiredDescription
css_selectorstringYesCSS selector. Requires decoded HTML or text body.

Extracts values from XML using an XPath expression.

extract {
  xpath = "//item/text()"
}
FieldTypeRequiredDescription
xpathstringYesXPath expression. Requires decoded XML or text body.

Output Template Variables

The result.output field is a Jinja2 template with access to:

VariableDescription
argsThe bound argument values passed to the command.
resultThe decoded (and optionally extracted) response body.
(alias)Same as result, available under the name set by result_alias.

secrets.* is not available in result.output. This prevents secrets from leaking through command output.

HCL Functions

Template files support built-in functions for loading external content into string fields.

FunctionDescription
file("path")Read a file relative to the template directory. Path must be relative and must not contain .. segments. The file must not resolve outside the template directory.
base64encode("value")Base64-encode a string value (standard encoding).
trimspace("value")Trim leading and trailing whitespace.

Functions can be composed:

sql {
  query = trimspace(file("queries/recent_orders.sql"))
}

Functions work in two forms:

  • As native HCL expressions: script = file("run.sh")
  • As string values: script = "file(\"run.sh\")"

Both produce the same result. The native form is preferred.

On this page