Complete reference for MagVarUpdate (MVU) framework - a Tavern Helper script that enhances message floor variable management.
MVU is a separate Tavern Helper script that provides:
- Set variables in world info entries
- Initialize variables from world info or chat history
- Auto-update variables from AI output
- Visualization support (
display_data,delta_data)
Always wait for MVU before using:
await waitGlobalInitialized('Mvu');
// Now safe to use Mvu functions
const data = Mvu.getMvuData({ type: 'message', message_id: -1 });MVU stores data in message floor variables at stat_data path:
// These are equivalent:
Mvu.getMvuData({ type: 'message', message_id: 5 })
_.get(getVariables({ type: 'message', message_id: 5 }), 'stat_data')Additional fields MVU sets:
stat_data- Actual variable valuesdisplay_data- Formatted for display (with units, colors, etc.)delta_data- Variable changes since last message
Get MVU data from message floor.
function getMvuData(option: {
type: 'message',
message_id: number | 'latest'
}): Record<string, any>Examples:
// Get latest message MVU data
const latest = Mvu.getMvuData({ type: 'message', message_id: 'latest' });
// Get specific message (negative = from end)
const prev = Mvu.getMvuData({ type: 'message', message_id: -2 });
// Access specific variables
console.log(latest.hp); // 100
console.log(latest.score); // 42Write MVU data to message floor.
function replaceMvuData(
data: Record<string, any>,
option: {
type: 'message',
message_id: number | 'latest'
}
): voidExamples:
// Update latest message
Mvu.replaceMvuData({ hp: 80, mp: 50 }, {
type: 'message',
message_id: 'latest'
});
// Update specific message
Mvu.replaceMvuData(newData, {
type: 'message',
message_id: 5
});Parse AI output for MVU commands and return updated data.
Important: Does NOT automatically write back to message floor.
function parseMessage(
oldData: Record<string, any>,
message: string
): Promise<{
stat_data: Record<string, any>;
display_data: Record<string, any>;
delta_data: Record<string, any>;
}>Usage scenario: When generating AI output manually (not through normal chat flow).
Example:
// Get old state
const oldData = Mvu.getMvuData({ type: 'message', message_id: -2 });
// Generate AI response manually
const aiOutput = await generate({
prompt: 'What happens next?'
});
// Parse MVU commands in AI output
const newData = await Mvu.parseMessage(oldData, aiOutput);
// Manually write back
Mvu.replaceMvuData(newData.stat_data, {
type: 'message',
message_id: -1
});
// Also update display and delta if needed
const messageVars = getVariables({ type: 'message', message_id: -1 });
messageVars.display_data = newData.display_data;
messageVars.delta_data = newData.delta_data;
replaceVariables(messageVars, { type: 'message', message_id: -1 });Why not auto-write?: Gives you fine-grained control over when and how data is saved.
MVU provides events for monitoring variable changes.
Enum of available MVU events.
declare const Mvu: {
events: {
BEFORE_UPDATE: string; // Before variables update
AFTER_UPDATE: string; // After variables update
PARSE_ERROR: string; // Parse error occurred
}
}import { eventOn } from '@types/iframe/event';
// After variables updated
eventOn(Mvu.events.AFTER_UPDATE, (data: {
message_id: number;
stat_data: Record<string, any>;
display_data: Record<string, any>;
delta_data: Record<string, any>;
}) => {
console.log('Variables updated for message', data.message_id);
console.log('New values:', data.stat_data);
console.log('Changes:', data.delta_data);
});
// Before update (for validation/modification)
eventOn(Mvu.events.BEFORE_UPDATE, (data) => {
// Can modify data here before it's saved
if (data.stat_data.hp < 0) {
data.stat_data.hp = 0;
}
});
// Parse errors
eventOn(Mvu.events.PARSE_ERROR, (error: {
message_id: number;
error: string;
}) => {
console.error('MVU parse error:', error);
toastr.error(`Variable parse failed: ${error.error}`);
});eventOn(Mvu.events.AFTER_UPDATE, (data) => {
// Show what changed in UI
Object.entries(data.delta_data).forEach(([key, delta]) => {
if (delta > 0) {
toastr.success(`${key} +${delta}`);
} else if (delta < 0) {
toastr.warning(`${key} ${delta}`);
}
});
});import { ref, watch } from 'vue';
import { klona } from 'klona';
const gameState = ref({
hp: 100,
mp: 50,
gold: 0
});
// Load from latest message
$(() => {
const mvuData = Mvu.getMvuData({ type: 'message', message_id: 'latest' });
gameState.value = { ...gameState.value, ...mvuData };
});
// Listen for AI updates
eventOn(Mvu.events.AFTER_UPDATE, (data) => {
// Update Vue state when AI changes variables
gameState.value = { ...gameState.value, ...data.stat_data };
});
// Optional: Write back to MVU when user changes state
watch(gameState, (newState) => {
Mvu.replaceMvuData(klona(newState), {
type: 'message',
message_id: 'latest'
});
}, { deep: true });eventOn(Mvu.events.BEFORE_UPDATE, (data) => {
// Clamp HP between 0-100
if (data.stat_data.hp !== undefined) {
data.stat_data.hp = Math.max(0, Math.min(100, data.stat_data.hp));
}
// Prevent negative gold
if (data.stat_data.gold !== undefined && data.stat_data.gold < 0) {
data.stat_data.gold = 0;
}
// Auto-calculate derived stats
data.stat_data.max_hp = (data.stat_data.level || 1) * 10;
});eventOn(Mvu.events.AFTER_UPDATE, (data) => {
// Update custom UI
const $statusBar = $('#game-status-bar');
// HP bar
const hpPercent = (data.stat_data.hp / data.stat_data.max_hp) * 100;
$statusBar.find('.hp-bar').css('width', `${hpPercent}%`);
// Show delta with animation
Object.entries(data.delta_data).forEach(([key, delta]) => {
const $deltaText = $(`<span class="delta-text">${delta > 0 ? '+' : ''}${delta}</span>`);
$statusBar.find(`.${key}-value`).after($deltaText);
gsap.to($deltaText[0], {
y: -50,
opacity: 0,
duration: 1,
onComplete: () => $deltaText.remove()
});
});
});MVU commands are embedded in AI output. Here are common patterns:
[hp: 80]
[mp: 50]
[gold: +10]
[hp: 80 | ❤️ 80/100]
[exp: +50 | ⭐ 150/200]
[if combat=true | hp: -20]
[if level>=5 | skill_unlocked: true]
Note: Frontend/script developers don't need to parse these - MVU handles it automatically. Just read the resulting stat_data.
Complete example of MVU-powered status display:
import { ref } from 'vue';
import { klona } from 'klona';
// Wait for MVU
await waitGlobalInitialized('Mvu');
// State
const gameState = ref({
hp: 100,
max_hp: 100,
mp: 50,
max_mp: 50,
gold: 0,
level: 1
});
// Load initial state
const initialData = Mvu.getMvuData({ type: 'message', message_id: 'latest' });
Object.assign(gameState.value, initialData);
// Listen for updates
eventOn(Mvu.events.AFTER_UPDATE, (data) => {
// Update state
Object.assign(gameState.value, data.stat_data);
// Show changes
Object.entries(data.delta_data).forEach(([key, delta]) => {
if (Math.abs(delta) > 0) {
toastr.info(`${key}: ${delta > 0 ? '+' : ''}${delta}`);
}
});
});
// Validate on save
eventOn(Mvu.events.BEFORE_UPDATE, (data) => {
// Clamp HP
data.stat_data.hp = Math.max(0, Math.min(data.stat_data.max_hp, data.stat_data.hp));
// Clamp MP
data.stat_data.mp = Math.max(0, Math.min(data.stat_data.max_mp, data.stat_data.mp));
});- Always
await waitGlobalInitialized('Mvu')before using - Use
AFTER_UPDATEevent for UI synchronization - Use
BEFORE_UPDATEfor validation and derived calculations - Handle negative
message_idfor relative indexing - Check if MVU data exists before accessing properties
- Don't assume MVU is always available (user may not have it installed)
- Don't parse MVU commands yourself (let MVU do it)
- Don't forget to call
replaceMvuData()afterparseMessage() - Don't directly modify
display_dataordelta_data(MVU manages these)
Full TypeScript definitions available at:
@types/iframe/exported.mvu.d.ts
api_reference.md- Tavern Helper APIbest_practices.md- General development guidelines