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.
Field Type Required Default Description 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>"block Yes -- One or more named command blocks. At least one is required.
Commands are identified by provider.command (e.g., github.search_issues).
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 { ... }
}
Field Type Required Default Description 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). annotationsblock Yes -- Mode and secret declarations. paramblock(s) No []Repeatable typed input parameters. operationblock Yes -- Protocol-specific execution definition. resultblock Yes -- Output decoding, extraction, and rendering.
annotations {
mode = "read"
secrets = [ "github.token" ]
}
Field Type Required Default Description 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.
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
}
Field Type Required Default Description namestringYes -- Parameter name (inferred from the block label). Must be unique within a command. typestringYes -- One of: string, integer, number, boolean, null, array, object. requiredbooleanNo falseWhether 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.
Type CLI Input Description 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.
The operation block defines what earl executes. The protocol field selects the operation shape.
Protocol Description Feature Flag httpStandard HTTP request http (default)graphqlGraphQL over HTTP POST graphql (default)grpcgRPC over HTTP/2 grpc (default)bashSandboxed Bash script execution bash (default)sqlParameterized SQL query execution sql (default)
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 { ... }
}
Field Type Required Default Description 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. authblock No -- Authentication configuration. See auth . bodyblock No -- Request body configuration. See body . transportblock No -- Transport options (timeouts, retries, TLS). See transport .
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 { ... }
}
Field Type Required Default Description 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. authblock No -- Authentication. See auth . graphqlblock Yes -- GraphQL query definition. transportblock No -- Transport options. See transport .
Field Type Required Default Description 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.
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 { ... }
}
Field Type Required Default Description protocolstringYes -- Must be "grpc". urlstringYes -- gRPC server URL. Must not be empty. headersmap<string, any>No -- gRPC metadata headers. authblock No -- Authentication. For gRPC, api_key.location must be header. See auth . grpcblock Yes -- gRPC call definition. transportblock No -- Transport options. proxy_profile and tls.min_version are not supported for gRPC.
Field Type Required Default Description 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.
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 { ... }
}
Field Type Required Default Description protocolstringYes -- Must be "bash". bashblock Yes -- Bash execution definition. transportblock No -- Transport options (only timeout_ms is typically relevant).
Field Type Required Default Description 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. sandboxblock No -- Sandbox restrictions.
Field Type Required Default Description 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.
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 { ... }
}
Field Type Required Default Description protocolstringYes -- Must be "sql". sqlblock Yes -- SQL query definition. transportblock No -- Transport options.
Field Type Required Default Description 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. sandboxblock No -- Sandbox restrictions.
Field Type Required Default Description 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.
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.
bearer api_key basic oauth2_profile none
Sends an Authorization: Bearer <token> header.
auth {
kind = "bearer"
secret = "github.token"
} Field Type Required Description kindstringYes Must be "bearer". secretstringYes Secret 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"
} Field Type Required Description kindstringYes Must be "api_key". location"header" | "query" | "cookie"Yes Where to send the API key. For gRPC, must be "header". namestringYes Header name, query parameter name, or cookie name. secretstringYes Secret key containing the API key value.
Sends an Authorization: Basic <base64(username:password)> header.
auth {
kind = "basic"
username = "{{ args.username }}"
password_secret = "service.password"
} Field Type Required Description kindstringYes Must be "basic". usernamestringYes Username. Supports template expressions. password_secretstringYes Secret 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"
} Field Type Required Description kindstringYes Must be "oauth2_profile". profilestringYes Name of the OAuth2 profile defined in configuration.
Explicitly disables authentication.
Field Type Required Description kindstringYes Must be "none".
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.
json form_urlencoded multipart raw_text raw_bytes_base64 file_stream none
Sends a JSON request body with Content-Type: application/json.
body {
kind = "json"
value = {
title = "{{ args.title }}"
body = "{{ args.body }}"
labels = {{ args.labels }}
}
} Field Type Required Description kindstringYes Must be "json". valueanyYes JSON 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 }}"
}
} Field Type Required Description kindstringYes Must be "form_urlencoded". fieldsmap<string, any>Yes Form 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 }}"
}
]
} Field Type Required Description kindstringYes Must be "multipart". partsarrayYes Array of part objects. Must include at least one part.
Field Type Required Description namestringYes Form field name. valuestringNo Text value. Mutually exclusive with bytes_base64 and file_path. bytes_base64stringNo Base64-encoded binary content. Mutually exclusive with value and file_path. file_pathstringNo Path to a file to upload. Mutually exclusive with value and bytes_base64. content_typestringNo MIME type for this part. filenamestringNo Filename sent in the multipart Content-Disposition header.
Sends a raw text body.
body {
kind = "raw_text"
value = "{{ args.payload }}"
content_type = "text/plain"
} Field Type Required Description kindstringYes Must be "raw_text". valuestringYes Text content. Supports template expressions. content_typestringNo MIME 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"
} Field Type Required Description kindstringYes Must be "raw_bytes_base64". valuestringYes Base64-encoded binary content. content_typestringNo MIME type override.
Streams a file as the request body.
body {
kind = "file_stream"
path = "{{ args.file_path }}"
content_type = "application/octet-stream"
} Field Type Required Description kindstringYes Must be "file_stream". pathstringYes Path to the file. Must not be empty. Supports template expressions. content_typestringNo MIME type override.
Explicitly sends no request body.
Field Type Required Description kindstringYes Must be "none".
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"
}
}
Field Type Required Default Description 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.
Field Type Required Default Description followbooleanNo trueWhether to follow HTTP redirects. max_hopsintegerNo 5Maximum number of redirect hops.
Field Type Required Default Description max_attemptsintegerNo 0Maximum number of retry attempts. backoff_msu64No 250Backoff 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.
Field Type Required Default Description min_versionstringNo -- Minimum TLS version. Accepted values: "1.0", "1.1", "1.2", "1.3". Not supported for gRPC.
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"
}
Field Type Required Default Description decodestringNo "auto"Decode strategy for the response body. extractblock No -- 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.
Value Description 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.
Exactly one extraction field must be provided in the extract block. Each shape requires a compatible decode mode.
json_pointer regex css_selector xpath
Extracts a value from a JSON response using an RFC 6901 JSON Pointer.
extract {
json_pointer = "/items/0/name"
} Field Type Required Description json_pointerstringYes JSON 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-]+)"
} Field Type Required Description regexstringYes Regular 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"
} Field Type Required Description css_selectorstringYes CSS selector. Requires decoded HTML or text body.
Extracts values from XML using an XPath expression.
extract {
xpath = "//item/text()"
} Field Type Required Description xpathstringYes XPath expression. Requires decoded XML or text body.
The result.output field is a Jinja2 template with access to:
Variable Description 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.
Template files support built-in functions for loading external content into string fields.
Function Description 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.