@@ -9,7 +9,9 @@ namespace InfinityFlow.CSharp.Eval.Tools;
99/// <summary>
1010/// MCP tool for evaluating and executing C# scripts using Roslyn.
1111/// </summary>
12- internal class CSharpEvalTools
12+
13+ [ McpServerToolType ]
14+ public class CSharpEvalTools
1315{
1416 [ McpServerTool ]
1517 [ Description ( "Evaluates and executes C# script code and returns the output. Can either execute code directly or from a file." ) ]
@@ -29,7 +31,7 @@ public async Task<string> EvalCSharp(
2931 }
3032
3133 string scriptCode ;
32-
34+
3335 try
3436 {
3537 if ( ! string . IsNullOrWhiteSpace ( csxFile ) )
@@ -38,13 +40,13 @@ public async Task<string> EvalCSharp(
3840 try
3941 {
4042 var fullPath = Path . GetFullPath ( csxFile ) ;
41-
43+
4244 // Ensure the file has .csx extension
4345 if ( ! fullPath . EndsWith ( ".csx" , StringComparison . OrdinalIgnoreCase ) )
4446 {
4547 return $ "Error: Only .csx files are allowed. Provided: { csxFile } ";
4648 }
47-
49+
4850 // Optional: Restrict to specific directories for additional security
4951 // This can be configured via environment variable
5052 var allowedPath = Environment . GetEnvironmentVariable ( "CSX_ALLOWED_PATH" ) ;
@@ -56,12 +58,12 @@ public async Task<string> EvalCSharp(
5658 return $ "Error: File access is restricted to { normalizedAllowedPath } ";
5759 }
5860 }
59-
61+
6062 if ( ! File . Exists ( fullPath ) )
6163 {
6264 return $ "Error: File not found: { fullPath } ";
6365 }
64-
66+
6567 scriptCode = await File . ReadAllTextAsync ( fullPath ) ;
6668 }
6769 catch ( Exception ex )
@@ -101,29 +103,29 @@ public async Task<string> EvalCSharp(
101103 // Capture console output
102104 var originalOut = Console . Out ;
103105 var outputBuilder = new StringBuilder ( ) ;
104-
106+
105107 try
106108 {
107109 using var stringWriter = new StringWriter ( outputBuilder ) ;
108110 Console . SetOut ( stringWriter ) ;
109111
110112 // Execute the script with timeout
111113 using var cts = new CancellationTokenSource ( TimeSpan . FromSeconds ( timeoutSeconds ) ) ;
112-
114+
113115 // Run script in a task so we can properly handle timeout
114- var scriptTask = Task . Run ( async ( ) =>
115- await CSharpScript . EvaluateAsync ( scriptCode , scriptOptions , cancellationToken : cts . Token ) ,
116+ var scriptTask = Task . Run ( async ( ) =>
117+ await CSharpScript . EvaluateAsync ( scriptCode , scriptOptions , cancellationToken : cts . Token ) ,
116118 cts . Token ) ;
117-
119+
118120 var timeoutTask = Task . Delay ( TimeSpan . FromSeconds ( timeoutSeconds ) ) ;
119121 var completedTask = await Task . WhenAny ( scriptTask , timeoutTask ) ;
120-
122+
121123 if ( completedTask == timeoutTask )
122124 {
123125 cts . Cancel ( ) ;
124126 throw new OperationCanceledException ( ) ;
125127 }
126-
128+
127129 var result = await scriptTask ;
128130
129131 // Add the result value if it's not null
@@ -149,26 +151,26 @@ await CSharpScript.EvaluateAsync(scriptCode, scriptOptions, cancellationToken: c
149151 var errorBuilder = new StringBuilder ( ) ;
150152 errorBuilder . AppendLine ( "Compilation Error(s):" ) ;
151153 errorBuilder . AppendLine ( ) ;
152-
154+
153155 foreach ( var diagnostic in e . Diagnostics )
154156 {
155157 var lineSpan = diagnostic . Location . GetLineSpan ( ) ;
156158 var line = lineSpan . StartLinePosition . Line + 1 ; // Convert to 1-based
157159 var column = lineSpan . StartLinePosition . Character + 1 ;
158-
160+
159161 errorBuilder . AppendLine ( $ " Line { line } , Column { column } : { diagnostic . Id } - { diagnostic . GetMessage ( ) } ") ;
160-
162+
161163 // Try to show the problematic code if available
162164 if ( ! diagnostic . Location . IsInSource ) continue ;
163-
165+
164166 var sourceText = diagnostic . Location . SourceTree ? . GetText ( ) ;
165167 if ( sourceText != null )
166168 {
167169 var lineText = sourceText . Lines [ lineSpan . StartLinePosition . Line ] . ToString ( ) ;
168170 if ( ! string . IsNullOrWhiteSpace ( lineText ) )
169171 {
170172 errorBuilder . AppendLine ( $ " Code: { lineText . Trim ( ) } ") ;
171-
173+
172174 // Add a pointer to the error position
173175 if ( column > 0 && column <= lineText . Length )
174176 {
@@ -179,7 +181,7 @@ await CSharpScript.EvaluateAsync(scriptCode, scriptOptions, cancellationToken: c
179181 }
180182 errorBuilder . AppendLine ( ) ;
181183 }
182-
184+
183185 return errorBuilder . ToString ( ) . TrimEnd ( ) ;
184186 }
185187 catch ( OperationCanceledException )
@@ -191,7 +193,7 @@ await CSharpScript.EvaluateAsync(scriptCode, scriptOptions, cancellationToken: c
191193 var errorBuilder = new StringBuilder ( ) ;
192194 errorBuilder . AppendLine ( $ "Runtime Error: { e . GetType ( ) . Name } ") ;
193195 errorBuilder . AppendLine ( $ "Message: { e . Message } ") ;
194-
196+
195197 // Try to extract the line number from the stack trace if it's a script error
196198 if ( e . StackTrace != null && e . StackTrace . Contains ( "Submission#0" ) )
197199 {
@@ -209,16 +211,16 @@ await CSharpScript.EvaluateAsync(scriptCode, scriptOptions, cancellationToken: c
209211 }
210212 }
211213 }
212-
214+
213215 if ( e . InnerException != null )
214216 {
215217 errorBuilder . AppendLine ( $ "Inner Exception: { e . InnerException . GetType ( ) . Name } : { e . InnerException . Message } ") ;
216218 }
217-
219+
218220 errorBuilder . AppendLine ( ) ;
219221 errorBuilder . AppendLine ( "Stack Trace:" ) ;
220222 errorBuilder . AppendLine ( e . StackTrace ) ;
221-
223+
222224 return errorBuilder . ToString ( ) . TrimEnd ( ) ;
223225 }
224226 }
0 commit comments