Skip to main content
Glama
suhitanantula

LLV Helix Framework

load_data

Restore saved lines, loops, vibes, and contexts from persistent storage to resume creative work or merge with existing data.

Instructions

Load lines, loops, vibes, and contexts from persistent storage

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filenameNoOptional custom filename (without extension)
mergeNoMerge with existing data instead of replacing

Implementation Reference

  • The main handler function for the 'load_data' tool. It reads data from a JSON file, optionally merges with existing in-memory data (lines, loops, vibes, contexts), regenerates rhythms, and returns a formatted result. Called from the CallToolRequestSchema switch statement.
    async loadDataTool(args) {
      if (!this.persistenceEnabled) {
        return {
          content: [
            {
              type: 'text',
              text: `❌ Data persistence is disabled. Set LLV_PERSISTENCE=true to enable.`,
            },
          ],
        };
      }
    
      try {
        const filename = args.filename || 'llv-session';
        const filepath = join(this.dataDir, `${filename}.json`);
        const merge = args.merge || false;
    
        let originalCounts = {};
        if (merge) {
          originalCounts = {
            lines: this.lines.size,
            loops: this.loops.size,
            vibes: this.vibes.size,
            contexts: this.contexts.size,
          };
        }
    
        const data = JSON.parse(await fs.readFile(filepath, 'utf8'));
    
        if (merge) {
          // Merge data instead of replacing
          for (const [name, line] of Object.entries(data.lines || {})) {
            if (!this.lines.has(name)) {
              this.lines.set(name, line);
              this.rhythms.set(`line_${name}`, this.generateRhythm(line.rhythm));
            }
          }
          for (const [name, loop] of Object.entries(data.loops || {})) {
            if (!this.loops.has(name)) {
              this.loops.set(name, loop);
              this.rhythms.set(`loop_${name}`, this.generateRhythm(loop.rhythm));
            }
          }
          for (const [name, vibe] of Object.entries(data.vibes || {})) {
            if (!this.vibes.has(name)) {
              this.vibes.set(name, vibe);
              this.rhythms.set(`vibe_${name}`, this.generateRhythm(vibe.rhythm));
            }
          }
          for (const [name, context] of Object.entries(data.contexts || {})) {
            if (!this.contexts.has(name)) {
              this.contexts.set(name, context);
            }
          }
        } else {
          // Replace all data
          this.lines = this.objToMap(data.lines);
          this.loops = this.objToMap(data.loops);
          this.vibes = this.objToMap(data.vibes);
          this.contexts = this.objToMap(data.contexts);
    
          // Regenerate rhythms
          this.rhythms.clear();
          for (const [name, line] of this.lines) {
            this.rhythms.set(`line_${name}`, this.generateRhythm(line.rhythm));
          }
          for (const [name, loop] of this.loops) {
            this.rhythms.set(`loop_${name}`, this.generateRhythm(loop.rhythm));
          }
          for (const [name, vibe] of this.vibes) {
            this.rhythms.set(`vibe_${name}`, this.generateRhythm(vibe.rhythm));
          }
        }
    
        let resultText = `📂 Data loaded successfully!\n\nFile: ${filepath}\nLoaded: ${data.timestamp}\n`;
    
        if (merge) {
          const newCounts = {
            lines: this.lines.size - originalCounts.lines,
            loops: this.loops.size - originalCounts.loops,
            vibes: this.vibes.size - originalCounts.vibes,
            contexts: this.contexts.size - originalCounts.contexts,
          };
          resultText += `\nMerged:\n• +${newCounts.lines} lines\n• +${newCounts.loops} loops\n• +${newCounts.vibes} vibes\n• +${newCounts.contexts} contexts`;
        } else {
          resultText += `\nCurrent state:\n• ${this.lines.size} lines\n• ${this.loops.size} loops\n• ${this.vibes.size} vibes\n• ${this.contexts.size} contexts`;
        }
    
        return {
          content: [
            {
              type: 'text',
              text: resultText,
            },
          ],
        };
      } catch (error) {
        return {
          content: [
            {
              type: 'text',
              text: `❌ Failed to load data: ${error.message}`,
            },
          ],
        };
      }
    }
  • Internal helper method loadData() used to load data on server startup. It replaces all in-memory data with file contents and regenerates rhythms. Does not accept merge or return user-facing messages.
    async loadData(filename = 'llv-session') {
      if (!this.persistenceEnabled) {
        return;
      }
    
      try {
        const filepath = join(this.dataDir, `${filename}.json`);
        const data = JSON.parse(await fs.readFile(filepath, 'utf8'));
    
        this.lines = this.objToMap(data.lines);
        this.loops = this.objToMap(data.loops);
        this.vibes = this.objToMap(data.vibes);
        this.contexts = this.objToMap(data.contexts);
    
        // Regenerate rhythms for loaded data
        this.rhythms.clear();
        for (const [name, line] of this.lines) {
          this.rhythms.set(`line_${name}`, this.generateRhythm(line.rhythm));
        }
        for (const [name, loop] of this.loops) {
          this.rhythms.set(`loop_${name}`, this.generateRhythm(loop.rhythm));
        }
        for (const [name, vibe] of this.vibes) {
          this.rhythms.set(`vibe_${name}`, this.generateRhythm(vibe.rhythm));
        }
    
        console.error(`Loaded data from ${filepath}: ${this.lines.size} lines, ${this.loops.size} loops, ${this.vibes.size} vibes`);
      } catch (error) {
        if (error.code !== 'ENOENT') {
          console.error('Failed to load data:', error);
        }
      }
    }
  • Input schema registration for the 'load_data' tool within ListToolsRequestSchema. Defines the 'filename' (optional string) and 'merge' (optional boolean, default false) parameters.
    {
      name: 'load_data',
      description: 'Load lines, loops, vibes, and contexts from persistent storage',
      inputSchema: {
        type: 'object',
        properties: {
          filename: {
            type: 'string',
            description: 'Optional custom filename (without extension)',
          },
          merge: {
            type: 'boolean',
            description: 'Merge with existing data instead of replacing',
            default: false,
          },
        },
      },
    },
  • index.js:353-354 (registration)
    Routes the 'load_data' tool name to the loadDataTool handler method in the CallToolRequestSchema switch statement.
    case 'load_data':
      return this.loadDataTool(args);
  • index.js:36-39 (registration)
    Calls loadData() on startup when persistence is enabled, to restore previous session data.
    // Load data on startup if persistence is enabled
    if (this.persistenceEnabled) {
      this.loadData().catch(console.error);
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description bears full responsibility. It only states 'Load from persistent storage' without detailing side effects, default file behavior, or how 'merge' affects existing data. Key behavioral traits like potential data replacement are omitted.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, succinct sentence that conveys the core functionality without redundancy. It is front-loaded with the action and key resources, earning its place with no wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations and no output schema, the description covers the resource types and persistence source but omits critical context like default filename, file format, error handling, and whether loading is destructive without 'merge'. Incomplete for a parameterized tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, with both parameters documented in the schema. The description adds no additional meaning beyond the schema, e.g., it does not explain what 'custom filename' implies or when 'merge' should be used. Baseline 3 is justified.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Load') and the specific resources ('lines, loops, vibes, and contexts') from persistent storage. It distinguishes itself from siblings like 'save_data' and 'create_line' by focusing on retrieval versus creation or persistence.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for retrieving stored data but provides no explicit guidance on when to use this tool over alternatives, such as when to use 'create_line' for new data or 'merge' vs 'replace'. No when-not or sibling comparisons are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/suhitanantula/llv-helix'

If you have feedback or need assistance with the MCP directory API, please join our Discord server