CLI (mrl)
A command-line interface for running RLM over local data. The CLI also includes model chat, agent-loop, and resource-management commands.
Quick Start
Run RLM against a read-only SQLite database:
mrl rlm \
"Which customers grew the most this quarter, and why?" \
--db ./app.db
Or analyze local files:
mrl rlm "Summarize the data and flag anomalies" -a ./events.csv
cat server.log | mrl rlm "Find the likely cause of the outage"
Installation
Homebrew (macOS/Linux)
brew install tensor-systems/tap/mrl
To upgrade:
brew upgrade mrl
Manual Download
Download the latest release from releases.modelrelay.ai/mrl and add to your PATH.
From Source
go install github.com/modelrelay/mrl@latest
Configuration
Environment Variables
export MODELRELAY_API_KEY=mr_sk_...
export MODELRELAY_PROJECT_ID=... # UUID (optional default)
export MODELRELAY_API_BASE_URL=... # optional
export MODELRELAY_MODEL=... # default model
Config File
Create ~/.config/mrl/config.toml:
[profiles.default]
api_key = "mr_sk_..."
base_url = "https://api.modelrelay.ai/api/v1"
project_id = "<uuid>"
model = "claude-sonnet-5"
output = "table" # or "json"
# Options for `mrl do` command
allow_all = true
trace = true
# allow = ["git ", "npm "] # alternative to allow_all
Managing Profiles
# Set values for a profile
mrl config set --profile dev --api-key mr_sk_...
# Switch to a profile
mrl config use dev
# Show current config
mrl config show
Resolve a direct response route
Inspect the authorized concrete model, selected provider, and canonical pricing
provenance without making a model call:
mrl response resolve --model gpt-5.6-sol --provider openai --json
--provider is optional. The command creates no usage or settlement record.
RLM
Python, file access, and database queries run on your machine; root and subcall model requests go through ModelRelay.
SQLite
mrl rlm "Compare revenue by plan and explain the largest changes" \
--db ./billing.db
The database is exposed as db. Choose a clearer sandbox name or apply a SQL policy when needed:
mrl rlm "Find suspicious refunds" \
--db ./commerce.db \
--db-name commerce \
--sql-profile "$MODELRELAY_SQL_PROFILE_ID"
SQLite access is read-only. --sql-profile selects a configured policy; when omitted, the CLI uses its default permissive read-only policy.
PostgreSQL
Keep the DSN out of process arguments by passing the name of an environment variable:
export MODELRELAY_POSTGRES_DSN='postgres://rlm_reader:password@db.internal/analytics?sslmode=require'
mrl rlm "Compare revenue by plan" \
--postgres-dsn-env MODELRELAY_POSTGRES_DSN \
--db-name warehouse
The local mrl broker owns the credential, validates every query, and enforces read-only transactions plus timeout, row, and byte caps. See PostgreSQL Edge Connector.
Snowflake
Snowflake uses two trust domains: a broker that owns credentials and policy, and a runner that gets only a short-lived broker capability. Start the broker in a credential-bearing container:
mrl snowflake serve \
--config /run/modelrelay/snowflake.json \
--listen 0.0.0.0:8081 \
--broker-token-env MODELRELAY_SNOWFLAKE_BROKER_TOKEN
Then point local RLM at it from a separate process or container:
mrl rlm "Compare weekly revenue and explain anomalies" \
--snowflake-broker-url http://broker:8081/v1/sql \
--snowflake-broker-token-env MODELRELAY_SNOWFLAKE_BROKER_TOKEN
The broker validates every query locally against the Snowflake policy in its config. The runner must not receive that config or any Snowflake secret. See Snowflake Edge Connector for configuration and deployment.
Remote MCP providers use repeatable --mcp-config files in local or customer-controlled VPC mode. Secrets are mapped by environment-variable name and stay behind a tenant/source-scoped host broker; generated code receives only Droste capability bindings. See Remote MCP providers.
Files and stdin
mrl rlm "Summarize the data" -a ./large_dataset.csv
mrl rlm "Compare all reports" -a ./reports/*.md
git diff | mrl rlm "Explain the architectural impact of these changes"
Small text files are included in context["files"][i]["text"]. Local mode can load larger attachments from their paths.
Root and subcall models
Use a strong model to drive the loop and a cheaper model for bounded semantic subcalls:
mrl rlm "Analyze the support backlog" -a ./tickets.json \
--model claude-sonnet-5 \
--subcall-model gemini-3.7-flash \
--subcall-reasoning-effort low \
--subcall-max-output-tokens 1024
Local Droste with a ModelRelay lease
Use --relay-session to exercise the external execution-lease contract. Droste
and task content remain local; ModelRelay resolves the immutable tier profile,
authorizes and accounts for root/subcall callbacks, and freezes content-free
evidence when the run finishes. Before running, the CLI verifies the integer
max_settled_spend_microcents authority returned by both resolution and creation.
mrl rlm --relay-session --customer customer-external-id \
--model preset:experimental-rlm \
"Summarize these records" -a ./records.json
mrl requires a project API key here. --customer names the customer’s
external ID; the CLI resolves that value once and sends the same
X-ModelRelay-Customer-Id authority on resolve, create, and finalize. The
CLI’s --token option is an account-login token, not a customer token. Direct
HTTP API clients may instead use a real customer bearer token and omit the
customer header.
The CLI owns one local CodeSession and resolves one runner asset path across
scaffold preflight and execution. The two operations therefore share the
session working directory, configured environment and limits, and the same
extracted runner assets. Each operation still launches a fresh Python
subprocess; preflight cannot leave Python globals, imports, or other in-memory
interpreter state for the run.
The profile is immutable, so --relay-session rejects --max-subcalls,
--max-depth, --exec-timeout-ms, and the --subcall-* flags rather than
silently ignoring them. It currently supports message/context workloads; --db,
--postgres-dsn-env, --snowflake-broker-url, and --mcp-config remain
available in ordinary local mode. See the
RLM execution-lease API for the
resolve, create, callback, and finalize contract.
RLM flags
| Flag | Description |
|---|---|
-a, --attachment |
Attach a local file; repeat for multiple files or use - for stdin |
--attachment-type |
Override an attachment MIME type |
--attach-stdin |
Attach stdin explicitly |
--db |
Expose a SQLite database as a read-only data source |
--db-name |
Set the database’s sandbox name; defaults to db |
--sql-profile |
Apply a read-only SQL policy by profile ID |
--postgres-dsn-env |
Name the environment variable holding a PostgreSQL DSN for the trusted read-only edge connector |
--snowflake-broker-url |
Connect RLM to a trusted Snowflake broker |
--snowflake-broker-token-env |
Name the environment variable containing the broker capability token |
--mcp-config |
Mount a trusted remote MCP source config file; repeatable |
--default-source |
Name the default generated-code data source when more than one is mounted |
--model |
Select the root model |
--subcall-model |
Select the llm_query and llm_batch model |
--subcall-max-output-tokens |
Bound output per subcall; server default is 2,048 |
--subcall-reasoning-effort |
Set effort to none, minimal, low, medium, high, xhigh, or max; provider support varies |
--max-subcalls |
Limit llm_query and llm_batch calls; default 50 |
--max-depth |
Limit subcall depth; default 1 |
--exec-timeout-ms |
Local only: limit each Python execution; 0 uses the runtime default |
--seed |
Set a run-specific rollout identity/attribution value from 0 through 9,007,199,254,740,991; omit when unavailable |
--python |
Choose the Python executable; defaults to python3 |
--max-inline-bytes |
Limit context stored inline |
--max-total-bytes |
Limit total context bytes |
--inline-text-max-bytes |
Limit inline text per attached file |
--system |
Prepend custom instructions to the RLM system prompt |
--system-override |
Replace the RLM system prompt |
--relay-session |
Run local Droste with a durable ModelRelay execution grant |
--customer |
External customer ID required by --relay-session when using a project API key |
--json reports iterations as a diagnostic. Its trajectory field is a
typed availability fact and is unavailable under the default
no-content-retention policy. Stderr is diagnostic output and is not
interpreted as progress.
For --relay-session, JSON also includes finalized execution_evidence with
typed root/subcall provider-returned model facts. Missing or conflicting
provider observations remain explicit unavailable facts.
To retry a seeded run with the same contract identity, pass the
same --seed again. The current engine and provider callbacks do not use it as
behavioral RNG, so it does not guarantee identical model behavior or output.
See Models and Subcalls for cost guidance and Security and Data Boundaries for execution details.
Other CLI commands
Chat directly with a model:
mrl "What is 2 + 2?"
mrl "Write a haiku" --stream
git diff | mrl "explain these changes"
Quick Tasks with do
The do command is a simpler alternative for quick agentic tasks with bash. It runs a local tool loop where the model executes shell commands to complete your task.
Basic Usage
mrl do "commit my changes" --allow-all
Configuration
Set defaults in your config to avoid repeating flags:
mrl config set --model claude-sonnet-5 --allow-all --trace
Then simply run:
mrl do "commit my changes"
How It Works
The do command runs an agentic loop: it calls the /responses API, executes any tool calls locally, and continues until the model completes the task.
Here’s the flow for mrl do "commit my changes":
sequenceDiagram
participant CLI as mrl CLI
participant API as /responses API
participant Local as Local Shell
Note over CLI: User: "commit my changes"
rect rgb(40, 40, 40)
Note over CLI,Local: Turn 1
CLI->>API: POST /responses
[system, user]
API-->>CLI: tool_call: bash
args: "git status"
CLI->>Local: git status
Local-->>CLI: "Changes not staged"
end
rect rgb(40, 40, 40)
Note over CLI,Local: Turn 2
CLI->>API: POST /responses
[...+ tool_result]
API-->>CLI: tool_call: bash
args: "git diff"
CLI->>Local: git diff
Local-->>CLI: shows actual changes
end
rect rgb(40, 40, 40)
Note over CLI,Local: Turn 3
CLI->>API: POST /responses
[...+ tool_result]
API-->>CLI: tool_call: bash
args: "git add && commit"
CLI->>Local: git add . && git commit -m "..."
Local-->>CLI: [abc123] feat: descriptive msg
end
rect rgb(40, 40, 40)
Note over CLI,Local: Turn 4
CLI->>API: POST /responses
[...+ tool_result]
API-->>CLI: text: "Committed."
tool_calls: []
end
Note over CLI: No tool calls → exit loop
Key points:
- 4 API calls to
/responses - 3 local bash executions (status → diff → add+commit)
- Model reads the diff before writing a descriptive commit message
- Model decides when the task is complete (no more tool calls)
- All tool execution happens locally on your machine
Flags
| Flag | Description |
|---|---|
--model |
Model ID (overrides config) |
--system |
Custom system prompt |
--allow |
Allow bash command prefix (repeatable) |
--allow-all |
Allow all bash commands |
--max-turns |
Max tool loop iterations (default: 50) |
--trace |
Show commands as they execute |
Config Options
These can be set with mrl config set:
| Option | Description |
|---|---|
--model |
Default model for all commands |
--allow-all |
Allow all bash commands by default |
--allow |
Default allowed command prefixes |
--trace |
Show commands by default |
Resource Management
Customers
# List customers
mrl customer list
# Get a customer
mrl customer get <customer_id>
# Create a customer
mrl customer create --external-id user_123 --email user@example.com
Tiers
# List tiers
mrl tier list
# Get a tier
mrl tier get <tier_id>
Usage
# View account usage
mrl usage account
Utility Commands
List Models
# List all models
mrl model list
# Filter by provider and capability
mrl model list --provider openai --capability text_generation
# Include deprecated models
mrl model list --include-deprecated --json
Lint JSON Schemas
Validate JSON schemas for provider compatibility:
# Basic lint
mrl schema lint ./schema.json
# Validate for specific provider
mrl schema lint ./schema.json --provider openai
# Validate tool schema
mrl schema lint ./tool-schema.json --provider openai --tool-schema
Version
mrl version
Output Formats
Table output is the default. Use --json for machine-readable output on any command:
mrl customer list --json
mrl model list --json
Global Flags
| Flag | Description |
|---|---|
--profile |
Config profile to use |
--api-key |
API key (overrides config) |
--base-url |
API base URL (overrides config) |
--project |
Project UUID (overrides config) |
--model |
Model ID (overrides config) |
--json |
Output JSON instead of table |
--timeout |
Request timeout (default: 30s) |
--stream |
Stream output as it’s generated (chat mode) |
--usage |
Show token usage after response (chat mode) |
--system |
System prompt (chat mode) |
Next Steps
- First Request - Make your first API call
- API Reference - Integrate through the supported REST contract
- Client Support Matrix - Check the lifecycle of each client surface