Skip to main content
Glama

analyze_loadout

Analyze a D&D 5e equipment loadout to determine total weight, cost, armor class, and encumbrance based on Strength.

Instructions

Analyze a D&D 5e equipment loadout. Calculates total weight, total cost, AC from armor/shield, and encumbrance status based on Strength score.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
itemsYesList of equipment item names to analyze
strength_scoreNoCharacter Strength score for encumbrance calculation

Implementation Reference

  • The main tool registration function `registerAnalyzeLoadout` registers the 'analyze_loadout' tool with the MCP server. The handler (async callback starting at line 109) performs the full loadout analysis: looks up equipment by name, calculates total weight and cost, computes AC from armor/shield, summarizes weapons, and computes encumbrance status based on Strength score.
    export function registerAnalyzeLoadout(
      server: McpServer,
      db: Database.Database,
    ): void {
      server.registerTool(
        'analyze_loadout',
        {
          description:
            'Analyze a D&D 5e equipment loadout. Calculates total weight, total cost, AC from armor/shield, and encumbrance status based on Strength score.',
          inputSchema: {
            items: z
              .array(z.string())
              .min(1)
              .describe('List of equipment item names to analyze'),
            strength_score: z
              .number()
              .min(1)
              .max(30)
              .optional()
              .describe('Character Strength score for encumbrance calculation'),
          },
        },
        async ({ items, strength_score }) => {
          const found: LoadoutItem[] = [];
          const notFound: string[] = [];
    
          for (const name of items) {
            const eq = getEquipmentByName(db, name);
            if (eq) {
              found.push({ equipment: eq, name: eq.name });
            } else {
              notFound.push(name);
            }
          }
    
          const lines: string[] = [];
          lines.push('# Equipment Loadout Analysis');
          lines.push('');
    
          if (notFound.length > 0) {
            lines.push('## Unrecognized Items');
            lines.push('');
            lines.push('The following items were not found in the SRD equipment list. Use `search_equipment` to find the correct name.');
            for (const name of notFound) {
              lines.push(`- ~~${name}~~ *(not found)*`);
            }
            lines.push('');
          }
    
          if (found.length === 0) {
            lines.push('*No recognized equipment items to analyze.*');
            return {
              content: [{ type: 'text' as const, text: lines.join('\n') }],
            };
          }
    
          // Inventory table
          lines.push('## Inventory');
          lines.push('');
          lines.push('| Item | Category | Cost | Weight |');
          lines.push('|------|----------|------|--------|');
    
          let totalWeight = 0;
          let totalCostGp = 0;
    
          for (const item of found) {
            const eq = item.equipment;
            const weight = eq.weight ?? 0;
            const cost = eq.cost_gp ?? 0;
            totalWeight += weight;
            totalCostGp += cost;
    
            const weightStr = eq.weight !== null ? `${eq.weight} lb.` : '—';
            lines.push(`| ${eq.name} | ${eq.category ?? '—'} | ${formatCost(eq.cost_gp, eq.cost_unit)} | ${weightStr} |`);
          }
    
          lines.push('');
          lines.push(`**Total Weight:** ${totalWeight} lb.`);
          lines.push(`**Total Cost:** ${totalCostGp} gp`);
          lines.push('');
    
          // AC breakdown
          lines.push('## Armor Class');
          lines.push('');
          lines.push(calculateAc(found));
          lines.push('');
    
          // Weapons summary
          const weapons = found.filter(
            (item) => item.equipment.damage_dice !== null,
          );
          if (weapons.length > 0) {
            lines.push('## Weapons');
            lines.push('');
            for (const w of weapons) {
              const eq = w.equipment;
              const props = eq.weapon_properties ?? '';
              const rangeStr =
                eq.range_normal !== null
                  ? ` (${eq.range_normal}/${eq.range_long ?? '—'} ft.)`
                  : '';
              lines.push(
                `- **${eq.name}**: ${eq.damage_dice} ${eq.damage_type ?? ''}${rangeStr}${props ? ` — ${props}` : ''}`,
              );
            }
            lines.push('');
          }
    
          // Encumbrance
          if (strength_score !== undefined) {
            lines.push('## Encumbrance');
            lines.push('');
            const carryCapacity = strength_score * 15;
            const encumberedThreshold = strength_score * 5;
            const heavilyEncumberedThreshold = strength_score * 10;
    
            lines.push(`**Strength:** ${strength_score}`);
            lines.push(`**Carry Capacity:** ${carryCapacity} lb.`);
            lines.push(`**Current Load:** ${totalWeight} lb. (${Math.round((totalWeight / carryCapacity) * 100)}%)`);
            lines.push('');
    
            if (totalWeight > carryCapacity) {
              lines.push(`**Status: OVER CAPACITY** — Cannot move. Exceeds carry capacity by ${totalWeight - carryCapacity} lb.`);
            } else if (totalWeight > heavilyEncumberedThreshold) {
              lines.push(`**Status: HEAVILY ENCUMBERED** — Speed reduced by 20 ft. (threshold: ${heavilyEncumberedThreshold} lb.)`);
            } else if (totalWeight > encumberedThreshold) {
              lines.push(`**Status: ENCUMBERED** — Speed reduced by 10 ft. (threshold: ${encumberedThreshold} lb.)`);
            } else {
              lines.push(`**Status: Unencumbered** — No speed penalty.`);
            }
    
            lines.push('');
            lines.push('| Threshold | Weight | Status |');
            lines.push('|-----------|--------|--------|');
            lines.push(`| 0–${encumberedThreshold} lb. | Unencumbered | No penalty |`);
            lines.push(`| ${encumberedThreshold + 1}–${heavilyEncumberedThreshold} lb. | Encumbered | Speed −10 ft. |`);
            lines.push(`| ${heavilyEncumberedThreshold + 1}–${carryCapacity} lb. | Heavily Encumbered | Speed −20 ft. |`);
            lines.push(`| ${carryCapacity + 1}+ lb. | Over Capacity | Cannot move |`);
          }
    
          return {
            content: [{ type: 'text' as const, text: lines.join('\n') }],
          };
        },
      );
    }
  • Input schema for 'analyze_loadout' tool: items (array of strings, min 1) for equipment names, and optional strength_score (1-30) for encumbrance calculation.
    'analyze_loadout',
    {
      description:
        'Analyze a D&D 5e equipment loadout. Calculates total weight, total cost, AC from armor/shield, and encumbrance status based on Strength score.',
      inputSchema: {
        items: z
          .array(z.string())
          .min(1)
          .describe('List of equipment item names to analyze'),
        strength_score: z
          .number()
          .min(1)
          .max(30)
          .optional()
          .describe('Character Strength score for encumbrance calculation'),
      },
  • Helper function `formatCost` formats cost with GP or alternative unit.
    function formatCost(costGp: number | null, costUnit: string | null): string {
      if (costGp === null) return '—';
      if (costUnit && costUnit !== 'gp') {
        return `${costGp} ${costUnit}`;
      }
      return `${costGp} gp`;
    }
  • Helper function `calculateAc` analyzes equipped armor/shields and calculates AC formula.
    function calculateAc(items: LoadoutItem[]): string {
      let wornArmor: EquipmentRow | null = null;
      let hasShield = false;
    
      for (const item of items) {
        const eq = item.equipment;
        const cat = eq.category?.toLowerCase() ?? '';
        if (cat === 'shields' || cat === 'shield') {
          hasShield = true;
        } else if ((eq.ac_base !== null && eq.armor_category !== null) || cat.includes('armor')) {
          if (!wornArmor || (eq.ac_base ?? 0) > (wornArmor.ac_base ?? 0)) {
            wornArmor = eq;
          }
        }
      }
    
      if (!wornArmor && !hasShield) {
        return 'No armor equipped. Base AC = 10 + DEX modifier.';
      }
    
      const lines: string[] = [];
    
      if (wornArmor) {
        const base = wornArmor.ac_base ?? 10;
        const dexBonus = wornArmor.ac_dex_bonus ? true : false;
        const maxBonus = wornArmor.ac_max_bonus;
    
        let acFormula: string;
        if (!dexBonus) {
          acFormula = `${base}`;
        } else if (maxBonus !== null && maxBonus !== undefined) {
          acFormula = `${base} + DEX modifier (max ${maxBonus})`;
        } else {
          acFormula = `${base} + DEX modifier`;
        }
    
        lines.push(`**Armor:** ${wornArmor.name} — AC ${acFormula}`);
    
        if (wornArmor.stealth_disadvantage) {
          lines.push('  *Disadvantage on Stealth checks*');
        }
        if (wornArmor.str_minimum) {
          lines.push(`  *Requires STR ${wornArmor.str_minimum} (speed reduced by 10 ft. if not met)*`);
        }
      } else {
        lines.push('**Armor:** None (base AC = 10 + DEX modifier)');
      }
    
      if (hasShield) {
        lines.push('**Shield:** +2 AC');
      }
    
      // Calculate total
      const armorAc = wornArmor?.ac_base ?? 10;
      const shieldBonus = hasShield ? 2 : 0;
      const dexNote = wornArmor && !wornArmor.ac_dex_bonus
        ? ''
        : ' + DEX mod';
      const maxNote = wornArmor?.ac_max_bonus !== null && wornArmor?.ac_max_bonus !== undefined && wornArmor?.ac_dex_bonus
        ? ` (max ${wornArmor.ac_max_bonus})`
        : '';
    
      lines.push(`**Total AC:** ${armorAc + shieldBonus}${dexNote}${maxNote}`);
    
      return lines.join('\n');
    }
  • Database helper `getEquipmentByName` queries the equipment table by name (case-insensitive).
    export function getEquipmentByName(
      db: Database.Database,
      name: string,
    ): EquipmentRow | undefined {
      return db
        .prepare('SELECT * FROM equipment WHERE LOWER(name) = LOWER(?)')
        .get(name) as EquipmentRow | undefined;
    }
  • src/server.ts:56-57 (registration)
    Registration call: `registerAnalyzeLoadout(server, db)` in the main server setup.
    registerCompareMonsters(server, db);
    registerAnalyzeLoadout(server, db);
Behavior3/5

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

With no annotations, description must fully disclose behavior. It lists computed outputs but does not mention side effects, permissions, or return format. Adequate but not thorough.

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?

Single, well-structured sentence that front-loads the action and lists key outputs. 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?

No output schema, so description should detail return values. It names calculations but not their structure. Lacks info on error handling or edge cases. Adequate for simple 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%, so baseline is 3. Description adds minimal context: 'for encumbrance calculation' for strength_score. Does not explain item naming conventions or expected format.

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

Purpose4/5

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

The description clearly states the tool analyzes a D&D 5e equipment loadout and lists specific calculations (weight, cost, AC, encumbrance). This distinguishes it from sibling tools like search_equipment or build_encounter, though not explicitly.

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

Usage Guidelines2/5

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

No guidance on when to use this tool versus alternatives like search_equipment or build_encounter. The description only states what it does, not when it's appropriate.

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/gregario/dnd-oracle'

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