Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 62 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ View full memory details with edit and delete options.

![Memory Detail](https://raw.githubusercontent.com/aitytech/agentkits-memory/main/assets/agentkits-memory-memory-detail.png)

### Manage Embeddings

Generate and manage vector embeddings for semantic search.

![Manage Embeddings](https://raw.githubusercontent.com/aitytech/agentkits-memory/main/assets/agentkits-memory-embedding.png)

---

## Quick Start
Expand Down Expand Up @@ -116,13 +122,67 @@ Once configured, your AI assistant can use these tools:
| Tool | Description |
|------|-------------|
| `memory_save` | Save decisions, patterns, errors, or context |
| `memory_search` | Search memories using semantic similarity |
| `memory_recall` | Recall everything about a specific topic |
| `memory_search` | **[Step 1]** Search memories - returns lightweight index |
| `memory_timeline` | **[Step 2]** Get context around a memory |
| `memory_details` | **[Step 3]** Get full content for specific IDs |
| `memory_recall` | Quick recall - returns full content (legacy) |
| `memory_list` | List recent memories |
| `memory_status` | Check memory system status |

---

## Progressive Disclosure (Token-Efficient Search)

AgentKits Memory uses a **3-layer search pattern** that saves ~70% tokens compared to fetching full content upfront.

### How It Works

```
┌─────────────────────────────────────────────────────────────┐
│ Step 1: memory_search │
│ Returns: IDs, titles, tags, scores (~50 tokens/item) │
│ → Review index, pick relevant memories │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Step 2: memory_timeline (optional) │
│ Returns: Context ±30 minutes around memory │
│ → Understand what happened before/after │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Step 3: memory_details │
│ Returns: Full content for selected IDs only │
│ → Fetch only what you actually need │
└─────────────────────────────────────────────────────────────┘
```

### Example Workflow

```typescript
// Step 1: Search - get lightweight index
memory_search({ query: "authentication" })
// → Returns: [{ id: "abc", title: "JWT pattern...", score: 85% }]

// Step 2: (Optional) See temporal context
memory_timeline({ anchor: "abc" })
// → Returns: What happened before/after this memory

// Step 3: Get full content only for what you need
memory_details({ ids: ["abc"] })
// → Returns: Full content for selected memory
```

### Token Savings

| Approach | Tokens Used |
|----------|-------------|
| **Old:** Fetch all content | ~500 tokens × 10 results = 5000 tokens |
| **New:** Progressive disclosure | 50 × 10 + 500 × 2 = 1500 tokens |
| **Savings** | **70% reduction** |

---

## CLI Commands

```bash
Expand Down
Binary file modified assets/agentkits-memory-add-memory.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/agentkits-memory-embedding.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/agentkits-memory-memory-detail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/agentkits-memory-memory-list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@aitytech/agentkits-memory",
"version": "2.0.2",
"version": "2.1.0",
"type": "module",
"description": "Persistent memory system for AI coding assistants via MCP. Works with Claude Code, Cursor, Copilot, Windsurf, Cline.",
"main": "dist/index.js",
Expand Down
80 changes: 79 additions & 1 deletion src/mcp/__tests__/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import type {
MemorySearchArgs,
MemoryRecallArgs,
MemoryListArgs,
MemoryTimelineArgs,
MemoryDetailsArgs,
} from '../types.js';

// Mock ProjectMemoryService for isolated testing
Expand All @@ -32,6 +34,8 @@ describe('MCP Server', () => {

expect(toolNames).toContain('memory_save');
expect(toolNames).toContain('memory_search');
expect(toolNames).toContain('memory_timeline');
expect(toolNames).toContain('memory_details');
expect(toolNames).toContain('memory_recall');
expect(toolNames).toContain('memory_list');
expect(toolNames).toContain('memory_status');
Expand Down Expand Up @@ -63,7 +67,7 @@ describe('MCP Server', () => {
});
});

describe('memory_search tool', () => {
describe('memory_search tool (Progressive Disclosure Layer 1)', () => {
const searchTool = MEMORY_TOOLS.find(t => t.name === 'memory_search')!;

it('should require query parameter', () => {
Expand All @@ -77,6 +81,47 @@ describe('MCP Server', () => {
it('should have limit option', () => {
expect(searchTool.inputSchema.properties.limit).toBeDefined();
});

it('should describe progressive disclosure workflow in description', () => {
expect(searchTool.description).toContain('Step 1/3');
expect(searchTool.description).toContain('memory_timeline');
expect(searchTool.description).toContain('memory_details');
});
});

describe('memory_timeline tool (Progressive Disclosure Layer 2)', () => {
const timelineTool = MEMORY_TOOLS.find(t => t.name === 'memory_timeline')!;

it('should require anchor parameter', () => {
expect(timelineTool.inputSchema.required).toContain('anchor');
});

it('should have before and after options', () => {
expect(timelineTool.inputSchema.properties.before).toBeDefined();
expect(timelineTool.inputSchema.properties.after).toBeDefined();
});

it('should describe as Step 2/3', () => {
expect(timelineTool.description).toContain('Step 2/3');
});
});

describe('memory_details tool (Progressive Disclosure Layer 3)', () => {
const detailsTool = MEMORY_TOOLS.find(t => t.name === 'memory_details')!;

it('should require ids parameter', () => {
expect(detailsTool.inputSchema.required).toContain('ids');
});

it('should have ids as array type', () => {
const idsProp = detailsTool.inputSchema.properties.ids;
expect(idsProp.type).toBe('array');
expect(idsProp.items).toEqual({ type: 'string' });
});

it('should describe as Step 3/3', () => {
expect(detailsTool.description).toContain('Step 3/3');
});
});

describe('memory_recall tool', () => {
Expand Down Expand Up @@ -177,5 +222,38 @@ describe('MCP Server', () => {
expect(args.category).toBe('error');
expect(args.limit).toBe(5);
});

it('MemoryTimelineArgs should accept valid arguments', () => {
const args: MemoryTimelineArgs = {
anchor: 'memory-123',
before: 30,
after: 30,
};

expect(args.anchor).toBe('memory-123');
expect(args.before).toBe(30);
expect(args.after).toBe(30);
});

it('MemoryTimelineArgs should work with minimal arguments', () => {
const args: MemoryTimelineArgs = {
anchor: 'memory-456',
};

expect(args.anchor).toBe('memory-456');
expect(args.before).toBeUndefined();
expect(args.after).toBeUndefined();
});

it('MemoryDetailsArgs should accept valid arguments', () => {
const args: MemoryDetailsArgs = {
ids: ['memory-1', 'memory-2', 'memory-3'],
};

expect(args.ids).toHaveLength(3);
expect(args.ids).toContain('memory-1');
expect(args.ids).toContain('memory-2');
expect(args.ids).toContain('memory-3');
});
});
});
Loading