Docs

HTTP Tools & OAuth

Call any OAuth-authenticated API — Gmail, Google Calendar, Twitter/X, Stripe, Slack, or your own — with typed schemas, automatic token injection, and generative UI out of the box.

Why this matters#

A generic shell or curl tool lets the LLM guess flags, guess endpoints, and improvise auth headers. HTTP tools take a different path: the agent declares the endpoint ahead of time, with a typed parameter schema and a body template. The runtime handles auth, labels, and UI.

Generic shell/curlhttp_tools
SchemaFreeform string inputTyped params the LLM can reason over
AuthLLM improvises headersTokens injected automatically from the user's own accounts
LabelsGeneric "Running command"Branded running/finished strings per tool
UIText outputOptional generative UI components per state
Blast radiusAny URL, any methodOnly the declared endpoint

This is how a Rush agent hits the user's real Gmail inbox or real Twitter account — not a sandbox, not scraping, not an API key in an env var. The user connects their account once; every agent that needs it picks up the token.

Declare an HTTP tool#

Add an http_tools block to agent.yaml. Each key is the tool name the LLM will see. Here is the twitter_post_tweet tool lifted directly from agents/content-writer/agent.yaml:

agent.yaml
http_tools:
  twitter_post_tweet:
    description: "Post a tweet to Twitter/X"
    endpoint: /api/v1/twitter/tweets
    method: POST
    auth: bearer
    twitter_token: true
    params:
      text:
        type: string
        in: body
        description: "The tweet text (max 280 characters)"
        required: true
      reply_to:
        type: string
        in: body
        description: "Optional tweet ID to reply to"
        required: false

When the LLM decides to post a tweet, the runtime builds the request, injects the user's Rush session token and their Twitter OAuth token, fires it at the proxy, and returns the parsed response.

All fields#

Full field list for HTTPToolSpec (source: harness/agent/types.go):

FieldRequiredDescription
descriptionyesWhat the tool does. The LLM uses this to decide when to call it.
endpointyesAPI path. Paths starting with / are resolved against the proxy base URL.
methodyesHTTP method: GET, POST, PUT, PATCH, DELETE.
authnoAuthentication type: bearer, apikey, basic, or none. bearer injects the user's Rush session token.
headersnoStatic headers to include on every call.
paramsnoParameter schema. Each param has type, description, required, optional in (body/query/path), default, and hidden.
body_templatenoGo text/template for the JSON request body. See the syntax section below.
google_tokennoBoolean. Adds X-Google-Access-Token header.
google_scopesnoRequired Google scopes (gmail, calendar, contacts, docs). The tool is filtered out if the user hasn't enabled the required scope.
twitter_tokennoBoolean. Adds X-Twitter-Access-Token header.
slack_tokennoBoolean. Adds X-Slack-Token header.
stripe_tokennoBoolean. Adds X-Stripe-User-Id header (the Stripe connected-account ID).
labelsnoCustom running/finished labels shown in the UI while the tool runs.
ui_componentnoMap of state (running, completed, failed) to a generative UI component.
asyncnoAsync polling config for long-running operations.
response_formatnoPost-process response before handing it to the LLM. toon converts uniform arrays to tabular form.

Only google_token, twitter_token, slack_token, and stripe_token exist on HTTPToolSpec today. Adding a new OAuth provider takes five small edits — see below.

Supported OAuth providers#

The providers below have both a header injection hook in createHTTPToolFromSpec and a server-side handler in prix/api/src/:

ServiceToken fieldInjected headerExample endpoints
Gmailgoogle_token: trueX-Google-Access-Token/api/v1/gmail/messages, /api/v1/gmail/send
Google Calendargoogle_token: trueX-Google-Access-Token/api/v1/gcal/events, /api/v1/gcal/calendars
Twitter / Xtwitter_token: trueX-Twitter-Access-Token/api/v1/twitter/tweets, /api/v1/twitter/followers
Slackslack_token: trueX-Slack-Token/api/v1/slack/chat.postMessage
Stripestripe_token: trueX-Stripe-User-IdStripe Connect routes (proxy-side)

Google scopes (gmail, calendar, contacts, docs) are enforced per-tool. If a tool declares google_scopes: [gmail] and the user hasn't granted the Gmail scope, the tool is filtered out at build time — the LLM never sees it.

The older tools page mentioned shopify_token and linkedin_token. Neither exists on HTTPToolSpec today. LinkedIn calls currently go through a dedicated tool, not the generic http_tools injection path. If you need Shopify, add it as a new provider using the five-step flow below.

Automatic auth injection#

When the agent calls an HTTP tool, the runtime reads ~/.rush/user.yaml, resolves the two tokens the tool needs, and injects them as headers before the request leaves the machine. The proxy then unpacks those headers and forwards to the upstream API.

The session token (Authorization: Bearer) gates access to the proxy. The service-specific header (X-Twitter-Access-Token, X-Google-Access-Token, etc.) is what the proxy uses to call the upstream API on the user's behalf. Two tokens, one round-trip.

Tokens refresh mid-session. The runtime registers a header resolver per tool, so a tool called at minute 59 uses a fresh token even if the first call used one about to expire.

body_template syntax#

body_template uses Go's text/template syntax. Parameters from params are available by name.

Basic substitution

body_template: '{"text": "{{text}}"}'

Optional fields

Use {{if param}}...{{end}} to include fields only when present. The leading comma goes inside the conditional so the output stays valid JSON:

body_template: '{
  "text": "{{text}}"
  {{if reply_to}}, "reply_to": "{{reply_to}}"{{end}}
  {{if media_ids}}, "media_ids": {{media_ids}}{{end}}
}'

Multi-line payloads

For richer bodies, keep the template readable and let Go's parser handle whitespace:

body_template: |
  {
    "to": "{{to}}",
    "subject": "{{subject}}",
    "body": "{{body}}"
    {{if cc}}, "cc": "{{cc}}"{{end}}
    {{if bcc}}, "bcc": "{{bcc}}"{{end}}
  }

String values must be quoted in your template — the renderer does not auto-quote. Numbers, booleans, and arrays should be emitted without quotes ({{max_results}} not "{{max_results}}").

Response formatting#

APIs often return verbose JSON the LLM doesn't need. Set response_format: toon to convert uniform arrays into a compact tabular form — same data, a fraction of the tokens:

agent.yaml
twitter_get_followers:
  description: "Get a sample of the user's Twitter followers"
  endpoint: /api/v1/twitter/followers
  method: GET
  auth: bearer
  twitter_token: true
  response_format: toon

For larger reshaping, use an async config or post-process inside a subagent.

Connect once, use everywhere#

When a user installs an agent that declares twitter_token: true, Rush checks ~/.rush/user.yaml. If Twitter isn't connected, the app prompts for OAuth before the agent runs. Once connected, every agent on that machine that needs a Twitter token picks it up automatically — no per-agent credentials, no env vars, no re-auth.

All tokens live in ~/.rush/user.yaml (mode 0600) under service-specific keys (session, google, twitter, slack). See Account & Tokens for storage details.

Adding a new OAuth provider#

Five edits to extend HTTP tools with a new provider. The example below adds Slack; swap names accordingly.

1

Add token field to AgentBuilder

harness/agent/builder.go
type AgentBuilder struct {
    slackAccessToken string
}

func (b *AgentBuilder) WithSlackAccessToken(token string) *AgentBuilder {
    b.slackAccessToken = token
    return b
}
2

Add field to HTTPToolSpec

harness/agent/types.go
type HTTPToolSpec struct {
    SlackToken bool `json:"slack_token,omitempty" yaml:"slack_token,omitempty"`
}
3

Inject the header in createHTTPToolFromSpec

harness/agent/builder.go
if spec.SlackToken && b.slackAccessToken != "" {
    httpTool.AddHeader("X-Slack-Token", b.slackAccessToken)
}
4

Load and pass the token in rush/cli

rush/cli/internal/cli/run.go
if slackToken := GetSlackAccessToken(); slackToken != "" {
    builder = builder.WithSlackAccessToken(slackToken)
}
5

Use it in agent YAML

agent.yaml
http_tools:
  slack_post_message:
    description: "Post a message to a Slack channel"
    endpoint: /api/v1/slack/chat.postMessage
    method: POST
    auth: bearer
    slack_token: true
    params:
      channel:
        type: string
        required: true
      text:
        type: string
        required: true

Don't forget the matching proxy endpoint in prix/api/src/ that reads the header and calls the upstream API, plus an OAuth callback handler if the provider doesn't already have one.

Real example: Twitter poster agent#

Minimal agent with three Twitter HTTP tools — post a tweet, read the user's recent tweets, read their followers. This is a stripped-down version of agents/content-writer/agent.yaml:

agent.yaml
name: twitter-poster
version: 1.0.0
description: Draft and post tweets with context from the user's timeline

prompt: |
  You help the user post tweets to their Twitter/X account.
  Before drafting, read their recent tweets with twitter_get_my_tweets
  to match their voice. Confirm the draft before calling twitter_post_tweet.

tools:
  - name: web_search

http_tools:
  twitter_post_tweet:
    description: "Post a tweet to Twitter/X"
    endpoint: /api/v1/twitter/tweets
    method: POST
    auth: bearer
    twitter_token: true
    params:
      text:
        type: string
        in: body
        description: "The tweet text (max 280 characters)"
        required: true
      reply_to:
        type: string
        in: body
        description: "Optional tweet ID to reply to"
        required: false

  twitter_get_my_tweets:
    description: "Get the user's recent tweets with engagement metrics"
    endpoint: /api/v1/twitter/tweets/me
    method: GET
    auth: bearer
    twitter_token: true
    params:
      max_results:
        type: integer
        in: query
        description: "Maximum number of tweets (1-100, default 20)"
        required: false

  twitter_get_followers:
    description: "Sample the user's followers to understand the audience"
    endpoint: /api/v1/twitter/followers
    method: GET
    auth: bearer
    twitter_token: true
    params:
      max_results:
        type: integer
        in: query
        required: false

Install it, run rush run twitter-poster, and the runtime takes care of auth, param validation, response parsing, and UI. The LLM sees three neatly typed tools and decides when to use each.

Documentation | Prix | Prix