Any agent

Not just Claude Code. Any agent.

The broker is a loopback HTTP endpoint — so anything that speaks HTTP can reach your providers through it: the OpenAI SDKs, LangChain, LlamaIndex, a one-off script, curl, a cron job. Point the client at one base URL, send one capability header, and the real key never leaves your keychain. STM stops being "a Claude Code plugin" and becomes a credential broker for every agent you run.

The one rule#

Every recipe on this page is the same two moves. If your client can do both, it can use the broker — no matter the language, framework, or vendor:

  1. Point it at the broker's base URL instead of the provider's — http://127.0.0.1:<port>/proxy/<tool>/<label>/<upstream-path>.
  2. Send the loopback capability token as an x-stm-token header (or a ?token= query param when you control the full URL).

That's it. The broker resolves <tool>:<label> from your OS keychain, attaches the real credential on the outbound call to the provider, and streams the response back with the key scrubbed out. Wherever the client normally wants an API key, you hand it a throwaway string — the broker drops whatever auth the client sends and injects the real one itself. See the broker mechanism for exactly how that boundary is enforced.

Why this is safe to paste anywhere

The x-stm-token is a capability, not a secret. It authorizes /proxy calls on loopback only — it can't read your inventory, open the dashboard, or reach off your machine — and it resets when the daemon restarts. So it is safe in a script, a dotfile, or an editor's settings in a way a real key never is.

Get your base URL + token#

Run stm broker. It ensures the local daemon is up and prints the exact base URL and capability token to drop into any client below:

shell
stm broker
subscribetome broker

  base URL : http://127.0.0.1:<port>/proxy/<tool>/<label>/<upstream-path>
  auth     : header  x-stm-token: <broker-token>

  Supported targets: anthropic, fal, groq, openai, openrouter, replicate, stripe.

In the snippets below, replace <port> and <broker-token> with the values it printed. Both are local and ephemeral — the port is chosen at daemon start and the token resets on restart, so read them from stm broker rather than hard-coding them for good.

curl / any shell#

The baseline. No Authorization header, no key anywhere in the command — only the loopback token:

shell
curl http://127.0.0.1:<port>/proxy/openai/default/v1/chat/completions \
  -H "x-stm-token: <broker-token>" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hello"}]}'

When you control the whole URL you can also carry the token as a query param — handy for tools that only take a URL: append ?token=<broker-token>. The broker strips it before forwarding, so it never reaches the provider.

Python — OpenAI SDK#

The official openai package accepts a base_url and default_headers. The api_key is required by the constructor but ignored on the wire — the broker injects the real one:

python
from openai import OpenAI

client = OpenAI(
    base_url="http://127.0.0.1:<port>/proxy/openai/default/v1",
    api_key="stm-broker",                 # ignored — broker injects the real key
    default_headers={"x-stm-token": "<broker-token>"},
)

resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "hello"}],
)
print(resp.choices[0].message.content)

Python — plain HTTP#

No SDK needed. Any HTTP library works the same way — here requests:

python
import requests

r = requests.post(
    "http://127.0.0.1:<port>/proxy/openai/default/v1/chat/completions",
    headers={
        "x-stm-token": "<broker-token>",
        "Content-Type": "application/json",
    },
    json={"model": "gpt-4o-mini",
          "messages": [{"role": "user", "content": "hello"}]},
)
print(r.json())

Node — OpenAI SDK#

The openai package for JS/TS takes baseURL and defaultHeaders:

typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "http://127.0.0.1:<port>/proxy/openai/default/v1",
  apiKey: "stm-broker",                    // ignored — broker injects the real key
  defaultHeaders: { "x-stm-token": "<broker-token>" },
});

const resp = await client.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "hello" }],
});
console.log(resp.choices[0].message.content);

Node — plain fetch#

typescript
const resp = await fetch(
  "http://127.0.0.1:<port>/proxy/openai/default/v1/chat/completions",
  {
    method: "POST",
    headers: {
      "x-stm-token": "<broker-token>",
      "content-type": "application/json",
    },
    body: JSON.stringify({
      model: "gpt-4o-mini",
      messages: [{ role: "user", content: "hello" }],
    }),
  },
);
console.log(await resp.json());

LangChain & LlamaIndex#

Both frameworks build on the OpenAI client, so they inherit its base_url + default_headers overrides. Same two moves, same throwaway key.

LangChain

python
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="gpt-4o-mini",
    base_url="http://127.0.0.1:<port>/proxy/openai/default/v1",
    api_key="stm-broker",
    default_headers={"x-stm-token": "<broker-token>"},
)
print(llm.invoke("hello").content)

LlamaIndex

python
from llama_index.llms.openai import OpenAI

llm = OpenAI(
    model="gpt-4o-mini",
    api_base="http://127.0.0.1:<port>/proxy/openai/default/v1",
    api_key="stm-broker",
    default_headers={"x-stm-token": "<broker-token>"},
)
print(llm.complete("hello"))

Anthropic-native clients follow the same pattern — swap the path segment to /proxy/anthropic/default and point the SDK's base URL there; the broker attaches the x-api-key for you.

Will my agent work?#

One question decides it: can the client set a custom base URL and a custom request header?

The client can…Then…
Set base URL + custom header
(OpenAI SDKs, LangChain, LlamaIndex, the Vercel AI SDK, most HTTP libraries)
Works directly — use the recipes above.
Only lets you build the full request URL
(a raw HTTP call, a webhook field)
Works — carry the token as ?token=<broker-token> in the URL.
Only lets you set a base URL, no headers
(some editor "custom OpenAI endpoint" fields)
Add the header with a tiny loopback forwarder — below.
Escape hatch: a loopback forwarder for header-less clients

If an agent only exposes a base-URL field, run this ~10-line Bun shim. It listens on a fixed local port, stamps the x-stm-token header on every request, and forwards to the broker. Point the header-less client at http://127.0.0.1:8787/proxy/openai/default/v1:

bun · stm-forward.ts
const TOKEN = process.env.STM_BROKER_TOKEN!;   // from `stm broker`
const UPSTREAM = process.env.STM_BROKER_URL!;   // http://127.0.0.1:<port>

Bun.serve({
  hostname: "127.0.0.1",
  port: 8787,
  async fetch(req) {
    const u = new URL(req.url);
    const headers = new Headers(req.headers);
    headers.set("x-stm-token", TOKEN);
    return fetch(UPSTREAM + u.pathname + u.search, {
      method: req.method, headers, body: req.body,
    });
  },
});

It never sees a real key either — it only adds the capability token, exactly as the header recipes do. Keep it on loopback; it is a convenience for local tools, not a public proxy.

The port and token are ephemeral

Both are re-generated when the daemon restarts, so a hard-coded config will break after a reboot. For anything you run repeatedly, read them from stm broker at start-up (e.g. export STM_BROKER_TOKEN) rather than pasting a fixed value.

Beyond LLMs#

The broker is not LLM-specific — it forwards any HTTP request to a configured target. The launch set already includes non-model providers, and the same two moves apply. Calling Stripe through the broker, with no secret key in the command:

shell
curl http://127.0.0.1:<port>/proxy/stripe/default/v1/customers?limit=3 \
  -H "x-stm-token: <broker-token>"

Whatever the agent, whatever the API, the shape is identical: your keychain holds the key, the broker adds it on the way out, and every brokered call is audited by method, path, and status — never by key. Targets are a data-driven registry, so widening coverage is a single entry.