# Setting Ferrow up with the agent you already have

You do not have to switch terminals to use Ferrow. If you already work inside Claude Code, Codex,
Aider, or anything else that can run shell commands, that agent can install and configure Ferrow for
you, and then keep using it as its own memory layer over MCP.

This page is the contract it executes. Every step has a command, the output that means it worked,
and what to do when it does not. That is what makes it agent-executable rather than a blog post: an
agent can check its own work at each step instead of guessing whether the last command landed.

Two things this contract will never do, and both are deliberate:

* It never types an API key for you. Connecting a provider asks for a secret, and a secret belongs to
  a human. The agent runs the command, then hands the keyboard back.
* It never turns on writes or skills behind your back. `ferrow serve-mcp` starts read-only. Enabling
  anything more is one file you edit on purpose, with the agent telling you exactly what changes.

## The prompt

Paste this into your agent. It is the whole contract; nothing below is required reading for the
machine, only for you.

```text
Install and configure Ferrow on this machine, then register it as one of your own MCP servers.
Work through the steps in order. After each step, run its VERIFY command and check the output
against EXPECT. If a VERIFY fails, stop and tell me what the command actually printed. Do not
continue past a failed step, do not use sudo, and do not invent or type any API key: when a step
needs a secret from me, stop and ask.

STEP 1 - Node
  RUN     node --version
  EXPECT  v20 or higher
  IF NOT  Stop. Tell me to install Node 20 from nodejs.org. Do not install it yourself.

STEP 2 - Install Ferrow
  RUN     npm install -g https://ferrow.ai/dl/ferrow.tgz
  VERIFY  ferrow --version
  EXPECT  a line beginning "ferrow"
  IF NOT  If the shell cannot find `ferrow`, print `npm bin -g` and tell me that directory is not
          on my PATH. Do not work around it by calling the tarball directly.

STEP 3 - The front door
  RUN     ferrow
  EXPECT  a one-line banner, then "Model provider:" and "Vault:" telling you the current state
          of this machine, then the single next step it recommends
  NOTE    This command is safe to run at any point. It reads local state only, never the network.

STEP 4 - The vault
  RUN     ferrow vault init
  EXPECT  "Initialized vault at <path>" (or "Vault: <path>" if one is already there), then
          "+ memory/profile.md" and "+ memory/preferences.md"
  VERIFY  ferrow log
  EXPECT  a table of changes, one row per file just written, each with an id like chg_...
  IF NOT  If it says "No recorded changes yet", the init did not write. Show me the output.

STEP 5 - A brain
  This is the step that needs me. Read the state you saw in STEP 3 and pick ONE:
  (a) I already have a key with a provider:
      RUN     ferrow connect openai        (or: anthropic, google, openrouter, custom)
      EXPECT  it asks for the key. STOP HERE and ask me to paste it. Do not type anything.
  (b) I run models locally:
      RUN     ferrow detect
      EXPECT  a list naming ollama, lmstudio, jan, gpt4all, llamacpp or vllm
      IF NOT  Tell me nothing local was found, and offer option (a).
  (c) I pay Ferrow to host the models:
      RUN     ferrow billing
      EXPECT  if this machine already carries a Ferrow token, the plan and its renewal date.
              If it says there is no subscription, tell me: starting one is `ferrow subscribe
              --managed`, it opens a checkout in my browser, and it is my decision, not yours.
  VERIFY  ferrow models
  EXPECT  at least one model listed, with the source it came from
  IF NOT  Nothing is connected yet. Say so plainly and stop; every later step needs a model.

STEP 6 - Prove it works end to end
  RUN     ferrow note inbox/setup.md "Ferrow was set up by my terminal agent on <today's date>"
  EXPECT  "Wrote inbox/setup.md  (chg_...)"
  RUN     ferrow remember prefers-terminal "I work from a terminal agent, not the Ferrow app"
  EXPECT  a line beginning "Remembered" and naming memory/facts/prefers-terminal.md
  VERIFY  ferrow search terminal
  EXPECT  at least one path:line row, ending in a "N match(es) in N note(s)" count
  IF NOT  Show me the output of `ferrow log`, which lists every write Ferrow has made.

STEP 7 - Register Ferrow as one of your MCP servers
  VERIFY FIRST  ferrow serve-mcp --check
  EXPECT        "Protocol: dual", a table where vault.read is on and vault.write is off, and a
                final line "Ready."
  IF NOT        Show me the output. A malformed ~/.ferrow/serve-mcp.json refuses to serve rather
                than guessing what I meant to expose, and the error names the file.
  THEN, if you are Claude Code:
    RUN     claude mcp add ferrow -- ferrow serve-mcp
    VERIFY  claude mcp list
    EXPECT  a "ferrow" entry
  If you are any other MCP client, add this to your own config instead and tell me where you put
  it:
    {"mcpServers":{"ferrow":{"command":"ferrow","args":["serve-mcp"]}}}
  VERIFY  restart or reload yourself, then call the tool vault_search with query "terminal"
  EXPECT  the note written in STEP 6
  IF NOT  Run `ferrow serve-mcp --check` again and show me both that output and the error your
          MCP client reported.

STEP 8 - Report
  Tell me, in five lines: where the vault is, which provider is connected, whether the MCP server
  is registered, which tool groups are exposed, and the one command that stops everything
  (`ferrow halt`).
```

## What the agent gets, and what it does not

With no configuration file, `ferrow serve-mcp` exposes three tools:

| Tool | Group | What it does |
|---|---|---|
| `vault_search` | `vault.read` | Ranked search across every note, returning `path:line` and a snippet |
| `vault_read` | `vault.read` | The full text of one note |
| `memory_recall` | `memory.read` | The durable facts Ferrow keeps about you |

Off by default, and named in the refusal if something calls them anyway:

| Tool | Group | What it does |
|---|---|---|
| `vault_note` | `vault.write` | Write or append a note, through the change log, undoable |
| `memory_remember`, `memory_forget` | `memory.write` | Teach or trash one durable fact |
| `skill_<name>` | `skills` | Run one installed, trusted skill inside its own capability contract |

A refusal names the tool, the group, the config file, and the edit that would turn it on. There is
no hidden state: `ferrow serve-mcp --check` prints exactly what a connecting client will see.

## Turning writes on

Writes are one file, and it is yours:

```bash
$EDITOR ~/.ferrow/serve-mcp.json
```

```json
{
  "protocol": "dual",
  "expose": {
    "vault.read": true,
    "vault.write": true,
    "memory.read": true,
    "memory.write": true,
    "skills": false
  }
}
```

VERIFY: `ferrow serve-mcp --check` now shows `vault.write on`, then restart the MCP client so it
picks up the new tool list.

Every write an agent makes lands in the change log attributed to `mcp-server:<client>`, so
`ferrow log` shows which agent on which client wrote what, and `ferrow log revert <id>` undoes any
of it. That is the reason writes are safe to grant at all, and the reason they are not granted by
default anyway.

Two notes on the group that runs skills:

* Only installed skills that pass the trust gate are offered. A skill that came from a registry or a
  URL and was signed by a key you have not trusted is not listed and cannot be run, which is the
  same answer `ferrow run` gives, decided by the same code.
* A skill still runs inside its own capability contract. Reaching Ferrow through MCP grants nothing
  that running it in your own terminal would not, and a skill whose contract requires confirmation is
  refused over MCP rather than auto-approved, because there is no human at the other end of a pipe.

## Stopping it

```bash
ferrow halt "reason"     # refuse to start anything new, including MCP tool calls
ferrow resume            # lift it
```

The switch is a file (`~/.ferrow/halt`), so it survives a crash and a reboot, and deleting the file
does the same thing as `ferrow resume`. A server that is already running and already connected
refuses its next call while that file exists, and says which file to delete. Work already in flight
finishes; nothing is killed.

## Pinning the protocol

MCP revision 2026-07-28 is stateless: there is no handshake, and every request carries its own
version. Almost every client deployed today still opens with the older `initialize` handshake.
Ferrow serves both, which is what `"protocol": "dual"` means and is the default. If a client
mishandles one era, pin it:

```json
{ "protocol": "legacy" }
```

`"modern"` is the other direction. `ferrow serve-mcp --check` prints which one is in force.

## Where this lives

* This document: `docs/SETUP-VIA-AGENT.md` in the repository, and the same steps on
  [ferrow.ai/download](https://ferrow.ai/download).
* The server: `src/mcp/server.mjs` (protocol and exposure) and `src/cli/serve-mcp-cmd.mjs` (the tool
  table, which is the whole capability surface, in one screen).
* Its tests: `test/mcp-serve.test.mjs`, which drives the real server with Ferrow's own MCP client.
