New in v0.15

MCP Server

Give Claude direct access to your decision graph. 31 tools for searching, tracing, and analyzing decisions. Works with Claude Code and Claude Desktop.

Quick Start: Claude Desktop

~/Library/Application Support/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "deciduous": {
      "command": "/Users/you/.cargo/bin/deciduous",
      "args": ["mcp"],
      "env": {
        "DECIDUOUS_DB_PATH": "/path/to/your/project/.deciduous/deciduous.db"
      }
    }
  }
}

Use the absolute path to the deciduous binary (run which deciduous to find it) — Claude Desktop doesn't inherit your shell PATH. Point DECIDUOUS_DB_PATH at the SQLite database in your project's .deciduous/ directory, then restart Claude Desktop.

Quick Start: Claude Code

Setting up MCP in Claude Code
Config
Add to .claude/settings.local.json:
{ "mcpServers": { "deciduous": { "command": "deciduous", "args": ["mcp"] } } }
Result
Restart Claude Code. Tools appear as mcp__deciduous__*
You
What decisions were made about authentication?
Claude
search_nodes({ query: "auth" })
Found 8 nodes matching "auth"
Claude
trace_chain({ node_id: 5, direction: "outgoing" })
{ node_count: 12, edge_count: 11 }
Claude
The auth system went through 2 pivots. Started with JWT everywhere (Goal #5), hit mobile cookie size limits (Observation #8), pivoted to hybrid approach (Revisit #9, Decision #10). 12 nodes trace the full story.

Also available: /query slash command wraps MCP tools into a natural language interface.

Natural Language Queries

In Claude Code, /query uses MCP tools to answer questions about your decisions:

Claude Code — /query in action
You
/query why did we choose an embedded MCP server over a CLI wrapper?
Claude
Querying decision graph...
search_nodes({ query: "embedded MCP" }) → 2 matches get_node_context({ node_id: 1361 }) → 3 parents, 7 children trace_chain({ node_id: 1361 }) → 13 nodes, 14 edges

Why Embedded Over CLI Wrapper

The embedded approach won at 92% confidence. It fits the project's "functional core, imperative shell" constraint and gives direct DB access with no subprocess overhead.

Options Compared

OptionConfidenceVerdictRationale
#1358 Embedded server90%ChosenSingle binary, pure functions, testable
#1359 CLI wrapper40%RejectedProcess spawn per call, fragile text parsing
#1360 Separate crate50%RejectedUnnecessary build complexity

Decision Chain

Goal #1356 — Add MCP server to deciduous
Option #1358 — Embedded MCP server chosen
Option #1359 — Thin CLI wrapper rejected
Option #1360 — Separate crate rejected
Decision #1361 — Choose embedded MCP server [92%]
Action #1362 — Implement protocol types
Action #1363 — Build tool registry
Action #1364 — Implement CRUD handlers
Action #1365 — Build query/analysis engine
Action #1366 — Wire server loop
Action #1367 — Write test suite

Key Observation

#1357: “User explicitly requested functional core, imperative shell, lots of tests” — this made pure-function parsing and zero-IO modules the obvious architectural choice.
7 actions completed. 108 MCP-specific tests passing. All pure functions trivially unit-testable, as the architecture predicted.

Searching Decisions

Real output from deciduous's own graph (1,300+ nodes from building this tool):

search_nodes — full-text across titles, descriptions, prompts
You
What decisions were made about the MCP server?
Claude
search_nodes({ query: "MCP" })
{ "count": 26, "nodes": [ { "id": 1356, "node_type": "goal", "title": "Add MCP server to deciduous", "confidence": 90 }, { "id": 1361, "node_type": "decision", "title": "Choose embedded MCP server (Option A)", "confidence": 92 }, { "id": 1381, "node_type": "outcome", "title": "Session lifecycle complete", "confidence": 95 }, ... ] }

Tracing Decision Chains

BFS traversal from any node. Shows the full connected decision tree with depth:

trace_chain — follow edges outward from a goal
You
Show me everything that came from the MCP server goal
Claude
trace_chain({ node_id: 1356, direction: "outgoing", max_depth: 2 })
{ "start_node_id": 1356, "node_count": 13, "edge_count": 14, "nodes": [ { "id": 1356, "node_type": "goal", "title": "Add MCP server to deciduous", "depth": 0 }, { "id": 1358, "node_type": "option", "title": "Embedded MCP server via deciduous mcp subcommand","depth": 1 }, { "id": 1359, "node_type": "option", "title": "Thin CLI wrapper - shells out to commands", "depth": 1 }, { "id": 1360, "node_type": "option", "title": "Separate mcp-deciduous crate/binary", "depth": 1 }, { "id": 1361, "node_type": "decision", "title": "Choose embedded MCP server (Option A)", "depth": 2 }, { "id": 1376, "node_type": "goal", "title": "Auto-create decision trees per conversation", "depth": 1 }, { "id": 1377, "node_type": "option", "title": "Session-scoped: start/end_session tools", "depth": 2 } ] }
Claude
The MCP goal spawned 3 options at depth 1 (embedded, CLI wrapper, separate crate). The embedded approach was chosen (depth 2). A sub-goal for conversation scoping also branched off at depth 1.

Node Context

Get a decision's full neighborhood — what options led to it, what actions came from it:

get_node_context — parents, children, siblings
You
What led to choosing the embedded MCP server, and what came from it?
Claude
get_node_context({ node_id: 1361 })
Node: #1361 [decision] "Choose embedded MCP server (Option A)" (92%) Parents (3 options considered): #1358 [option] "Embedded MCP server via deciduous mcp subcommand" (90%) #1359 [option] "Thin CLI wrapper MCP - shells out to commands" (40%) #1360 [option] "Separate mcp-deciduous crate/binary" (50%) Children (7 actions spawned): #1362 [action] "Implement MCP protocol types (protocol.rs)" #1363 [action] "Build tool registry and definitions (tools.rs)" #1364 [action] "Implement CRUD handlers (handlers.rs)" #1365 [action] "Build query/analysis engine (query.rs)" #1366 [action] "Wire server loop and CLI subcommand" #1367 [action] "Write comprehensive test suite" #1374 [action] "Create /query skill for NL reports"
Claude
3 options were considered. The embedded approach won at 92% confidence (CLI wrapper was only 40% — too fragile). It spawned 7 implementation actions covering protocol, tools, handlers, query engine, server loop, tests, and a slash command.

Graph Pulse

Health check — stats, active goals, and what needs attention:

get_pulse — branch health at a glance
Claude
get_pulse({ branch: "add-mcp-server" })
total_nodes: 37 | total_edges: 1229 By type: goal: 4 | option: 6 | decision: 3 action: 11 | outcome: 11 | observation: 2 Active goals: #1356 Add MCP server to deciduous #1376 MCP server auto-creates decision trees for each conversation #1387 Write MCP setup docs for README + deciduous.dev #1391 Rewrite MCP docs: quickstarts first, real examples

Sessions: Per-Conversation Trees

Each conversation gets its own scoped decision tree. Nodes auto-associate.

Claude starting a session
Claude
start_session({ name: "add MCP server", goal_title: "Add MCP server to deciduous" })
{ "session_id": 1, "root_node_id": 1356, "name": "add MCP server" }
Later
... hours of work, server may restart ...
Claude
get_session({})
{ "session_id": 1, "is_active": true, "node_count": 37, "nodes": [...] }
After a break
Explicitly reconnect to a previous session:
Claude
resume_session({ session_id: 1 })
{ "session_id": 1, "node_count": 37, "message": "Resumed session #1 (37 nodes)" }

Sessions persist to .deciduous/active_session. Server restarts auto-resume. Long pauses use resume_session.

Conversational Memory

Every conversation with Claude gets its own decision tree. When a session starts, a root goal node is created. Every node you add during that conversation is automatically associated with that session — no manual bookkeeping. When the conversation ends, an optional summary captures what was accomplished.

The result: each conversation produces a self-contained, queryable record of what was discussed, decided, and built.

How Sessions Work

  1. start_session creates a root goal node and returns a session ID
  2. All subsequent add_node calls auto-associate with the active session
  3. end_session marks the session complete with an optional summary
Session lifecycle
Claude
start_session({ name: "auth refactor", goal_title: "Refactor authentication to use OAuth2", goal_prompt: "I need to replace our custom JWT auth with OAuth2..." })
{ "session_id": 3, "root_node_id": 42, "name": "auth refactor" }
During conversation
Every add_node call auto-associates with session #3
Claude
add_node({ node_type: "option", title: "Use Auth0 as provider" })
Node #43 created — automatically part of session #3
Claude
end_session({ summary: "Chose Auth0, implemented OAuth2 flow, tests passing" })
{ "session_id": 3, "node_count": 11, "message": "Ended session #3 (11 nodes)" }

Resuming Across Conversations

Pick up where you left off. Use list_sessions to find previous conversations, then resume_session to reconnect. New nodes will continue building on the same decision tree.

Resuming a previous conversation
You
List my sessions
Claude
list_sessions({})
{ "count": 3, "sessions": [ { "session_id": 1, "name": "initial setup", "is_active": false }, { "session_id": 2, "name": "add caching", "is_active": false }, { "session_id": 3, "name": "auth refactor", "is_active": false } ]}
You
Resume the auth refactor
Claude
resume_session({ session_id: 3 })
{ "session_id": 3, "name": "auth refactor", "node_count": 11, "message": "Resumed session #3 (11 nodes)" }
Claude
get_session({ session_id: 3 })
Returns all 11 nodes from the previous conversation for full context

Persistence

The active session ID is written to .deciduous/active_session on disk. When the MCP server starts (or restarts — Claude Desktop and Claude Code may kill and re-spawn the server process), it reads this file and automatically resumes the active session. No manual intervention needed.

If the session was already ended, the stale file is cleaned up automatically.

Master Memory: One Graph Across All Conversations

Claude Desktop users get a persistent decision graph across every conversation. The MCP config points DECIDUOUS_DB_PATH at a single .deciduous/deciduous.db file. Every conversation — whether it runs for five minutes or five hours — writes to the same database.

This means you can ask Claude in a new conversation about decisions made in previous ones. The graph is the memory layer that outlasts any single chat.

How it works

Conversation 1 (Jan)  --> .deciduous/deciduous.db  <-- Session #1 (14 nodes)
Conversation 2 (Feb)  --> .deciduous/deciduous.db  <-- Session #2 (8 nodes)
Conversation 3 (Mar)  --> .deciduous/deciduous.db  <-- Session #3 (11 nodes)

New conversation:
  "Why did we choose Auth0 over Okta?"
  --> search_nodes({ query: "Auth0" })
  --> trace_chain({ node_id: 43 })
  --> Full decision history from Session #3

The database is the single source of truth. Sessions organize it by conversation. Search and trace work across all of them.