Skip to content
mercury framework
GitHub

Your first agent

By the end of this page you’ll have an agent running in Docker on your machine, answering you in a terminal, and using a small tool of your own that reads a secret from its configuration. Every command below was run as written, and the outputs are copied from that run.

You need three things installed:

  • Bun, the JavaScript runtime Mercury runs on. Install it from bun.sh, then check it with bun --version.
  • Docker, where the agent runs (on a Mac, Docker Desktop). docker --version should answer, and Docker Desktop must be running.
  • Ollama, the program that serves the language model. Install it from ollama.com and pull two models: one that talks and can call tools, and one that turns text into numbers for the memory (an embedding model).
Terminal window
ollama pull <chat-model>
ollama pull nomic-embed-text

<chat-model> is yours to choose: any model in Ollama’s library that supports tool calling works, and a bigger one answers better if your machine can take it. The agent runs inside Docker and reaches Ollama on your machine at the address host.docker.internal, which is already the default; an Ollama on another machine works too, through OLLAMA_HOST (step 2).

An app is a folder with your agent in it: its configuration, its personality, and what Docker needs to run it. bun create mercury-agent writes one for you.

Terminal window
bun create mercury-agent my-agent --yes

--yes takes the defaults for everything. Without it, the command asks you the app’s name, the assistant’s name and role, which channels and plugins to include, and a git remote; you can leave every answer at its default for now. It ends like this:

Created my-agent in /…/my-agent
bun install: done
git: first commit on main
Next:
cd /…/my-agent
cp .env.example .env # then fill it in
mfw start # bunx mfw start, without a global mfw
Optional, to have mfw everywhere:
bun add -g @mercury-fw/cli

The app is also a git repository with a first commit, so you can see every change you make from here on.

The settings that change from one machine to the next, and every secret, live in a file called .env, which is never committed. The app comes with an example to copy:

Terminal window
cd my-agent
cp .env.example .env

Open .env and fill in the chat model you pulled, the only line left empty:

.env
OLLAMA_MODEL=<chat-model>

The other lines already point at Ollama on your machine (OLLAMA_HOST; set it to http://<host>:11434 when Ollama runs elsewhere), at the embedding model, and at the two places Mercury keeps its memory. The environment reference explains each one.

mfw is Mercury’s command-line tool, and the app has its own copy: bunx mfw runs it.

Terminal window
bunx mfw start

The first start builds the Docker image, which takes a while. It starts two containers, the agent and Qdrant, the database behind the long-term memory:

Container my-agent-qdrant-1 Started
Container my-agent-mercury-1 Started

The agent is now a service waiting for messages from its channels. It has none yet, so the way to talk to it is the terminal.

Terminal window
bunx mfw repl

A REPL is an interactive prompt: type a message, press Enter, read the answer. This is what the run above printed for “Hi! Who are you?” (the model’s reasoning, printed in grey after Thinking…, is left out, and your model words its answer its own way):

[~?k tokens] > Hi! Who are you?
[turn] start
Thinking…
I'm Mercury, an internal assistant for your team. I can search and read the shared wiki, keep personal notes for you, and help with questions about team conventions, project status, and workflows.
[turn] done in 7.1s: 1 step, 0 tool calls, reasoning 232 chars, answer 196 chars, finish stop
[~2k/~262k tokens] >

The [turn] lines are for you, not for the person chatting: how long the answer took, and how many tools the model called. It called none: the wiki it mentions is its own memory, which every app has, and no tool of yours is there yet. The prompt shows how much of the model’s context the conversation fills. Press Ctrl+D to leave the REPL. The agent keeps running until bunx mfw stop.

An agent without tools can only talk. What it can do comes from plugins, and a plugin can be a package from npm or a file in your app. Here you’ll write one: a tool that tells people the office guest Wi-Fi, whose password lives in .env like every secret should.

Your first plugin walks through it line by line. In short, add the packages a plugin is written with:

Terminal window
bun add @mercury-fw/kit ai zod

then create src/plugins/guest-wifi.ts with the plugin from that page, declare it in mercury.config.ts, and add its two settings to .env:

.env
GUEST_WIFI_NAME=Office-Guest
GUEST_WIFI_PASSWORD=correct-horse-battery

Restart, so the image picks up the new file and the container the new settings:

Terminal window
bunx mfw restart
Terminal window
bunx mfw repl
[~?k tokens] > How do I get on the guest Wi-Fi?
[turn] start
Thinking…
Using guest_wifi…
[tool] guest_wifi({})
[tool result] {"ok":true,"network":"Office-Guest","password":"correct-horse-battery"}
Thinking…
Guest Wi-Fi:
- Network: Office-Guest
- Password: correct-horse-battery
Just connect to "Office-Guest" and enter the password and you should be online.
[turn] done in 15.9s: 2 steps, 1 tool call, reasoning 147 chars, answer 151 chars, finish stop

Using guest_wifi… is the status line the person sees while the tool runs, and the [tool] lines show you the model’s call and what came back: the answer is built on it. The password never appeared in your code, only in .env.

  • Concepts explains the pieces you just used: the app, the core, channels, plugins.
  • Plugins lists the ready-made ones, and how to add each.
  • To let people reach the agent from somewhere other than your terminal, add a channel: Google Chat or HTTP.