MCP server for NornicDB, a Neo4j-compatible graph database with semantic search capabilities. Provides tools for storing, recalling, discovering, and linking knowledge nodes with vector embeddings.
NornicDB provides 6 tools with complete JSON Schema input definitions and generally detailed descriptions. All tools have descriptions exceeding 100 characters with clear use-case guidance. Parameter schemas are well-structured with types, descriptions, and appropriate constraints (enums, ranges, formats). Tool naming follows verb_noun patterns (store, recall, discover, link, task, tasks). However, output schemas are not documented, LLMs have no formal specification of what fields to expect in responses, making chaining and result extraction error-prone. Error handling guidance is absent from tool descriptions. The task/tasks tools lack idempotent hints and confirmation patterns for destructive operations. No tool annotations (readOnlyHint, destructiveHint, idempotentHint) are present despite clear write/read semantics. Parameter defaults are reasonable (limit=10, min_similarity=0, depth=1) but no guidance on mutual exclusivity or dependency chains.
Find knowledge by MEANING, not exact keywords. Uses vector embeddings to find semantically similar content. Automatically falls back to keyword search if embeddings disabled. Use when you're asking "what do we know about X?" or "find similar to Y". Examples: - discover(query="database connection pooling") - discover(query="authentication bugs", type=["code", "decision"], depth=2) - discover(query="user preferences", min_similarity=0.01)
Create a relationship between two nodes. Use this to connect related knowledge, show dependencies, or build knowledge graphs. Relationships have types and properties. from and to MUST be exact node identifiers (e.g. the id returned by store, or elementId(n) from a Cypher query like MATCH (n:Label) RETURN n, elementId(n)). Do NOT use human-readable names, titles, or content—only the exact id string. Query nodes first to get their ids, then call link. Examples: - link(from="<node-id-1>", to="<node-id-2>", relation="relates_to") - link(from="<id-a>", to="<id-b>", relation="depends_on", strength=0.8, metadata={"reason": "API dependency"})
Retrieve specific knowledge by ID, or search by criteria (type, tags, date range). Use when you know WHAT you're looking for. For semantic "find similar" use discover instead. Examples: - recall(id="node-abc123") - recall(type=["decision"], tags=["database"]) - recall(since="2024-11-01T00:00:00Z", limit=20)
Store a piece of knowledge, decision, or any information as a labeled node in the graph. Returns node ID for future reference. Automatically generates embeddings for semantic search. Examples: - store(content="PostgreSQL is our primary database", labels=["Decision", "Infrastructure"]) - store(content="ML is a subset of AI", type="Concept", tags=["AI"])
No output schemas documented for any tool. LLMs cannot know what fields to expect in responses, making chaining tools, extracting specific fields, and planning multi-step workflows error-prone. For example, after calling store(), does the response include an 'id' field? 'embedding_confidence'? 'created_at'? Unknown.
Missing tool annotations (readOnlyHint, destructiveHint, idempotentHint) despite clear semantic differences. store/link/task (write ops) should have destructiveHint=true. recall/discover/tasks (read ops) should have readOnlyHint=true. This forces LLMs to infer safety semantics from descriptions rather than machine-readable metadata.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | C | 68 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 46 | - | v1 |
Create, read, update, or delete a task. Omit 'id' to create, include 'id' to update/delete. Status can be 'open', 'in_progress', or 'done'. Tasks are stored as labeled nodes in the graph.
Query tasks by status, priority, tags, or date range. Returns matching tasks.
No error handling guidance in tool descriptions. Agents do not know: (1) when to retry vs give up, (2) how to recover from specific failures (e.g., node not found, embedding generation timeout), (3) what invalid inputs trigger which error categories. Descriptions like 'Automatically falls back to keyword search if embeddings disabled' lack actionable guidance on failure modes.
Destructive operations (task deletion, potential graph overwrites in link) lack confirmation/dry-run patterns. The task() tool supports delete=true but has no confirmation step, agents could accidentally delete tasks without user approval. No mention of dry-run behavior.
Parameter idempotence not documented. Calling store() with identical content twice, does it create two nodes or return the existing one? Calling link() twice with same from/to/relation, is it idempotent? Agents retry on ambiguous failures; non-idempotent semantics risk duplicate records, graph loops, or multi-version task conflicts.
The link() tool's 'from' and 'to' parameters require exact node IDs, not human-readable names. Description warns against this but puts burden on agent to obtain IDs first (via store or Cypher). Consider accepting node names/titles as primary parameters and resolving to IDs server-side, or providing a resolve_node_id() utility tool.
recall() and tasks() lack pagination clarity. The limit parameter is present (default=10, max=100) but no documentation of cursor/offset behavior, total_count field, or next_page guidance. Large result sets risk context window exhaustion without clear pagination semantics.
discover() includes a 'depth' parameter (1-3) for graph traversal but no guidance on performance implications, response size growth, or when to increase depth. An agent might assume depth=3 always provides richer context without understanding latency/token cost tradeoffs.