feat: add execute_code tool for running arbitrary C# in Unity Editor#1001
feat: add execute_code tool for running arbitrary C# in Unity Editor#1001zaferdace wants to merge 3 commits intoCoplayDev:betafrom
Conversation
Adds a built-in `execute_code` tool that compiles and runs C# code inside the Unity Editor via CSharpCodeProvider. No external dependencies (Roslyn not required), no script files created. ## Actions - `execute` — compile and run C# method body, return result - `get_history` — list past executions with previews - `replay` — re-run a history entry with original settings - `clear_history` — clear execution history ## Safety - `safety_checks` (default: true) blocks known dangerous patterns (File.Delete, Process.Start, AssetDatabase.DeleteAsset, infinite loops) - Clearly documented as pattern-based blocklist, NOT a security sandbox - `destructiveHint=True` annotation for MCP clients ## Features - In-memory compilation with all loaded assembly references - User-friendly error line numbers (wrapper offset subtracted) - Execution history (max 50 entries) with code preview truncation - Replay preserves original safety_checks setting - CLI commands: `code execute`, `code history`, `code replay`, `code clear-history` ## Files - C#: `MCPForUnity/Editor/Tools/ExecuteCode.cs` (329 lines) - Python: `Server/src/services/tools/execute_code.py` (85 lines) - CLI: `Server/src/cli/commands/code.py` (+89 lines) - Tests: `Server/tests/test_execute_code.py` (17 tests, all passing) - Manifest: added `execute_code` entry Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reviewer's GuideIntroduces a new execute_code tool that compiles and runs arbitrary C# in the Unity Editor with safety checks and history, wires it through the MCP server, and exposes it via new CLI subcommands and manifest registration. Sequence diagram for replay action using historysequenceDiagram
actor cli_user
participant CLI as CLI_code_command
participant Server as Server_execute_code_tool
participant Transport as Unity_transport
participant Editor as Unity_Editor
participant Tool as ExecuteCode
cli_user->>CLI: unity-mcp code replay 0
CLI->>Server: run_command execute_code {action: replay, index: 0}
Server->>Transport: async_send_command_with_retry execute_code params
Transport->>Editor: JSON RPC execute_code params
Editor->>Tool: HandleCommand(JObject params)
Tool->>Tool: HandleReplay(params)
Tool->>Tool: lookup HistoryEntry by index
Tool->>Tool: HandleExecute(replayParams)
Tool->>Tool: CompileAndExecute(stored code)
Tool-->>Tool: SuccessResponse or ErrorResponse
Tool-->>Editor: response
Editor-->>Transport: response
Transport-->>Server: response dict
Server-->>CLI: {success, message, data}
CLI-->>cli_user: formatted output + Result line
Class diagram for new ExecuteCode tool and history entriesclassDiagram
class ExecuteCode {
<<static>>
-int MaxCodeLength
-int MaxHistoryEntries
-int MaxHistoryCodePreview
-int WrapperLineOffset
-string WrapperClassName
-string WrapperMethodName
-string ActionExecute
-string ActionGetHistory
-string ActionClearHistory
-string ActionReplay
-List~HistoryEntry~ _history
-HashSet~string~ _blockedPatterns
+object HandleCommand(JObject params)
-object HandleExecute(JObject params)
-object HandleGetHistory(JObject params)
-object HandleClearHistory()
-object HandleReplay(JObject params)
-object CompileAndExecute(string code)
-string WrapUserCode(string code)
-void AddReferences(CompilerParameters parameters)
-string CheckBlockedPatterns(string code)
-void AddToHistory(string code, object result, double elapsedMs, bool safetyChecks)
-object SerializeResult(object result)
}
class HistoryEntry {
+string code
+bool success
+string resultPreview
+double elapsedMs
+string timestamp
+bool safetyChecksEnabled
}
ExecuteCode "1" o-- "*" HistoryEntry
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughNew Unity Editor tool Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as CLI Client
participant Server as MCP Server
participant Tool as execute_code Tool
participant Unity as Unity Editor<br/>(ExecuteCode)
CLI->>Server: code execute --source "..."
Server->>Tool: execute_code(action="execute", code="...", safety_checks=True)
Tool->>Tool: validate & isolate params
Tool->>Unity: send_with_unity_instance(tool_name="execute_code", params={...})
Unity->>Unity: safety checks, wrap code, compile via CSharpCodeProvider
Unity->>Unity: invoke compiled method, capture result/exception
Unity-->>Tool: SuccessResponse / ErrorResponse
Tool->>Server: normalized {success, message, data}
Server->>CLI: formatted output (Result if success)
sequenceDiagram
participant CLI as CLI Client
participant Server as MCP Server
participant Tool as execute_code Tool
participant Unity as Unity Editor<br/>(ExecuteCode)
CLI->>Server: code history --limit 5
Server->>Tool: execute_code(action="get_history", limit=5)
Tool->>Tool: clamp limit to [1,50]
Tool->>Unity: send_with_unity_instance(tool_name="execute_code", params={action, limit})
Unity->>Unity: retrieve _history slice and format entries
Unity-->>Tool: SuccessResponse with history
Tool->>Server: normalized dict
Server->>CLI: print history entries
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The hard-coded
WrapperLineOffset = 9is brittle since it depends on the exact structure ofWrapUserCode; consider computing the offset dynamically (e.g., by counting wrapper lines) so compiler error line mapping stays correct if the wrapper changes. - In
AddReferences, walking allAppDomain.CurrentDomain.GetAssemblies()and adding locations on every execution could become expensive; consider caching the resolved reference list in a static field so subsequent compilations reuse it.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The hard-coded `WrapperLineOffset = 9` is brittle since it depends on the exact structure of `WrapUserCode`; consider computing the offset dynamically (e.g., by counting wrapper lines) so compiler error line mapping stays correct if the wrapper changes.
- In `AddReferences`, walking all `AppDomain.CurrentDomain.GetAssemblies()` and adding locations on every execution could become expensive; consider caching the resolved reference list in a static field so subsequent compilations reuse it.
## Individual Comments
### Comment 1
<location path="MCPForUnity/Editor/Tools/ExecuteCode.cs" line_range="20-29" />
<code_context>
+ private const int WrapperLineOffset = 9;
</code_context>
<issue_to_address>
**issue (bug_risk):** WrapperLineOffset appears off by one, which will skew reported user line numbers.
User code begins at physical line 11, but WrapperLineOffset is set to 9. That makes an error on the first user line map to 2 instead of 1 (11 - 9), so all reported error lines are off by one. Please update WrapperLineOffset to 10, or compute it directly from WrapUserCode so it stays correct if the wrapper changes.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@MCPForUnity/Editor/Tools/ExecuteCode.cs`:
- Line 20: The WrapperLineOffset constant is off-by-one; update the value of
WrapperLineOffset (used to adjust compiler error line numbers) from 9 to 10 so
it accounts for the full 10-line wrapper header generated by the template (the
wrapper that declares MCPDynamicCode and Execute). Locate the constant named
WrapperLineOffset and change its value to 10, then run a test compiling a sample
user snippet that triggers an error on the first user line to verify the
reported line now matches the user's first line.
In `@Server/src/cli/commands/code.py`:
- Around line 38-40: The file-reading branch uses a plain open(...) call which
relies on platform default encoding; change the open in the CLI command where it
reads into variable "source" (the with open(file, "r") as f: block) to
explicitly set the encoding (e.g., encoding="utf-8") so non-ASCII characters are
handled consistently across platforms; keep the same read() logic and consider
adding errors="replace" if you want to tolerate malformed bytes.
In `@Server/src/services/tools/execute_code.py`:
- Around line 21-34: The mcp_for_unity_tool decorator invocation for the Execute
Code tool is missing the required group parameter; update the decorator call
(mcp_for_unity_tool) to include group='scripting_ext' (or another allowed group
from 'core','vfx','animation','ui','scripting_ext','probuilder','testing') so
the tool is properly categorized, keeping the existing description and
ToolAnnotations (title="Execute Code", destructiveHint=True) intact; ensure the
new group argument is added at the top-level decorator arguments alongside
description and annotations.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 096cf8a3-00d7-4c55-87a7-da20c45d6e63
📒 Files selected for processing (6)
MCPForUnity/Editor/Tools/ExecuteCode.csMCPForUnity/Editor/Tools/ExecuteCode.cs.metaServer/src/cli/commands/code.pyServer/src/services/tools/execute_code.pyServer/tests/test_execute_code.pymanifest.json
- Fix off-by-one in WrapperLineOffset (9 → 10) - Cache resolved assembly paths in static field for performance - Add encoding="utf-8" to CLI file read - Add group="scripting_ext" to Python tool decorator Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
Server/src/cli/commands/code.py (1)
75-92: Consider extracting result printing helper.The result printing logic (lines 89-92) is duplicated from the
executecommand (lines 53-56). This is minor given it's only 4 lines.♻️ Optional: Extract helper function
def _print_execution_result(result: dict[str, Any]) -> None: """Print execution result if present.""" if result.get("success"): data = result.get("data", {}) if data and data.get("result") is not None: print_success(f"Result: {data['result']}")Then use in both commands:
click.echo(format_output(result, config.format)) - if result.get("success"): - data = result.get("data", {}) - if data and data.get("result") is not None: - print_success(f"Result: {data['result']}") + _print_execution_result(result)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Server/src/cli/commands/code.py` around lines 75 - 92, The replay command duplicates result-printing logic found in execute; extract a small helper (e.g., _print_execution_result(result: dict[str, Any])) that encapsulates the existing conditional printing logic and call it from both replay() and execute() to remove duplication; locate the two blocks in replay (function replay) and execute (function execute) and replace the repeated lines that check result.get("success") / data.get("result") with a call to _print_execution_result(result).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@Server/src/cli/commands/code.py`:
- Around line 75-92: The replay command duplicates result-printing logic found
in execute; extract a small helper (e.g., _print_execution_result(result:
dict[str, Any])) that encapsulates the existing conditional printing logic and
call it from both replay() and execute() to remove duplication; locate the two
blocks in replay (function replay) and execute (function execute) and replace
the repeated lines that check result.get("success") / data.get("result") with a
call to _print_execution_result(result).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b1318bce-6322-422c-abd5-cf7f754d5ed5
📒 Files selected for processing (3)
MCPForUnity/Editor/Tools/ExecuteCode.csServer/src/cli/commands/code.pyServer/src/services/tools/execute_code.py
🚧 Files skipped from review as they are similar to previous changes (1)
- MCPForUnity/Editor/Tools/ExecuteCode.cs
Address CodeRabbit nitpick — deduplicate result printing logic between execute and replay commands. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Adds a built-in
execute_codetool that compiles and runs arbitrary C# code inside the Unity Editor. UsesCSharpCodeProviderfor in-memory compilation — no Roslyn dependency, no script files created.This fills the gap between
execute_menu_item(limited to menu paths) and theruntime_compilationcustom tool (requires Roslyn + opt-in setup).execute_codeworks out of the box with zero configuration.Actions
executeget_historyreplayclear_historySafety
safety_checks(default:true) blocks known dangerous patterns:File.Delete,Process.Start,AssetDatabase.DeleteAsset,EditorApplication.Exit, infinite loops (while(true),for(;;))destructiveHint=Trueannotation for MCP clientssafety_checks=falsefor trusted workflowsExample usage
CLI
Files
MCPForUnity/Editor/Tools/ExecuteCode.csServer/src/services/tools/execute_code.pyServer/tests/test_execute_code.pyServer/src/cli/commands/code.pymanifest.jsonTest plan
uv run python -m pytest tests/test_execute_code.py -v)Design decisions
Camera.main,Selection, etc. Infinite loop prevention is handled by safety_checks pattern matching instead.safety_checksnotsandbox: Deliberately named to avoid implying stronger protection than a pattern blocklist provides.🤖 Generated with Claude Code
Summary by Sourcery
Add a new execute_code tool for running arbitrary C# code in the Unity Editor, with history and safety controls, and expose it through the MCP service and CLI.
New Features:
Enhancements:
Tests:
Summary by CodeRabbit
code execute,code history,code replay,code clear-history.