Skip to content

MCP Tools Quick Reference

For LLMs: This is your cheat sheet for using NornicDB's memory system.

Note: index and unindex tools have been moved to Mimir (the intelligence layer).
NornicDB focuses on storage, embeddings, and search. File indexing is handled by Mimir.


🎯 Quick Decision Tree

Want to remember something? β†’ store
Know the ID, need to fetch? β†’ recall
Search by meaning/topic? β†’ discover
Connect two things? β†’ link
Work with tasks? β†’ task (single) or tasks (multiple)


Core Tools (One-Liner Each)

Tool Use When Example
store Remembering any information store(content="Use Postgres", type="decision")
recall Getting something by ID or filters recall(id="node-123")
discover Finding by meaning, not keywords discover(query="auth implementation")
link Connecting two nodes (from/to must be node IDs from store or Cypher) link(from="node-abc", to="node-xyz", relation="depends_on")
task Single task CRUD task(title="Fix bug", priority="high")
tasks Query/list multiple tasks tasks(status=["pending"], unblocked_only=true)

πŸ’‘ Common Patterns

1. id1 = store(content="We use PostgreSQL", type="decision")
2. id2 = store(content="Add connection pooling", type="task")
3. link(from=id2, to=id1, relation="implements")

Search & Recall Pattern

1. results = discover(query="authentication bugs", limit=5)
2. For each result.id:
   - details = recall(id=result.id)  # Get full context

Task Workflow Pattern

1. tasks(status=["pending"], unblocked_only=true)  # Find work
2. task(id="task-123", status="active")           # Start task
3. task(id="task-123", status="done")             # Complete task

Code Search Pattern

# File indexing is done by Mimir; NornicDB stores and searches the result.
1. discover(query="database connection pool", type=["file", "file_chunk"])
2. recall(id="file-xyz")  # Get full file content

πŸ“‹ Parameter Cheat Sheet

store

content: string βœ… REQUIRED
type: "memory" | "decision" | "concept" | "task" | "note" | "file" | "code"
title: string (auto-generated if omitted)
tags: ["tag1", "tag2"]
metadata: {key: "value"}

recall

id: "node-123" (fetch specific node)
OR
type: ["decision", "task"]
tags: ["urgent"]
since: "2024-11-01"
limit: 10

discover

query: "natural language search" βœ… REQUIRED
type: ["file", "decision", "task"] (filter)
limit: 10
min_similarity: 0.70 (0.0-1.0, lower=more results)
depth: 1 (1-3, higher=more related context)
from: "node-123" βœ… REQUIRED
to: "node-456" βœ… REQUIRED
relation: "depends_on" | "relates_to" | "implements" | "blocks" βœ… REQUIRED
strength: 1.0 (0.0-1.0)
metadata: {key: "value"}

task

# CREATE:
title: "Fix auth bug" βœ… REQUIRED
description: "Details..."
status: "pending" | "active" | "done" | "blocked"
priority: "low" | "medium" | "high" | "critical"
depends_on: ["task-123", "task-456"]
assign: "agent-worker-1"

# UPDATE:
id: "task-123" βœ… REQUIRED
status: "done" (or omit to toggle: pending→active→done)

tasks

status: ["pending", "active"]
priority: ["high", "critical"]
assigned_to: "agent-worker-1"
unblocked_only: true (no blocking dependencies)
limit: 20

πŸ”₯ Most Common Mistakes

❌ recall(query="authentication")  # Wrong! recall is for ID/filters
βœ… discover(query="authentication") # Right! discover is for meaning

❌ Forgetting required parameters

❌ store(type="decision")          # Missing content!
βœ… store(content="...", type="decision")

❌ Wrong relation names

❌ link(from=A, to=B, relation="connected")  # Not a valid relation
βœ… link(from=A, to=B, relation="relates_to") # Valid

❌ Using tasks for single task operations

❌ tasks(id="task-123")            # tasks is for multiple!
βœ… task(id="task-123")             # task is for single

πŸ“Š Response Fields You Should Use

store response

{
  "id": "node-abc123",           // ← Use this for linking!
  "title": "Generated Title",
  "embedded": true,
  "suggestions": [...]            // ← Similar nodes for auto-linking
}

recall response

{
  "nodes": [{...}],
  "count": 5,
  "related": [...]                // ← 1-hop neighbors for context
}

discover response

{
  "results": [{...}],
  "method": "vector",             // ← "vector" or "keyword"
  "total": 10,
  "suggestions": [...]            // ← Related searches
}

task response

{
  "task": {...},
  "blockers": [...],              // ← Tasks blocking this one
  "subtasks": [...],              // ← Child tasks
  "next_action": "..."            // ← Suggested next step
}

tasks response

{
  "tasks": [...],
  "stats": {                      // ← Aggregate statistics
    "total": 50,
    "by_status": {...},
    "by_priority": {...}
  },
  "dependency_graph": [...],      // ← Task dependencies
  "recommended": [...]            // ← Best tasks to work on
}

πŸš€ Performance Tips

  1. Use IDs, not repeated queries

    ❌ discover() β†’ recall() β†’ discover() β†’ recall()  # Slow!
    βœ… discover() β†’ get IDs β†’ link(from=id1, to=id2) # Fast!
    

  2. Batch related operations

    ❌ store() β†’ link() β†’ store() β†’ link() (4 calls)
    βœ… id1=store() β†’ id2=store() β†’ link() (3 calls, parallel possible)
    

  3. Adjust similarity threshold

    Too few results? β†’ Lower min_similarity (0.65 instead of 0.75)
    Too many results? β†’ Raise min_similarity (0.85 instead of 0.75)
    

  4. Use depth wisely

    Quick search β†’ depth=1 (direct matches only)
    Need context β†’ depth=2 (includes 1-hop neighbors)
    Deep research β†’ depth=3 (2-hop expansion, slower)
    


🎯 Task Status State Machine

pending β†’ active β†’ done
   ↓         ↓       ↑
   └──> blocked β”€β”€β”€β”€β”˜

Use task(id=X) with no status to auto-advance:
- pending β†’ active
- active β†’ done
- done β†’ (no change)
- blocked β†’ (no change, fix blockers first)

Scenario Tool Why
"Get node abc-123" recall(id="abc-123") You know the ID
"Find all high priority tasks" recall(type=["task"], filters={priority:"high"}) Exact property match
"What do we know about auth?" discover(query="authentication") Semantic meaning search
"Show pending tasks" tasks(status=["pending"]) Specialized task query

πŸ’¬ Example Conversations

Example 1: Storing a Decision

User: "We decided to use PostgreSQL instead of MongoDB for better ACID guarantees"

LLM: I'll store this decision.
β†’ store(
    content="We decided to use PostgreSQL instead of MongoDB for better ACID guarantees",
    type="decision",
    tags=["database", "architecture", "postgres"]
  )
← {id: "node-123", title: "Database Choice: PostgreSQL vs MongoDB"}

Done! Stored as decision node-123.
User: "What do we know about our database setup?"

LLM: Let me search our knowledge base.
β†’ discover(query="database setup and configuration", type=["decision", "code", "file"])
← {results: [
    {id: "node-123", title: "Database Choice: PostgreSQL..."},
    {id: "file-456", title: "database.go - connection pool"},
    ...
  ], method: "vector"}

Found 5 relevant items:
1. Decision: Database Choice: PostgreSQL vs MongoDB
2. File: database.go - connection pool configuration
...

Example 3: Task Management

User: "Show me what I should work on next"

LLM: Looking for unblocked, high-priority tasks.
β†’ tasks(status=["pending"], priority=["high", "critical"], unblocked_only=true)
← {tasks: [...], recommended: [
    {id: "task-789", title: "Implement auth", priority: "high", blockers: []}
  ]}

I recommend starting with:
1. task-789: Implement auth (high priority, no blockers)

πŸ“š Further Reading

  • Using MCP tools in Bifrost (agentic loop): The same tools can be exposed to the Heimdall chat assistant so the LLM can call store/recall/link etc. in process. They are off by default. See Enabling MCP tools in the agentic loop.
  • Rationale and tool set: This document describes the current MCP tool set (store, recall, discover, link, task, tasks); implementation lives in pkg/mcp/.
  • Configuration: To enable or restrict which MCP tools are available in the agentic loop, see Configuration Guide (Heimdall / MCP sections) and heimdall-mcp-tools.