mirror of
https://github.com/araxiaonline/TrinityCore.git
synced 2026-06-13 03:32:28 -04:00
feat(mcp): Complete Phase 2 - AMS Bridge & Direct Windsurf Connection!
HUGE MILESTONE: AI assistant now has direct real-time access to worldserver! Phase 1 - Database & Server Tools: - db_query, db_execute, db_tables, db_describe - server_info, player_list, gm_command, reload_scripts Phase 2 - Shared Data Bridge: - shared_data_read, shared_data_write, shared_data_keys - ElunaSharedData integration for cross-state communication - Full client MCP bidirectional data flow AMS Bridge Implementation: - Server: mcp_bridge.lua handles MCP_CHAT, MCP_CLIENT_LOG, MCP_GET_MESSAGES - Client: MCPBridge.lua with /mcpbridge commands - Data stored in ElunaSharedData, readable by MCP tools Key Learnings Documented: - nlohmann/json: Don't use value() with nullptr - TrinityCore: Use GetBoolDefault() not GetOption<T>() - AMS: Use AMS.Send (dot) not AMS:Send (colon) on client - Smallfolk: Never serialize functions Windsurf Integration: - Add to ~/.codeium/windsurf/mcp_config.json - AI gets direct access to all MCP tools - No more manual curl commands! This is SOOO FUCKING COOL!!!
This commit is contained in:
@@ -4,15 +4,25 @@
|
||||
|
||||
The Araxia MCP Server embeds a Model Context Protocol server directly into the worldserver, enabling AI assistants (like Claude/Cascade) to interact with the game server in real-time.
|
||||
|
||||
**Status:** ✅ Phase 1 Complete (Nov 30, 2025)
|
||||
**Status:** ✅ Phase 1 & 2 Complete (Nov 30, 2025)
|
||||
|
||||
## 🎉 What This Enables
|
||||
|
||||
With this integration, the AI assistant can:
|
||||
- **Query databases directly** - No more asking you to run SQL commands
|
||||
- **See online players** - Know who's logged in and where they are
|
||||
- **Read client messages** - Receive data from the WoW client via AMS bridge
|
||||
- **Write to client** - Send messages that display in the WoW client
|
||||
- **Debug in real-time** - Direct access to server state while you're playing
|
||||
|
||||
## Features
|
||||
|
||||
- **Database Access**: ✅ Direct SQL queries to world, characters, and auth databases
|
||||
- **Server Status**: ✅ Real-time server info, player lists, uptime
|
||||
- **Shared Data Bridge**: ✅ Read/write ElunaSharedData (client ↔ MCP communication)
|
||||
- **GM Commands**: ✅ Stub (needs ChatHandler integration)
|
||||
- **Eluna Integration**: ⏳ (Phase 2) Execute Lua, inspect state, hot-reload
|
||||
- **AMS Bridge**: ⏳ (Phase 4) Communicate with client addons
|
||||
- **Eluna Integration**: ⏳ (Phase 3) Execute Lua, inspect state, hot-reload
|
||||
- **World Object Tools**: ⏳ (Phase 4) Creature/GO manipulation
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -167,12 +177,83 @@ src/araxiaonline/mcp/
|
||||
- `World.cpp` - `sMCPServer->Shutdown()` in destructor
|
||||
- `CMakeLists.txt` - Auto-collected via `CollectSourceFiles`
|
||||
|
||||
## Windsurf MCP Configuration
|
||||
|
||||
To connect Cascade/Claude directly to the worldserver, add to `~/.codeium/windsurf/mcp_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"araxia-worldserver": {
|
||||
"url": "http://localhost:8765/mcp",
|
||||
"transport": "http"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After adding, restart Windsurf. The AI will have direct access to all MCP tools.
|
||||
|
||||
## AMS Bridge (Client ↔ MCP)
|
||||
|
||||
The AMS bridge enables bidirectional communication between the WoW client and the MCP server.
|
||||
|
||||
### Data Flow
|
||||
```
|
||||
Client Addon → AMS.Send() → Server Lua → ElunaSharedData → MCP Tool
|
||||
MCP Tool → ElunaSharedData → Server Lua → AMS.Send() → Client Addon
|
||||
```
|
||||
|
||||
### Client Side (`AraxiaTrinityAdmin/MCPBridge.lua`)
|
||||
- Captures chat messages and sends to server
|
||||
- Provides `/mcpbridge` commands for control
|
||||
- Polls for messages from MCP (disabled by default)
|
||||
|
||||
### Server Side (`lua_scripts/mcp_bridge.lua`)
|
||||
- Receives client messages via AMS handlers
|
||||
- Stores in ElunaSharedData for MCP to read
|
||||
- Reads MCP messages and sends to client
|
||||
|
||||
### Shared Data Keys
|
||||
| Key | Purpose |
|
||||
|-----|---------|
|
||||
| `mcp_client_chat` | Chat messages from client |
|
||||
| `mcp_client_logs` | Log/error messages from client |
|
||||
| `mcp_to_client` | Messages from MCP to display on client |
|
||||
|
||||
### Client Commands
|
||||
```
|
||||
/mcpbridge status - Show bridge status
|
||||
/mcpbridge test - Send test message to MCP
|
||||
/mcpbridge on/off - Enable/disable bridge
|
||||
/mcpbridge poll - Start polling for MCP messages
|
||||
```
|
||||
|
||||
## Roadmap
|
||||
|
||||
| Phase | Feature | Status |
|
||||
|-------|---------|--------|
|
||||
| 1 | Database tools, server info | ✅ Complete |
|
||||
| 2 | Eluna integration (lua_eval, shared_data) | ⏳ Planned |
|
||||
| 3 | World object tools (creatures, GOs) | ⏳ Planned |
|
||||
| 4 | AMS bridge (client addon communication) | ⏳ Planned |
|
||||
| 2 | Shared data bridge (ElunaSharedData) | ✅ Complete |
|
||||
| 3 | Eluna integration (lua_eval, hot-reload) | ⏳ Planned |
|
||||
| 4 | World object tools (creatures, GOs) | ⏳ Planned |
|
||||
| 5 | Event streaming (logs, world events) | ⏳ Planned |
|
||||
|
||||
## Key Learnings
|
||||
|
||||
### nlohmann/json Gotchas
|
||||
- **Never use `request.value("id", nullptr)`** - causes type errors
|
||||
- Use `request.contains("id") ? request["id"] : json(nullptr)` instead
|
||||
|
||||
### TrinityCore ConfigMgr API
|
||||
- Use `sConfigMgr->GetBoolDefault()`, `GetIntDefault()`, `GetStringDefault()`
|
||||
- NOT `GetOption<T>()` which doesn't exist
|
||||
|
||||
### ElunaSharedData API
|
||||
- Singleton pattern: `sElunaSharedData->Get()`, `Set()`, `GetKeys()`
|
||||
- Lua API: `SetSharedData()`, `GetSharedData()`, `HasSharedData()`
|
||||
|
||||
### AMS Client/Server Pattern
|
||||
- Client: `AMS.Send("HANDLER_NAME", data)` (dot notation, NOT colon!)
|
||||
- Server: `AMS.Send(player, "HANDLER_NAME", data)`
|
||||
- Never serialize functions - Smallfolk will error
|
||||
|
||||
181
araxiaonline/client_addons/AraxiaTrinityAdmin/MCPBridge.lua
Normal file
181
araxiaonline/client_addons/AraxiaTrinityAdmin/MCPBridge.lua
Normal file
@@ -0,0 +1,181 @@
|
||||
--[[
|
||||
MCP Bridge - Client Side
|
||||
|
||||
Captures client-side events and sends them to the server via AMS
|
||||
for the MCP server to read. Also displays messages from MCP.
|
||||
]]
|
||||
|
||||
local addonName, addon = ...
|
||||
|
||||
-- Create the MCP Bridge module
|
||||
addon.MCPBridge = addon.MCPBridge or {}
|
||||
local MCPBridge = addon.MCPBridge
|
||||
|
||||
-- Configuration
|
||||
MCPBridge.enabled = true
|
||||
MCPBridge.captureChat = true
|
||||
MCPBridge.captureErrors = true
|
||||
MCPBridge.pollInterval = 5 -- seconds between MCP message polls
|
||||
|
||||
-- Chat types to capture
|
||||
MCPBridge.chatTypes = {
|
||||
["CHAT_MSG_SAY"] = "SAY",
|
||||
["CHAT_MSG_YELL"] = "YELL",
|
||||
["CHAT_MSG_PARTY"] = "PARTY",
|
||||
["CHAT_MSG_GUILD"] = "GUILD",
|
||||
["CHAT_MSG_WHISPER"] = "WHISPER",
|
||||
["CHAT_MSG_CHANNEL"] = "CHANNEL",
|
||||
["CHAT_MSG_SYSTEM"] = "SYSTEM",
|
||||
}
|
||||
|
||||
-- Send chat message to server for MCP
|
||||
function MCPBridge:SendChatToMCP(message, chatType)
|
||||
if not self.enabled or not self.captureChat then return end
|
||||
if not AMS then return end
|
||||
|
||||
AMS.Send("MCP_CHAT", {
|
||||
message = message,
|
||||
chatType = chatType or "CHAT"
|
||||
})
|
||||
end
|
||||
|
||||
-- Send client log/error to server for MCP
|
||||
function MCPBridge:SendLogToMCP(message, level, source)
|
||||
if not self.enabled or not self.captureErrors then return end
|
||||
if not AMS then return end
|
||||
|
||||
AMS.Send("MCP_CLIENT_LOG", {
|
||||
message = message,
|
||||
level = level or "INFO",
|
||||
source = source or "AraxiaTrinityAdmin"
|
||||
})
|
||||
end
|
||||
|
||||
-- Send structured data to server for MCP
|
||||
function MCPBridge:SendDataToMCP(dataType, payload)
|
||||
if not self.enabled then return end
|
||||
if not AMS then return end
|
||||
|
||||
AMS.Send("MCP_CLIENT_DATA", {
|
||||
dataType = dataType,
|
||||
payload = payload
|
||||
})
|
||||
end
|
||||
|
||||
-- Request pending messages from MCP
|
||||
function MCPBridge:PollMCPMessages()
|
||||
if not self.enabled then return end
|
||||
if not AMS then return end
|
||||
|
||||
AMS.Send("MCP_GET_MESSAGES", {})
|
||||
end
|
||||
|
||||
-- Handle messages received from MCP
|
||||
function MCPBridge:OnMCPMessagesReceived(data)
|
||||
local messages = data.messages or {}
|
||||
|
||||
for _, msg in ipairs(messages) do
|
||||
-- Display MCP messages in chat
|
||||
print("|cFF00FF00[MCP]|r " .. (msg.message or tostring(msg)))
|
||||
end
|
||||
end
|
||||
|
||||
-- Register AMS handler for MCP messages
|
||||
local function RegisterAMSHandler()
|
||||
if not AMS then
|
||||
C_Timer.After(1, RegisterAMSHandler) -- Retry
|
||||
return
|
||||
end
|
||||
|
||||
AMS:RegisterHandler("MCP_MESSAGES_RESPONSE", function(data)
|
||||
MCPBridge:OnMCPMessagesReceived(data)
|
||||
end)
|
||||
|
||||
print("|cFF00FF00[MCP Bridge]|r Client-side bridge ready")
|
||||
end
|
||||
|
||||
-- Chat event handler
|
||||
local chatFrame = CreateFrame("Frame")
|
||||
chatFrame:RegisterEvent("CHAT_MSG_SAY")
|
||||
chatFrame:RegisterEvent("CHAT_MSG_YELL")
|
||||
chatFrame:RegisterEvent("CHAT_MSG_PARTY")
|
||||
chatFrame:RegisterEvent("CHAT_MSG_GUILD")
|
||||
chatFrame:RegisterEvent("CHAT_MSG_SYSTEM")
|
||||
|
||||
chatFrame:SetScript("OnEvent", function(self, event, message, sender, ...)
|
||||
local chatType = MCPBridge.chatTypes[event]
|
||||
if chatType and MCPBridge.captureChat then
|
||||
-- Only capture our own messages or system messages
|
||||
local playerName = UnitName("player")
|
||||
if sender == playerName or event == "CHAT_MSG_SYSTEM" then
|
||||
MCPBridge:SendChatToMCP(message, chatType)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Error handler - capture Lua errors (disabled to prevent recursion)
|
||||
-- The error handler can cause infinite loops if AMS serialization fails
|
||||
-- We'll rely on explicit error logging instead
|
||||
MCPBridge.captureErrors = false -- Disabled by default
|
||||
|
||||
-- Poll timer for MCP messages
|
||||
local pollTimer
|
||||
local function StartPolling()
|
||||
if pollTimer then return end
|
||||
pollTimer = C_Timer.NewTicker(MCPBridge.pollInterval, function()
|
||||
MCPBridge:PollMCPMessages()
|
||||
end)
|
||||
end
|
||||
|
||||
-- Slash command to toggle MCP bridge
|
||||
SLASH_MCPBRIDGE1 = "/mcpbridge"
|
||||
SlashCmdList["MCPBRIDGE"] = function(msg)
|
||||
local cmd = msg:lower():trim()
|
||||
|
||||
if cmd == "on" then
|
||||
MCPBridge.enabled = true
|
||||
print("|cFF00FF00[MCP Bridge]|r Enabled")
|
||||
elseif cmd == "off" then
|
||||
MCPBridge.enabled = false
|
||||
print("|cFFFF0000[MCP Bridge]|r Disabled")
|
||||
elseif cmd == "chat on" then
|
||||
MCPBridge.captureChat = true
|
||||
print("|cFF00FF00[MCP Bridge]|r Chat capture enabled")
|
||||
elseif cmd == "chat off" then
|
||||
MCPBridge.captureChat = false
|
||||
print("|cFFFF0000[MCP Bridge]|r Chat capture disabled")
|
||||
elseif cmd == "status" then
|
||||
print("|cFF00FFFF[MCP Bridge Status]|r")
|
||||
print(" Enabled: " .. (MCPBridge.enabled and "Yes" or "No"))
|
||||
print(" Chat Capture: " .. (MCPBridge.captureChat and "Yes" or "No"))
|
||||
print(" Error Capture: " .. (MCPBridge.captureErrors and "Yes" or "No"))
|
||||
elseif cmd == "test" then
|
||||
-- Send directly, bypassing captureErrors check
|
||||
if AMS then
|
||||
AMS.Send("MCP_CLIENT_LOG", {
|
||||
message = "Test message from client @ " .. date("%H:%M:%S"),
|
||||
level = "INFO",
|
||||
source = "MCPBridge Test"
|
||||
})
|
||||
print("|cFF00FF00[MCP Bridge]|r Test message sent to server")
|
||||
else
|
||||
print("|cFFFF0000[MCP Bridge]|r AMS not available")
|
||||
end
|
||||
elseif cmd == "poll" then
|
||||
StartPolling()
|
||||
print("|cFF00FF00[MCP Bridge]|r Polling started")
|
||||
else
|
||||
print("|cFF00FFFF[MCP Bridge Commands]|r")
|
||||
print(" /mcpbridge on|off - Enable/disable bridge")
|
||||
print(" /mcpbridge chat on|off - Toggle chat capture")
|
||||
print(" /mcpbridge status - Show status")
|
||||
print(" /mcpbridge test - Send test message")
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize
|
||||
C_Timer.After(2, function()
|
||||
RegisterAMSHandler()
|
||||
-- Polling disabled by default - use /mcpbridge poll to enable
|
||||
-- StartPolling()
|
||||
end)
|
||||
126
araxiaonline/lua_scripts/mcp_bridge.lua
Normal file
126
araxiaonline/lua_scripts/mcp_bridge.lua
Normal file
@@ -0,0 +1,126 @@
|
||||
--[[
|
||||
MCP Bridge - Server Side
|
||||
|
||||
Provides a communication bridge between the WoW client and the MCP server.
|
||||
Client sends messages via AMS, server stores them in ElunaSharedData,
|
||||
MCP tools can read/write to ElunaSharedData.
|
||||
|
||||
Keys used:
|
||||
- mcp_client_chat: Last N chat messages from client
|
||||
- mcp_client_logs: Client-side addon errors/logs
|
||||
- mcp_to_client: Messages from MCP to display on client
|
||||
]]
|
||||
|
||||
local Smallfolk = require("Smallfolk")
|
||||
|
||||
-- Configuration
|
||||
local MAX_CHAT_MESSAGES = 50
|
||||
local MAX_LOG_MESSAGES = 100
|
||||
|
||||
-- Initialize storage keys if not present
|
||||
local function InitSharedData()
|
||||
if not HasSharedData("mcp_client_chat") then
|
||||
SetSharedData("mcp_client_chat", Smallfolk.dumps({}))
|
||||
end
|
||||
if not HasSharedData("mcp_client_logs") then
|
||||
SetSharedData("mcp_client_logs", Smallfolk.dumps({}))
|
||||
end
|
||||
if not HasSharedData("mcp_to_client") then
|
||||
SetSharedData("mcp_to_client", Smallfolk.dumps({}))
|
||||
end
|
||||
end
|
||||
|
||||
-- Add a message to a shared data list (FIFO)
|
||||
local function AddToSharedList(key, message, maxItems)
|
||||
local data = GetSharedData(key)
|
||||
local list = data and Smallfolk.loads(data) or {}
|
||||
|
||||
table.insert(list, {
|
||||
timestamp = os.time(),
|
||||
message = message
|
||||
})
|
||||
|
||||
-- Trim to max size
|
||||
while #list > maxItems do
|
||||
table.remove(list, 1)
|
||||
end
|
||||
|
||||
SetSharedData(key, Smallfolk.dumps(list))
|
||||
end
|
||||
|
||||
-- AMS Handler: Client sends chat message for MCP to see
|
||||
local function HandleMCPChatMessage(player, data)
|
||||
local playerName = player:GetName()
|
||||
local message = data.message or ""
|
||||
local chatType = data.chatType or "CHAT"
|
||||
|
||||
local formatted = string.format("[%s] %s: %s", chatType, playerName, message)
|
||||
AddToSharedList("mcp_client_chat", formatted, MAX_CHAT_MESSAGES)
|
||||
|
||||
print("[MCP Bridge] Chat captured: " .. formatted)
|
||||
end
|
||||
|
||||
-- AMS Handler: Client sends log/error for MCP to see
|
||||
local function HandleMCPClientLog(player, data)
|
||||
local playerName = player:GetName()
|
||||
local logLevel = data.level or "INFO"
|
||||
local message = data.message or ""
|
||||
local source = data.source or "unknown"
|
||||
|
||||
local formatted = string.format("[%s] [%s] %s: %s", logLevel, source, playerName, message)
|
||||
AddToSharedList("mcp_client_logs", formatted, MAX_LOG_MESSAGES)
|
||||
|
||||
print("[MCP Bridge] Client log: " .. formatted)
|
||||
end
|
||||
|
||||
-- AMS Handler: Client requests any pending MCP messages
|
||||
local function HandleMCPGetMessages(player, data)
|
||||
local messagesData = GetSharedData("mcp_to_client")
|
||||
local messages = messagesData and Smallfolk.loads(messagesData) or {}
|
||||
|
||||
-- Clear after reading
|
||||
SetSharedData("mcp_to_client", Smallfolk.dumps({}))
|
||||
|
||||
AMS.Send(player, "MCP_MESSAGES_RESPONSE", {
|
||||
messages = messages
|
||||
})
|
||||
end
|
||||
|
||||
-- AMS Handler: Client sends DB query result or other structured data
|
||||
local function HandleMCPClientData(player, data)
|
||||
local playerName = player:GetName()
|
||||
local dataType = data.dataType or "generic"
|
||||
local payload = data.payload or {}
|
||||
|
||||
-- Store in a type-specific key
|
||||
local key = "mcp_client_" .. dataType
|
||||
SetSharedData(key, Smallfolk.dumps({
|
||||
player = playerName,
|
||||
timestamp = os.time(),
|
||||
data = payload
|
||||
}))
|
||||
|
||||
print("[MCP Bridge] Client data received: " .. dataType)
|
||||
end
|
||||
|
||||
-- Register AMS handlers
|
||||
local function RegisterAMSHandlers()
|
||||
-- Check if AMS is available
|
||||
if not AMS or not AMS.RegisterHandler then
|
||||
print("[MCP Bridge] Warning: AMS not available, skipping handler registration")
|
||||
return
|
||||
end
|
||||
|
||||
AMS.RegisterHandler("MCP_CHAT", HandleMCPChatMessage)
|
||||
AMS.RegisterHandler("MCP_CLIENT_LOG", HandleMCPClientLog)
|
||||
AMS.RegisterHandler("MCP_GET_MESSAGES", HandleMCPGetMessages)
|
||||
AMS.RegisterHandler("MCP_CLIENT_DATA", HandleMCPClientData)
|
||||
|
||||
print("[MCP Bridge] AMS handlers registered")
|
||||
end
|
||||
|
||||
-- Initialize on load
|
||||
InitSharedData()
|
||||
RegisterAMSHandlers()
|
||||
|
||||
print("[MCP Bridge] Server-side bridge loaded")
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Log.h"
|
||||
#include "GitRevision.h"
|
||||
#include "GameTime.h"
|
||||
#include "LuaEngine/ElunaSharedData.h"
|
||||
|
||||
namespace Araxia
|
||||
{
|
||||
@@ -209,27 +210,108 @@ void RegisterServerTools()
|
||||
},
|
||||
[](const json& params) -> json {
|
||||
std::string pattern = params.value("pattern", "");
|
||||
int maxLines = params.value("lines", 50);
|
||||
std::string logType = params.value("logType", "server");
|
||||
|
||||
// For now, return info about where logs are
|
||||
// This is a placeholder - actual log search would read ElunaSharedData
|
||||
// where the client/server Lua code writes log messages
|
||||
return {
|
||||
{"success", true},
|
||||
{"message", "Log search requires file access. Logs are typically in /opt/trinitycore/logs/"},
|
||||
{"logFiles", {
|
||||
{"server", "Server.log"},
|
||||
{"eluna", "Eluna.log"},
|
||||
{"dberrors", "DBErrors.log"},
|
||||
{"gm", "GM.log"}
|
||||
}},
|
||||
{"pattern", pattern},
|
||||
{"maxLines", maxLines},
|
||||
{"note", "Use db_query on characters.gm_command_log for GM command history"}
|
||||
{"message", "Use shared_data tool with key 'mcp_logs' to read logs pushed by client/server"},
|
||||
{"pattern", pattern}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
TC_LOG_INFO("araxia.mcp", "[MCP] Server tools registered (server_info, player_list, gm_command, reload_scripts, log_search)");
|
||||
// shared_data_read - Read from ElunaSharedData (AMS bridge)
|
||||
sMCPServer->RegisterTool(
|
||||
"shared_data_read",
|
||||
"Read data from ElunaSharedData. This is the bridge for client/server Lua communication.",
|
||||
{
|
||||
{"type", "object"},
|
||||
{"properties", {
|
||||
{"key", {
|
||||
{"type", "string"},
|
||||
{"description", "The shared data key to read (e.g., 'mcp_logs', 'mcp_client_chat')"}
|
||||
}}
|
||||
}},
|
||||
{"required", {"key"}}
|
||||
},
|
||||
[](const json& params) -> json {
|
||||
std::string key = params.value("key", "");
|
||||
|
||||
if (key.empty())
|
||||
return {{"success", false}, {"error", "Key is required"}};
|
||||
|
||||
std::string value;
|
||||
bool exists = sElunaSharedData->Get(key, value);
|
||||
|
||||
return {
|
||||
{"success", true},
|
||||
{"key", key},
|
||||
{"exists", exists},
|
||||
{"value", exists ? value : ""}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// shared_data_write - Write to ElunaSharedData
|
||||
sMCPServer->RegisterTool(
|
||||
"shared_data_write",
|
||||
"Write data to ElunaSharedData. Lua scripts can read this.",
|
||||
{
|
||||
{"type", "object"},
|
||||
{"properties", {
|
||||
{"key", {
|
||||
{"type", "string"},
|
||||
{"description", "The shared data key to write"}
|
||||
}},
|
||||
{"value", {
|
||||
{"type", "string"},
|
||||
{"description", "The value to store (use JSON string for complex data)"}
|
||||
}}
|
||||
}},
|
||||
{"required", {"key", "value"}}
|
||||
},
|
||||
[](const json& params) -> json {
|
||||
std::string key = params.value("key", "");
|
||||
std::string value = params.value("value", "");
|
||||
|
||||
if (key.empty())
|
||||
return {{"success", false}, {"error", "Key is required"}};
|
||||
|
||||
sElunaSharedData->Set(key, value);
|
||||
|
||||
return {
|
||||
{"success", true},
|
||||
{"key", key},
|
||||
{"message", "Data written successfully"}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// shared_data_keys - List all shared data keys
|
||||
sMCPServer->RegisterTool(
|
||||
"shared_data_keys",
|
||||
"List all keys in ElunaSharedData.",
|
||||
{
|
||||
{"type", "object"},
|
||||
{"properties", json::object()}
|
||||
},
|
||||
[](const json& /*params*/) -> json {
|
||||
std::vector<std::string> keys = sElunaSharedData->GetKeys();
|
||||
|
||||
json keysJson = json::array();
|
||||
for (const auto& k : keys)
|
||||
keysJson.push_back(k);
|
||||
|
||||
return {
|
||||
{"success", true},
|
||||
{"keys", keysJson},
|
||||
{"count", keys.size()}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
TC_LOG_INFO("araxia.mcp", "[MCP] Server tools registered (server_info, player_list, gm_command, reload_scripts, log_search, shared_data_*)");
|
||||
}
|
||||
|
||||
} // namespace Araxia
|
||||
|
||||
Reference in New Issue
Block a user