save
Record new information, preferences, or experiences by saving a memory to a tiered storage system. Creates a markdown file with frontmatter for organized retrieval.
Instructions
Save a new memory to a specified tier. Creates a markdown file with frontmatter. Use for recording new information, preferences, or experiences.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| tier | Yes | Memory tier: episodic (experiences), semantic (facts/preferences), procedural (workflows), reflection (meta), working (in-session) | |
| name | Yes | Filename slug (no extension, use dashes: my-memory-name) | |
| content | Yes | Full markdown content of the memory (including title heading) | |
| tags | No | Comma-separated tags (optional) |
Implementation Reference
- nexus_mcp.py:136-152 (schema)Input schema definition for the 'save' tool, defining required parameters (tier, name, content) and optional tags.
"name": "save", "description": "Save a new memory to a specified tier. Creates a markdown file with frontmatter. Use for recording new information, preferences, or experiences.", "inputSchema": { "type": "object", "properties": { "tier": { "type": "string", "enum": ["episodic", "semantic", "procedural", "reflection", "working"], "description": "Memory tier: episodic (experiences), semantic (facts/preferences), procedural (workflows), reflection (meta), working (in-session)", }, "name": {"type": "string", "description": "Filename slug (no extension, use dashes: my-memory-name)"}, "content": {"type": "string", "description": "Full markdown content of the memory (including title heading)"}, "tags": {"type": "string", "description": "Comma-separated tags (optional)"}, }, "required": ["tier", "name", "content"], }, }, - nexus_mcp.py:218-260 (handler)Handler for the 'save' tool. Maps tier to directory, creates a markdown file with YAML frontmatter (type, strength, dates, tags, links, source, access_count), and returns the relative path on success.
elif name == "save": tier = args["tier"] name_slug = args["name"] content = args["content"] tags = args.get("tags", "") tier_dir_map = { "episodic": "episodic", "semantic": "semantic", "procedural": "procedural", "reflection": "reflections", "working": "working", } dir_name = tier_dir_map.get(tier) if not dir_name: return None, {"code": -32602, "message": f"Invalid tier: {tier}"} target_dir = os.path.join(MEMORY_DIR, dir_name) os.makedirs(target_dir, exist_ok=True) filepath = os.path.join(target_dir, name_slug + ".md") if os.path.exists(filepath): return None, {"code": -32602, "message": f"Memory already exists: {name_slug}.md"} today = date.today().isoformat() tag_line = f" tags: [{tags}]" if tags else " tags: []" frontmatter = f"""--- type: {tier} strength: 1.0 created: {today} updated: {today} {tag_line} links: "" source: mcp access_count: 1 last_accessed: {today} --- {content} """ with open(filepath, "w", encoding="utf-8") as f: f.write(frontmatter) return {"content": [{"type": "text", "text": f"Saved: {os.path.relpath(filepath, MEMORY_DIR)}"}]}, None - nexus_mcp.py:120-170 (registration)Registration of all tools including 'save' in the TOOL_DEFS list, returned via handle_tools_list.
TOOL_DEFS = [ { "name": "search", "description": "Search across all memory files by keyword. Returns matching file names, titles, strengths, and context snippets.", "inputSchema": { "type": "object", "properties": {"query": {"type": "string", "description": "Keyword or phrase to search for"}}, "required": ["query"], }, }, { "name": "stats", "description": "Show memory health statistics: file counts per tier, average strength, active/decaying/archived counts.", "inputSchema": {"type": "object", "properties": {}}, }, { "name": "save", "description": "Save a new memory to a specified tier. Creates a markdown file with frontmatter. Use for recording new information, preferences, or experiences.", "inputSchema": { "type": "object", "properties": { "tier": { "type": "string", "enum": ["episodic", "semantic", "procedural", "reflection", "working"], "description": "Memory tier: episodic (experiences), semantic (facts/preferences), procedural (workflows), reflection (meta), working (in-session)", }, "name": {"type": "string", "description": "Filename slug (no extension, use dashes: my-memory-name)"}, "content": {"type": "string", "description": "Full markdown content of the memory (including title heading)"}, "tags": {"type": "string", "description": "Comma-separated tags (optional)"}, }, "required": ["tier", "name", "content"], }, }, { "name": "touch", "description": "Boost a memory's strength (simulate access, counteracts Ebbinghaus decay). Use this when a memory is referenced or found relevant.", "inputSchema": { "type": "object", "properties": { "name": {"type": "string", "description": "Memory filename (with or without .md extension)"}, "boost": {"type": "number", "description": "Strength boost amount (default 0.15, max 1.0)"}, }, "required": ["name"], }, }, { "name": "decay", "description": "Run a dry-run decay check. Calculates Ebbinghaus decay for all memories and reports which would be archived. Does NOT modify any files.", "inputSchema": {"type": "object", "properties": {}}, }, ] - nexus_mcp.py:172-174 (registration)handle_tools_list returns TOOL_DEFS which includes the 'save' tool definition.
def handle_tools_list(req_id): return {"tools": TOOL_DEFS} - nexus_mcp.py:176-330 (handler)Routing in handle_tools_call dispatches to the 'save' branch when name == 'save'.
def handle_tools_call(req_id, name, args): if name == "search": query = args.get("query", "").lower() if not query: return {"content": [{"type": "text", "text": "No query provided."}]}, None files = list_memory_files() results = [] for f in files: with open(os.path.join(MEMORY_DIR, f["path"]), encoding="utf-8") as fh: text = fh.read().lower() if query in text: lines = text.splitlines() snippets = [] for i, line in enumerate(lines): if query in line.lower(): start = max(0, i - 1) end = min(len(lines), i + 2) snippet = "\n".join(lines[start:end]).strip() if len(snippet) > 200: snippet = snippet[:200] + "..." snippets.append(snippet) results.append({ "title": f["title"] or f["name"], "path": f["path"], "tier": f["tier"], "strength": f["strength"], "snippets": snippets[:3], }) if not results: return {"content": [{"type": "text", "text": f"No memories matched '{query}'."}]}, None lines = [f"Found {len(results)} memory(s) for '{query}':", ""] for r in results: lines.append(f" [{r['tier']}] {r['title']} (strength: {r['strength']:.2f})") lines.append(f" path: {r['path']}") for s in r["snippets"]: lines.append(f" > {s}") lines.append("") return {"content": [{"type": "text", "text": "\n".join(lines)}]}, None elif name == "stats": return {"content": [{"type": "text", "text": calc_stats()}]}, None elif name == "save": tier = args["tier"] name_slug = args["name"] content = args["content"] tags = args.get("tags", "") tier_dir_map = { "episodic": "episodic", "semantic": "semantic", "procedural": "procedural", "reflection": "reflections", "working": "working", } dir_name = tier_dir_map.get(tier) if not dir_name: return None, {"code": -32602, "message": f"Invalid tier: {tier}"} target_dir = os.path.join(MEMORY_DIR, dir_name) os.makedirs(target_dir, exist_ok=True) filepath = os.path.join(target_dir, name_slug + ".md") if os.path.exists(filepath): return None, {"code": -32602, "message": f"Memory already exists: {name_slug}.md"} today = date.today().isoformat() tag_line = f" tags: [{tags}]" if tags else " tags: []" frontmatter = f"""--- type: {tier} strength: 1.0 created: {today} updated: {today} {tag_line} links: "" source: mcp access_count: 1 last_accessed: {today} --- {content} """ with open(filepath, "w", encoding="utf-8") as f: f.write(frontmatter) return {"content": [{"type": "text", "text": f"Saved: {os.path.relpath(filepath, MEMORY_DIR)}"}]}, None elif name == "touch": target = args["name"] boost = min(float(args.get("boost", 0.15)), 1.0) target_lower = target.lower().replace(".md", "") files = list_memory_files() match = None for f in files: if f["name"].lower() == target_lower: match = f break if not match: return None, {"code": -32602, "message": f"Memory not found: {target}"} filepath = os.path.join(MEMORY_DIR, match["path"]) with open(filepath, encoding="utf-8") as fh: text = fh.read() old_s = match["strength"] new_s = min(old_s + boost, 1.0) text = re.sub(r'^(strength:\s*)[\d.]+', rf'\g<1>{new_s:.2f}', text, flags=re.MULTILINE) text = re.sub(r'^(\s+strength:\s*)[\d.]+', rf'\g<1>{new_s:.2f}', text, flags=re.MULTILINE) today = date.today().isoformat() if re.search(r'last_accessed:', text): text = re.sub(r'^(last_accessed:\s*).*', rf'\g<1>{today}', text, flags=re.MULTILINE) text = re.sub(r'^(\s+last_accessed:\s*).*', rf'\g<1>{today}', text, flags=re.MULTILINE) if re.search(r'access_count:', text): ac = parse_frontmatter(text, "access_count") new_ac = int(ac) + 1 if ac else 1 text = re.sub(r'^(access_count:\s*)\d+', rf'\g<1>{new_ac}', text, flags=re.MULTILINE) text = re.sub(r'^(\s+access_count:\s*)\d+', rf'\g<1>{new_ac}', text, flags=re.MULTILINE) with open(filepath, "w", encoding="utf-8") as fh: fh.write(text) return {"content": [{"type": "text", "text": f"Touched {target}: strength {old_s:.2f} → {new_s:.2f}"}]}, None elif name == "decay": files = list_memory_files() today_epoch = datetime.now().timestamp() decay_map = { "semantic": 90, "episodic": 30, "procedural": 180, "reflection": 60, "working": 7, "core": 999999, } results_lines = ["Decay check (dry-run):", ""] for f in files: with open(os.path.join(MEMORY_DIR, f["path"]), encoding="utf-8") as fh: text = fh.read() created = parse_frontmatter(text, "created") or "2026-05-18" try: dt = datetime.strptime(created[:10], "%Y-%m-%d") days = max(1, (datetime.now() - dt).days) except ValueError: days = 1 dc = decay_map.get(f["type"], 30) new_s = f["strength"] * math.exp(-days / dc) new_s = max(0.05, min(1.0, new_s)) if abs(new_s - f["strength"]) > 0.01: status = "" if new_s < 0.2: status = " [WOULD ARCHIVE]" elif new_s < 0.4: status = " [DECAYING]" results_lines.append(f" {f['name']}: {f['strength']:.2f} → {new_s:.2f} ({days}d){status}") if len(results_lines) == 2: results_lines.append(" All memories at expected strength.") return {"content": [{"type": "text", "text": "\n".join(results_lines)}]}, None return None, {"code": -32601, "message": f"Unknown tool: {name}"}