Skip to main content
Build your own agent

An agent that remembers. On your devices, not ours.

Install Gnarly, open the starter template, and paste one prompt into Claude Code, Cursor or Codex. Your assistant builds the agent; Gnarl gives it a memory that never leaves the people it belongs to.

Four steps

From nothing to an agent with memory.

  1. 01

    Install Gnarly

    The desktop app runs a Gnarl node on your machine. Memory is embedded and stored there by a local model: no account, no API key, no cloud copy.

    Download Gnarly
  2. 02

    Open the starter

    Use the agent-starter template: a working agent in Python and TypeScript, plus AGENTS.md, CLAUDE.md and a Cursor rule that teach your assistant the real memory API.

    agent-starter on GitHub
  3. 03

    Paste one prompt

    Open the folder in Claude Code, Cursor or Codex and paste a prompt. The assistant reads AGENTS.md first, so it builds on what the node actually serves.

    See the prompts
  4. 04

    It remembers, privately

    Your agent recalls what each user told it, across sessions. Paste your subscription key on your other devices and that memory follows you, without passing through anyone else.

    How agent memory works
Your coding assistant

Give the assistant memory while it builds.

Gnarly also serves MCP at http://127.0.0.1:43300/mcp, to this machine only. The template already wires it up for all three assistants, so decisions made while building are still there next session. Your agent itself uses the client libraries below, not MCP.

Project scope: .mcp.json at the repository root. Claude Code asks you to approve it the first time you open the folder.

.mcp.json
{
"mcpServers": {
"gnarl": {
"type": "http",
"url": "http://127.0.0.1:43300/mcp"
}
}
}
# Already done for you: the template ships .mcp.json.
# To add the same server anywhere else:
claude mcp add --transport http gnarl http://127.0.0.1:43300/mcp

Already clicked Connect in Gnarly’s agent memory settings? Then your assistant has this server under another name, and you can switch the template’s copy off.

One prompt

Paste it, then watch it build.

Every prompt in the template starts the same way: read AGENTS.md first. That file holds the exact memory calls, how to scope them to a user, and what to do when the node is busy or missing, so the assistant does not have to guess.

  • Meeting notes that remember decisions
  • A personal research assistant
  • Customer support with per-customer memory
  • A journaling companion
  • A CLI helper that remembers project conventions
  • A team knowledge bot
Prompt: meeting-notes agent
Read AGENTS.md first and follow it exactly. Using the Python starter in python/ as the base, build a meeting-notes agent. I paste in raw notes or a transcript; it extracts decisions, owners and deadlines and stores each as one self-contained memory with fact_type "decision" in namespace "meetings", user set from a --team flag and session set to the meeting date. Before storing, recall and skip anything already known. Add a "what did we decide about X?" command that recalls across all meetings and cites the meeting date of each memory. Never store anything looks_secret() flags. Add unit tests with a fake memory, and a smoke test that skips when no node answers.

Want to see memory work before any model is involved? Run the starter with no API key:

cd python
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
python -m gnarl_agent --user alice --no-llm
Two calls

Recall before answering. Remember after.

That is the whole memory loop. Each app gets its own namespace; each person their own user, so one user’s recall never returns another’s; each conversation a session. Embedding happens on the node, and the client finds the node itself: no address, no key.

from gnarl import Client # pip install gnarl-client==0.2.1

with Client() as c: # finds the Gnarly app on this machine
c.memory.remember("Alice prefers window seats on morning flights",
namespace="my-agent", user="alice", session="chat-42")
res = c.memory.recall("how does Alice like to fly?",
namespace="my-agent", user="alice", k=5)
for m in res.memories:
print(f"{m.score:.2f} {m.content}")

On a framework already? Adapters plug Gnarl in as its memory: LangChain and LangGraph, LlamaIndex and the Vercel AI SDK.

What private means here

The memory stays with the person it is about.

  • Text is embedded by a model on the node. No embedding API sees it.
  • Memories are stored in the node’s own data directory. There is no Gnarl cloud copy.
  • The desktop node answers on loopback; its MCP endpoint refuses other machines.
  • Sharing between devices goes over your private mesh, peer to peer.
  • Recall is partitioned per user, and per app by namespace. With a token, the node binds each call to the token’s user.
  • Copies on your other devices are not encrypted yet. On a mesh shared with other people, their devices can read them.
  • The only text that leaves is what your agent sends to an LLM, and the starter runs with none.

Build the agent. Keep the memory.

Five minutes from install to an agent that knows who it is talking to, and tells nobody else.