PromptHub
Back to Blog
Developer Tools Artificial Intelligence

angelnicolasc/graymatter: Persistent Memory for Stateless AI Agents

B

Bright Coding

Author

10 min read 74 views
angelnicolasc/graymatter: Persistent Memory for Stateless AI Agents

angelnicolasc/graymatter: Persistent Memory for Stateless AI Agents

Every AI agent starts stateless. Each run re-injects the full conversation history — and that history grows linearly. Two prompts in, you've burned half your daily quota. That's not a memory problem. That's a money and performance problem. Existing solutions like Mem0, Zep, and Supermemory require running servers, Python↗ Bright Coding Blog or TypeScript stacks, and ongoing infrastructure. The Go ecosystem has lacked a production-ready, embeddable, zero-dependency memory layer — until angelnicolasc/graymatter.

angelnicolasc/graymatter gives AI agents persistent memory in three lines of code. It reduces token consumption by approximately 90% while maintaining — and even improving — context quality over time. One binary. Drop it in. Run it. No Docker↗ Bright Coding Blog, no databases, no config files, no cloud accounts. This article breaks down what it does, how it works, and whether it fits your stack.


What is angelnicolasc/graymatter?

angelnicolasc/graymatter is a general-purpose MCP (Model Context Protocol) server and Go library for agent memory. Created by angelnicolasc, it currently holds 438 GitHub stars, 34 forks, and is released under the MIT License. The primary language is Go, with the last commit dated June 13, 2026.

At its core, GrayMatter solves a specific gap: stateless AI agents need memory, but existing solutions force infrastructure choices developers don't want to make. It functions as both a standalone binary (MCP server for Claude Code, Cursor, Codex, OpenCode, Antigravity, and any MCP-compatible client) and a plain Go library you import directly.

The project targets backend developers, DevOps↗ Bright Coding Blog engineers, and ML practitioners building CLI agents, automation tools, or embedded AI systems in Go. Its value proposition is deliberately narrow: be the missing stateful layer, not a framework, not a hosted service, not an enterprise knowledge base. This constraint is its strength — it does one thing, works offline, and imposes zero vendor lock-in.


Key Features

Zero-dependency deployment. GrayMatter ships as a single static binary (~10 MB). No Docker containers, no Redis instances, no PostgreSQL↗ Bright Coding Blog, no API keys for storage. The data layer uses bbolt (pure Go, ACID-compliant) for key-value storage and chromem-go for vector indexing — both embedded, both zero external dependencies.

MCP-native with broad client support. Auto-wires into Claude Code, Cursor, Codex (OpenAI), OpenCode, and Antigravity via graymatter init. For any other MCP client, graymatter mcp serve exposes standard stdio or HTTP transport. The schema is plain MCP — command plus args: ["mcp", "serve"] — no proprietary glue.

Hybrid retrieval with graceful degradation. Recall combines vector similarity, keyword matching (TF-IDF), and recency scoring via RRF fusion. If no embedding model is available, it falls back to keyword-only mode. Auto-detection order: Ollama → OpenAI → Anthropic → keyword. This means it works out of the box, then improves as you add capabilities.

Agent self-curation via memory_reflect. The LLM itself can add, update, forget, or link memories mid-session. Combined with background consolidation (summarization, decay, pruning), memory quality improves over time rather than accumulating noise.

Built-in observability. graymatter tui launches a live terminal dashboard showing facts stored, memory cost on disk, recall counts, health metrics, token spend breakdown by model, and activity timelines. Auto-refreshes every 5 seconds. No extra setup.

Durable vector reconciliation. If vector upsert fails after bbolt write succeeds, facts queue durably and retry on configurable intervals. Crash-safe, with PendingVectorCount() for health introspection.


Use Cases

Long-running CLI agents. A Go-based DevOps agent that executes infrastructure tasks across multiple sessions needs to remember "us-east-1 subnet-abc123 failed last Tuesday, avoid it." GrayMatter's Remember/Recall pattern injects this context without re-sending full history each time.

Cursor/Claude Code augmentation. Developers using AI-powered editors hit token limits on large codebases. GrayMatter's MCP integration lets the editor self-curate: store architectural decisions, coding preferences, and project conventions via memory_reflect, then recall them precisely when relevant.

Cost-sensitive autonomous systems. A 24/7 monitoring agent calling LLM APIs accumulates expensive context. GrayMatter's consolidation reduces ~90% of context tokens at 100 sessions (per benchmark: 6,960 tokens full injection vs. ~670 with GrayMatter). For high-volume agents, this directly reduces API spend.

Offline or air-gapped environments. No cloud accounts, no network calls for storage. Ollama-hosted embeddings work entirely locally. Suitable for security-conscious deployments where external SaaS memory is non-starter.

Cross-session workflow resumption. Checkpoints capture session state; checkpoint_resume restores them. An agent interrupted mid-task can pick up exactly where it left off, with full memory intact.


Installation & Setup

Binary install (recommended):

# Linux (x86_64)
curl -sSL -o graymatter.tar.gz https://github.com/angelnicolasc/graymatter/releases/download/v0.6.0/graymatter_0.6.0_linux_amd64.tar.gz
tar -xzf graymatter.tar.gz
sudo mv graymatter /usr/local/bin/

# Linux (ARM64)
curl -sSL -o graymatter.tar.gz https://github.com/angelnicolasc/graymatter/releases/download/v0.6.0/graymatter_0.6.0_linux_arm64.tar.gz
tar -xzf graymatter.tar.gz
sudo mv graymatter /usr/local/bin/

# macOS (Apple Silicon)
curl -sSL -o graymatter.tar.gz https://github.com/angelnicolasc/graymatter/releases/download/v0.6.0/graymatter_0.6.0_darwin_arm64.tar.gz
tar -xzf graymatter.tar.gz
sudo mv graymatter /usr/local/bin/

# Windows (PowerShell)
iwr https://github.com/angelnicolasc/graymatter/releases/download/v0.6.0/graymatter_0.6.0_windows_amd64.zip -OutFile graymatter.zip
Expand-Archive graymatter.zip -DestinationPath .\graymatter_cli

Go install:

go install github.com/angelnicolasc/graymatter/cmd/graymatter@latest

Library dependency:

go get github.com/angelnicolasc/graymatter

Each binary install downloads the v0.6.0 release, extracts the single static binary, and places it on PATH. /usr/local/bin/ is standard for macOS and Linux; the Windows PowerShell command creates a local directory. Go install requires a working Go toolchain and compiles from source. The library import is for embedding GrayMatter directly into your Go application.


Real Code Examples

Basic library usage (3 lines)

The README's core promise — three lines to persistent memory:

ctx := context.Background()
mem := graymatter.New(".graymatter")
mem.Remember(ctx, "agent", "user prefers bullet points, hates long intros")
facts, _ := mem.Recall(ctx, "agent", "how should I format this response?")
// ["user prefers bullet points, hates long intros"]

graymatter.New(".graymatter") opens or creates a store in the local directory. Remember stores a fact associated with an agent identifier. Recall retrieves relevant facts for a query — here, matching the stored preference against the formatting question. The underscore ignores errors for brevity; production code should handle them.

Production-ready pattern with health check

ctx := context.Background()
mem := graymatter.New(".graymatter")
defer mem.Close()

// Always check health in production — New() never panics, but it may degrade
// to no-op mode if the data dir is unwritable or bbolt fails to open.
if !mem.Healthy() {
    log.Fatalf("graymatter: %v", mem.Status().InitError)
}

// Store an observation.
mem.Remember(ctx, "sales-closer", "Maria didn't reply Wednesday. Third touchpoint due Friday.")

// Retrieve relevant context for a query.
facts, _ := mem.Recall(ctx, "sales-closer", "follow up Maria")
// ["Maria didn't reply Wednesday. Third touchpoint due Friday."]

The defer mem.Close() ensures clean shutdown. Healthy() and Status() expose initialization failures without panics — critical for production reliability. Context propagation means timeouts and cancellation work end-to-end.

Full agent pattern: recall before LLM call

ctx := context.Background()
mem := graymatter.New(project.Root + "/.graymatter")
defer mem.Close()
if !mem.Healthy() {
    log.Fatalf("graymatter: %v", mem.Status().InitError)
}

// 1. Recall before calling the LLM.
memCtx, _ := mem.Recall(ctx, skill.Name, task.Description)

messages := []anthropic.MessageParam{
    {Role: "system", Content: skill.Identity + "\n\n## Memory\n" + strings.Join(memCtx, "\n")},
    {Role: "user",   Content: task.Description},
}

// 2. Call your LLM.
response, _ := client.Messages.New(ctx, anthropic.MessageNewParams{...})

// 3a. If you already have a clean string worth keeping, store it directly.
mem.Remember(ctx, skill.Name, "Maria prefers Slack over email; replies within 2h.")

// 3b. Or let GrayMatter pull atomic facts out of the raw response for you.
//     Uses ANTHROPIC_API_KEY if set; otherwise stores the raw text as a single fact.
mem.RememberExtracted(ctx, skill.Name, responseText)

This demonstrates the complete lifecycle: prime the LLM with relevant memory, execute the task, then curate what to store. RememberExtracted uses an LLM to extract atomic facts from unstructured text, with fallback to raw storage if no API key is configured.


Advanced Usage & Best Practices

Always check Healthy() in production. New() degrades to no-op rather than panic, so silent failures are possible without this guard. Log Status().InitError for diagnostics.

Use context.WithTimeout for bounded operations. All public methods accept context.Context as the first argument. This propagates cancellation through bbolt transactions and vector operations — essential for responsive CLI tools.

Configure ConsolidateThreshold for your workload. Default is 20 facts. Lower values trigger consolidation sooner, useful for demos or rapidly evolving memory. Higher values reduce LLM API calls for consolidation.

Set OnVectorIndexError and VectorReconcileInterval hooks. The default 30-second retry interval works for most cases; tune based on your durability requirements. The hook enables alerting on vector lag without polling.

Prefer graymatter init over manual MCP config. It merges rather than overwrites existing entries, preserves your CLAUDE.md/AGENTS.md content, and writes the agent instruction block that tells models to actually use memory tools — a common failure mode when wiring manually.


Comparison with Alternatives

angelnicolasc/graymatter Mem0 Zep
Language Go (library + binary) Python/TypeScript Python
Deployment Single static binary, embedded Server required Server required
Dependencies Zero (bbolt + chromem-go embedded) Redis, PostgreSQL, etc. PostgreSQL, etc.
MCP support Native, auto-wired Via wrapper Via wrapper
Offline capable Yes — keyword fallback, Ollama No No
Go ecosystem fit Native FFI/binding needed FFI/binding needed
Maturity v0.6.0, active development More mature, broader features More mature, enterprise focus

GrayMatter trades breadth for embeddability. Mem0 and Zep offer richer feature sets and larger communities but require running infrastructure and Python/TypeScript stacks. For Go-native, zero-dependency deployments — especially CLI tools and embedded agents — GrayMatter fills a specific gap. If you need enterprise multi-tenant features or already run Python infrastructure, evaluate Mem0 or Zep seriously.


FAQ

What license is angelnicolasc/graymatter under? MIT License — free for commercial and personal use.

Does it require an API key? No for storage. Optional for embeddings (OpenAI/Anthropic) and consolidation; Ollama and keyword modes work without keys.

What Go version is required? The README doesn't specify; check go.mod in the repository for minimum version.

Can multiple processes share one store? Daemon mode (v0.6.0) enables concurrent access via local socket; otherwise bbolt's single-writer lock limits one process.

How do I debug "MCP connected but nothing stored"? Run graymatter doctor. Most common causes: missing agent instructions in CLAUDE.md/AGENTS.md, or an orphaned manual graymatter mcp serve holding the store lock.

Is Windows fully supported? Yes — CI runs on Linux, macOS, and Windows. Binary releases available for all three.

What's the test coverage? 73.5% for core library, measured with real bbolt + chromem-go instances (not mocks).


Conclusion

angelnicolasc/graymatter is purpose-built for developers who need persistent agent memory without infrastructure overhead. It excels in Go-native environments, offline deployments, and cost-sensitive applications where token reduction directly impacts API spend. The MCP integration makes it immediately usable with popular AI editors, while the library form factors cleanly into custom agents.

It's not a replacement for full-featured memory platforms like Mem0 or Zep if you need enterprise multi-tenancy or already run Python infrastructure. But if you're building Go CLI tools, embedding memory into existing applications, or simply refuse to run another database — GrayMatter is the specific, credible solution the ecosystem has lacked.

Ready to cut your agent's token usage? Get started at https://github.com/angelnicolasc/graymatter.

Comments (0)

Comments are moderated before appearing.

No comments yet. Be the first to share your thoughts!

Recommended Prompts

View All