Skip to main content
Glama

edit_level_metadata

Modify VibeTide level metadata like name, description, enemy count, and spawn rates without altering the tile layout for efficient parameter adjustments.

Instructions

Edit only the metadata of a VibeTide level without changing the tile layout.

This is much more efficient than edit_entire_level when you only want to change game parameters like enemy count, spawn rates, name, or description. Args: encoded_level: The encoded level string to modify new_name: New name for the level (optional) new_description: New description for the level (optional) max_enemies: Maximum enemies parameter (0-10, optional) enemy_spawn_chance: Enemy spawn chance percentage (0-100, optional) coin_spawn_chance: Coin spawn chance percentage (0-100, optional) Returns: The modified level data with new encoded string

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
encoded_levelYes
new_nameNo
new_descriptionNo
max_enemiesNo
enemy_spawn_chanceNo
coin_spawn_chanceNo

Implementation Reference

  • The main handler function for the 'edit_level_metadata' tool. Decorated with @mcp.tool() for automatic registration. Decodes the level, updates specified metadata fields, re-encodes, and returns the updated level data.
    @mcp.tool() async def edit_level_metadata( encoded_level: str, new_name: Optional[str] = None, new_description: Optional[str] = None, max_enemies: Optional[int] = None, enemy_spawn_chance: Optional[float] = None, coin_spawn_chance: Optional[float] = None, ) -> Dict[str, Any]: """Edit only the metadata of a VibeTide level without changing the tile layout. This is much more efficient than edit_entire_level when you only want to change game parameters like enemy count, spawn rates, name, or description. Args: encoded_level: The encoded level string to modify new_name: New name for the level (optional) new_description: New description for the level (optional) max_enemies: Maximum enemies parameter (0-10, optional) enemy_spawn_chance: Enemy spawn chance percentage (0-100, optional) coin_spawn_chance: Coin spawn chance percentage (0-100, optional) Returns: The modified level data with new encoded string """ try: # Decode the existing level to get current data level_data = level_encoder.decode(encoded_level) # Update only the specified parameters (keep existing if not specified) if new_name is not None: level_data["name"] = new_name if new_description is not None: level_data["description"] = new_description if max_enemies is not None: level_data["maxEnemies"] = max_enemies if enemy_spawn_chance is not None: level_data["enemySpawnChance"] = enemy_spawn_chance if coin_spawn_chance is not None: level_data["coinSpawnChance"] = coin_spawn_chance # Re-encode with the updated metadata new_encoded_level = level_encoder.encode(level_data) level_url = f"{VIBE_TIDE_CONFIG['web_player_url']}?level={new_encoded_level}" level_data["level_url"] = level_url return { "success": True, "level_data": level_data, "encoded_level": new_encoded_level, "play_url": level_url, "change_summary": "Updated metadata only - tiles unchanged", "message": f"Successfully updated level metadata", } except Exception as e: logger.error(f"Failed to edit level metadata: {e}") return {"success": False, "error": f"Failed to edit level metadata: {str(e)}"}
  • Input schema and output description defined in the function docstring, used by FastMCP to generate JSON schema for the tool.
    Args: encoded_level: The encoded level string to modify new_name: New name for the level (optional) new_description: New description for the level (optional) max_enemies: Maximum enemies parameter (0-10, optional) enemy_spawn_chance: Enemy spawn chance percentage (0-100, optional) coin_spawn_chance: Coin spawn chance percentage (0-100, optional) Returns: The modified level data with new encoded string
  • LevelEncoder class and global instance used by edit_level_metadata for decoding and encoding level data.
    class LevelEncoder: """Fast level encoding/decoding without agent tools""" def __init__(self): # Character mapping for tiles (URL-safe) self.tile_chars = { 0: ".", # Empty - most common 1: "G", # Grass 2: "R", # Rock 3: "Y", # Yellow 4: "I", # Ice 5: "F", # Fire/Red 6: "S", # Spikes 7: "W", # Water } self.char_tiles = {char: tile for tile, char in self.tile_chars.items()} def encode(self, level_data: Dict[str, Any]) -> str: """Encode level data to URL-safe string""" try: tiles = level_data.get("tiles", []) if not tiles: raise ValueError("No tiles found") height = len(tiles) width = len(tiles[0]) if tiles else 0 # Convert 2D array to character string tile_string = "" for row in tiles: for tile in row: tile_string += self.tile_chars.get(tile, ".") # Apply run-length encoding for empty tiles tile_string = self._run_length_encode(tile_string) # Create base format encoded = f"{width}x{height}:{tile_string}" # Add game parameters if provided params = {} for param in ["maxEnemies", "enemySpawnChance", "coinSpawnChance"]: if param in level_data and level_data[param] is not None: params[param] = level_data[param] if params: params_json = json.dumps(params) encoded += f"|{self._base64url_encode(params_json)}" return self._base64url_encode(encoded) except Exception as e: logger.error(f"Failed to encode level: {e}") raise ValueError(f"Level encoding failed: {str(e)}") def decode(self, encoded_string: str) -> Dict[str, Any]: """Decode URL-safe string back to level data""" try: encoded_string = encoded_string.strip() if not encoded_string: raise ValueError("Empty encoded string") # Decode from base64url decoded = self._base64url_decode(encoded_string) # Parse parameters if present main_data = decoded game_params = {} if "|" in decoded: parts = decoded.split("|", 1) if len(parts) == 2: main_data, params_data = parts try: params_json = self._base64url_decode(params_data) game_params = json.loads(params_json) except Exception as e: logger.warning(f"Failed to parse parameters: {e}") # Parse format: widthxheight:encoded_data if ":" not in main_data: raise ValueError("Invalid format: missing colon") dimensions, tile_data = main_data.split(":", 1) if "x" not in dimensions: raise ValueError("Invalid format: missing dimensions") width, height = map(int, dimensions.split("x")) if width < 1 or height < 1: raise ValueError(f"Invalid dimensions: {width}x{height}") # Decode run-length encoding tile_string = self._run_length_decode(tile_data) # Convert back to 2D array tiles = [] index = 0 for y in range(height): tiles.append([]) for x in range(width): char = tile_string[index] if index < len(tile_string) else "." tiles[y].append(self.char_tiles.get(char, 0)) index += 1 result = { "tiles": tiles, "width": width, "height": height, "name": self._generate_level_name(tiles), } # Add game parameters for param in ["maxEnemies", "enemySpawnChance", "coinSpawnChance"]: if param in game_params: result[param] = game_params[param] return result except Exception as e: logger.error(f"Failed to decode level: {e}") raise ValueError(f"Level decoding failed: {str(e)}") def _run_length_encode(self, s: str) -> str: """Run-length encoding for repeated empty tiles""" if not s: return s result = "" count = 1 current = s[0] for i in range(1, len(s)): if s[i] == current and current == ".": count += 1 else: if current == "." and count > 2: result += f".{count}" else: result += current * count current = s[i] count = 1 # Handle final sequence if current == "." and count > 2: result += f".{count}" else: result += current * count return result def _run_length_decode(self, s: str) -> str: """Decode run-length encoding""" result = "" i = 0 while i < len(s): char = s[i] if char == "." and i + 1 < len(s) and s[i + 1].isdigit(): # Extract number num_str = "" j = i + 1 while j < len(s) and s[j].isdigit(): num_str += s[j] j += 1 count = int(num_str) result += "." * count i = j else: result += char i += 1 return result def _base64url_encode(self, s: str) -> str: """URL-safe base64 encoding""" encoded = base64.b64encode(s.encode("utf-8")).decode("ascii") return encoded.replace("+", "-").replace("/", "_").rstrip("=") def _base64url_decode(self, s: str) -> str: """URL-safe base64 decoding""" try: # Add padding if necessary padding = (4 - len(s) % 4) % 4 s += "=" * padding s = s.replace("-", "+").replace("_", "/") decoded_bytes = base64.b64decode(s) return decoded_bytes.decode("utf-8") except Exception as e: logger.error(f"Base64 decode error: {e}") raise ValueError(f"Invalid base64 encoding: {str(e)}") def _generate_level_name(self, tiles: List[List[int]]) -> str: """Generate a name based on level content""" stats = self._analyze_level_content(tiles) name = "" if stats["water"] > 0.3: name += "Aquatic " if stats["spikes"] > 0.1: name += "Dangerous " if stats["ice"] > 0.2: name += "Icy " if stats["platforms"] < 0.1: name += "Minimal " if stats["platforms"] > 0.4: name += "Dense " name += "Adventure" return name.strip() def _analyze_level_content(self, tiles: List[List[int]]) -> Dict[str, float]: """Analyze level content for statistics""" if not tiles: return { "empty": 1.0, "platforms": 0.0, "ice": 0.0, "dangerous": 0.0, "spikes": 0.0, "water": 0.0, } total = len(tiles) * len(tiles[0]) counts = {i: 0 for i in range(8)} for row in tiles: for tile in row: counts[tile] = counts.get(tile, 0) + 1 return { "empty": counts[0] / total, "platforms": (counts[1] + counts[2] + counts[3]) / total, "ice": counts[4] / total, "dangerous": counts[5] / total, "spikes": counts[6] / total, "water": counts[7] / total, }

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/banjtheman/vibe_tide_mcp'

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