Module: ClaudeMemory::MCP::InstructionsBuilder
- Defined in:
- lib/claude_memory/mcp/instructions_builder.rb
Overview
Generates dynamic MCP server instructions from database state. Injected into the LLM system prompt via the initialize response, giving Claude immediate context about memory state without extra tool calls.
Source: QMD mcp.ts:91-152 (buildInstructions pattern)
Constant Summary collapse
- DECISION_PREDICATES =
%w[decided decision chose selected adopted rejected].freeze
- CONVENTION_PREDICATES =
%w[convention style_rule prefers uses_style coding_standard].freeze
Class Method Summary collapse
- .build(store_or_manager) ⇒ Object
- .conflict_summary(manager) ⇒ Object
- .count_by_predicates(store, predicates) ⇒ Object
- .database_summary(manager) ⇒ Object
- .knowledge_summary(manager) ⇒ Object
-
.proactive_recall_guidance ⇒ Object
Directive guidance for when Claude should proactively consult memory.
- .single_db_summary(store) ⇒ Object
- .usage_hint(store_or_manager) ⇒ Object
- .vec_available?(store_or_manager) ⇒ Boolean
Class Method Details
.build(store_or_manager) ⇒ Object
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/claude_memory/mcp/instructions_builder.rb', line 16 def build(store_or_manager) parts = ["ClaudeMemory v#{ClaudeMemory::VERSION} — long-term memory for Claude Code."] if store_or_manager.is_a?(Store::StoreManager) parts << database_summary(store_or_manager) parts << knowledge_summary(store_or_manager) parts << conflict_summary(store_or_manager) elsif store_or_manager.respond_to?(:facts) parts << single_db_summary(store_or_manager) end parts << usage_hint(store_or_manager) parts.compact.join("\n\n") rescue => e # Never fail initialization — return minimal instructions ClaudeMemory.logger.debug("InstructionsBuilder failed: #{e.}") "ClaudeMemory v#{ClaudeMemory::VERSION} — long-term memory for Claude Code." end |
.conflict_summary(manager) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/claude_memory/mcp/instructions_builder.rb', line 84 def conflict_summary(manager) count = 0 if manager.global_exists? count += manager.global_store.conflicts.where(status: "open").count end if manager.project_exists? count += manager.project_store.conflicts.where(status: "open").count end return nil if count == 0 "#{count} open conflict#{"s" unless count == 1} — use memory.conflicts to review." end |
.count_by_predicates(store, predicates) ⇒ Object
131 132 133 134 135 136 |
# File 'lib/claude_memory/mcp/instructions_builder.rb', line 131 def count_by_predicates(store, predicates) store.facts .where(status: "active") .where(predicate: predicates) .count end |
.database_summary(manager) ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/claude_memory/mcp/instructions_builder.rb', line 35 def database_summary(manager) lines = [] if manager.global_exists? manager.ensure_global! global = manager.global_store g_facts = global.facts.where(status: "active").count lines << "Global: #{g_facts} active facts" end if manager.project_exists? manager.ensure_project! project = manager.project_store p_facts = project.facts.where(status: "active").count lines << "Project: #{p_facts} active facts" end return nil if lines.empty? "Database state: #{lines.join(", ")}." end |
.knowledge_summary(manager) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/claude_memory/mcp/instructions_builder.rb', line 56 def knowledge_summary(manager) decisions = 0 conventions = 0 entities = 0 [manager.global_exists? && manager.global_store, manager.project_exists? && manager.project_store].each do |store| next unless store decisions += count_by_predicates(store, DECISION_PREDICATES) conventions += count_by_predicates(store, CONVENTION_PREDICATES) entities += store.entities.count end return nil if decisions == 0 && conventions == 0 && entities == 0 parts = [] parts << "#{decisions} decision#{"s" unless decisions == 1}" if decisions > 0 parts << "#{conventions} convention#{"s" unless conventions == 1}" if conventions > 0 parts << "#{entities} #{(entities == 1) ? "entity" : "entities"}" if entities > 0 "Knowledge: #{parts.join(", ")}." end |
.proactive_recall_guidance ⇒ Object
Directive guidance for when Claude should proactively consult memory. Validated by A/B testing: without these directives, Claude writes code using known-dangerous patterns (e.g. Sequel.sqlite) and hallucinates file paths instead of consulting memory for the correct structure.
120 121 122 123 124 125 126 127 128 129 |
# File 'lib/claude_memory/mcp/instructions_builder.rb', line 120 def proactive_recall_guidance <<~GUIDANCE.strip IMPORTANT — check memory proactively in these situations: - Before writing code: call memory.conventions to verify project patterns and avoid known gotchas - Before explaining architecture: call memory.architecture for structural knowledge without file traversal - Before refactoring: call memory.decisions to understand why past choices were made - When asked about preferences: global facts store user environment and style preferences across all projects - When adding to the codebase: recall which files and patterns to follow (memory knows correct paths and relationships) GUIDANCE end |
.single_db_summary(store) ⇒ Object
79 80 81 82 |
# File 'lib/claude_memory/mcp/instructions_builder.rb', line 79 def single_db_summary(store) facts = store.facts.where(status: "active").count "Database state: #{facts} active facts." end |
.usage_hint(store_or_manager) ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/claude_memory/mcp/instructions_builder.rb', line 99 def usage_hint(store_or_manager) lines = [ "Use memory.recall to search facts, memory.decisions for architectural decisions, memory.conventions for coding style." ] vec = vec_available?(store_or_manager) if vec lines << "Semantic search available — use memory.recall_semantic for natural language queries, memory.search_concepts for multi-concept intersection." end escalation = vec ? "recall_semantic, explain, or fact_graph" : "explain or fact_graph" lines << "Start with fast tools (recall, decisions, conventions) before escalating to #{escalation}." lines << proactive_recall_guidance lines.join("\n") end |
.vec_available?(store_or_manager) ⇒ Boolean
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/claude_memory/mcp/instructions_builder.rb', line 138 def vec_available?(store_or_manager) if store_or_manager.is_a?(Store::StoreManager) [store_or_manager.global_exists? && store_or_manager.global_store, store_or_manager.project_exists? && store_or_manager.project_store].any? do |store| store&.vector_index&.available? end elsif store_or_manager.respond_to?(:vector_index) store_or_manager.vector_index.available? end rescue false end |