Skip to content

Autonomous Agent CLIs

Run autonomous agent CLIs against Amazon Bedrock models with stdapi.ai, using the same provider configuration you would point at OpenAI or Anthropic directly — three client-side changes: the base URL, the API key, and the model name, the last only where it differs from what the client already sends, now chosen from every provider in the catalogue rather than one vendor's list.

At a glance

  • No Vendor Lock-In
    Point the CLI's existing OpenAI- or Anthropic-compatible provider settings at stdapi.ai—no fork, no plugin, no custom integration.

  • Access Amazon Bedrock Models
    Claude, Nova, DeepSeek, Qwen, and 100+ models, driven through the same agent loop your CLI already runs.

  • No Third-Party AI Cloud
    Tool calls and model responses are processed by the AWS services you enable, reached through your own deployment — no third-party AI vendor sits in the request path.

  • Pay-Per-Use Pricing
    No per-seat or per-agent licensing. Pay only Amazon Bedrock rates for the calls the agent actually makes.

%%{init: {'flowchart': {'htmlLabels': true}} }%%
flowchart LR
  agent["<img src='../styles/logo_hermes_agent.svg' style='height:64px;width:auto;vertical-align:middle;' /> <img src='../styles/logo_openclaw.svg' style='height:64px;width:auto;vertical-align:middle;' /><br/>Autonomous Agent CLI<br/>(Hermes, OpenClaw)"] --> stdapi["<img src='../styles/logo.svg' style='height:64px;width:auto;vertical-align:middle;' /> stdapi.ai"]
  stdapi --> bedrock["<img src='../styles/logo_amazon_bedrock.svg' style='height:64px;width:auto;vertical-align:middle;' /> Amazon Bedrock"]

About Autonomous Agent CLIs

Unlike IDE coding assistants, autonomous agent CLIs plan and execute multi-step tasks on their own—reading files, calling tools, and iterating toward a goal without a human approving each step. They typically run on infrastructure you control (a server, a container, a scheduled job) rather than inside an editor.

What you can build:

  • Personal assistants - Agents that read, search, and act on your behalf from the command line
  • Autonomous research and task loops - Multi-turn tool-calling sessions that run unattended
  • Self-hosted agent backends - CLIs wired into cron jobs, CI pipelines, or your own orchestration

Connect Your Own Instance

Point Hermes or OpenClaw—wherever you run it—at your stdapi.ai gateway. Nothing below requires the AWS samples in Part 2.

Prerequisites

Before you start
  • stdapi.ai deployed - See deployment guide or run locally with Docker; see Part 2 for a Terraform-deployed option
  • Your stdapi.ai URL - e.g., https://api.example.com or http://localhost:8000 for local
  • Your API key - From Terraform output or configuration (optional for local development)

Bootstrapping Authentication

An agent handed a key in an environment variable is ready to go. An agent handed only a URL is not — and that is the common case for an MCP client, a marketplace agent, or anything a user points at a gateway it has never seen. stdapi.ai answers that case with the standard OAuth 2.0 discovery flow, so the agent works out the rest by itself:

sequenceDiagram
    autonumber
    participant A as Agent
    participant G as stdapi.ai
    participant I as Authorization server
    A->>G: Request without a token
    G-->>A: 401 + WWW-Authenticate: Bearer resource_metadata="…", scope="…"
    A->>G: GET /.well-known/oauth-protected-resource
    G-->>A: authorization_servers, scopes_supported
    A->>I: Read the issuer's own metadata, then sign in
    I-->>A: Access token
    A->>G: Retry with Authorization: Bearer <token>
    G-->>A: 200

The agent never needs to be told which identity provider you use, where its endpoints are, or which scopes to request — every one of those comes out of step 2 and 4. Enable it by setting OAUTH_RESOURCE_IDENTIFIER and OAUTH_AUTHORIZATION_SERVERS on the deployment.

The agent still needs a client identity

Discovery tells the agent where to authenticate; the authorization server decides who may. With an Amazon Cognito user pool, register the agent as an app client in the pool and give it that client ID — Cognito supports neither dynamic client registration nor client-id metadata documents, so it cannot be skipped.

Full walkthrough and the exact document served: Authentication Discovery for Agents.

An authenticated agent can be billed as itself

Once callers arrive with their own identity, per-user cost attribution runs each one's model calls under a short-lived role session of their own, and AWS reports their spend separately in Cost Explorer and the Cost and Usage Report — per agent, per tenant or per end user, from the invoice rather than an estimate. Behind a shared API key the same split is available from the identifier the request declares (safety_identifier, or metadata.user_id on the Messages API), with the caveat that a caller chooses its own: that is cost metadata, not an authorization boundary.

Giving an Agent Your Own Documents

An agent loop is only as grounded as what it can look up. Two paths reach the same vector stores, and neither needs a retrieval feature in the CLI itself:

  • As a tool the model calls — an agent that composes its own /v1/responses request declares file_search with the stores it may read, and the model runs the searches its turn needs and cites the files it answered from.
  • As an MCP tool — with MCP enabled, openai_vector_store_search is one more tool in the agent's list, usable by any MCP client whatever wire format it chats with. This is the path for a CLI that builds its own request bodies — Hermes, below, connects any MCP server this way.

Building the store itself — parsing, chunking, embedding, filters — is covered in the RAG Pipelines guide; an Amazon Bedrock knowledge base you already run is addressed as a store too.

State the gateway can hold for you

An agent that resends its whole history each turn can instead keep the thread server-side with the Conversations API and continue it by id — useful for long-running or resumable agents, and for handing one session between processes.

Hermes

Hermes (PyPI package hermes-agent) is an autonomous agent CLI written in Python.

Configuration

The simplest setup points Hermes at stdapi.ai through the same environment variables an OpenAI-compatible client would use:

export OPENAI_API_KEY=YOUR_STDAPI_KEY
export OPENAI_BASE_URL=https://YOUR_STDAPI_URL/v1

To select a specific wire format or model, declare a provider in Hermes' config.yaml instead:

providers:
  stdapi:
    name: stdapi.ai
    api: https://YOUR_STDAPI_URL/v1
    key_env: STDAPI_API_KEY
    transport: chat_completions
    default_model: anthropic.claude-fable-5

model:
  provider: stdapi
  default: anthropic.claude-fable-5

key_env names the environment variable Hermes reads the API key from—set STDAPI_API_KEY (or whatever name you choose) to your stdapi.ai key.

Transport Selection

transport is the standout setting: it picks which of the three chat routes below the provider speaks, and api has to match the route serving it:

transport api base URL API
chat_completions https://YOUR_STDAPI_URL/v1 Chat Completions
codex_responses https://YOUR_STDAPI_URL/v1 Responses
anthropic_messages https://YOUR_STDAPI_URL/anthropic Anthropic Messages

Declare more than one entry under providers to reach more than one route side by side.

Anthropic Prompt-Caching Breakpoints

On the anthropic_messages transport, Hermes automatically places prompt-caching breakpoints on the system prompt and recent messages when the target model is Claude-named. Choose the cache lifetime with prompt_caching.cache_ttl:

prompt_caching:
  cache_ttl: 1h  # or "5m" (the default)

Only 5m and 1h are accepted—any other value is ignored. This pairs directly with stdapi.ai's own Anthropic Messages prompt-caching support: Hermes' breakpoints arrive as standard cache_control markers, which stdapi.ai translates into Bedrock cache points, up to the four the Converse API allows per request.

OpenClaw

OpenClaw is a personal-assistant CLI (npm package openclaw) that also writes code as part of a task. Custom endpoints are registered through its onboarding wizard, not through the agent command itself.

openclaw onboard \
  --custom-provider-id stdapi \
  --custom-base-url https://YOUR_STDAPI_URL/v1 \
  --custom-model-id anthropic.claude-fable-5 \
  --custom-compatibility openai \
  --custom-api-key YOUR_STDAPI_API_KEY

Omit --custom-api-key to read the key from CUSTOM_API_KEY in the environment instead. Then run the agent with the model qualified by the provider id:

openclaw agent --model stdapi/anthropic.claude-fable-5

--custom-compatibility is the standout setting here: one flag picks which of the three chat routes below OpenClaw speaks, and --custom-base-url has to match the route serving it:

--custom-compatibility --custom-base-url API
openai https://YOUR_STDAPI_URL/v1 Chat Completions
openai-responses https://YOUR_STDAPI_URL/v1 Responses
anthropic https://YOUR_STDAPI_URL/anthropic Anthropic Messages

Re-run openclaw onboard with a different --custom-provider-id to register more than one route side by side.

Deploy the Full Stack on AWS

The samples below are one worked example of a credible AWS deployment, not the only architecture that works. The gateway is a normal HTTPS service, and either agent can run wherever you already operate it.

Architecture

The diagram below is the shape the Hermes and OpenClaw Terraform samples share: the agent CLI and the stdapi.ai gateway run as separate ECS Fargate tasks in the same private app subnets, with only the agent's own web surface reachable — through an Application Load Balancer — from outside the VPC. Hermes exposes that ALB as two listeners (gateway API and dashboard); OpenClaw multiplexes both onto one. The diagram collapses either shape into a single listener box.

%%{init: {'flowchart': {'htmlLabels': true, 'nodeSpacing': 20, 'rankSpacing': 40, 'subGraphTitleMargin': {'top': 8, 'bottom': 10}}} }%%
flowchart TB
  user["👤 You<br/>(browser · operator)"]
  internet["Internet<br/>image registry · tool destinations"]

  subgraph public["Your VPC · public subnets"]
    alb["<img src='../styles/logo_amazon_load_balancing.svg' style='height:40px;width:auto;vertical-align:middle;' /> Application Load Balancer<br/>optional HTTPS · ACM cert + Route 53"]
  end

  subgraph private["Your VPC · private app subnets — no inbound route from the internet"]
    agent["<img src='../styles/logo_hermes_agent.svg' style='height:40px;width:auto;vertical-align:middle;' /> <img src='../styles/logo_openclaw.svg' style='height:40px;width:auto;vertical-align:middle;' /><br/>Agent CLI<br/>(Hermes or OpenClaw) · ECS Fargate"]
    stdapi["<img src='../styles/logo.svg' style='height:40px;width:auto;vertical-align:middle;' /> stdapi.ai<br/>ECS Fargate"]
    egress["<img src='../styles/logo_amazon_vpc.svg' style='height:40px;width:auto;vertical-align:middle;' /> NAT gateways<br/>one per Availability Zone"]
  end

  subgraph regional["AWS service endpoints · your account, the regions you configure"]
    bedrock["<img src='../styles/logo_amazon_bedrock.svg' style='height:40px;width:auto;vertical-align:middle;' /> Amazon Bedrock"]
    s3["<img src='../styles/logo_amazon_s3.svg' style='height:40px;width:auto;vertical-align:middle;' /> Amazon S3<br/>SSE-KMS"]
    cw["<img src='../styles/logo_amazon_cloudwatch.svg' style='height:40px;width:auto;vertical-align:middle;' /> Amazon CloudWatch<br/>logs · metrics · alarms"]
  end

  user -->|"HTTPS (custom domain) or HTTP<br/>from the deploying IP only"| alb
  alb -->|"HTTP · private subnet"| agent
  agent -->|"OpenAI/Anthropic-compatible API · API key<br/>Cloud Map private DNS, no public endpoint"| stdapi
  agent -->|"HTTPS · image pull, plus the tool<br/>destinations you configure"| egress
  egress --> internet
  stdapi -->|"HTTPS · SigV4"| egress
  egress -->|"HTTPS · SigV4"| bedrock
  egress -->|"HTTPS · SigV4"| s3
  egress --> cw

Two things are worth reading off the picture. stdapi.ai has no listener of its own: the agent reaches it only through AWS Cloud Map private DNS inside the private subnets, so the ALB never forwards to it — only to the agent's own ports. And the agent's egress is a separate path from the gateway's: image pulls and any tool destinations you configure leave as ordinary internet-bound HTTPS, while the gateway's own calls are SigV4-signed and reach AWS service endpoints only. That second path is one of the compensating controls for the sandboxing warning documented under OpenClaw — alongside a task dedicated to nothing but this agent, a task role scoped to what it declares, and a private subnet with no inbound route from the internet.

What Each AWS Service Does Here

AWS service Role in this integration Where it is configured
Amazon ECS on AWS Fargate Runs the agent CLI and the stdapi.ai gateway as separate tasks, each with its own IAM role Terraform sample (module "hermes"/module "openclaw", module "stdapi_ai")
Elastic Load Balancing Public entry point for the agent's own gateway API and dashboard/Control UI; never forwards to stdapi.ai Terraform sample (alb.tf)
AWS Certificate Manager & Amazon Route 53 Optional TLS certificate and DNS record for the ALB, created only when a custom domain is supplied Terraform sample (alb_domain_name, alb_route53_zone_name)
AWS Cloud Map Private DNS namespace (internal) the agent uses to resolve stdapi.ai without a public endpoint Terraform sample (aws_service_discovery_private_dns_namespace "internal", service_discovery_dns_name)
Amazon Bedrock Serves the model calls the agent's chosen wire dialect sends through stdapi.ai AWS_BEDROCK_REGIONS
Amazon S3 The gateway's own bucket for generated and temporary files; KMS-encrypted, versioned, lifecycle-managed Module baseline (storage.tf)
AWS KMS Customer-managed key encrypting the gateway's S3 bucket; a separate key, created by the ECS module, encrypts the agent's EFS volumes and Fargate ephemeral storage Terraform module baseline
Amazon EFS Persists the agent's own state (config, sessions, workspace) across redeployments. For Hermes it holds all state, including every SQLite database, which is why that service runs a single task Terraform sample (mount_points in hermes.tf/openclaw.tf)
Amazon CloudWatch Container logs, gateway request logs, and, when enabled, EMF usage metrics Logging & monitoring
AWS IAM Separate least-privilege task roles per ECS task; the gateway's role grants only the Bedrock/AI-service actions it calls IAM permissions

Security Measures in This Flow

  • Credential the agent presents — in these samples the agent authenticates to the gateway with a generated API key baked into its seeded config file (config.yaml for Hermes, openclaw.json for OpenClaw); an agent that starts with only a URL instead works out where and how to authenticate through the OAuth 2.0 protected-resource discovery flow documented above.
  • Encryption in transit — HTTPS from the operator's browser to the ALB when a custom domain and ACM certificate are configured, plain HTTP otherwise; plain HTTP from the ALB to the agent container and from the agent to stdapi.ai, both confined to the private subnet; HTTPS with SigV4 from the gateway to Amazon Bedrock.
  • Encryption at rest — SSE-KMS on the gateway's S3 bucket, and a separate customer-managed key encrypting the EFS volumes that hold the agent's persistent state.
  • Least privilege / task-role scoping — the agent and the gateway each run under their own ECS task role; the gateway's role carries only the Bedrock/AI-service actions it calls, and the agent's task carries none of it — every model call still goes through the gateway. See IAM permissions.
  • Content policy — a Bedrock guardrail configured on the gateway applies to model calls regardless of which wire dialect the agent's provider settings select.
  • Cost / identity attributionper-user cost attribution turns a caller's declared identity into a billing boundary only under AUTHENTICATION_MODE=cognito; the API-key mode these samples use makes that identifier client-declared, so every call from a shared deployment is billed to the gateway's own identity.

Hermes

Deploy Hermes + stdapi.ai together on ECS Fargate, with config.yaml pre-seeded and ready to run:

📦 stdapi-ai/samples/getting_started_hermes

What's included:

  • Hermes gateway API and web dashboard on ECS Fargate, each behind its own generated credential
  • stdapi.ai gateway connected to Amazon Bedrock, registered as a custom OpenAI-compatible provider
  • config.yaml seeded with the stdapi.ai URL and API key already filled in, and re-seeded whenever the rendered template changes — so a model or key changed in Terraform reaches the deployment, at the cost of overwriting edits made in the dashboard
  • Persistent state (config, sessions, memories, skills) on EFS, so it survives redeployments
  • Exactly one task, because every one of those stores is SQLite with no external backend (upstream #38185). Recovery is a Fargate reschedule, with no failover — see Single instance by design in the sample's README
  • No local image build — the public nousresearch/hermes-agent image is pulled anonymously by Fargate
  • ECS Exec enabled for shelling into the container or driving Hermes' interactive CLI directly

Deploy:

git clone https://github.com/stdapi-ai/samples.git
cd samples/getting_started_hermes/terraform
tofu init
tofu apply

What It Costs to Run

Charge Driver
stdapi.ai licence $0.10 per gateway container-hour, metered through AWS Marketplace, with a 14-day free trial on the licence
ECS Fargate The Hermes task — pinned to exactly one, see Single instance by design in the sample's README — and the gateway task, each sized independently; a short-lived init container re-seeds config.yaml on every deployment
Load balancing and networking One ALB fronting the gateway API and dashboard, plus the NAT gateways the private subnets egress through
Amazon EFS Standing storage and throughput cost for /opt/data — config, SQLite state, sessions, memories, skills
Amazon Bedrock usage AWS rates, billed to your account with no markup — for a long agent loop, how much of each turn's context is served from a cache read rather than fresh input is the dominant lever; see Anthropic Prompt-Caching Breakpoints, above

Read a model's price before sending anything to it with GET /model_pricing. Setting COST_TRACKING=true additionally puts a per-request cost on each usage entry — estimated from published AWS prices, not read back from your invoice.

What to Watch

Hermes and the gateway each log to CloudWatch: Hermes writes its own container logs, and the gateway writes one structured request event (or request_stream for streamed replies) per call, carrying the request id, path, status code, execution_time_ms, and a nested usage list with the token counts AWS billed — including cached_tokens (cache reads) and cache_write_tokens on each entry. Turning on CLOUDWATCH_METRICS republishes those counts as EMF metrics in the stdapi namespace, dimensioned by Model.

An agent loop that resends its history every turn should, once the session warms up, read far more input tokens from cache than it pays for fresh:

fields Model, InputTokens, CachedTokens, CacheWriteTokens
| filter _aws.CloudWatchMetrics is not null
| stats sum(CachedTokens) as cache_reads, sum(InputTokens) as fresh_input, sum(CacheWriteTokens) as cache_writes by Model
| sort cache_reads desc

A model whose cache_reads stay near zero mid-session points at breakpoints that are not landing — check that Hermes is actually sending cache_control markers before assuming the model itself is at fault.

OpenClaw

Deploy OpenClaw + stdapi.ai together on ECS Fargate, with the provider and default model preconfigured:

📦 stdapi-ai/samples/getting_started_openclaw

What's included:

  • OpenClaw gateway and Control UI on ECS Fargate, authenticated with a generated token
  • stdapi.ai gateway connected to Amazon Bedrock, registered as a custom OpenAI-compatible provider (api: "openai-completions")
  • openclaw.json seeded on first boot with the provider and default model already filled in
  • Persistent config, auth material, and workspace on EFS
  • No local image build — the public ghcr.io/openclaw/openclaw image is pulled anonymously by Fargate

Agent sandboxing is off

OpenClaw can run agent tool calls inside a nested sandbox, but Fargate exposes none of the backends that requires (most commonly a host Docker socket), so this sample sets agents.defaults.sandbox.mode to "off". Tool calls therefore run directly inside the container itself, with only the container's own boundary as isolation. Do not point this deployment at an agent workload you would not trust to run arbitrary commands there.

Device pairing stays manual

OpenClaw's Control UI requires pairing a browser device before use, done through a few aws ecs execute-command calls after deployment — see the sample's README for the exact commands. This is not automated by Terraform.

Deploy:

git clone https://github.com/stdapi-ai/samples.git
cd samples/getting_started_openclaw/terraform
tofu init
tofu apply

What It Costs to Run

Charge Driver
stdapi.ai licence $0.10 per gateway container-hour, metered through AWS Marketplace, with a 14-day free trial on the licence
ECS Fargate The OpenClaw task — pinned to exactly one, see Single Instance, by Design in the sample's README — and the gateway task, each sized independently; a short-lived init container seeds openclaw.json on first boot only, and runs harmlessly on every later deployment
Load balancing and networking One ALB fronting the gateway and Control UI, plus the NAT gateways the private subnets egress through
Amazon EFS Standing storage and throughput cost for OpenClaw's config, auth material and workspace
Amazon Bedrock usage AWS rates, billed to your account with no markup

Read a model's price before sending anything to it with GET /model_pricing. Setting COST_TRACKING=true additionally puts a per-request cost on each usage entry — estimated from published AWS prices, not read back from your invoice.

What to Watch

OpenClaw and the gateway each write their own container logs to CloudWatch; the gateway additionally writes one structured request event per call, carrying the request id, path, status code, execution_time_ms, the model that served it, and the token counts AWS billed. Turning on CLOUDWATCH_METRICS republishes those counts as EMF metrics in the stdapi namespace, dimensioned by Model.

fields model_id, execution_time_ms
| filter type = "request" and ispresent(model_id)
| stats count(*) as calls, pct(execution_time_ms, 95) as p95_ms by model_id
| sort calls desc

A rising p95 here is what an operator notices as a slow agent turn, before it shows up in any cost report.

Next Steps