Skip to content

Commit 12b359d

Browse files
authored
Implement NuGet restore and SDK warmup
Add restore and warmup logic for dotnet commands
1 parent 573e3c8 commit 12b359d

File tree

1 file changed

+47
-5
lines changed

1 file changed

+47
-5
lines changed

tools/mcp/mcp_server.py

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,34 @@
1010
mcp = FastMCP("roslyn-analyzers-dev")
1111
BASE_DIR = Path(__file__).resolve().parents[2] # repo root (../../)
1212
DEFAULT_TIMEOUT = 900 # 15 minutes for long builds/tests
13+
STATE_DIR = BASE_DIR / ".mcp_state"
14+
STATE_DIR.mkdir(exist_ok=True)
15+
RESTORE_SENTINEL = STATE_DIR / "restored.ok"
16+
17+
def _ensure_restored() -> bool:
18+
"""
19+
Ensure NuGet restore (and initial SDK warmup) has happened once per server session.
20+
Returns True if this call performed the restore (i.e., cold start), else False.
21+
"""
22+
if RESTORE_SENTINEL.exists():
23+
return False # already warm
24+
25+
# Quick SDK warmup to avoid first-time experience overhead
26+
_run(["dotnet", "--info"], timeout=60)
27+
28+
# Restore the whole solution once so later commands can use --no-restore
29+
_run(["dotnet", "restore", "Philips.CodeAnalysis.sln"], timeout=600)
30+
31+
# Optional: compile the test project once, so later we can use --no-build
32+
_run([
33+
"dotnet", "build",
34+
"Philips.CodeAnalysis.Test/Philips.CodeAnalysis.Test.csproj",
35+
"--configuration", "Release",
36+
"--no-restore"
37+
], timeout=600)
38+
39+
RESTORE_SENTINEL.write_text("ok", encoding="utf-8")
40+
return True
1341

1442
def _run(cmd: list[str], *, timeout: int = DEFAULT_TIMEOUT) -> tuple[int, str]:
1543
if (
@@ -71,6 +99,7 @@ def search_helpers() -> Dict[str, Any]:
7199
@mcp.tool
72100
def build_strict() -> Dict[str, Any]:
73101
"""dotnet build solution with warnings as errors."""
102+
_ensure_restored()
74103
_run(["dotnet", "clean", "Philips.CodeAnalysis.sln"])
75104
rc, out = _run([
76105
"dotnet", "build", "Philips.CodeAnalysis.sln",
@@ -82,12 +111,25 @@ def build_strict() -> Dict[str, Any]:
82111
@mcp.tool
83112
def run_tests() -> Dict[str, Any]:
84113
"""Run tests against main test project."""
85-
# Use 120s timeout to accommodate both building and testing from clean state
86-
# Tests take ~49s including build when starting clean, so 120s provides adequate buffer
87-
rc, out = _run([
114+
# Warmup/restore on the very first call; returns True if we just did it now.
115+
did_restore_now = _ensure_restored()
116+
117+
# If we just restored/built, tests will be quick — and we can skip restore/build
118+
cmd = [
88119
"dotnet", "test", "Philips.CodeAnalysis.Test/Philips.CodeAnalysis.Test.csproj",
89-
"--configuration", "Release", "--logger", "trx;LogFileName=test-results.trx"
90-
], timeout=120)
120+
"--configuration", "Release",
121+
"--logger", "trx;LogFileName=test-results.trx",
122+
"--no-restore"
123+
]
124+
# If the initial build above succeeded, we can also skip building:
125+
test_bin = BASE_DIR / "Philips.CodeAnalysis.Test" / "bin" / "Release"
126+
if test_bin.exists():
127+
cmd.append("--no-build")
128+
129+
# Give a bigger timeout only on the first ever run, else be tight
130+
timeout = 600 if did_restore_now else 180
131+
132+
rc, out = _run(cmd, timeout=timeout)
91133

92134
# Parse test results from output
93135
test_results = {"passed": 0, "failed": 0, "skipped": 0, "total": 0, "duration": ""}

0 commit comments

Comments
 (0)