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