Skip to content

Commit 9a12e48

Browse files
authored
add corerun build tool for MCP server (#4938)
* add corerun build tool for mcp server * add version control tools * add exception handling * handle error * return for all branches * improve prompts for corerun build tools
1 parent 59aa0ec commit 9a12e48

File tree

2 files changed

+228
-0
lines changed

2 files changed

+228
-0
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
using System.ComponentModel;
2+
using System.Diagnostics;
3+
using ModelContextProtocol.Server;
4+
5+
namespace GC.Infrastructure.MCPServer
6+
{
7+
[McpServerToolType]
8+
internal class CoreRun
9+
{
10+
private static readonly string[] ValidBuildConfigs = new string[] { "Debug", "Release", "Checked" };
11+
private static readonly string[] ValidArchs = new string[] { "x64", "x86", "arm64" };
12+
13+
[McpServerTool(Name = "build_clr_libs"), Description("Builds the CoreCLR runtime and base class libraries for the .NET runtime. This is a prerequisite step before generating CoreRun executables for performance testing and benchmarking.")]
14+
public async Task<string> BuildCLRAndLibs(
15+
[Description("The absolute path to the root directory of the .NET runtime repository (e.g., 'C:\\runtime'). This should contain the build.cmd script.")] string runtimeRoot,
16+
[Description("The build configuration for CoreCLR compilation. Debug includes debugging symbols and assertions, Release is optimized for performance, and Checked includes some debugging features with optimizations. Valid options: Debug, Release, Checked.")] string buildConfig,
17+
[Description("The target CPU architecture for the build. Determines which instruction set and calling conventions to use. Valid options: x64 (64-bit Intel/AMD), x86 (32-bit Intel/AMD), arm64 (64-bit ARM).")] string arch = "x64")
18+
{
19+
if (!ValidBuildConfigs.Contains(buildConfig))
20+
{
21+
return $"Invalid build configuration: '{buildConfig}'. Valid options are: {string.Join(", ", ValidBuildConfigs)}. Choose Debug for development with full debugging info, Release for optimized production builds, or Checked for optimized builds with some debugging features.";
22+
}
23+
if (!ValidArchs.Contains(arch))
24+
{
25+
return $"Invalid architecture: '{arch}'. Valid options are: {string.Join(", ", ValidArchs)}. Use x64 for 64-bit Intel/AMD, x86 for 32-bit Intel/AMD, or arm64 for 64-bit ARM processors.";
26+
}
27+
28+
string fileName = "cmd.exe";
29+
string arguments = $"/C build.cmd clr+libs -runtimeConfiguration {buildConfig} -librariesConfiguration Release -arch {arch}";
30+
try
31+
{
32+
bool isSuccess = true;
33+
using (var process = new Process())
34+
{
35+
process.StartInfo.FileName = fileName;
36+
process.StartInfo.Arguments = arguments;
37+
process.StartInfo.RedirectStandardOutput = true;
38+
process.StartInfo.RedirectStandardError = true;
39+
process.StartInfo.UseShellExecute = false;
40+
process.StartInfo.CreateNoWindow = true;
41+
process.StartInfo.WorkingDirectory = runtimeRoot;
42+
process.OutputDataReceived += (sender, e) =>
43+
{
44+
if (!string.IsNullOrEmpty(e.Data))
45+
{
46+
if (e.Data.ToLower().Contains("build failed with exit code"))
47+
{
48+
isSuccess = false;
49+
}
50+
}
51+
};
52+
process.ErrorDataReceived += (sender, e) =>
53+
{
54+
if (!string.IsNullOrEmpty(e.Data))
55+
{
56+
if (e.Data.ToLower().Contains("build failed with exit code"))
57+
{
58+
isSuccess = false;
59+
}
60+
}
61+
};
62+
process.Start();
63+
process.BeginOutputReadLine();
64+
process.BeginErrorReadLine();
65+
await process.WaitForExitAsync();
66+
67+
if (!isSuccess)
68+
{
69+
return "Failed to build CoreCLR and libraries. Please check the build log output above for detailed error information. Common issues include missing dependencies, incorrect paths, or insufficient permissions.";
70+
}
71+
else
72+
{
73+
return "Successfully built CoreCLR and libraries. The runtime components are now available for generating CoreRun executables.";
74+
}
75+
}
76+
}
77+
catch (Exception ex)
78+
{
79+
return $"Failed to execute build command '{fileName} {arguments}'. Error: {ex.Message}. Please verify the runtime root path is correct and the build.cmd script exists.";
80+
}
81+
}
82+
83+
[McpServerTool(Name = "generate_corerun"), Description("Generates a CoreRun executable for the .NET runtime. CoreRun is a lightweight host that can run .NET applications without the full SDK, commonly used for performance testing, benchmarking, and isolated runtime scenarios.")]
84+
public async Task<string> GenerateCoreRun(
85+
[Description("The absolute path to the root directory of the .NET runtime repository (e.g., 'C:\\runtime'). This should contain the build.cmd script.")] string runtimeRoot,
86+
[Description("The build configuration for CoreCLR compilation. Debug includes debugging symbols and assertions, Release is optimized for performance, and Checked includes some debugging features with optimizations. Valid options: Debug, Release, Checked.")] string buildConfig,
87+
[Description("The target CPU architecture for the build. Determines which instruction set and calling conventions to use. Valid options: x64 (64-bit Intel/AMD), x86 (32-bit Intel/AMD), arm64 (64-bit ARM).")] string arch = "x64")
88+
{
89+
if (!ValidBuildConfigs.Contains(buildConfig))
90+
{
91+
return $"Invalid build configuration: '{buildConfig}'. Valid options are: {string.Join(", ", ValidBuildConfigs)}. Choose Debug for development with full debugging info, Release for optimized production builds, or Checked for optimized builds with some debugging features.";
92+
}
93+
if (!ValidArchs.Contains(arch))
94+
{
95+
return $"Invalid architecture: '{arch}'. Valid options are: {string.Join(", ", ValidArchs)}. Use x64 for 64-bit Intel/AMD, x86 for 32-bit Intel/AMD, or arm64 for 64-bit ARM processors.";
96+
}
97+
98+
string workingDirectory = Path.Combine(runtimeRoot, "src", "tests");
99+
if (!Directory.Exists(workingDirectory))
100+
{
101+
return $"The required directory '{workingDirectory}' does not exist. Please ensure you have a complete .NET runtime repository with the src/tests folder.";
102+
}
103+
string fileName = "cmd.exe";
104+
string arguments = $"/C build.cmd generatelayoutonly {arch} {buildConfig}";
105+
try
106+
{
107+
bool isSuccess = true;
108+
using (var process = new Process())
109+
{
110+
process.StartInfo.FileName = fileName;
111+
process.StartInfo.Arguments = arguments;
112+
process.StartInfo.RedirectStandardOutput = true;
113+
process.StartInfo.RedirectStandardError = true;
114+
process.StartInfo.UseShellExecute = false;
115+
process.StartInfo.CreateNoWindow = true;
116+
process.StartInfo.WorkingDirectory = workingDirectory;
117+
process.OutputDataReceived += (sender, e) =>
118+
{
119+
if (!string.IsNullOrEmpty(e.Data))
120+
{
121+
if (e.Data.ToLower().Contains("build failed with exit code"))
122+
{
123+
isSuccess = false;
124+
}
125+
}
126+
};
127+
process.ErrorDataReceived += (sender, e) =>
128+
{
129+
if (!string.IsNullOrEmpty(e.Data))
130+
{
131+
if (e.Data.ToLower().Contains("build failed with exit code"))
132+
{
133+
isSuccess = false;
134+
}
135+
}
136+
};
137+
process.Start();
138+
process.BeginOutputReadLine();
139+
process.BeginErrorReadLine();
140+
await process.WaitForExitAsync();
141+
142+
if (!isSuccess)
143+
{
144+
return "Failed to generate CoreRun executable. Please check the build log output above for detailed error information. Ensure that CoreCLR and libraries were built successfully first using the build_clr_libs tool.";
145+
}
146+
else
147+
{
148+
return $"Successfully generated CoreRun executable. You can find the CoreRun.exe in the test layout directory for {arch} {buildConfig} configuration.";
149+
}
150+
}
151+
}
152+
catch (Exception ex)
153+
{
154+
return $"Failed to execute CoreRun generation command '{fileName} {arguments}'. Error: {ex.Message}. Please verify the runtime root path is correct and the src/tests/build.cmd script exists.";
155+
}
156+
}
157+
}
158+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using ModelContextProtocol.Server;
2+
using System.ComponentModel;
3+
using System.Diagnostics;
4+
5+
namespace GC.Infrastructure.MCPServer
6+
{
7+
[McpServerToolType]
8+
internal class VersionControl
9+
{
10+
[McpServerTool(Name = "checkout_branch"), Description("Switch to specific branch.")]
11+
public async Task<string> CheckoutBranch(
12+
[Description("The root directory of the runtime.")] string runtimeRoot,
13+
[Description("The name of branch.")] string branchName)
14+
{
15+
string fileName = "git";
16+
string arguments = $"checkout {branchName}";
17+
try
18+
{
19+
bool isSuccess = true;
20+
using (var process = new Process())
21+
{
22+
process.StartInfo.FileName = fileName;
23+
process.StartInfo.Arguments = arguments;
24+
process.StartInfo.RedirectStandardOutput = true;
25+
process.StartInfo.RedirectStandardError = true;
26+
process.StartInfo.UseShellExecute = false;
27+
process.StartInfo.CreateNoWindow = true;
28+
process.StartInfo.WorkingDirectory = runtimeRoot;
29+
process.OutputDataReceived += (sender, e) =>
30+
{
31+
if (!string.IsNullOrEmpty(e.Data))
32+
{
33+
if (e.Data.Contains("error:"))
34+
{
35+
isSuccess = false;
36+
}
37+
}
38+
};
39+
process.ErrorDataReceived += (sender, e) =>
40+
{
41+
if (!string.IsNullOrEmpty(e.Data))
42+
{
43+
if (e.Data.Contains("error:"))
44+
{
45+
isSuccess = false;
46+
}
47+
}
48+
};
49+
process.Start();
50+
process.BeginOutputReadLine();
51+
process.BeginErrorReadLine();
52+
await process.WaitForExitAsync();
53+
54+
if (!isSuccess)
55+
{
56+
return $"Fail to switch to {branchName}, please check the git log for more details.";
57+
}
58+
else
59+
{
60+
return $"Successfully switch to {branchName}";
61+
}
62+
}
63+
}
64+
catch (Exception ex)
65+
{
66+
return $"Fail to run command `{fileName} {arguments}`: {ex.Message}";
67+
}
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)