Skip to main content
Glama

use_scroll

Cast spells from D&D 5e scrolls with automatic success for known spells or Arcana checks for higher-level magic, consuming the scroll on use.

Instructions

Use a spell scroll in D&D 5e. If spell is on your class list and same/lower level: auto-success. If spell is higher level: Arcana check DC 10 + spell level. On failure: scroll is consumed with no effect. On success: spell is cast from scroll, scroll consumed.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
characterIdYes
scrollNameYes
spellLevelYes
casterLevelYes
targetIdNo
targetIdsNo
targetPositionNo
arcanaBonusNo
rollModeNo
manualRollNo
manualRollsNo
isAttackSpellNo
spellSchoolNo

Implementation Reference

  • The main handler function that implements the use_scroll tool logic. Handles Arcana checks when using spell scrolls according to D&D 5e rules (auto-success if spell level <= caster level, otherwise DC 10 + spell level). Generates ASCII art output with scroll stats, targeting info, and result.
    export function useScroll(input: UseScrollInput): string {
      const content: string[] = [];
      const {
        characterId,
        scrollName,
        spellLevel,
        casterLevel,
        targetId,
        targetIds,
        targetPosition,
        arcanaBonus = 0,
        rollMode = 'normal',
        manualRoll,
        manualRolls,
        isAttackSpell,
        spellSchool,
      } = input;
    
      // Get scroll stats
      const stats = SCROLL_STATS[spellLevel];
    
      // Extract spell name from scroll name
      const spellName = scrollName.replace(/^Scroll of /i, '');
    
      content.push(centerText('SPELL SCROLL', DISPLAY_WIDTH));
      content.push('');
      content.push(`Caster: ${characterId}`);
      content.push(`Scroll: ${scrollName}`);
      content.push(`Spell Level: ${spellLevel}`);
    
      if (spellSchool) {
        content.push(`School: ${spellSchool}`);
      }
    
      content.push('');
      content.push(BOX.LIGHT.H.repeat(DISPLAY_WIDTH));
      content.push('');
    
      // Check if Arcana roll is needed
      const needsArcanaCheck = spellLevel > casterLevel;
      let success = true;
    
      if (needsArcanaCheck) {
        const arcanaDC = 10 + spellLevel;
        content.push(`Spell level (${spellLevel}) exceeds caster level (${casterLevel})`);
        content.push(`Arcana Check Required: DC ${arcanaDC}`);
        content.push('');
    
        // Roll Arcana check
        const { finalRoll, rollDisplay } = resolveArcanaRoll(rollMode, manualRoll, manualRolls);
        const total = finalRoll + arcanaBonus;
        success = total >= arcanaDC;
    
        content.push(`Roll: ${rollDisplay}`);
        if (arcanaBonus !== 0) {
          content.push(`Modifier: ${formatModifier(arcanaBonus)}`);
          content.push(`Total: ${finalRoll} ${formatModifier(arcanaBonus)} = ${total}`);
        } else {
          content.push(`Total: ${total}`);
        }
        content.push('');
        content.push(BOX.LIGHT.H.repeat(DISPLAY_WIDTH));
        content.push('');
      }
    
      // Show targeting info if provided
      if (targetId || targetIds || targetPosition) {
        content.push('Targeting:');
        if (targetId) {
          content.push(`  Target: ${targetId}`);
        }
        if (targetIds && targetIds.length > 0) {
          content.push(`  Targets: ${targetIds.join(', ')}`);
        }
        if (targetPosition) {
          content.push(`  Position: (${targetPosition.x}, ${targetPosition.y}${targetPosition.z !== undefined ? `, ${targetPosition.z}` : ''})`);
        }
        content.push('');
      }
    
      // Show spell stats
      content.push(`Spell Save DC: DC ${stats.saveDC}`);
      if (isAttackSpell) {
        content.push(`Spell Attack: +${stats.attackBonus}`);
      }
      content.push('');
      content.push(BOX.LIGHT.H.repeat(DISPLAY_WIDTH));
      content.push('');
    
      // Result
      if (success) {
        content.push(centerText('SUCCESS', DISPLAY_WIDTH));
        content.push('');
        content.push(`${spellName} has been cast!`);
        content.push('The scroll crumbles to dust (consumed).');
      } else {
        content.push(centerText('FAILED', DISPLAY_WIDTH));
        content.push('');
        content.push('The magic fizzles and the scroll is lost!');
        content.push('The scroll crumbles to dust (consumed).');
      }
    
      const title = success ? 'SCROLL CAST' : 'SCROLL FAILED';
      return createBox(title, content);
    }
  • Zod input schema for the use_scroll tool, defining required fields like characterId, scrollName, spellLevel, casterLevel, and optional targeting, roll modes, and spell properties.
    export const useScrollSchema = z.object({
      characterId: z.string(),
      scrollName: z.string(),
      spellLevel: z.number().min(0).max(9),
      casterLevel: z.number().min(1).max(20),
    
      // Targeting (all optional)
      targetId: z.string().optional(),
      targetIds: z.array(z.string()).optional(),
      targetPosition: PositionSchema.optional(),
    
      // Arcana check for higher-level scrolls
      arcanaBonus: z.number().optional(),
      rollMode: RollModeSchema.optional(),
      manualRoll: z.number().min(1).max(20).optional(),
      manualRolls: z.array(z.number().min(1).max(20)).length(2).optional(),
    
      // Optional spell properties
      isAttackSpell: z.boolean().optional(),
      spellSchool: z.string().optional(),
    });
  • Tool registration entry in the central registry. Converts Zod schema to JSON schema for MCP, wraps the handler with validation and error handling using createTypedHandler pattern.
    use_scroll: {
      name: 'use_scroll',
      description: 'Use a spell scroll in D&D 5e. If spell is on your class list and same/lower level: auto-success. If spell is higher level: Arcana check DC 10 + spell level. On failure: scroll is consumed with no effect. On success: spell is cast from scroll, scroll consumed.',
      inputSchema: toJsonSchema(useScrollSchema),
      handler: async (args) => {
        try {
          const validated = useScrollSchema.parse(args);
          const result = useScroll(validated);
          return success(result);
        } catch (err) {
          if (err instanceof z.ZodError) {
            const messages = err.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ');
            return error(`Validation failed: ${messages}`);
          }
          const message = err instanceof Error ? err.message : String(err);
          return error(message);
        }
      },
    },

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/Mnehmos/ChatRPG'

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