Narra
Narrative intelligence engine for AI-assisted fiction writing.
Narra is a structured database for fictional universes — characters, locations, events, scenes, relationships, knowledge, perceptions, facts, and notes — all queryable by meaning via local embeddings.
It goes beyond CRUD. Narra detects dramatic irony, tracks character evolution through embedding drift, measures perception gaps between characters, surfaces unresolved tensions, clusters emergent themes, finds knowledge conflicts, traces influence propagation through relationship networks, and previews what-if scenarios — all without external API calls.
Everything runs locally: embedded SurrealDB with RocksDB persistence, BGE-small-en-v1.5 embeddings via fastembed, single binary, zero runtime dependencies. Use it as a CLI tool or as an MCP server for AI-assisted writing workflows.
Table of Contents
- Features
- Quick Start
- Installation
- CLI Command Reference
- CLI Walkthrough
- MCP Server
- Data Storage
- Architecture
- Development
- License
Features
World Building
- Characters — flexible profile system with structured keys (wound, desire, contradiction, secret) plus arbitrary custom keys
- Locations — hierarchical parent-child structure with types
- Events — sequence-ordered timeline with optional dates and date precision
- Scenes — anchored to event + location, with typed participants
- Relationships — 7 built-in types (family, romantic, professional, social, antagonistic, mentorship, custom) with subtypes and labels
- Knowledge — append-only ledger with 7 certainty levels (knows, suspects, believes_wrongly, uncertain, assumes, denies, forgotten) and 8 learning methods (told, overheard, witnessed, discovered, deduced, read, remembered, initial), full provenance (source character, event)
- Perceptions — asymmetric: A's view of B is independent of B's view of A, with feelings, tension level, and history
- Universe facts — world rules with enforcement levels: informational (context only), warning (flags violations), strict (blocks mutations)
- Notes — freeform text attachable to any entity
- Import/Export — round-trip YAML with dependency-ordered processing (characters → locations → events → scenes → relationships → knowledge → notes → facts)
Narrative Intelligence
Dual Interface
CLI: 30+ commands including high-level workflows (explore, ask, find), graph operations (path, references), entity management (create, update, delete, protect), and organized subcommands: analyze (20+ analytics), world (status, health, import/export, backfill, validate, graph), session (context, pin/unpin).
MCP Server: 5 tools with 70 total operations — query (40 ops), mutate (25 ops), session (3 ops), export_world (1 op), generate_graph (1 op). Plus resources (narra:// URIs) and workflow prompts.
Technical
- Embedded SurrealDB + RocksDB — no external database to install or manage
- Local BGE-small-en-v1.5 embeddings (384 dimensions, fully offline via fastembed)
- graphrs for graph analytics, linfa for K-means clustering
- Cross-platform: macOS (Intel + Apple Silicon), Linux (x86_64 + aarch64)
- Single statically-linked binary, release builds with LTO
- JSON output mode (
--json) for scripting and integration
Quick Start
# Install (macOS Apple Silicon — see Installation for other platforms)
curl -sL https://github.com/florinutz/narra/releases/latest/download/narra-aarch64-apple-darwin.tar.gz | tar xz
sudo mv narra /usr/local/bin/
# Create a character
narra create character --name "Alice Voss" --role "Detective" \
--description "Homicide detective haunted by her partner's unsolved murder" \
--profile '{"wound": ["partner murdered 3 years ago"], "secret": ["knows who did it"]}'
# Generate embeddings, then search by meaning
narra world backfill
narra find --semantic-only "characters hiding dark secrets"
Installation
From GitHub Releases
Download the latest binary for your platform:
# macOS (Apple Silicon)
curl -sL https://github.com/florinutz/narra/releases/latest/download/narra-aarch64-apple-darwin.tar.gz | tar xz
sudo mv narra /usr/local/bin/
# macOS (Intel)
curl -sL https://github.com/florinutz/narra/releases/latest/download/narra-x86_64-apple-darwin.tar.gz | tar xz
sudo mv narra /usr/local/bin/
# Linux (x86_64)
curl -sL https://github.com/florinutz/narra/releases/latest/download/narra-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv narra /usr/local/bin/
# Linux (aarch64)
curl -sL https://github.com/florinutz/narra/releases/latest/download/narra-aarch64-unknown-linux-gnu.tar.gz | tar xz
sudo mv narra /usr/local/bin/
From Source
cargo install --git https://github.com/florinutz/narra.git
CLI Command Reference
Narra's CLI provides intuitive, hierarchical commands for all operations. Commands support --json output for scripting and --brief/--full flags for detail control.
High-Level Workflows
narra explore <entity>
Deep entity exploration with relationships, knowledge, perceptions, and semantically similar entities.
narra explore alice # Full exploration
narra explore alice --no-similar # Skip similarity search
narra explore alice --depth 3 # Extend graph traversal depth
narra ask <question>
Natural language queries about your narrative world.
narra ask "What secrets does Alice know that Gray doesn't?"
narra ask "Which characters are connected to the murder event?" --limit 20
narra ask "Show me all locations in the city" --context false # Skip contextual summaries
narra find [query]
Search across entities with automatic hybrid search (keyword + semantic).
# Basic search (hybrid by default)
narra find "Gray murder"
narra find "betrayal" --type scene --limit 10
# Semantic-only (find by meaning)
narra find --semantic-only "characters struggling with duty"
# Keyword-only (exact text matching)
narra find --keyword-only "Commissioner"
# Advanced search subcommands
narra find join "characters whose desires conflict with Alice's wounds"
narra find knowledge "the royal succession" --character alice
narra find graph alice "betrayal and deception" --hops 2
narra find perspectives "threatening and dangerous" --observer bob
narra find similar alice gray --bias "with more tension"
Find Subcommands:
join— Cross-type semantic search by meaningknowledge— Search within character knowledgegraph— Graph proximity + semantic similarityperspectives— Search perceptions/perspectives by meaningsimilar— Find relationships similar to a reference pair
narra path <from> <to>
Find shortest connection paths between entities.
narra path alice eddie # Shortest paths
narra path alice eddie --max-hops 5 # Extend search depth
narra path alice eddie --include-events # Include event co-participation
narra references <entity>
Discover what references a target entity.
narra references alice # All references
narra references alice --types scene,event # Filter by type
narra references location:castle --limit 50
Entity Management
narra create <type>
Create new entities: character, location, event, scene, knowledge, relationship, perception, fact, note.
# Character
narra create character --name "Alice Voss" --role "Detective" \
--description "Homicide detective haunted by her partner's murder" \
--profile '{"wound": ["partner murdered 3 years ago"], "secret": ["knows who did it"]}'
# Location
narra create location --name "Precinct 13" --loc-type "building" \
--description "Aging police station" --parent location:downtown
# Event
narra create event --title "The Confrontation" --sequence 10 \
--description "Alice confronts Gray with evidence" \
--date "2024-06-15" --date-precision day
# Scene
narra create scene --title "Gray's Office" --event event:confrontation \
--location location:city_hall --summary "The truth comes out"
# Knowledge
narra create knowledge --character alice --fact "Gray ordered the hit" \
--certainty knows --method discovered --event event:investigation
# Relationship
narra create relationship --from alice --to gray --type antagonistic \
--label "Hunter and prey — neither knows who is which"
# Perception
narra create perception --observer bob --target alice \
--perception "Sees her as relentless and dangerous" \
--feelings "fear, respect" --tension 8
# Fact (universe rules)
narra create fact --title "Magic requires sacrifice" \
--description "All magic extracts a personal cost" \
--categories physics_magic --enforcement strict
# Note
narra create note --title "Plot thread" --body "Revisit Eddie's backstory" \
--attach-to character:eddie,event:tip
narra get <entity>
Retrieve any entity by ID or name (auto-resolves).
narra get alice # By name (auto-resolves)
narra get character:alice # By full ID
narra get event:confrontation
narra list <type>
List entities with optional filters.
narra list character
narra list event --limit 50
narra list knowledge --character alice
narra list fact --category physics_magic --enforcement strict
narra list note --entity character:alice
narra update <entity>
Update entity fields.
# Update with JSON object
narra update character:alice --fields '{"role": "Lead Detective"}'
# Update individual fields
narra update character:alice --set role="Lead Detective" --set description="..."
# Link/unlink facts
narra update character:alice --link fact:magic_rules
narra update character:alice --unlink fact:old_rule
narra delete <entity>
Delete entity (with impact analysis prompt).
narra delete character:bob # Soft delete (respects protection)
narra delete character:bob --hard # Hard delete (bypass protection)
Entity Protection
narra protect <entity>
Mark entity as protected. Protected entities trigger CRITICAL severity in impact analysis and require --hard flag for deletion.
narra protect alice # Protect protagonist
narra protect event:climax # Protect key plot point
narra protect location:headquarters # Protect central location
Use cases:
- Protagonists: Prevent accidental deletion of main characters
- Plot anchors: Protect pivotal events that the story depends on
- World foundations: Protect core locations or universe facts
narra unprotect <entity>
Remove protection from entity.
narra unprotect character:minor_npc
Analysis Commands
All under narra analyze <operation>:
# Network analysis
narra analyze centrality # Network centrality metrics
narra analyze centrality --scope character:alice --limit 20
narra analyze influence alice --depth 3 # Influence propagation
# Knowledge analysis
narra analyze irony # Dramatic irony report
narra analyze irony --character alice --threshold 5
narra analyze asymmetries alice gray # Pairwise knowledge gaps
narra analyze conflicts # Knowledge conflicts (believes_wrongly)
narra analyze conflicts --character alice
# Perception analysis
narra analyze perception-gap alice bob # How wrong is alice about bob?
narra analyze perception-matrix bob # How do all observers see bob?
narra analyze perception-shift alice bob # How has alice's view evolved?
narra analyze tensions # Unresolved perception tensions
# Arc tracking (requires baseline snapshots)
narra analyze arc-drift # Most-changed entities
narra analyze arc-drift --type character --limit 20
narra analyze arc-history alice # Entity evolution timeline
narra analyze arc-compare alice bob --window "recent:10"
narra analyze arc-moment alice --event event:betrayal
# Thematic analysis
narra analyze themes # K-means clustering
narra analyze themes --types character,event --clusters 5
narra analyze thematic-gaps --min-size 3
# Temporal & consistency
narra analyze temporal alice --event event:confrontation
narra analyze contradictions alice --depth 3
narra analyze impact alice --description "major personality shift"
# Composite reports
narra analyze situation-report # High-level narrative overview
narra analyze dossier alice # Comprehensive character report
narra analyze scene-prep alice,bob,gray # Scene planning for character meeting
narra analyze what-if alice --fact knowledge:secret --certainty suspects
Session Management
Session state persists between CLI invocations and MCP server usage.
narra session context
View current session state: hot entities (recently accessed), pinned entities, pending decisions, and world overview.
narra session context
narra session context --json # Machine-readable output
Session context includes:
- Hot entities: Recently viewed or modified (last 50)
- Pinned entities: Manually pinned for persistent reference
- Pending decisions: Entities with validation warnings
- World overview: Entity counts, embedding coverage
narra session pin <entity>
Pin entity to persistent session context.
narra session pin alice # Keep protagonist in context
narra session pin event:climax # Pin pivotal event
narra session pin location:headquarters
Use cases:
- Active writing: Pin characters/locations you're currently writing
- Continuity: Pin entities you need to reference frequently
- Cross-session: Maintain context across MCP and CLI usage
narra session unpin <entity>
Remove entity from pinned context.
narra session unpin character:minor_npc
World Management
All under narra world <operation>:
narra world status
World overview dashboard with entity counts and embedding coverage.
narra world status
narra world health
Embedding health report: coverage, staleness, missing embeddings.
narra world health
narra world backfill
Generate embeddings for all entities (run after initial data entry).
narra world backfill # All entity types
narra world backfill --type character # Single type only
narra world baseline-arcs
Create baseline arc snapshots for arc tracking (run once after backfill).
narra world baseline-arcs # All types with embeddings
narra world baseline-arcs --type character
narra world import <file>
Import world data from YAML file.
narra world import story-world.yaml
narra world import story-world.yaml --on-conflict update # Merge with existing
narra world import story-world.yaml --dry-run # Preview without writing
Conflict modes: error (default, skip conflicts), skip (silent), update (merge fields).
narra world export
Export world data to YAML.
narra world export # Auto-named export
narra world export -o backup.yaml # Custom filename
narra world validate
Validate entity consistency against universe facts and timeline.
narra world validate # General check
narra world validate character:alice # Single entity
narra world graph
Generate Mermaid relationship diagram.
narra world graph --scope full # All characters
narra world graph --scope character:alice --depth 2
narra world graph -o graph.mmd # Save to file
Batch Operations
narra batch <type>
Batch-create entities from YAML (stdin or file).
# From file
narra batch character --file characters.yaml
# From stdin
cat characters.yaml | narra batch character
Output Control
Global flags available on all commands:
--json # JSON output for scripting
--md # Markdown output
--brief # Less detail
--full # Maximum detail
--no-semantic # Disable semantic search (use keyword only)
Shell Completions
Generate shell completions for your shell:
narra completions bash > /etc/bash_completion.d/narra
narra completions zsh > ~/.zsh/completion/_narra
narra completions fish > ~/.config/fish/completions/narra.fish
Claude Code Plugin
For Claude Code integration with slash commands, skills, and hooks for fiction writing workflows:
https://github.com/florinutz/flo-market/tree/main/plugins/narra
The plugin auto-downloads the binary, provides session management, and exposes Narra operations as conversational commands.
CLI Walkthrough
A noir mystery walkthrough: detective Alice Voss, corrupt commissioner Gray, and street informant Eddie — building a story world from scratch.
1. Bootstrap the world
The fastest way to set up a story world is via YAML import, which lets you specify your own IDs:
narra world import noir-world.yaml
characters:
- id: alice
name: "Alice Voss"
role: "Detective"
description: "Homicide detective, 15 years on the force. Methodical, relentless."
profile:
wound: ["partner murdered 3 years ago"]
desire: ["justice for her partner"]
secret: ["knows Commissioner Gray ordered the hit"]
contradiction: ["upholds the law while planning extralegal revenge"]
- id: gray
name: "Commissioner Gray"
role: "Antagonist"
description: "Police commissioner with a public image of reform. Privately runs a protection racket."
profile:
wound: ["grew up in poverty, fears losing power"]
desire: ["absolute control of the city"]
secret: ["ordered the murder of Alice's partner"]
contradiction: ["champion of justice who is the biggest criminal"]
- id: eddie
name: "Eddie Malone"
role: "Informant"
description: "Street-level informant who plays all sides. Knows more than he lets on."
profile:
wound: ["abandoned by family at 14"]
desire: ["enough money to disappear"]
secret: ["suspects Gray is dirty but has no proof"]
contradiction: ["sells information to survive while hating betrayal"]
locations:
- id: precinct
name: "Precinct 13"
loc_type: "building"
description: "Aging police station in the warehouse district. Alice's home base."
- id: blind_pig
name: "The Blind Pig"
loc_type: "bar"
description: "Dive bar where Eddie brokers information. Back booth, cash only."
- id: city_hall
name: "City Hall"
loc_type: "building"
description: "Commissioner Gray's seat of power. Marble corridors, closed doors."
events:
- id: murder
title: "Partner's Murder"
sequence: 1
description: "Alice's partner killed in an apparent robbery. Case goes cold."
- id: promotion
title: "Gray's Promotion"
sequence: 2
description: "Gray becomes commissioner, promising to clean up the city."
- id: tip
title: "Eddie's Tip"
sequence: 3
description: "Eddie approaches Alice with a rumor about Gray's protection racket."
- id: confrontation
title: "The Confrontation"
sequence: 4
description: "Alice confronts Gray with evidence. The truth comes out."
relationships:
- from_character_id: "character:alice"
to_character_id: "character:gray"
rel_type: "antagonistic"
label: "Hunter and prey — neither knows who is which"
- from_character_id: "character:eddie"
to_character_id: "character:alice"
rel_type: "professional"
subtype: "informant"
label: "Eddie feeds Alice information for cash"
- from_character_id: "character:gray"
to_character_id: "character:eddie"
rel_type: "professional"
subtype: "asset"
label: "Gray uses Eddie as eyes on the street"
knowledge:
- character_id: "character:alice"
target_id: "character:gray"
fact: "Commissioner Gray ordered the murder of Alice's partner"
certainty: "knows"
method: "discovered"
- character_id: "character:eddie"
target_id: "character:gray"
fact: "Commissioner Gray is connected to organized crime"
certainty: "suspects"
method: "deduced"
- character_id: "character:gray"
target_id: "character:alice"
fact: "No one suspects my involvement in the partner's murder"
certainty: "believes_wrongly"
method: "deduced"
Three characters with intertwined secrets. Profile keys (wound, desire, secret, contradiction) become part of each character's embedding — enabling semantic search across psychological dimensions. Alice knows what Gray believes no one knows. Eddie suspects but can't prove it.
All entity creation uses the unified narra create <type> interface. See the CLI Command Reference below for complete command documentation.
2. Generate embeddings
narra world backfill
Computes BGE-small-en-v1.5 embeddings for all entities. This enables semantic search, arc tracking, and perception analysis.
3. Explore and search
# Deep exploration: relationships, knowledge, perceptions, similar entities
narra explore alice
# Natural language questions
narra ask "What secrets does Alice know that Gray doesn't?"
# Simple search (hybrid by default - combines keyword + semantic)
narra find "Gray murder"
# Semantic-only search (find by meaning, not keywords)
narra find --semantic-only "characters hiding dark secrets"
# Advanced searches
narra find join "characters whose desires conflict with Alice's wounds"
narra find knowledge "the royal succession" --character alice
narra find graph alice "betrayal and deception" --hops 2
narra find perspectives "threatening and dangerous" --observer bob
narra find similar alice gray # Find relationships similar to Alice-Gray dynamic
# Connection analysis
narra path alice eddie # Shortest connection paths
narra references alice # What mentions/references Alice?
High-level commands (
explore,ask,find) provide intuitive access to Narra's intelligence. Semantic search finds entities by psychological profile, not just keywords. Hybrid search combines both approaches.
4. Analyze the network
# Who are the most connected characters?
narra analyze centrality
# How would information spread from Eddie?
narra analyze influence eddie
# What knowledge asymmetries exist?
narra analyze irony
# What does Alice know that Gray doesn't, and vice versa?
narra analyze asymmetries alice gray
# Who believes things that are wrong?
narra analyze conflicts
# Where are the unresolved tensions?
narra analyze tensions
# Which characters have changed the most?
narra analyze arc-drift
5. Visualize
narra world graph --scope full
Generates a Mermaid diagram of the relationship network.
6. Export
narra world export -o noir-export.yaml
Exports the entire world to YAML. Re-importable with
narra world import noir-export.yaml.
With the MCP server, an AI assistant can run all of these operations plus many more — including perception gaps, perception matrices, arc comparison, thematic clustering, what-if analysis, and semantic graph search.
MCP Server
Start the MCP server (stdio transport, designed for Claude Code and other MCP clients):
narra mcp
Tools Overview
Highlighted Operations
Resources and Prompts
Resources (narra:// URIs):
narra://session/context— hot entities, pinned items, pending decisionsnarra://entity/{type}:{id}— full entity view with attributes and relationshipsnarra://consistency/issues— current violations by severitynarra://schema/import-template— YAML template for world importnarra://schema/import-schema— JSON Schema for import validation
Prompts:
check_consistency— guided validation with fix suggestionsdramatic_irony— knowledge asymmetry analysis between characters
Claude Code Plugin
For integrated Claude Code workflows, install the Narra plugin. It provides slash commands, skills, hooks, and automatic binary management.
Data Storage
By default, Narra stores data in ~/.narra/. You can override this per-project or globally:
# Per-project: create a .narra directory in your project root
mkdir .narra
narra list character # uses ./.narra/ automatically
# Environment variable
export NARRA_DATA_PATH=/path/to/data
# CLI flag (highest priority)
narra --data-path /path/to/data list character
Resolution order: --data-path flag > NARRA_DATA_PATH env > ./.narra (if exists) > ~/.narra
Architecture
CLI (clap) ──┐
├─→ AppContext ──→ Services ──→ Repositories ──→ SurrealDB (RocksDB)
MCP (rmcp) ──┘
- AppContext (
init.rs) — central dependency wiring. Both CLI and MCP share the same services - Trait objects — all services use
Arc<dyn XService + Send + Sync>for testability - Tagged enum dispatch — MCP tools receive
#[serde(tag = "operation")]discriminated unions - Embedding service trait —
EmbeddingServicewithLocalEmbeddingService(fastembed) for production andNoopEmbeddingServicefor tests - Caching — moka async caches for context and summary services
- Data providers — services like IronyService, InfluenceService, GraphAnalyticsService use data provider traits, enabling unit tests with mock data
Development
git clone https://github.com/florinutz/narra.git
cd narra
cargo build # Debug build
cargo test # All tests
cargo test --lib # Unit tests only
cargo test --test '*' # Integration tests only
cargo clippy -- -D warnings # Lint (CI enforces warnings-as-errors)
cargo fmt -- --check # Format check
Key make targets:
make build # Debug build
make lint # Clippy + fmt check
make test # All tests
make quick-check # Lint + build (no tests)
make test-embedding # Embedding-specific tests
Tests use an isolated SurrealDB instance per test (temp directory), with fluent builders for test data (CharacterBuilder, LocationBuilder, etc.) in tests/common/.