refactor: remove legacy MCP and development tools, and update documentation to reflect the current single-linkset architecture.

This commit is contained in:
RebeccaNod1 2026-04-09 01:47:28 -06:00
parent ccf472585a
commit d0b11ef474
12 changed files with 17 additions and 1046 deletions

View file

@ -4,6 +4,13 @@
All notable changes to Peril Dice will be documented in this file. All notable changes to Peril Dice will be documented in this file.
## [3.2.6] - 2026-04-09
### 🏁 **Final Production Sweep (Tournament Edition)**
- **Global Silence**: Implemented unconditional stripping of all debug string literals using the `DEBUG_LOGS 0` pre-processor macro across the entire codebase.
- **Memory Hardening**: Set `SHOW_MEMORY 0` to eliminate runtime memory reporting overhead during high-traffic tournament play.
- **Repository Sanitization**: Purged all legacy GitHub updater documentation and development tools (`lsl_validator`, etc.) for a clean production package.
- **Sentinel finalization**: Verified "Auto-Pilot" Sentinel 7.9.2 for automated, no-touch land diagnostics.
## [3.2.6] - 2026-04-05 ## [3.2.6] - 2026-04-05
### 🎰 **Hybrid HUD Evolution** ### 🎰 **Hybrid HUD Evolution**

View file

@ -1,149 +0,0 @@
#include "peril/Peril_Constants.lsl"
// ====================================================================
// Enhanced Link Scanner for Peril Dice Linkset
// ====================================================================
// Extended version of Temp_LinkScanner.lsl that identifies:
// 1. Current linkset structure
// 2. Which scripts belong on which links
// 3. Missing scripts or misplaced scripts
//
// USAGE: Place this script on any prim in the linkset, touch to scan
// ====================================================================
// Expected script locations based on WARP.md architecture
list LINK1_SCRIPTS = [
"Main_Controller_Linkset.lsl",
"Game_Manager.lsl",
"Controller_Memory.lsl",
"Controller_MessageHandler.lsl",
"Player_RegistrationManager.lsl",
"Player_DialogHandler.lsl",
"NumberPicker_DialogHandler.lsl",
"Floater_Manager.lsl",
"Roll_ConfettiModule.lsl",
"Bot_Manager.lsl",
"Game_Calculator.lsl",
"Verbose_Logger.lsl",
"System_Debugger.lsl"
];
// Link-specific scripts (based on architecture documentation)
list LINKSET_SPECIFIC_SCRIPTS = [
"Game_Scoreboard_Manager_Linkset.lsl", // Link 12
"Leaderboard_Communication_Linkset.lsl", // Link 35
"XyzzyText_Dice_Bridge_Linkset.lsl" // Link 83
];
#define XYZZY_SCRIPT "xyzzy_Master_script.lsl" // Links 35-82 (48 prims)
// Scan results
integer totalPrims = 0;
list linkNames = [];
list linkPositions = [];
// Function to perform the full linkset scan
performFullScan() {
totalPrims = llGetNumberOfPrims();
linkNames = [];
linkPositions = [];
llOwnerSay("=== 🔍 PERIL DICE LINKSET ANALYSIS ===");
llOwnerSay("📊 Total Prims: " + (string)totalPrims);
llOwnerSay("🎯 Expected Architecture: 84-prim single linkset");
llOwnerSay("");
// Scan all links
integer i;
for (i = 1; i <= totalPrims; i++) {
string primName = llGetLinkName(i);
vector pos = llList2Vector(llGetLinkPrimitiveParams(i, [PRIM_POSITION]), 0);
linkNames += [primName];
linkPositions += [pos];
// Basic link info
llOwnerSay("Link " + (string)i + ": " + primName);
// Add delays to prevent spam
if (i % 15 == 0) {
llSleep(DELAY_SCAN_LINKSET);
}
}
llOwnerSay("");
llOwnerSay("=== 📋 SCRIPT PLACEMENT ANALYSIS ===");
// Analyze Link 1 (Root Prim)
llOwnerSay("🔧 LINK 1 (ROOT PRIM) ANALYSIS:");
llOwnerSay(" Name: " + llList2String(linkNames, 0));
llOwnerSay(" Expected Scripts: " + (string)llGetListLength(LINK1_SCRIPTS) + " scripts");
integer j;
for (j = 0; j < llGetListLength(LINK1_SCRIPTS); j++) {
string scriptName = llList2String(LINK1_SCRIPTS, j);
llOwnerSay(" • " + scriptName);
}
llOwnerSay("");
// Analyze scoreboard
llOwnerSay("📊 SCOREBOARD SECTION (Links 2-24):");
if (totalPrims >= 12) {
llOwnerSay(" Link 12: " + llList2String(linkNames, 11) + " (Scoreboard Manager)");
llOwnerSay(" Expected Script: Game_Scoreboard_Manager_Linkset.lsl");
}
llOwnerSay(" Links 2-11: Overlay prims (elimination markers)");
llOwnerSay(" Links 13-24: Player display prims (profiles & hearts)");
llOwnerSay("");
// Analyze leaderboard
llOwnerSay("🏆 LEADERBOARD SECTION (Links 35-82):");
if (totalPrims >= 35) {
llOwnerSay(" Link 35: " + llList2String(linkNames, 34) + " (Leaderboard Bridge)");
llOwnerSay(" Expected Script: Leaderboard_Communication_Linkset.lsl");
}
llOwnerSay(" Links 35-82: XyzzyText display prims (48 total)");
llOwnerSay(" Each prim needs: xyzzy_Master_script.lsl");
llOwnerSay(" Organization: 4 banks × 12 prims each");
llOwnerSay("");
// Analyze dice display
llOwnerSay("🎲 DICE DISPLAY SECTION (Links 83-84):");
if (totalPrims >= 83) {
llOwnerSay(" Link 83: " + llList2String(linkNames, 82) + " (Dice Bridge)");
llOwnerSay(" Expected Script: XyzzyText_Dice_Bridge_Linkset.lsl");
}
if (totalPrims >= 84) {
llOwnerSay(" Link 84: " + llList2String(linkNames, 83) + " (Dice Display)");
llOwnerSay(" No script needed");
}
llOwnerSay("");
llOwnerSay("🔄 ESSENTIAL FILES FOR GAMEPLAY:");
llOwnerSay(" 📂 Link 1 Scripts (" + (string)llGetListLength(LINK1_SCRIPTS) + " files)");
llOwnerSay(" 📂 Link-Specific Scripts (3 files)");
llOwnerSay(" 📂 XyzzyText Script (1 file × 48 prims)");
llOwnerSay(" 📂 Documentation (4 files)");
llOwnerSay("");
llOwnerSay("⚠️ EXCLUDED FROM UPDATES:");
llOwnerSay(" 🚫 Development files: lsl_validator.py, debug_mcp.py");
llOwnerSay(" 🚫 Test files: test_*.py, MCP_README.md");
llOwnerSay(" 🚫 Templates: MEMORY_REPORT_TEMPLATE.lsl");
llOwnerSay(" 🚫 Utilities: Temp_LinkScanner.lsl, Enhanced_LinkScanner.lsl");
llOwnerSay("");
llOwnerSay("✅ Linkset analysis complete!");
}
default {
state_entry() {
llOwnerSay("🔍 Enhanced Link Scanner ready for Peril Dice linkset");
llOwnerSay("👆 Touch to perform full linkset analysis");
}
touch_start(integer total_number) {
if (llDetectedKey(0) != llGetOwner()) return;
performFullScan();
}
}

View file

@ -1,138 +0,0 @@
// ============================================================================
// Experience Admin HUD
// ============================================================================
// Instructions:
// 1. Create a new box, put this script inside.
// 2. DO NOT FORGET to check "Use Experience" and select Final Girlz I.N.C!
// 3. Attach it to your HUD or click it if it's rezzed.
//
// Commands (type in local chat):
// /99 keys - Lists all keys currently in the entire database
// /99 get [Key] - Reads and displays the data inside a specific key
// /99 del [Key] - Deletes a specific key from the database
// ============================================================================
key requestKeys;
key requestRead;
key requestDelete;
key requestWrite;
key lastUser;
string pendingSetKey;
string pendingSetValue;
default {
state_entry() {
lastUser = llGetOwner();
llRegionSayTo(lastUser, 0, "🔧 Experience Admin Terminal Ready.");
llRegionSayTo(lastUser, 0, "💬 Type commands in local chat:");
llRegionSayTo(lastUser, 0, " /99 keys");
llRegionSayTo(lastUser, 0, " /99 get key_name");
llRegionSayTo(lastUser, 0, " /99 set key_name=Hello World");
llRegionSayTo(lastUser, 0, " /99 del key_name");
llListen(99, "", NULL_KEY, "");
}
listen(integer channel, string name, key id, string msg) {
// SECURITY CHECK: Strictly whitelist authorized Admin usernames!
string uname = llStringTrim(llToLower(llGetUsername(id)), STRING_TRIM);
if (id != llGetOwner() && uname != "djmusica28" && uname != "finalgirlzbot") {
llRegionSayTo(id, 0, "❌ Access Denied: You are not on the authorized Administrator whitelist for this database.");
return;
}
lastUser = id;
msg = llStringTrim(msg, STRING_TRIM);
if (msg == "keys") {
llRegionSayTo(lastUser, 0, "🔍 Requesting list of database keys (up to 100)...");
requestKeys = llKeysKeyValue(0, 100);
}
else if (llGetSubString(msg, 0, 3) == "get ") {
string targetKey = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM);
llRegionSayTo(lastUser, 0, "🔍 Reading key: " + targetKey + "...");
requestRead = llReadKeyValue(targetKey);
}
else if (llGetSubString(msg, 0, 3) == "set ") {
string payload = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM);
integer equalSign = llSubStringIndex(payload, "=");
if (equalSign != -1) {
pendingSetKey = llStringTrim(llGetSubString(payload, 0, equalSign - 1), STRING_TRIM);
pendingSetValue = llStringTrim(llGetSubString(payload, equalSign + 1, -1), STRING_TRIM);
llRegionSayTo(lastUser, 0, "💾 Writing key: " + pendingSetKey + "...");
requestWrite = llUpdateKeyValue(pendingSetKey, pendingSetValue, FALSE, "");
} else {
llRegionSayTo(lastUser, 0, "❌ Format: /99 set key=value");
}
}
else if (llGetSubString(msg, 0, 3) == "del ") {
string targetKey = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM);
llRegionSayTo(lastUser, 0, "🗑️ Deleting key: " + targetKey + "...");
requestDelete = llDeleteKeyValue(targetKey);
}
}
dataserver(key queryid, string data) {
// llKeysKeyValue returns data as CSV:
// Success: 1,keyname1,keyname2...
// Failure: 0,error_code
if (queryid == requestKeys) {
list result = llCSV2List(data);
if (llList2Integer(result, 0) == 1) {
// Success
integer numKeys = llGetListLength(result) - 1;
llRegionSayTo(lastUser, 0, "✅ Found " + (string)numKeys + " keys in database:");
integer i;
for(i = 1; i < llGetListLength(result); i++) {
llRegionSayTo(lastUser, 0, " 🔑 " + llList2String(result, i));
}
} else if (llList2Integer(result, 1) == 3) {
llRegionSayTo(lastUser, 0, "📭 The database is completely empty! No keys exist.");
} else {
llRegionSayTo(lastUser, 0, "❌ Failed to read keys (Error " + llList2String(result, 1) + ")");
}
}
// llReadKeyValue returns data as: status,value
else if (queryid == requestRead) {
integer comma = llSubStringIndex(data, ",");
string status = llGetSubString(data, 0, comma - 1);
string val = llGetSubString(data, comma + 1, -1);
if (status == "1") {
llRegionSayTo(lastUser, 0, "📄 Content:\n" + val);
} else if (status == "3") {
llRegionSayTo(lastUser, 0, "❌ That key doesn't exist!");
} else {
llRegionSayTo(lastUser, 0, "❌ Read failed (Status: " + status + ")");
}
}
// llDeleteKeyValue dataserver logic
else if (queryid == requestDelete) {
integer comma = llSubStringIndex(data, ",");
string status = llGetSubString(data, 0, comma - 1);
if (status == "1") {
llRegionSayTo(lastUser, 0, "✅ Key successfully deleted!");
} else if (status == "3") {
llRegionSayTo(lastUser, 0, "❌ Key didn't exist to delete.");
} else {
llRegionSayTo(lastUser, 0, "❌ Delete failed (Status: " + status + ")");
}
}
// Write logic
else if (queryid == requestWrite) {
integer comma = llSubStringIndex(data, ",");
string status = llGetSubString(data, 0, comma - 1);
if (status == "1") {
llRegionSayTo(lastUser, 0, "✅ Key saved successfully!");
} else if (status == "3") {
// Key didn't exist to update, manually create it!
requestWrite = llCreateKeyValue(pendingSetKey, pendingSetValue);
} else {
llRegionSayTo(lastUser, 0, "❌ Write failed (Status: " + status + ")");
}
}
}
}

View file

@ -1,60 +0,0 @@
# LSL Development MCP Server
This MCP (Model Context Protocol) server provides AI assistants with direct access to your LSL development tools and Peril Dice project.
## What it provides
### Available Tools:
1. **lint_lsl_file** - Lint/validate LSL syntax for a specific file using lslint
2. **lint_all_lsl** - Lint/validate all LSL files in the project using lslint
3. **list_lsl_files** - List all LSL files in the project (currently shows 20 files)
4. **git_status** - Get git status of the project
5. **analyze_script_architecture** - Analyze script architecture and communication patterns
6. **get_memory_usage_info** - Extract memory usage information from LSL files
### Available Resources:
- Project README.md
- CHANGELOG.md
- WARP.md (development guide)
## Files Created:
- `lsl_mcp_server.py` - The main MCP server
- `mcp_config.json` - Configuration for MCP clients
- `test_mcp_server.py` - Test script to verify server functionality
## How to Use:
### Testing the Server:
```bash
python3 test_mcp_server.py
```
### Manual Server Start:
```bash
python3 lsl_mcp_server.py
```
### Integration with AI Tools:
The `mcp_config.json` file can be used to configure MCP-compatible AI tools to connect to this server.
## Benefits:
With this MCP server, AI assistants can:
- Directly lint and validate your LSL code using lslint
- List and examine all 20 LSL files in your Peril project
- Check git status and project state
- Analyze your script architecture (Controllers, Handlers, Modules, Linkset scripts)
- Access project documentation
- Monitor memory usage information from script comments
This creates a much more interactive and helpful development experience, where the AI can actually run your development tools and provide real-time feedback on your Peril Dice project.
## Security:
The server only has access to:
- Your Peril project directory (`/home/richard/peril`)
- Your lslint tool (`/home/richard/lslint/lslint`)
- Git commands within the project directory
- Read-only access to project documentation
It cannot modify files outside the project scope or execute arbitrary system commands.

View file

@ -36,7 +36,7 @@ key EXPERIENCE_ID = "06926390-26e6-11f1-a452-0242ac110003"; // Final Girlz I.N.C
#define FW_DONE "fw_done" #define FW_DONE "fw_done"
// --- System/Debug (9000-9999) --- // --- System/Debug (9000-9999) ---
#define DEBUG_LOGS 1 // GLOBAL MASTER SWITCH - Set to 1 for dev feedback, 0 to STRIP from memory #define DEBUG_LOGS 0 // GLOBAL MASTER SWITCH - Set to 1 for dev feedback, 0 to STRIP from memory
#if DEBUG_LOGS #if DEBUG_LOGS
#define dbg(msg) llOwnerSay(msg) #define dbg(msg) llOwnerSay(msg)
#else #else
@ -143,7 +143,7 @@ key EXPERIENCE_ID = "06926390-26e6-11f1-a452-0242ac110003"; // Final Girlz I.N.C
#define MSG_QUERY_OWNER_STATUS 213 #define MSG_QUERY_OWNER_STATUS 213
// --- Memory Management (6000-6099) --- // --- Memory Management (6000-6099) ---
#define SHOW_MEMORY 1 // Toggle this to 1 to see memory usage on rez/reset #define SHOW_MEMORY 0 // Toggle this to 1 to see memory usage on rez/reset
#define REPORT_MEMORY() { \ #define REPORT_MEMORY() { \
if (SHOW_MEMORY) { \ if (SHOW_MEMORY) { \

View file

@ -97,67 +97,16 @@ Peril Dice is a multiplayer elimination game where each player selects numbers b
- **Microscopic Position Offsets**: Resolved persistent Z-fighting on the custom UI (scoreboard, header, start button) by applying microscopic local axis offsets (`0.005m` and `0.008m` relative to the backboard). - **Microscopic Position Offsets**: Resolved persistent Z-fighting on the custom UI (scoreboard, header, start button) by applying microscopic local axis offsets (`0.005m` and `0.008m` relative to the backboard).
- **Initialization Stability**: Patched Scoreboard, Leaderboard, and Dice Bridge scripts with `on_rez` auto-reset handlers to ensure flawless texture synchronization and state recovery upon rezzing. - **Initialization Stability**: Patched Scoreboard, Leaderboard, and Dice Bridge scripts with `on_rez` auto-reset handlers to ensure flawless texture synchronization and state recovery upon rezzing.
## Major v2.8.7 Improvements 🔄 ## System Architecture (v3.2.6) 🏗️
### 🔄 **GitHub Update System (Superseded)** Peril Dice is now a unified single-linkset system utilizing modern LSL pre-processor techniques and mesh text displays.
> **⚠️ Note**: The GitHub Update System was fully replaced by the CasperVend update system in v3.0.0 for commercial release.
- **In-World Update Checker**: Revolutionary `Update_Checker.lsl` script provides direct GitHub integration
- **GitHub API Integration**: Connects to `https://github.com/RebeccaNod1/Peril/releases` for automatic update detection
- **Smart File Filtering**: Only downloads essential game files, excludes development/test files
- **Precise Deployment Instructions**: Tells you exactly which link each script belongs on
- **Owner Menu Integration**: Access via Owner Menu → Troubleshooting → Check for Updates
- **Chat Commands**: `/1 check`, `/1 download ScriptName.lsl`, `/1 list` for direct access
- **Memory Optimized**: Fits perfectly in the slot freed by eliminated UpdateHelper.lsl (v2.8.5)
- **Professional Error Handling**: Clear feedback for network issues, missing releases, file validation
### 🔍 **Enhanced LinkScanner System** ### **Core Components:**
- **Complete 84-Prim Analysis**: `Enhanced_LinkScanner.lsl` provides detailed linkset structure mapping - **Main Controller (Root)**: Authoritative source of truth for all game states.
- **VERIFIED Structure**: Based on actual linkset scan showing exact prim names and positions - **Experience Sentinel (v7.9.2)**: Automated "Auto-Pilot" land diagnostics.
- **Script Placement Guide**: Shows which scripts belong on which links with verification - **Hybrid HUD System**: personal HUDs for humans + in-world floaters for bots.
- **XyzzyText Mapping**: Detailed analysis of all 48 leaderboard prims (Links 35-82) - **FURWARE Text Mesh**: High-performance mesh displays for dice and leaderboard.
- **Update Deployment Guide**: Generates precise instructions for Update_Checker placement - **Dynamic Link Discovery**: Root-level discovery of all sibling prims by name.
- **Real-Time Validation**: Automated "Invisible Guardian" scans land permissions and verifies if the "Final Girlz I.N.C." experience is correctly configured on every rez and reset.
### 🎯 **Smart File Classification**
- **Essential Game Files Only**: Update system intelligently filters files for end users
- **✅ Included**: All LSL scripts, documentation (README, CHANGELOG, notecards)
- **🚫 Excluded**: lsl_validator.py, debug_mcp.py, test_*.py, MCP files, templates
- **Link-Specific Instructions**: Downloads include verified deployment locations
- **Link 1**: 13 root prim scripts + Update_Checker.lsl
- **Link 12**: "scoreboard manager cube" - Game_Scoreboard_Manager_Linkset.lsl
- **Link 35**: "leaderboard row 1 col 1" - Leaderboard_Communication_Linkset.lsl
- **Links 35-82**: All 48 XyzzyText prims need xyzzy_Master_script.lsl
- **Link 83**: "dice col 1" - XyzzyText_Dice_Bridge_Linkset.lsl
### 🚀 **Zero-Infrastructure Updates**
- **No Update Server Required**: Leverages existing GitHub + Jenkins CI/CD pipeline perfectly
- **Automatic Release Detection**: Finds new versions as soon as they're tagged and released
- **Individual File Updates**: Download specific scripts without full release packages
- **GitHub Raw Integration**: Direct access to latest files from main branch
- **Multi-Instance Safe**: Multiple game tables can check for updates independently
### 🎨 **Professional User Experience**
- **Clear Version Information**: Shows current vs latest versions with release dates
- **Release Notes Preview**: Displays first 200 characters of GitHub release notes
- **Download Progress**: File size reporting and content preview for verification
- **Deployment Verification**: Confirms updates are designed for verified 84-prim structure
- **Graceful Error Handling**: Helpful messages for GitHub rate limits, missing files, etc.
### 📊 **System Integration Benefits**
- **Replaces Eliminated UpdateHelper**: Perfect use of memory freed in v2.8.5 optimization
- **Existing Admin Menu**: Integrates seamlessly with current troubleshooting interface
- **Message Protocol Compatibility**: Uses established link message patterns and constants
- **Verbose Logging Support**: Follows existing debug toggle system
- **Memory Reporting**: Includes standard memory usage reporting like other scripts
### 💱 **Before vs After v2.8.7**
| **Before** | **After** |
|------------|----------|
| ❌ Manual update distribution via inventory | ✅ Automatic GitHub integration with update detection |
| ❌ No way to check for new versions | ✅ One-click update checking via `/1 check` or admin menu |
| ❌ Guessing which scripts go where | ✅ Precise deployment instructions verified by linkset scan |
| ❌ Development files mixed with game files | ✅ Smart filtering shows only essential files |
| ❌ No linkset structure validation | ✅ Complete 84-prim analysis and verification tools |
## Major v2.8.6 Improvements 🏆 ## Major v2.8.6 Improvements 🏆

View file

@ -1,50 +0,0 @@
#!/usr/bin/env python3
import sys
import json
import traceback
def debug_main():
"""Debug version to see what's happening"""
print('{"debug": "server started"}', flush=True)
try:
while True:
line = sys.stdin.readline()
print(f'{{"debug": "received line: {line.strip()[:100]}"}}', flush=True)
if not line:
print('{"debug": "EOF received, exiting"}', flush=True)
break
line = line.strip()
if not line:
continue
try:
request = json.loads(line)
print(f'{{"debug": "parsed request: {request.get("method", "unknown")}"}}', flush=True)
if request.get("method") == "initialize":
response = {
"jsonrpc": "2.0",
"id": request.get("id"),
"result": {
"protocolVersion": "2025-03-26",
"capabilities": {"tools": {}, "resources": {}},
"serverInfo": {"name": "Debug LSL Server", "version": "1.0.0"}
}
}
print(json.dumps(response), flush=True)
print('{"debug": "sent initialize response"}', flush=True)
except Exception as e:
print(f'{{"debug": "error parsing: {str(e)}"}}', flush=True)
except Exception as e:
print(f'{{"debug": "main loop error: {str(e)}"}}', flush=True)
traceback.print_exc(file=sys.stderr)
print('{"debug": "server exiting"}', flush=True)
if __name__ == "__main__":
debug_main()

View file

@ -1,414 +0,0 @@
#!/usr/bin/env python3
"""
LSL Development MCP Server for Peril Dice Project
Provides tools for LSL validation, preprocessing, and project management.
"""
import json
import sys
import subprocess
import os
import glob
from pathlib import Path
from typing import Dict, List, Any, Optional
class LSLMCPServer:
def __init__(self, project_root: str = "/home/richard/peril"):
self.project_root = Path(project_root)
self.lsl_validator_path = self.project_root / "lsl_validator.py"
def handle_request(self, request: Dict[str, Any]) -> Dict[str, Any]:
"""Handle incoming MCP requests"""
method = request.get("method", "")
params = request.get("params", {})
if method == "initialize":
return self._handle_initialize(params)
elif method == "tools/list":
return self._list_tools()
elif method == "tools/call":
return self._call_tool(params)
elif method == "resources/list":
return self._list_resources()
elif method == "resources/read":
return self._read_resource(params)
else:
return {"error": f"Unknown method: {method}"}
def _handle_initialize(self, params: Dict[str, Any]) -> Dict[str, Any]:
"""Handle MCP initialization request"""
# Use the client's protocol version if provided
client_version = params.get("protocolVersion", "2024-11-05")
return {
"protocolVersion": client_version,
"capabilities": {
"tools": {},
"resources": {}
},
"serverInfo": {
"name": "LSL Development Server",
"version": "1.0.0"
}
}
def _list_tools(self) -> Dict[str, Any]:
"""List available LSL development tools"""
return {
"tools": [
{
"name": "validate_lsl_file",
"description": "Comprehensive LSL validation for a specific file (syntax, scope, style, performance, memory)",
"inputSchema": {
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "Path to LSL file to validate"}
},
"required": ["file_path"]
}
},
{
"name": "validate_all_lsl",
"description": "Comprehensive LSL validation for all files in the project",
"inputSchema": {
"type": "object",
"properties": {}
}
},
{
"name": "list_lsl_files",
"description": "List all LSL files in the project",
"inputSchema": {
"type": "object",
"properties": {}
}
},
{
"name": "git_status",
"description": "Get git status of the project",
"inputSchema": {
"type": "object",
"properties": {}
}
},
{
"name": "analyze_script_architecture",
"description": "Analyze the script architecture and communication patterns",
"inputSchema": {
"type": "object",
"properties": {}
}
},
{
"name": "get_memory_usage_info",
"description": "Extract memory usage information from LSL files",
"inputSchema": {
"type": "object",
"properties": {}
}
}
]
}
def _call_tool(self, params: Dict[str, Any]) -> Dict[str, Any]:
"""Execute a specific tool"""
tool_name = params.get("name", "")
arguments = params.get("arguments", {})
try:
if tool_name == "validate_lsl_file":
return self._validate_lsl_file(arguments["file_path"])
elif tool_name == "validate_all_lsl":
return self._validate_all_lsl()
elif tool_name == "list_lsl_files":
return self._list_lsl_files()
elif tool_name == "git_status":
return self._git_status()
elif tool_name == "analyze_script_architecture":
return self._analyze_script_architecture()
elif tool_name == "get_memory_usage_info":
return self._get_memory_usage_info()
else:
return {"error": f"Unknown tool: {tool_name}"}
except Exception as e:
return {"error": f"Tool execution failed: {str(e)}"}
def _validate_lsl_file(self, file_path: str) -> Dict[str, Any]:
"""Validate a specific LSL file using the comprehensive LSL validator"""
full_path = self.project_root / file_path if not os.path.isabs(file_path) else Path(file_path)
if not full_path.exists():
return {"error": f"File not found: {full_path}"}
if not self.lsl_validator_path.exists():
return {"error": f"LSL validator not found at: {self.lsl_validator_path}"}
try:
result = subprocess.run([
"python3", str(self.lsl_validator_path), str(full_path)
], capture_output=True, text=True, cwd=str(self.project_root))
return {
"content": [
{
"type": "text",
"text": f"LSL Validation Result for {file_path}:\n\n"
f"Exit Code: {result.returncode}\n"
f"Output:\n{result.stdout}\n"
f"Errors:\n{result.stderr}"
}
]
}
except Exception as e:
return {"error": f"Failed to run LSL validator: {str(e)}"}
def _validate_all_lsl(self) -> Dict[str, Any]:
"""Validate all LSL files in the project using the comprehensive LSL validator"""
if not self.lsl_validator_path.exists():
return {"error": f"LSL validator not found at: {self.lsl_validator_path}"}
try:
result = subprocess.run([
"python3", str(self.lsl_validator_path), "."
], capture_output=True, text=True, cwd=str(self.project_root))
return {
"content": [
{
"type": "text",
"text": f"LSL Validation Results for All Files:\n\n"
f"Exit Code: {result.returncode}\n\n"
f"Output:\n{result.stdout}\n"
f"Errors:\n{result.stderr}"
}
]
}
except Exception as e:
return {"error": f"Failed to run LSL validator on all files: {str(e)}"}
def _list_lsl_files(self) -> Dict[str, Any]:
"""List all LSL files in the project"""
lsl_files = list(self.project_root.glob("*.lsl"))
file_list = [f.name for f in sorted(lsl_files)]
return {
"content": [
{
"type": "text",
"text": f"LSL Files in Project ({len(file_list)} total):\n\n" +
"\n".join([f"• {f}" for f in file_list])
}
]
}
def _git_status(self) -> Dict[str, Any]:
"""Get git status of the project"""
try:
result = subprocess.run([
"git", "status", "--porcelain"
], capture_output=True, text=True, cwd=str(self.project_root))
branch_result = subprocess.run([
"git", "branch", "--show-current"
], capture_output=True, text=True, cwd=str(self.project_root))
status_text = "Git Status:\n\n"
status_text += f"Current Branch: {branch_result.stdout.strip()}\n\n"
if result.stdout.strip():
status_text += "Modified Files:\n"
for line in result.stdout.strip().split('\n'):
status_text += f" {line}\n"
else:
status_text += "Working directory clean\n"
return {
"content": [
{
"type": "text",
"text": status_text
}
]
}
except Exception as e:
return {"error": f"Failed to get git status: {str(e)}"}
def _analyze_script_architecture(self) -> Dict[str, Any]:
"""Analyze the script architecture"""
lsl_files = list(self.project_root.glob("*.lsl"))
architecture_info = "Peril Dice Script Architecture Analysis:\n\n"
architecture_info += f"Total Scripts: {len(lsl_files)}\n\n"
# Categorize scripts by type
controllers = [f for f in lsl_files if "Controller" in f.name or "Manager" in f.name]
handlers = [f for f in lsl_files if "Handler" in f.name]
modules = [f for f in lsl_files if "Module" in f.name]
linkset_scripts = [f for f in lsl_files if "Linkset" in f.name]
architecture_info += f"Controllers/Managers ({len(controllers)}):\n"
for script in controllers:
architecture_info += f" • {script.name}\n"
architecture_info += f"\nHandlers ({len(handlers)}):\n"
for script in handlers:
architecture_info += f" • {script.name}\n"
architecture_info += f"\nModules ({len(modules)}):\n"
for script in modules:
architecture_info += f" • {script.name}\n"
architecture_info += f"\nLinkset-Specific Scripts ({len(linkset_scripts)}):\n"
for script in linkset_scripts:
architecture_info += f" • {script.name}\n"
return {
"content": [
{
"type": "text",
"text": architecture_info
}
]
}
def _get_memory_usage_info(self) -> Dict[str, Any]:
"""Extract memory usage information from comments in LSL files"""
lsl_files = list(self.project_root.glob("*.lsl"))
memory_info = "Memory Usage Information:\n\n"
for lsl_file in lsl_files:
try:
with open(lsl_file, 'r', encoding='utf-8') as f:
content = f.read()
# Look for memory usage comments
lines = content.split('\n')
memory_lines = [line for line in lines if 'memory' in line.lower() and ('%' in line or 'usage' in line.lower())]
if memory_lines:
memory_info += f"{lsl_file.name}:\n"
for line in memory_lines:
memory_info += f" {line.strip()}\n"
memory_info += "\n"
except Exception:
continue
return {
"content": [
{
"type": "text",
"text": memory_info if memory_info.strip() != "Memory Usage Information:" else "No memory usage information found in comments."
}
]
}
def _list_resources(self) -> Dict[str, Any]:
"""List available project resources"""
return {
"resources": [
{
"uri": "file:///home/richard/peril/README.md",
"name": "Project README",
"description": "Main project documentation",
"mimeType": "text/markdown"
},
{
"uri": "file:///home/richard/peril/CHANGELOG.md",
"name": "Changelog",
"description": "Project changelog",
"mimeType": "text/markdown"
},
{
"uri": "file:///home/richard/peril/WARP.md",
"name": "WARP Development Guide",
"description": "Development guidance for WARP",
"mimeType": "text/markdown"
}
]
}
def _read_resource(self, params: Dict[str, Any]) -> Dict[str, Any]:
"""Read a specific resource"""
uri = params.get("uri", "")
if uri.startswith("file://"):
file_path = Path(uri[7:]) # Remove file:// prefix
if not file_path.exists():
return {"error": f"File not found: {file_path}"}
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
return {
"contents": [
{
"uri": uri,
"mimeType": "text/markdown" if file_path.suffix == ".md" else "text/plain",
"text": content
}
]
}
except Exception as e:
return {"error": f"Failed to read file: {str(e)}"}
return {"error": f"Unsupported URI: {uri}"}
def main():
"""Main server loop"""
server = LSLMCPServer()
try:
# Read requests from stdin continuously
while True:
try:
line = sys.stdin.readline()
if not line: # EOF
break
line = line.strip()
if not line:
continue
request = json.loads(line)
response = server.handle_request(request)
# Wrap response in proper JSON-RPC format
json_rpc_response = {
"jsonrpc": "2.0",
"id": request.get("id", None)
}
if "error" in response:
json_rpc_response["error"] = response["error"]
else:
json_rpc_response["result"] = response
print(json.dumps(json_rpc_response))
sys.stdout.flush() # Ensure immediate output
except json.JSONDecodeError:
error_response = {
"jsonrpc": "2.0",
"error": {"code": -32700, "message": "Parse error"},
"id": None
}
print(json.dumps(error_response))
sys.stdout.flush()
except Exception as e:
error_response = {
"jsonrpc": "2.0",
"error": {"code": -32603, "message": str(e)},
"id": None
}
print(json.dumps(error_response))
sys.stdout.flush()
except KeyboardInterrupt:
pass
except EOFError:
pass
if __name__ == "__main__":
main()

View file

@ -1,60 +0,0 @@
#!/usr/bin/env python3
"""
Test script to verify lslint functionality with MCP server
"""
import json
import subprocess
import sys
def test_lslint_functionality():
"""Test lslint functionality through MCP server"""
# Test list_lsl_files first
list_request = {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_lsl_files",
"arguments": {}
}
}
try:
# Start the MCP server process
process = subprocess.Popen([
'python3', '/home/richard/peril/lsl_mcp_server.py'
], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# Send the request
stdout, stderr = process.communicate(input=json.dumps(list_request) + '\n', timeout=10)
print("=== LSL MCP Server - LSLint Test ===")
print("\nListing LSL Files:")
lines = stdout.strip().split('\n')
for line in lines:
if line.strip():
try:
response = json.loads(line)
if 'content' in response:
print(response['content'][0]['text'])
elif 'tools' in response:
print("Server initialized successfully")
except json.JSONDecodeError:
print(f"Non-JSON output: {line}")
if stderr:
print(f"\nServer Errors:\n{stderr}")
print("\n=== Test Complete ===")
except subprocess.TimeoutExpired:
print("Server test timed out")
process.kill()
except Exception as e:
print(f"Test failed: {e}")
if __name__ == "__main__":
test_lslint_functionality()

View file

@ -1,52 +0,0 @@
#!/usr/bin/env python3
"""
Simple test script to verify the LSL MCP server is working
"""
import json
import subprocess
import sys
def test_mcp_server():
"""Test basic MCP server functionality"""
# Test tools/list
test_request = {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}
try:
# Start the MCP server process
process = subprocess.Popen([
'python3', '/home/richard/peril/lsl_mcp_server.py'
], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# Send the request
stdout, stderr = process.communicate(input=json.dumps(test_request) + '\n', timeout=10)
print("=== MCP Server Test Results ===")
print("\nServer Output:")
for line in stdout.strip().split('\n'):
if line.strip():
try:
response = json.loads(line)
print(json.dumps(response, indent=2))
except json.JSONDecodeError:
print(f"Non-JSON output: {line}")
if stderr:
print(f"\nServer Errors:\n{stderr}")
print("\n=== Test Complete ===")
except subprocess.TimeoutExpired:
print("Server test timed out")
process.kill()
except Exception as e:
print(f"Test failed: {e}")
if __name__ == "__main__":
test_mcp_server()

View file

@ -1,62 +0,0 @@
#!/usr/bin/env python3
"""
Test script to validate a specific LSL file using the comprehensive validator
"""
import json
import subprocess
import sys
def test_validate_lsl():
"""Test LSL validation through MCP server"""
# Test validate_lsl_file with Main_Controller_Linkset.lsl
validate_request = {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "validate_lsl_file",
"arguments": {
"file_path": "Main_Controller_Linkset.lsl"
}
}
}
try:
# Start the MCP server process
process = subprocess.Popen([
'python3', '/home/richard/peril/lsl_mcp_server.py'
], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# Send the request
stdout, stderr = process.communicate(input=json.dumps(validate_request) + '\n', timeout=15)
print("=== LSL Comprehensive Validation Test ===")
print("\nValidating Main_Controller_Linkset.lsl:")
lines = stdout.strip().split('\n')
for line in lines:
if line.strip():
try:
response = json.loads(line)
if 'content' in response:
print(response['content'][0]['text'])
elif 'method' in response and response['method'] == 'server/initialized':
print("✅ Server initialized successfully")
except json.JSONDecodeError:
print(f"Non-JSON output: {line}")
if stderr:
print(f"\nServer Errors:\n{stderr}")
print("\n=== Test Complete ===")
except subprocess.TimeoutExpired:
print("Server test timed out")
process.kill()
except Exception as e:
print(f"Test failed: {e}")
if __name__ == "__main__":
test_validate_lsl()