@loominal/pattern
v0.4.0
Published
MCP server for hierarchical agent memory
Maintainers
Readme
Pattern
MCP server providing hierarchical memory for AI agents.
Beta: Core functionality tested and stable. Part of the Loominal multi-agent infrastructure.
Overview
Pattern is a Model Context Protocol (MCP) server that provides hierarchical memory capabilities for AI agents. It enables agents to:
- Remember information across sessions with automatic expiration
- Share learnings with other agents in the same project
- Recall context efficiently at session startup
- Isolate memories by project and agent for security
- Unified identity with Warp for consistent agent identification
Agent Identity
Pattern v0.2.0 integrates with Warp's unified identity system. Instead of generating ephemeral IDs, Pattern reads agent identity from Warp's NATS KV store.
How It Works
- Warp initializes identity at startup, storing it in NATS KV bucket
loom-identity-{projectId} - Pattern loads identity with retry logic (10 attempts, ~5.5s total wait time)
- Same agent, same memories: Restart Claude Code and your memories persist
Multi-Machine Scenarios
For agents running on multiple machines that should share the same identity and memories, set LOOMINAL_AGENT_ID in Warp's configuration to override the automatic ID derivation.
Sub-Agent Memory Access
Sub-agents (spawned by root agents for specialized tasks) can access their parent's memories:
| Parent Memory Scope | Sub-Agent Access |
|---------------------|------------------|
| private (recent, tasks, longterm) | Read access |
| personal (core) | No access (identity-defining, protected) |
| team | Full read/write |
| public | Full read/write |
Quick Start
Prerequisites
- Node.js >= 18.0.0
- NATS server with JetStream enabled
Installation
npm install @loominal/patternOr run directly:
npx @loominal/patternConfiguration
Set environment variables:
export NATS_URL="nats://localhost:4222" # NATS server URL
export LOOMINAL_PROJECT_ID="my-project" # Project isolation key (optional, derived from path)
export LOOMINAL_SUBAGENT_TYPE="specialized" # Set by parent agent when spawning sub-agents
export DEBUG="true" # Enable debug logging (optional)Running
# Start the MCP server
pattern
# Or with npm
npm startClaude Code Integration
Add to your Claude Code MCP settings (~/.claude/settings.json):
{
"mcpServers": {
"pattern": {
"command": "npx",
"args": ["@loominal/pattern"],
"env": {
"NATS_URL": "nats://localhost:4222",
"LOOMINAL_PROJECT_ID": "my-project"
}
}
}
}Memory Model
Unified Scope System
Pattern uses a 4-value scope system that determines both visibility and storage location:
| Scope | Visibility | Storage | Use Case |
|-------|------------|---------|----------|
| private | Just this agent, this project | Project bucket | Working notes, temp observations |
| personal | Just this agent, everywhere | User bucket | Profile, preferences, cross-project identity |
| team | All agents in this project | Project bucket | Project decisions, shared learnings |
| public | All agents everywhere | Global bucket | Global knowledge, public templates |
Private/Personal Categories (Agent-Specific)
| Category | TTL | Default Scope | Description |
|----------|-----|---------------|-------------|
| recent | 24h | private | Short-term observations and learnings |
| tasks | 24h | private | Current work items and todos |
| longterm | None | private | Permanent insights worth keeping |
| core | None | personal | Identity-defining memories (follows user across projects) |
Team/Public Categories (Shared)
| Category | Scope | Description |
|----------|-------|-------------|
| decisions | team | Project decisions and rationale |
| architecture | team | Architecture choices and patterns |
| learnings | team | Knowledge shared across agents |
MCP Tools
remember
Store a new memory with specified scope and category.
{
"content": "The API uses REST with JSON responses",
"scope": "private", // "private" | "personal" | "team" | "public"
"category": "longterm",
"metadata": {
"tags": ["api", "architecture"],
"priority": 1
}
}Security Note (v0.3.1+): Content is scanned for common secret patterns (API keys, passwords, etc.) and warnings are issued if detected. Never store credentials directly. See Security Best Practices.
remember-task
Quick shorthand to remember a task (private, 24h TTL).
{
"content": "Fix the authentication bug in login.ts"
}remember-learning
Quick shorthand to remember a learning (private, 24h TTL).
{
"content": "The database uses PostgreSQL with pgvector"
}commit-insight
Promote a temporary memory to permanent storage.
{
"memoryId": "550e8400-e29b-41d4-a716-446655440000",
"newContent": "Updated insight with more details"
}core-memory
Store an identity-defining memory with personal scope (follows user across projects, max 100 per agent).
{
"content": "I am a coding assistant that prioritizes test coverage"
}Security Note: Core memories use personal scope and follow the agent across all projects. Never store project-specific secrets in core memories. Sub-agents cannot access parent core memories for additional protection.
forget
Delete a memory by ID.
{
"memoryId": "550e8400-e29b-41d4-a716-446655440000",
"force": true // Required for core memories
}recall-context
Retrieve memory context at session start with powerful filtering and search capabilities.
Basic Parameters:
{
"scopes": ["private", "personal", "team"], // Filter by scope
"categories": ["core", "longterm", "decisions"], // Filter by category
"limit": 50, // Max memories to return (default: 50, max: 200)
"since": "2025-01-01T00:00:00Z" // Only memories updated after this
}Advanced Filtering (v0.4.0+):
{
// Tag filtering (AND logic - memory must have all tags)
"tags": ["api", "documentation"],
// Priority filtering (1=high, 2=medium, 3=low)
"minPriority": 1, // Minimum priority
"maxPriority": 2, // Maximum priority
// Date range filtering
"createdAfter": "2025-01-01T00:00:00Z", // Created after this date
"createdBefore": "2025-01-31T00:00:00Z", // Created before this date
"updatedAfter": "2025-01-15T00:00:00Z", // Updated after this date
"updatedBefore": "2025-01-31T00:00:00Z", // Updated before this date
// Content search (case-insensitive)
"search": "authentication"
}Examples:
Find high-priority API documentation created this month:
{
"tags": ["api", "docs"],
"maxPriority": 1,
"createdAfter": "2025-01-01T00:00:00Z",
"search": "REST"
}Find recent architecture decisions:
{
"scopes": ["team"],
"categories": ["architecture", "decisions"],
"updatedAfter": "2025-01-01T00:00:00Z"
}Returns:
{
"private": [...],
"personal": [...],
"team": [...],
"public": [...],
"summary": "Key points from your memories...",
"counts": {
"private": 25,
"personal": 5,
"team": 10,
"public": 0,
"expired": 5
}
}Filter Logic:
- All filters are applied as AND conditions (memories must match all specified filters)
- Tag filtering uses AND logic (memory must have ALL specified tags)
- Memories without priority metadata default to priority 2 (medium)
- Empty or unspecified filters are ignored (e.g.,
tags: []returns all memories)
share-learning
Share a private/personal memory with all project agents (moves to team scope).
{
"memoryId": "550e8400-e29b-41d4-a716-446655440000",
"category": "learnings",
"keepOriginal": false
}Security Note: Sharing makes the memory visible to all agents in the project. Review content for sensitive information before sharing. Once shared to team scope, it cannot be un-shared (only deleted).
cleanup
Run maintenance tasks to expire TTL memories and enforce limits.
{
"expireOnly": false
}export-memories
Export memories to a JSON file for backup or transfer. Supports filtering by scope, category, and date range.
{
"outputPath": "/path/to/backup.json", // Optional, defaults to memories-backup-TIMESTAMP.json
"scope": "private", // Optional: "private" | "personal" | "team" | "public"
"category": "longterm", // Optional: filter by category
"since": "2025-01-01T00:00:00Z", // Optional: only export memories updated after this
"includeExpired": false // Optional: include expired memories (default: false)
}Returns:
{
"exported": 42,
"filepath": "/absolute/path/to/backup.json",
"bytes": 8192
}Use Cases:
- Create backups before making major changes
- Export specific categories for sharing or archival
- Transfer memories between agents or projects
- Export recent changes using the
sinceparameter
Security Note: Exported files are unencrypted JSON. Store backups securely and review contents before sharing. Never commit backup files with sensitive information to version control.
import-memories
Import memories from a JSON backup file created by export-memories.
{
"inputPath": "/path/to/backup.json", // Required: path to backup file
"overwriteExisting": false, // Optional: overwrite if memory ID exists (default: false)
"skipInvalid": true // Optional: skip invalid entries instead of failing (default: true)
}Returns:
{
"imported": 40,
"skipped": 2,
"errors": [
"Memory mem-123 already exists (use overwriteExisting to replace)",
"Invalid memory structure: mem-456"
]
}Validation:
- Checks JSON format and export version
- Validates memory structure and required fields
- Validates scope/category combinations
- Checks for duplicate memory IDs (unless
overwriteExistingis true)
Error Handling:
- With
skipInvalid: true(default): Continues importing valid memories, reports errors - With
skipInvalid: false: Stops on first error and throws exception
Security Note: Import validates data but trusts the source file. Only import from trusted sources. Review the backup file contents before importing, especially when restoring from external sources.
remember-bulk
Store multiple memories at once with validation and error handling. Efficiently batch-store memories with optional pre-flight validation.
{
"memories": [
{
"content": "First memory",
"category": "longterm"
},
{
"content": "Second memory",
"scope": "team",
"category": "decisions",
"metadata": {
"tags": ["important"],
"priority": 1
}
},
{
"content": "Third memory",
"scope": "personal",
"category": "core"
}
],
"stopOnError": false, // Optional: stop on first error vs continue (default: false)
"validate": true // Optional: validate all before storing any (default: true)
}Returns:
{
"stored": 3,
"failed": 0,
"errors": [],
"memoryIds": [
"550e8400-e29b-41d4-a716-446655440000",
"6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"7c9e6679-7425-40de-944b-e07fc1f90ae7"
]
}Parameters:
memories: Array of memory objects, each with the same fields as theremembertoolstopOnError: If true, stops on first error; if false (default), continues and reports all errorsvalidate: If true (default), validates all memories before storing any; if false, validates during storage
Validation (when validate: true):
- Pre-flight validation checks all memories before storing
- Validates content size (max 32KB), scope/category combinations, metadata structure
- If any validation errors occur, nothing is stored and a PatternError is thrown
- All validation errors are reported with their array indices
Error Handling:
- With
stopOnError: false(default): Continues storing valid memories, reports all errors - With
stopOnError: true: Stops at first error, returns partial results - Errors include the array index and error message for troubleshooting
Use Cases:
- Bulk import of memories from external sources
- Initializing agent memory with multiple entries
- Batch creation of project documentation or decisions
- Migrating memories between systems
Performance: Not atomic - failures can leave partial results. Use validation to catch errors upfront.
Security Note: Each memory is scanned individually for sensitive content (same as remember tool). Review warnings in logs.
forget-bulk
Delete multiple memories by IDs with error handling. Efficiently batch-delete memories with optional force flag for core memories.
{
"memoryIds": [
"550e8400-e29b-41d4-a716-446655440000",
"6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"7c9e6679-7425-40de-944b-e07fc1f90ae7"
],
"stopOnError": false, // Optional: stop on first error vs continue (default: false)
"force": false // Optional: force delete core memories (default: false)
}Returns:
{
"deleted": 3,
"failed": 0,
"errors": []
}Parameters:
memoryIds: Array of memory IDs (UUIDs) to deletestopOnError: If true, stops on first error; if false (default), continues and reports all errorsforce: If true, allows deletion of core memories; if false (default), core memories are protected
Error Handling:
- With
stopOnError: false(default): Continues deleting valid memories, reports all errors - With
stopOnError: true: Stops at first error, returns partial results - Common errors: memory not found, core memory without force flag, access denied (for team/public memories created by others)
- Errors include the memory ID and error message for troubleshooting
Access Control:
- Can only delete your own private/personal memories
- Can only delete team/public memories you created
- Core memories require
force: trueflag - Same rules as the
forgettool apply to each memory
Use Cases:
- Cleanup of multiple obsolete memories
- Bulk removal of temporary or expired entries
- Clearing out test data
- Memory management during refactoring
Performance: Not atomic - failures can leave partial results. Each memory is deleted independently.
pattern_health
Check server health and connection status.
Authentication
Pattern supports NATS authentication via:
- URL credentials:
nats://user:pass@host:port - Environment variables:
NATS_USERandNATS_PASS
For WebSocket connections through proxies:
export NATS_URL="wss://user:[email protected]"Storage Limits
| Limit | Value | |-------|-------| | Max memory size | 32KB | | Max memories per agent | 10,000 | | Max shared memories per project | 10,000 | | Max core memories per agent | 100 | | Recent/Tasks category limit | 1,000/500 |
Security Best Practices
What NOT to Store
Pattern stores data in plaintext in NATS KV. Never store:
- 🚫 Credentials: Passwords, API keys, tokens, certificates
- 🚫 PII: Social Security Numbers, credit cards, medical records
- 🚫 Secrets: Database credentials, OAuth secrets, private keys
Content Scanning (v0.3.1+)
Pattern includes an opt-in content scanner that detects common secret patterns (API keys, passwords, private keys, etc.) and warns before storage. Disable if needed:
export PATTERN_DISABLE_CONTENT_SCAN=trueSecure Your NATS Connection
Use TLS for production:
# WebSocket with TLS
export NATS_URL="wss://user:[email protected]"
# TCP with TLS
export NATS_URL="tls://user:[email protected]:4222"Client-Side Encryption
For sensitive content that must be stored, use client-side encryption:
import { createCipheriv, randomBytes } from 'crypto';
// Encrypt before storing
const key = Buffer.from(process.env.PATTERN_ENCRYPTION_KEY!, 'hex');
const iv = randomBytes(16);
const cipher = createCipheriv('aes-256-cbc', key, iv);
const encrypted = cipher.update('sensitive data', 'utf8', 'base64') + cipher.final('base64');
await remember({
content: iv.toString('base64') + ':' + encrypted,
metadata: { tags: ['encrypted'] }
});See docs/SECURITY.md for comprehensive security guidance, including:
- Threat model and what Pattern protects against
- Encryption examples with key management
- Access control best practices
- Incident response procedures
Development
# Clone the repository
git clone https://github.com/loominal/pattern.git
cd pattern
# Install dependencies
npm install
# Run tests
npm test
npm run test:coverage
# Build
npm run build
# Watch mode
npm run dev
# Lint and format
npm run lint
npm run formatArchitecture
flowchart TB
subgraph Clients["MCP Clients"]
CC["Claude Code"]
OA["Other Agent"]
end
subgraph Server["MCP Server"]
Pattern["Pattern<br/>(Memory)"]
end
subgraph Storage["Storage Backend"]
NATS["NATS KV<br/>(JetStream)"]
end
CC <--> Pattern
OA <--> Pattern
Pattern <--> NATSKey Design Decisions
- Unified Identity: Pattern reads identity from Warp's NATS KV store instead of generating ephemeral UUIDs. Same computer + same folder = same agent across restarts.
- Unified Scope Model: 4-value scope (
private,personal,team,public) shared with Warp and Weft for consistent semantics - Multi-Bucket Storage: Different scopes route to different NATS KV buckets (project bucket, user bucket, global bucket)
- Project Isolation: Each project gets its own NATS KV bucket for
privateandteamscope - Personal Memories: Core memories use
personalscope, stored in user bucket, accessible across all projects - Agent Privacy: Private memories keyed by agentId, never visible to others
- TTL Management: Application-level expiration (NATS KV doesn't support per-key TTL)
- Summary Generation: 4KB max summary from prioritized memories
Related
- Loominal — Multi-agent infrastructure
- Warp — MCP server for messaging
- Weft — Work coordinator
- Shuttle — Fleet management CLI
License
MIT - Michael LoPresti
