一键导入
opensim-mcp
Control OpenSimulator via MCP - start/stop the simulator, manage regions, terrain, users, and assets through REST console commands
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Control OpenSimulator via MCP - start/stop the simulator, manage regions, terrain, users, and assets through REST console commands
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | opensim-mcp |
| description | Control OpenSimulator via MCP - start/stop the simulator, manage regions, terrain, users, and assets through REST console commands |
| version | 1.0.0 |
| category | gaming |
Controls OpenSimulator via REST console. The MCP server translates tool calls into OpenSim console commands via HTTP.
~/opensimulator/~/Documents/git/opensim-server-mcp/ (symlinked)Test, pass=secretThe MCP server has NO __main__ module. The pyproject.toml entry point is opensim_mcp.server:main, not opensim_mcp.__main__. All startup methods must invoke it as:
cd ~/Documents/git/opensim-server-mcp
OPENSIM_CONSOLE_USER=Test OPENSIM_CONSOLE_PASS=secret \
.venv/bin/python -c "from opensim_mcp.server import main; main()"
Or via the installed script (which works correctly):
~/.hermes/skills/opensim/opensim-mcp/scripts/start-mcp.sh
Never use: python -m opensim_mcp (fails with No module named opensim_mcp.__main__).
Scripts are in scripts/ subdirectory.
| Script | Purpose |
|---|---|
scripts/start-opensim.sh | Launch OpenSimulator in background with REST console |
scripts/stop-opensim.sh | Graceful shutdown via console quit command |
scripts/restart-opensim.sh | Stop then start |
scripts/opensim-status.sh | Check if running and show info |
scripts/start-mcp.sh | Start the MCP bridge server |
The OpenSim skill library is organized under a single umbrella:
~/.hermes/skills/opensim/
├── opensim-mcp/ # MCP REST bridge — console control, world queries
│ ├── SKILL.md
│ ├── scripts/ # start/stop/status scripts, lsl-search.sh
│ ├── references/ # LSL docs, console commands, prim creation XML
│ └── opensim-server-mcp/ # symlink → ~/Documents/git/opensim-server-mcp
└── opensim-asset-creation/ # In-world LSL/OSSL scripts, IAR/BVH tools
├── SKILL.md
├── scripts/ # master-controller.lsl, rezzer, trigger, give, etc.
└── references/ # asset/anim/library docs, script fixes
The opensim-asset-creation skill contains ready-to-deploy LSL scripts for in-world object creation, animation triggers, particle systems, inventory give, and media URL. The opensim-mcp skill provides console-level control via REST.
Path migration (2025-05-09): Both skills were moved from minecraft/opensim-* to opensim/*. All internal script paths and SKILL.md references were updated.
# IMPORTANT: If opensim.sh has CRLF line endings (from Windows/mixed origins),
# fix it first or you get "cannot execute: required file not found":
sed -i 's/\r$//' ~/opensimulator/bin/opensim.sh
chmod +x ~/opensimulator/bin/opensim.sh
# Start OpenSimulator (the wrapper only works from bin directory)
cd ~/opensimulator/bin && ./opensim.sh -console=rest &
# Wait for startup, then verify
sleep 5 && curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9000/
# Start MCP server (from skill directory)
~/.hermes/skills/opensim/opensim-mcp/scripts/start-mcp.sh
Or use the helper scripts:
~/.hermes/skills/opensim/opensim-mcp/scripts/start-opensim.sh # Start simulator
~/.hermes/skills/opensim/opensim-mcp/scripts/start-mcp.sh # Start MCP bridge
Note: The opensim.sh wrapper is fragile — it calls dotnet OpenSim.dll without an absolute path, so it only works when cd'd into bin/. The start script uses the direct form: /usr/bin/dotnet OpenSim.dll -console=rest.
| Tool | Use for |
|---|---|
get(command, args="") | Read-only queries: show, info, stats |
set(command, args="") | Mutations: create, delete, save, load, restart |
run(command) | Any raw console command |
Server Info
get("show version")get("show uptime")get("show info")get("show stats")Regions
get("show regions") / get("show region")set("change region", "RegionName")set("create region", "NewRegion Regions/NewRegion.ini")set("restart") / set("region restart notice", '"Msg" 120 60 30')Terrain
set("terrain load", "terrain.png")set("terrain save", "terrain.png")set("terrain fill", "25")set("terrain elevate", "5.0")get("terrain show", "128 128")Archives (OAR/IAR)
set("save oar", "backup.oar")set("load oar", "backup.oar")set("save iar", "First Last /Inventory password archive.iar")set("load iar", "First Last /Inventory password archive.iar")Users
get("show users") / get("show users", "full")set("alert", "Server restarting in 5 minutes")set("kick user", "First Last Please relog")set("teleport user", "First Last RegionName")Objects
get("show object name", '"Object Name"')set("delete object name", '"Object Name"')set("backup")Scripts
set("scripts stop") / set("scripts start")set("scripts suspend") / set("scripts resume")get("scripts show")Land
get("land show")set("land clear")Config
get("config show")get("config get", "Network")set("config set", "Section Key Value")Debug/Logging
get("get log level")set("set log level", "debug")set("debug http", "in 5")Shutdown
set("quit") or set("shutdown") - graceful stop| Variable | Default | Description |
|---|---|---|
OPENSIM_PATH | ~/opensimulator | OpenSim installation path |
OPENSIM_CONSOLE_URL | http://127.0.0.1:9000 | REST console URL |
OPENSIM_CONSOLE_USER | Test | REST console username |
OPENSIM_CONSOLE_PASS | secret | REST console password |
OpenSim must be running with REST console:
cd ~/opensimulator/bin && ./opensim.sh -console=rest
REST console must be configured in OpenSim.ini:
[Network]
ConsoleUser = "Test"
ConsolePass = "secret"
console_port = 9000
MCP server must start AFTER OpenSim is running and listening on port 9000.
| Can Do | How |
|---|---|
| Query avatar/world state | get("show users", "full") |
| Manage existing objects | get("show object name", ...) / set("delete object name", ...) |
| Terrain, OAR/IAR, config, scripts | Full CRUD via set/get |
| Restart/shutdown regions | set("restart") / set("quit") |
Object creation via console does NOT work. The load xml2 command appears to succeed (returns to prompt silently) but does NOT add objects to the scene — no error, but show object name <name> returns nothing. This is a confirmed OpenSim 0.9.3.0 bug. The create-prim.sh script will not produce actual objects.
Workaround: use viewer or LSL script — rez a prim manually, add a script with llRezObject() to spawn additional objects from inventory. Console has no working object creation command.
create cylinder → "Invalid command" (expected)load xml2 → silent success, no object addedcreate-prim.sh → uses load xml2, does not workConsole-based prim creation does not work. load xml2 silently fails to add objects. Use the viewer to rez prims, or use an LSL/OSSL script with llRezObject() inside a rezzer prim.
In-world approach (llRezObject):
llRezObject() to spawn objects from the prim's inventory at runtimereferences/lsl-functions.md → llRezObject for the full signatureThe SceneObjectGroup XML must follow these requirements precisely or OpenSim returns Error parsing object:
<scene> with one <SceneObjectGroup> childxmlns:xsi and xmlns:xsd attributes:
<SceneObjectPart xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<UUID><UUID>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</UUID></UUID>
NOT <UUID>...</UUID> directly. This applies to all UUID fields (CreatorID, OwnerID, etc.)0, NOT a UUID element:
<ParentID>0</ParentID>
<Color> element with R/G/B/A sub-elements — this sets the prim color directlyWhy earlier approaches failed:
<ParentID><UUID>...</UUID></ParentID> → "Error parsing element ParentID" — wrong structurexmlns:xsi → deserialization fails silently<UUID>uuid-string</UUID> (bare) → wrong, needs <UUID><UUID>...</UUID></UUID>Tried to add null scene objectGetting a working template: save xml2 /tmp/allprims.xml exports all current objects in a correctly-formatted XML2 format. Use this as a template — copy an object's XML subtree, change UUIDs and position fields, load it back with load xml2.
Finding avatar position: parse position from show users output.
Get full avatar + world info in one session:
# Start session and get a command result
SESSION=$(curl -s -X POST http://127.0.0.1:9000/StartSession/ \
-d "USER=Test" -d "PASS=secret" | grep -oP '<SessionID>\K[^<]+')
curl -s -X POST http://127.0.0.1:9000/SessionCommand/ \
-d "ID=$SESSION" -d "COMMAND=show users full"
curl -s -X POST http://127.0.0.1:9000/ReadResponses/$SESSION/
curl -s -X POST http://127.0.0.1:9000/CloseSession/ -d "ID=$SESSION"
LSL (Linden Scripting Language) is the in-world scripting language for OpenSimulator.
The KB is built from language_guide.pdf (Linden Lab, 2003) + sl-basics.pdf.
# Full-text search across all LSL docs
~/.hermes/skills/opensim/opensim-mcp/scripts/lsl-search.sh "llRezObject"
~/.hermes/skills/opensim/opensim-mcp/scripts/lsl-search.sh "collision"
~/.hermes/skills/opensim/opensim-mcp/scripts/lsl-search.sh "timer event"
# Get function details
~/.hermes/skills/opensim/opensim-mcp/scripts/lsl-search.sh --fn llSetScale
~/.hermes/skills/opensim/opensim-mcp/scripts/lsl-search.sh --fn llRezObject
# Get event details
~/.hermes/skills/opensim/opensim-mcp/scripts/lsl-search.sh --event touch_start
~/.hermes/skills/opensim/opensim-mcp/scripts/lsl-search.sh --event timer
# List types and operators
~/.hermes/skills/opensim/opensim-mcp/scripts/lsl-search.sh --types
~/.hermes/skills/opensim/opensim-mcp/scripts/lsl-search.sh --ops
# Show category overview
~/.hermes/skills/opensim/opensim-mcp/scripts/lsl-search.sh --categories
# Rebuild the index (after adding reference .md files)
~/.hermes/skills/opensim/opensim-mcp/scripts/lsl-search.sh --rebuild
| File | Content |
|---|---|
references/lsl-functions.md | 204 LSL functions with signatures and descriptions |
references/lsl-events-states.md | All events (touch, collision, timer, sensor, etc.) and state mechanics |
references/commands.md | OpenSim console command reference |
references/opensim-library-assets.md | Built-in library animations, gestures, body parts, clothing — with test script |
references/opensim-mcp-rest-bridge.md | MCP REST bridge debugging, session-based curl fallback |
references/ariadne4j-params.md | Ariadne4j asset editor params: particle systems, RLV, animations, MVP medical equipment, chat channels, attachment points |
Core types: integer float string key vector <x,y,z> rotation <x,y,z,s> list
Creating objects in-world: No console command exists. Use an LSL script with llRezObject() — put the script in a prim, the script rezzes new objects from inventory. See references/lsl-functions.md → llRezObject.
Common pattern — rez a brown tall cylinder at target position:
default {
state_entry() {
vector target = <129.5, 130.0, 26.0>; // where to rez
llRezObject("CylinderTemplate", target, <0,0,0>, ZERO_ROTATION, 0);
}
on_rez(integer param) {
llSetScale(<0.5, 0.5, 8.0>); // 8m tall
llSetColor(<0.55, 0.27, 0.07>, ALL_SIDES); // brown
}
}
Useful constant quick ref: TRUE=1 FALSE=0 NULL_KEY="" ALL_SIDES=-1 PI=3.14159
default is the entry point. Switch with state otherstate;llSetScale(), llSay(), llGetPos(), etc.llRequestPermissions()OpenSim extends LSL with additional functions (OSSL). Key extensions:
osSetPenColor(string color) — set drawing colorosGetAgents() — list avatars in regionosNpcCreate/Remove/MoveTo — NPC/obot managementosSetRegionWaterHeight(height) — set water levelosTerrainHeight(x, y) — get terrain heightMCP fails to connect
LSL Compile Errors — OpenSim LSL ≠ Standard SL LSL
These are NOT viewer or viewer-side issues — OpenSim's YEngine doesn't support some standard LSL patterns:
| Error | Cause | Fix |
|---|---|---|
(line, col) Error: undefined constant/function/variable break | break keyword doesn't exist in OpenSim | Exit loop via i = count; or restructure |
(line, col) Error: undefined constant/function/variable llClamp | llClamp() doesn't exist | Manual: if(v<min)v=min; if(v>max)v=max; |
(line, col) Error: undefined constant/function/variable llGetInventoryType(string,integer) | Wrong arg count | Use llGetInventoryType(name) with 1 arg only |
(line, col) Error: undefined name llStringTrim(string) | llStringTrim() doesn't exist in OpenSim YEngine | Write a manual trim helper or use bare string checks |
llDialog: Button label cannot be blank | Empty strings in button list | Remove blank " " separators, use only valid button names |
break)// WRONG - break doesn't exist in OpenSim LSL:
for (i = 0; i < count; i++) {
if (found) break; // COMPILE ERROR
}
// CORRECT:
integer found = -1;
for (i = 0; i < count; i++) {
if (llGetInventoryName(INVENTORY_OBJECT, i) == target) {
found = i;
i = count; // Exit loop
}
}
llClamp) / Manual Trim (no llStringTrim)// WRONG - llClamp doesn't exist:
float val = llClamp(value, 0.0, 1.0); // COMPILE ERROR
// CORRECT:
float val = value;
if (val < 0.0) val = 0.0;
if (val > 1.0) val = 1.0;
// WRONG - llStringTrim doesn't exist:
string t = llStringTrim(input); // COMPILE ERROR
// CORRECT (if you need trim):
string t = input; // Use bare string comparison, or:
if (llGetSubString(t, 0, 0) == " ") t = llGetSubString(t, 1, -1); // strip leading space
Note: In most cases you can skip trim entirely — if (str != "") works without trimming. Only add manual trim if the user explicitly passes trimmed text to another function that requires it.
// WRONG - blank strings cause error:
list buttons = ["OK", "", "──────────", "", "Help"]; // ERROR
// CORRECT - no blank entries:
list buttons = ["OK", "Status", "Help"];
OpenSim's YEngine does not support many particle constants that work in standard SL LSL. If you get Error: undefined constant/function/variable PSYS_*, that constant is not available — remove it from the particle list.
Known unsupported constants:
| Constant | Purpose | Fix |
|---|---|---|
PSYS_PART_FOUR_SOURCE_DEPTH | Depth flag | Remove; use PSYS_PART_EMISSIVE_MASK alone |
PSYS_BF_SOURCE_ALPHA | Blend source | Remove |
PSYS_BF_ONE_MINUS_SOURCE_ALPHA | Blend dest | Remove |
PSYS_PART_START_GLOW / PSYS_PART_END_GLOW | Glow values | Remove |
PSYS_SRC_BURN_COLOR | Burn color | Remove |
PSYS_SRC_OUTER_TARGET_ALPHA | Outer alpha target | Remove |
PSYS_SRC_INNER_TARGET_ALPHA | Inner alpha target | Remove |
PSYS_SRC_OMEGA | Angular velocity | Remove |
Working particle template (fire preset — ALL numeric constants, no symbolic names):
// Fire effect — NO symbolic PSYS constants in OpenSim 0.9.3.0
llParticleSystem([
PSYS_PART_FLAGS, 256, // EMISSIVE
PSYS_PART_MAX_AGE, 2.0,
PSYS_PART_START_SCALE, <0.5, 0.5, 0.0>,
PSYS_PART_END_SCALE, <0.1, 0.1, 0.0>,
PSYS_PART_START_COLOR, <1.0, 0.5, 0.3>,
PSYS_PART_END_COLOR, <0.9, 0.2, 0.1>,
PSYS_PART_START_ALPHA, 1.0,
PSYS_PART_END_ALPHA, 0.0,
PSYS_SRC_PATTERN, 2, // EXPLODE
PSYS_SRC_MAX_AGE, 0,
PSYS_SRC_TEXTURE, ""
]);
For cone/angle patterns (e.g. fountain, bubble, heart), use:
PSYS_SRC_PATTERN, 4, // ANGLE_CONE (not PSYS_SRC_PATTERN_CONE)
Full working presets (6 types — fire, smoke, fountain, bubble, sparkle, heart):
See opensim-asset-creation/scripts/master-controller.lsl → applyParticlePreset() function for the complete implementation with all 6 presets using only numeric constants.
Known unsupported constants (all symbolic PSYS names fail in OpenSim YEngine):
| Constant | Fix |
|---|---|
PSYS_SRC_PATTERN_EXPLODE | Use 2 |
PSYS_SRC_PATTERN_CONE | Use 4 |
PSYS_PART_EMISSIVE_MASK | Use 256 |
PSYS_SRC_LIFETIME | Remove — does not exist |
PSYS_SRC_ANGLE_BEGIN / PSYS_SRC_ANGLE_END | Remove — do not exist |
PSYS_PART_FOUR_SOURCE_DEPTH | Remove |
PSYS_BF_SOURCE_ALPHA / PSYS_BF_ONE_MINUS_SOURCE_ALPHA | Remove |
PSYS_PART_START_GLOW / PSYS_PART_END_GLOW | Remove |
PSYS_SRC_BURN_COLOR, PSYS_SRC_OUTER_TARGET_ALPHA, PSYS_SRC_INNER_TARGET_ALPHA, PSYS_SRC_OMEGA | Remove |
See opensim-asset-creation skill for the complete working particle system implementation.
See opensim-asset-creation skill for fixed in-world scripts (master-controller.lsl, etc.) that work with OpenSim's YEngine.
LSL Compile Errors ("undefined constant/function/variable")
break in loops → use i = count; to exitllClamp(x,min,max) → use if(x<min)x=min;if(x>max)x=max;llGetInventoryType(name, type) → use llGetInventoryType(name) (1 arg only)opensim-asset-creation for the fixed rezzer script.Session-based curl works when MCP fails
SESSION=$(curl -s -X POST http://127.0.0.1:9000/StartSession/ \
-d "USER=Test" -d "PASS=secret" | grep -oP '<SessionID>\K[^<]+')
curl -s -X POST http://127.0.0.1:9000/SessionCommand/ \
-d "ID=$SESSION" -d "COMMAND=show users full"
curl -s -X POST http://127.0.0.1:9000/ReadResponses/$SESSION/
opensim.sh fails with "cannot execute: required file not found"
sed -i 's/\r$//' ~/opensimulator/bin/opensim.sh && chmod +x ~/opensimulator/bin/opensim.shdotnet OpenSim.dll so it only works from the bin/ directory. Use the direct form instead:
/usr/bin/dotnet OpenSim.dll -console=restPort 9000 occupied after crash
ss -tlnp | grep 9000 (shows PID/command) or lsof -i :9000kill <PID> then restart OpenSim-console=rest (MCP bridge), one without (region server). Port 9000 is bound by whichever starts first. A second region server fails "Address already in use" even if the first had no console flag.tail -n 3 ~/opensimulator/bin/OpenSim.log. If timestamps match current time, it's the live one. Stale entries = that instance crashed and a new dotnet process (visible in ps aux | grep dotnet) took over.bin/. Only OpenSim.log (298KB+) is active. opensim.log (22KB), opensim-console.log, opensim_direct.log are stale from previous runs.create cylinder returns "Invalid command"
llRezObject() inside a rezzer prim.To modify an existing object's scale via console
set("edit scale", '"ObjectName" <x> <y> <z>') e.g. set("edit scale", '"Tree" 2 2 10')load xml2 silently fails — object not created
show object name <name> finds nothingllRezObject() in an LSL script inside a rezzer primsave xml2 produces valid exports — the issue is on deserializationllRequestURL() / osRequestURL() denied in standalone mode
llRequestURL() in state_entry, receives URL_REQUEST_DENIED, and log shows "ExternalHostNameForLSL not defined in configuration, HTTP listener for LSL disabled"ExternalHostNameForLSL from the [Network] config section. If that section's include order loads AFTER the URL module's config read, the value is not yet set. Also, [URLAccessModule] section (line ~696) is read but if it appears before the module initializes, it may be skipped.[Network]
ExternalHostNameForLSL = "127.0.0.1"
The [Network] section already contains console/auth settings (ConsoleUser, ConsolePass, console_port, http_listener_port). Adding ExternalHostNameForLSL there works. It also exists in the [URLAccessModule] section at line 696, but the [Network] location is what UrlModule.cs reads (line 134: config.Configs["Network"].GetString("ExternalHostNameForLSL", null)).[Network] section that OVERRIDES the one in OpenSim.ini. The include order matters — later includes replace earlier ones.OutboundDisallowForUserScriptsExcept = "127.0.0.1:8080|127.0.0.1:9000"
This allows scripts to make HTTP requests TO the REST console (MCP bridge, etc.).llSleep(2.0) before first llRequestURL() call.URL_REQUEST_DENIED, wait 5s and call llRequestURL() again.https://opensimulator.dev/ (not opensimulator.org). The domain migrated.Known PSYS pattern constant values for OpenSim 0.9.3.0:
| Constant | Value | Notes |
|---|---|---|
PSYS_SRC_PATTERN = EXPLODE | 2 | Symbolic name doesn't exist |
PSYS_SRC_PATTERN = ANGLE_CONE | 4 | Symbolic name doesn't exist |
PSYS_PART_FLAGS = EMISSIVE | 256 | Symbolic name doesn't exist |
Use numeric values directly — all symbolic PSYS constant names fail in OpenSim YEngine.