这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"mcpServers": {
"csharp-mcp": {
"type": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/infinityflowapp/csharp-mcp:latest"
],
"env": {}
}
}
}
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,76 @@ The following namespaces are automatically available:

## MCP Configuration

### Cursor

Add to your Cursor settings (`.cursor/mcp_settings.json` or via Settings UI):

```json
{
"mcpServers": {
"csharp-eval": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/infinityflowapp/csharp-mcp:latest"],
"env": {
"CSX_ALLOWED_PATH": "/scripts"
}
}
}
}
```

Or if installed as a dotnet tool:

```json
{
"mcpServers": {
"csharp-eval": {
"command": "infinityflow-csharp-eval",
"env": {
"CSX_ALLOWED_PATH": "${workspaceFolder}/scripts"
}
}
}
}
```

### Claude Code

Add to your Claude Code configuration (`claude_desktop_config.json`):

- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **Linux**: `~/.config/claude/claude_desktop_config.json`

```json
{
"mcpServers": {
"csharp-eval": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/infinityflowapp/csharp-mcp:latest"],
"env": {
"CSX_ALLOWED_PATH": "/scripts"
}
}
}
}
```

Or if installed as a dotnet tool:

```json
{
"mcpServers": {
"csharp-eval": {
"command": "infinityflow-csharp-eval",
"env": {
"CSX_ALLOWED_PATH": "/Users/your-username/scripts"
}
}
}
}
```

### VS Code

Create `.vscode/mcp.json`:
Expand Down
48 changes: 25 additions & 23 deletions src/InfinityFlow.CSharp.Eval/Tools/CSharpEvalTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ namespace InfinityFlow.CSharp.Eval.Tools;
/// <summary>
/// MCP tool for evaluating and executing C# scripts using Roslyn.
/// </summary>
internal class CSharpEvalTools

[McpServerToolType]
public class CSharpEvalTools
{
[McpServerTool]
[Description("Evaluates and executes C# script code and returns the output. Can either execute code directly or from a file.")]
Expand All @@ -29,7 +31,7 @@ public async Task<string> EvalCSharp(
}

string scriptCode;

try
{
if (!string.IsNullOrWhiteSpace(csxFile))
Expand All @@ -38,13 +40,13 @@ public async Task<string> EvalCSharp(
try
{
var fullPath = Path.GetFullPath(csxFile);

// Ensure the file has .csx extension
if (!fullPath.EndsWith(".csx", StringComparison.OrdinalIgnoreCase))
{
return $"Error: Only .csx files are allowed. Provided: {csxFile}";
}

// Optional: Restrict to specific directories for additional security
// This can be configured via environment variable
var allowedPath = Environment.GetEnvironmentVariable("CSX_ALLOWED_PATH");
Expand All @@ -56,12 +58,12 @@ public async Task<string> EvalCSharp(
return $"Error: File access is restricted to {normalizedAllowedPath}";
}
}

if (!File.Exists(fullPath))
{
return $"Error: File not found: {fullPath}";
}

scriptCode = await File.ReadAllTextAsync(fullPath);
}
catch (Exception ex)
Expand Down Expand Up @@ -101,29 +103,29 @@ public async Task<string> EvalCSharp(
// Capture console output
var originalOut = Console.Out;
var outputBuilder = new StringBuilder();

try
{
using var stringWriter = new StringWriter(outputBuilder);
Console.SetOut(stringWriter);

// Execute the script with timeout
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));

// Run script in a task so we can properly handle timeout
var scriptTask = Task.Run(async () =>
await CSharpScript.EvaluateAsync(scriptCode, scriptOptions, cancellationToken: cts.Token),
var scriptTask = Task.Run(async () =>
await CSharpScript.EvaluateAsync(scriptCode, scriptOptions, cancellationToken: cts.Token),
cts.Token);

var timeoutTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds));
var completedTask = await Task.WhenAny(scriptTask, timeoutTask);

if (completedTask == timeoutTask)
{
cts.Cancel();
throw new OperationCanceledException();
}

var result = await scriptTask;

// Add the result value if it's not null
Expand All @@ -149,26 +151,26 @@ await CSharpScript.EvaluateAsync(scriptCode, scriptOptions, cancellationToken: c
var errorBuilder = new StringBuilder();
errorBuilder.AppendLine("Compilation Error(s):");
errorBuilder.AppendLine();

foreach (var diagnostic in e.Diagnostics)
{
var lineSpan = diagnostic.Location.GetLineSpan();
var line = lineSpan.StartLinePosition.Line + 1; // Convert to 1-based
var column = lineSpan.StartLinePosition.Character + 1;

errorBuilder.AppendLine($" Line {line}, Column {column}: {diagnostic.Id} - {diagnostic.GetMessage()}");

// Try to show the problematic code if available
if (!diagnostic.Location.IsInSource) continue;

var sourceText = diagnostic.Location.SourceTree?.GetText();
if (sourceText != null)
{
var lineText = sourceText.Lines[lineSpan.StartLinePosition.Line].ToString();
if (!string.IsNullOrWhiteSpace(lineText))
{
errorBuilder.AppendLine($" Code: {lineText.Trim()}");

// Add a pointer to the error position
if (column > 0 && column <= lineText.Length)
{
Expand All @@ -179,7 +181,7 @@ await CSharpScript.EvaluateAsync(scriptCode, scriptOptions, cancellationToken: c
}
errorBuilder.AppendLine();
}

return errorBuilder.ToString().TrimEnd();
}
catch (OperationCanceledException)
Expand All @@ -191,7 +193,7 @@ await CSharpScript.EvaluateAsync(scriptCode, scriptOptions, cancellationToken: c
var errorBuilder = new StringBuilder();
errorBuilder.AppendLine($"Runtime Error: {e.GetType().Name}");
errorBuilder.AppendLine($"Message: {e.Message}");

// Try to extract the line number from the stack trace if it's a script error
if (e.StackTrace != null && e.StackTrace.Contains("Submission#0"))
{
Expand All @@ -209,16 +211,16 @@ await CSharpScript.EvaluateAsync(scriptCode, scriptOptions, cancellationToken: c
}
}
}

if (e.InnerException != null)
{
errorBuilder.AppendLine($"Inner Exception: {e.InnerException.GetType().Name}: {e.InnerException.Message}");
}

errorBuilder.AppendLine();
errorBuilder.AppendLine("Stack Trace:");
errorBuilder.AppendLine(e.StackTrace);

return errorBuilder.ToString().TrimEnd();
}
}
Expand Down
Loading