Skip to content
mercury framework
GitHub

HTTP channel

Package
@mercury-fw/channel-http
Role
Channel
Version
0.13.2
Terminal window
bun add @mercury-fw/channel-http
VariableWhat it is
HTTP_SURFACE_PORTPort of the HTTP surface (default 4100)
HTTP_SURFACE_CORS_ORIGINOrigin allowed to call the HTTP surface from a browser, if any
HTTP_SURFACE_PUBLIC_URLAddress people's browsers reach the HTTP surface at, for logging in to the services plugins act on as them
HTTP_SURFACE_RESUME_MAX_CHARSCharacters of a reopened conversation that come back to the model (empty: the core's SESSION_RESUME_MAX_CHARS, 0: off)

The HTTP channel lets any program talk to the agent: a web page you build, a mobile app, a script. Each message is one request, and the answer comes back as it’s written, as a stream of Server-Sent Events (a standard way for a server to send a sequence of messages over one HTTP response). Next to it there are routes to confirm actions and to read what the agent holds: conversations, the wiki, the memory, its plugins.

It needs an auth provider to tell who is calling, and doesn’t start without one: OpenID Connect for real people, static tokens to try it on your machine.

Create an app with the channel and static tokens:

Terminal window
bun create mercury-agent http-agent --channels http --auth static --yes
cd http-agent
cp .env.example .env

In .env, fill in OLLAMA_MODEL as in Your first agent, and give a test user a token:

.env
AUTH_STATIC_TOKENS={"alice-token": {"id": "alice", "displayName": "Alice"}}

Start it, and the log says the channel is up:

Terminal window
bunx mfw start
bunx mfw logs mercury
mercury-1 | [channel] http started

Now send a message as Alice. -N makes curl print the events as they arrive:

Terminal window
curl -N -X POST localhost:4100/turn \
-H 'Authorization: Bearer alice-token' \
-H 'Content-Type: application/json' \
-d '{"text": "Hi! In one sentence, who are you?", "conversationId": "first"}'

The answer arrives in pieces (text events), then whole (final):

event: text
data: {"chunk":" documents"}
event: text
data: {"chunk":"."}
event: final
data: {"text":"I am Mercury, an internal assistant with access to the team's wiki documents."}

Send the same conversationId again to continue the conversation. A wrong token gets 401 before anything runs.

An app created with --channels http has all of this already. In an existing app, bun add @mercury-fw/channel-http and an auth provider (here @mercury-fw/auth-static), and declare both:

mercury.config.ts
import { httpChannel } from "@mercury-fw/channel-http";
import { staticAuth } from "@mercury-fw/auth-static";
channels: [httpChannel],
auth: staticAuth,

The channel listens inside the container, so the compose file has to publish its port on your machine. Under the mercury service in docker-compose.yml:

docker-compose.yml
ports:
- "${HTTP_SURFACE_PORT:-4100}:${HTTP_SURFACE_PORT:-4100}"
  • Every conversation belongs to whoever opened it. The same conversationId sent by someone else is a conversation of their own.
  • Confirmations arrive as a pending event with a token for each choice; send the token back as the next message’s text, or to POST /confirm. See Confirmations.
  • Logins to the services plugins act on as the person arrive as a login event with a link. They need HTTP_SURFACE_PUBLIC_URL, the address people’s browsers reach the channel at, because the service sends them back to <HTTP_SURFACE_PUBLIC_URL>/login/callback.
  • Browsers on another origin can call it: set HTTP_SURFACE_CORS_ORIGIN to your interface’s origin.
  • Languages. The texts the person reads that aren’t the model’s (a status line, a choice’s label, a confirmation’s reply) come in their language: the one they asked the agent for, else the one the request’s Accept-Language header asks for, which browsers send on their own. A plain confirmation has one choice with an empty label, which your interface words itself (a “Confirm” button), and when the model wrote nothing because the turn is waiting for a confirmation, the final event says so with pendingNote, so your interface can hide that sentence next to its own confirmation. See Languages.
  • The time. The model reads each message with the time the person wrote it, in their time zone: send the browser’s in an X-Time-Zone header (Intl.DateTimeFormat().resolvedOptions().timeZone gives it), and offer it in your settings with PATCH /me, which saves it for the person. A zone they saved wins over the header. See The time where the person is.
  • Which Mercury it is. GET /version answers the framework’s version ({ "ok": true, "version": "0.59.0" }) with no token, so your interface can show it anywhere, the login page included.

The HTTP API reference describes every route and event.