Skip to content

Commit 61ebbab

Browse files
committed
brought back 2 tools
1 parent 4705292 commit 61ebbab

11 files changed

Lines changed: 142 additions & 3 deletions

File tree

.claude/agents/mcp-server-engineer.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Request flow: `HttpListener` accepts on the thread pool → detached `UniTaskVoi
4444
1. One class in `Tools/`, implementing `IMcpTool` (`Name` snake_case, 1–2 sentence `Description` written for an agent, `InputSchemaJson` as a verbatim JSON Schema string, `internal` constructor).
4545
2. Parse args with the `McpToolArgs` extensions; validate before switching threads; expected failures return `McpToolResult.Error(...)` (never throw — JSON-RPC errors are for protocol-level failures only).
4646
3. Register it in `McpServerPlugin.InjectToWorld`; dependencies must be readable from `DynamicWorldContainer.CreateAsync` scope (never mutate containers).
47-
4. ECS writes go through **intent components** — reuse `GlobalWorldActions` (`MoveAndRotatePlayerAsync`, `RotateCamera`) or `IChatMessagesBus` / `ECSReloadScene` / `IWorldInfoHub` before inventing anything. A new `BaseUnityLoopSystem` is justified only when a value must be re-asserted every frame against a real-input system (see `McpInputOverrideSystem`, ordered `[UpdateAfter(typeof(UpdateInputMovementSystem))]`).
47+
4. ECS writes go through **intent components** — reuse `GlobalWorldActions` (`MoveAndRotatePlayerAsync`, `RotateCamera`, `TriggerEmote`) or `IChatMessagesBus` / `ECSReloadScene` / `IWorldInfoHub` before inventing anything. A new `BaseUnityLoopSystem` is justified only when a value must be re-asserted every frame against a real-input system (see `McpInputOverrideSystem`, ordered `[UpdateAfter(typeof(UpdateInputMovementSystem))]`).
4848
5. Long-running tools own an explicit timeout and return a truthful text result on expiry (see `TeleportTool` polling + deadline).
4949
6. Update **all three** agent-facing surfaces: tool catalog in `docs/mcp-automation.md`, `docs/app-arguments.md` if flags changed, and the skill if the loop changes.
5050

.claude/skills/mcp-scene-iteration/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Repeat until **every requirement has proof**: a screenshot or state read demonst
7878
2. **Confirm the scene is healthy**: `get_scene_state` — a `state` of `JavaScriptError` or `EcsError` means your code crashed the scene runtime.
7979
3. **Read the runtime output**: `get_scene_logs` with `sinceSeq` set to the last sequence number you saw. Scene `console.log` output and exceptions land here.
8080
4. **Look and verify**: position the view (`teleport`, `move_to`, `walk`, `look_at` — for precise framing or free-camera sweeps read [`reference/camera-and-movement.md`](reference/camera-and-movement.md)), then `screenshot` and inspect the image against what the scene code should produce.
81-
5. **Exercise behavior**: `walk` into trigger areas, `click_entity` on interactables, and re-screenshot to verify reactions. `list_scene_entities` + `get_entity_details` show the scene's ECS state when visuals aren't enough.
81+
5. **Exercise behavior**: `walk` into trigger areas, `click_entity` on interactables, `send_chat` for commands, `trigger_emote`, and re-screenshot to verify reactions. `list_scene_entities` + `get_entity_details` show the scene's ECS state when visuals aren't enough.
8282

8383
**Cross-examine** every conclusion: confirm each visual claim with a state read (ECS values via `get_entity_details`, logs, `get_player_state` position), and each state claim with pixels. One channel lies routinely — colliders exist that pixels don't show, entities render invisible while their state looks healthy, animations silently don't play. The reference files call out where cross-examination is mandatory.
8484

Explorer/Assets/DCL/McpServer/DCL.McpServer.asmdef

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"GUID:571dc9f8bded0034f98595106462e3d0",
1818
"GUID:0df5180c0c3a0594fbfa11f83736de9f",
1919
"GUID:3c7b57a14671040bd8c549056adc04f5",
20-
"GUID:f51ebe6a0ceec4240a699833d6309b23"
20+
"GUID:f51ebe6a0ceec4240a699833d6309b23",
21+
"GUID:e25ef972de004615a22937e739de2def"
2122
],
2223
"includePlatforms": [],
2324
"excludePlatforms": [],

Explorer/Assets/DCL/McpServer/Systems/McpServerPlugin.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,11 @@ public void InjectToWorld(ref ArchSystemsWorldBuilder<Arch.Core.World> builder,
124124
.Add(new SetCameraModeTool(globalWorld, exposedCameraData))
125125
.Add(new SetCameraPoseTool(globalWorld, arguments.PlayerEntity, exposedCameraData))
126126
.Add(new WalkTool(globalWorld, arguments.PlayerEntity))
127+
.Add(new SendChatTool(chatMessagesBus))
127128
.Add(new ReloadSceneTool(reloadSceneController, scenesCache, globalWorld, arguments.PlayerEntity, arguments.SkyboxEntity))
128129
.Add(new ListSceneEntitiesTool(worldInfoHub))
129130
.Add(new GetEntityDetailsTool(worldInfoHub))
131+
.Add(new TriggerEmoteTool(globalWorldActions))
130132
.Add(new ClickEntityTool(globalWorld, arguments.PlayerEntity))
131133
.Build();
132134

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using Cysharp.Threading.Tasks;
2+
using DCL.Chat.History;
3+
using DCL.Chat.MessageBus;
4+
using DCL.McpServer.Core;
5+
using Newtonsoft.Json.Linq;
6+
using System.Threading;
7+
8+
namespace DCL.McpServer.Tools
9+
{
10+
/// <summary>
11+
/// Sends a message to the Nearby chat channel. Messages starting with '/' run through the chat
12+
/// command pipeline, so agents can drive commands the same way a user typing in chat would.
13+
/// </summary>
14+
public class SendChatTool : IMcpTool
15+
{
16+
private const int MAX_MESSAGE_LENGTH = 500;
17+
18+
private readonly IChatMessagesBus chatMessagesBus;
19+
20+
public string Name => "send_chat";
21+
22+
public string Description =>
23+
"Send a message to the Nearby chat channel. Messages starting with '/' run chat commands "
24+
+ "(e.g. /goto x,y, /reload, /help); command output appears in chat and scene logs.";
25+
26+
public JObject InputSchema =>
27+
McpInputSchema.Object()
28+
.String("message", "The chat message or /command to send.", required: true)
29+
.Build();
30+
31+
public McpToolAnnotations Annotations => McpToolAnnotations.Mutating(destructive: false, idempotent: false);
32+
33+
public SendChatTool(IChatMessagesBus chatMessagesBus)
34+
{
35+
this.chatMessagesBus = chatMessagesBus;
36+
}
37+
38+
public async UniTask<McpToolResult> ExecuteAsync(JObject arguments, CancellationToken ct)
39+
{
40+
string message = arguments.GetString("message", string.Empty);
41+
42+
if (string.IsNullOrWhiteSpace(message))
43+
return McpToolResult.Error("message is required.");
44+
45+
if (message.Length > MAX_MESSAGE_LENGTH)
46+
return McpToolResult.Error($"message exceeds the {MAX_MESSAGE_LENGTH} character limit.");
47+
48+
await UniTask.SwitchToMainThread(ct);
49+
50+
chatMessagesBus.SendWithUtcNowTimestamp(ChatChannel.NEARBY_CHANNEL, message, ChatMessageOrigin.CHAT);
51+
52+
return McpToolResult.Text($"Sent to Nearby: {message}");
53+
}
54+
}
55+
}

Explorer/Assets/DCL/McpServer/Tools/SendChatTool.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using CrdtEcsBridge.RestrictedActions;
2+
using Cysharp.Threading.Tasks;
3+
using DCL.ECSComponents;
4+
using DCL.McpServer.Core;
5+
using Newtonsoft.Json.Linq;
6+
using System.Threading;
7+
8+
namespace DCL.McpServer.Tools
9+
{
10+
/// <summary>
11+
/// Plays or stops an avatar emote through <see cref="IGlobalWorldActions" />, the same intent path
12+
/// a scene uses, so agents can verify emote-driven behaviour without touching the avatar directly.
13+
/// </summary>
14+
public class TriggerEmoteTool : IMcpTool
15+
{
16+
private readonly IGlobalWorldActions globalWorldActions;
17+
18+
public string Name => "trigger_emote";
19+
20+
public string Description =>
21+
"Play an avatar emote by URN (e.g. a base emote like 'wave', 'dance', 'clap'), or stop the current one with stop: true.";
22+
23+
public JObject InputSchema =>
24+
McpInputSchema.Object()
25+
.String("urn", "Emote URN or base emote id (wave, dance, clap...).")
26+
.Boolean("loop", "Loop the emote until stopped. Default false.")
27+
.Boolean("stop", "Stop the currently playing emote instead of triggering one.")
28+
.Build();
29+
30+
public McpToolAnnotations Annotations => McpToolAnnotations.Mutating(destructive: false, idempotent: false);
31+
32+
public TriggerEmoteTool(IGlobalWorldActions globalWorldActions)
33+
{
34+
this.globalWorldActions = globalWorldActions;
35+
}
36+
37+
public async UniTask<McpToolResult> ExecuteAsync(JObject arguments, CancellationToken ct)
38+
{
39+
bool stop = arguments.GetBool("stop", false);
40+
string urn = arguments.GetString("urn", string.Empty);
41+
42+
if (!stop && string.IsNullOrEmpty(urn))
43+
return McpToolResult.Error("urn is required (or pass stop: true).");
44+
45+
await UniTask.SwitchToMainThread(ct);
46+
47+
if (stop)
48+
{
49+
globalWorldActions.StopEmote();
50+
return McpToolResult.Text("Emote stopped.");
51+
}
52+
53+
bool loop = arguments.GetBool("loop", false);
54+
globalWorldActions.TriggerEmote(urn, loop, AvatarEmoteMask.AemFullBody);
55+
56+
return McpToolResult.Text($"Emote '{urn}' triggered{(loop ? " (looping)" : string.Empty)}.");
57+
}
58+
}
59+
}

Explorer/Assets/DCL/McpServer/Tools/TriggerEmoteTool.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Explorer/Assets/DCL/PerformanceAndDiagnostics/Diagnostics/ReportsHandling/ReportsHandlingSettingsDevelopment.asset

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,8 @@ MonoBehaviour:
716716
Severity: 4
717717
- Category: MCP
718718
Severity: 1
719+
- Category: MCP
720+
Severity: 2
719721
sentryMatrix:
720722
entries: []
721723
debounceEnabled: 1

Explorer/Assets/DCL/PerformanceAndDiagnostics/Diagnostics/ReportsHandling/ReportsHandlingSettingsProduction.asset

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,16 @@ MonoBehaviour:
448448
Severity: 0
449449
- Category: MULTIPLAYER
450450
Severity: 2
451+
- Category: MCP
452+
Severity: 4
453+
- Category: MCP
454+
Severity: 1
455+
- Category: MCP
456+
Severity: 3
457+
- Category: MCP
458+
Severity: 2
459+
- Category: MCP
460+
Severity: 0
451461
sentryMatrix:
452462
entries:
453463
- Category: AUDIO_SOURCES

0 commit comments

Comments
 (0)