Skip to content

Commit 36c58ba

Browse files
committed
Vally: fix MCP boot race + drop misconfigured grader (#15948)
- Launch pre-built DLLs via 'dotnet <dll>' in both .vally.yaml files instead of 'dotnet run', so N parallel workers no longer race on Roslyn's exclusive write lock for the output DLL. - Add 'Build MCP servers' step to eng/pipelines/skill-eval.yml so the CI runner has the DLLs ready before vally starts. - Drop the skill-invocation grader from generate-sdk-for-existing-release-plan (no preflight reasoning step required; tools-only). - Strip 'I'm in a checkout of azure-rest-api-specs.' preamble from prompts; the worktree already provides that context. - Remove stray '// tools skills response' artifact in live release-planner.eval.yaml. - README: document 'dotnet build' as a prereq; rewrite workers warning. Validated: scenarios-mock at --workers 6 -> 5/5 stimuli pass, 0 race hits, ~4 min.
1 parent af3db0c commit 36c58ba

6 files changed

Lines changed: 47 additions & 33 deletions

File tree

.github/skills/.vally.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@ paths:
1212
evalFilenames: ["eval.yaml", "*.eval.yaml"]
1313

1414
environments:
15+
# Launch the pre-built DLLs via `dotnet <dll>`, NOT `dotnet run` — avoids the
16+
# MSBuild boot race under parallel workers. See issue #15948.
17+
# CI builds the DLLs in the 'Build MCP servers' step of skill-eval.yml.
1518
azsdk-mcp:
1619
mcpServers:
1720
azure-sdk-mcp:
1821
type: stdio
1922
command: dotnet
20-
args: ["run", "--project", "../../tools/azsdk-cli/Azure.Sdk.Tools.Cli", "--", "start"]
23+
args: ["../../artifacts/bin/Azure.Sdk.Tools.Cli/Debug/net8.0/azsdk.dll", "start"]
2124
timeout: "60s"
2225
env:
2326
AZSDKTOOLS_AGENT_TESTING: "true"
@@ -27,5 +30,5 @@ environments:
2730
azure-sdk-mcp:
2831
type: stdio
2932
command: dotnet
30-
args: ["run", "--project", "../../tools/azsdk-cli/Azure.Sdk.Tools.Mock"]
33+
args: ["../../artifacts/bin/Azure.Sdk.Tools.Mock/Debug/net8.0/azsdk-mock.dll"]
3134
timeout: "60s"

eng/pipelines/skill-eval.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ jobs:
4141
- script: npm install -g @github/copilot-sdk
4242
displayName: 'Install Copilot SDK'
4343

44+
# Pre-build the MCP servers so vally launches `dotnet <dll>` instead of
45+
# `dotnet run` — avoids the MSBuild boot race under parallel workers.
46+
# See issue #15948.
47+
- script: |
48+
dotnet build tools/azsdk-cli/Azure.Sdk.Tools.Cli -c Debug --nologo
49+
dotnet build tools/azsdk-cli/Azure.Sdk.Tools.Mock -c Debug --nologo
50+
displayName: 'Build MCP servers'
51+
4452
- script: |
4553
input_areas=$(echo "${{ parameters.areas }}" | xargs)
4654
if [ -n "$input_areas" ]; then

tools/azsdk-cli/Azure.Sdk.Tools.Vally/.vally.yaml

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,26 @@ paths:
1111
results: results/
1212

1313
environments:
14-
# Default for unit + mock scenarios. Runs the dedicated Azure.Sdk.Tools.Mock
15-
# MCP server — a separate process whose tool surface mirrors the real CLI
16-
# but with deterministic in-memory responses.
17-
#
18-
# Relative `--project` paths are resolved by `dotnet` against the cwd of
19-
# the vally invocation. Always run vally from this directory:
20-
# cd tools/azsdk-cli/Azure.Sdk.Tools.Vally && vally eval ...
21-
# Same convention as .github/skills/.vally.yaml.
14+
# Launch the pre-built DLL via `dotnet <dll>`, NOT `dotnet run` — avoids the
15+
# MSBuild boot race under parallel workers. See issue #15948.
16+
# Run `dotnet build ../Azure.Sdk.Tools.Mock -c Debug` once before vally.
2217
azsdk-mcp-mock:
2318
mcpServers:
2419
azure-sdk-mcp:
2520
type: stdio
2621
command: dotnet
27-
args: ["run", "--project", "../Azure.Sdk.Tools.Mock"]
28-
timeout: "60s"
22+
args: ["../../../artifacts/bin/Azure.Sdk.Tools.Mock/Debug/net8.0/azsdk-mock.dll"]
23+
timeout: "30s"
2924

30-
# Live MCP — real Azure.Sdk.Tools.Cli against real DevOps (test area path),
31-
# real GitHub, real pipelines. AZSDKTOOLS_AGENT_TESTING=true keeps the
32-
# handful of write tools (e.g. create_release_plan) inside the test area.
33-
# Bound only by scenarios under evals/workflow-scenarios/live/ and selected
34-
# by the `scenarios-live` / `nightly` suites.
25+
# Live MCP. AZSDKTOOLS_AGENT_TESTING=true keeps write tools inside the test
26+
# area. Pre-built DLL pattern — see issue #15948.
27+
# Run `dotnet build ../Azure.Sdk.Tools.Cli -c Debug` once before vally.
3528
azsdk-mcp-live:
3629
mcpServers:
3730
azure-sdk-mcp:
3831
type: stdio
3932
command: dotnet
40-
args: ["run", "--project", "../Azure.Sdk.Tools.Cli", "--", "start"]
33+
args: ["../../../artifacts/bin/Azure.Sdk.Tools.Cli/Debug/net8.0/azsdk.dll", "start"]
4134
timeout: "5m"
4235
env:
4336
AZSDKTOOLS_AGENT_TESTING: "true"

tools/azsdk-cli/Azure.Sdk.Tools.Vally/README.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,19 @@ Prereqs:
144144
cd eng/skill-eval
145145
npm ci
146146
```
147+
- **Build the MCP servers once** before running vally. `.vally.yaml`
148+
launches the pre-built DLLs via `dotnet <dll>` to avoid the build-time
149+
race that crashes parallel workers with `MCP error -32000: Connection
150+
closed`. See [`primer-vally-mcp-race.html`](https://github.com/Azure/azure-sdk-tools/blob/main/doc/) (notebook) for the full write-up.
151+
152+
```powershell
153+
# From repo root — builds both Azure.Sdk.Tools.Mock and (transitively) Cli
154+
dotnet build tools/azsdk-cli/Azure.Sdk.Tools.Cli -c Debug
155+
dotnet build tools/azsdk-cli/Azure.Sdk.Tools.Mock -c Debug
156+
```
157+
158+
Rebuild after editing any tool source. Vally itself does **not** rebuild
159+
the MCP server — it just spawns the existing DLL.
147160

148161
Run a suite (recommended):
149162

@@ -168,11 +181,12 @@ $skills = '../../../.github/skills'
168181
> the agent never loads the project skills and the `skill-invocation`
169182
> grader fails even when the tool calls are correct.
170183
>
171-
> Each agent boots its own MCP child process, so parallel workers compete
172-
> for stdio startup. Keep `--workers` at 1–2 for `scenarios-mock` / live
173-
> runs until we share a single Mock MCP server across workers — higher
174-
> concurrency triggers `MCP server 'azure-sdk-mcp' failed to load:
175-
> Connection closed` on most stimuli.
184+
> Each agent still boots its own MCP child process, but `.vally.yaml`
185+
> launches the **pre-built** `azsdk-mock.dll` / `azsdk.dll` via
186+
> `dotnet <dll>` (read-only memory-map, no MSBuild on the hot path), so
187+
> `--workers 6+` is safe for `scenarios-mock`. The old MSBuild boot race
188+
> is gone; the only remaining concurrency limit is rate limits on the
189+
> Copilot CLI subprocesses.
176190
177191
Run a single eval:
178192

tools/azsdk-cli/Azure.Sdk.Tools.Vally/evals/workflow-scenarios/live/release-planner.eval.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ stimuli:
6363
source: C:/Users/gaoh/.vally-cache/azure-rest-api-specs
6464
ref: main
6565
prompt: |
66-
I'm in a checkout of azure-rest-api-specs. Walk me through the full
66+
Walk me through the full
6767
release-plan + SDK-generation flow for the Contoso Widget Manager
6868
end-to-end. Do every step below, in order, and use real tools (no
6969
dry-run, no simulation):

tools/azsdk-cli/Azure.Sdk.Tools.Vally/evals/workflow-scenarios/mock/release-planner-workflows.eval.yaml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ stimuli:
4444
source: C:/Users/gaoh/.vally-cache/azure-rest-api-specs
4545
ref: main
4646
prompt: |
47-
I'm in a checkout of azure-rest-api-specs. Create a public preview
47+
Create a public preview
4848
release plan for the Contoso Widget Manager. Here is all the context
4949
you need:
5050
- TypeSpec project: "specification/contosowidgetmanager/Contoso.WidgetManager"
@@ -81,7 +81,7 @@ stimuli:
8181
source: C:/Users/gaoh/.vally-cache/azure-rest-api-specs
8282
ref: main
8383
prompt: |
84-
I'm in a checkout of azure-rest-api-specs. Walk me through creating
84+
Walk me through creating
8585
a release plan and then generating SDK for the Contoso Widget Manager:
8686
- TypeSpec project: "specification/contosowidgetmanager/Contoso.WidgetManager"
8787
- API release type: "Public Preview"
@@ -125,7 +125,7 @@ stimuli:
125125
source: C:/Users/gaoh/.vally-cache/azure-rest-api-specs
126126
ref: main
127127
prompt: |
128-
I'm in a checkout of azure-rest-api-specs. Using the release-planner
128+
Using the release-planner
129129
flow, generate SDK for all languages for the Contoso Widget Manager
130130
release plan. Here is the context you need:
131131
- release plan work item ID: "29262"
@@ -135,10 +135,6 @@ stimuli:
135135
max_turns: 8
136136
max_tokens: 10000
137137
graders:
138-
- type: skill-invocation
139-
config:
140-
required:
141-
- azsdk-common-prepare-release-plan
142138
- type: tool-calls
143139
config:
144140
required:
@@ -156,7 +152,7 @@ stimuli:
156152
source: C:/Users/gaoh/.vally-cache/azure-rest-api-specs
157153
ref: main
158154
prompt: |
159-
I'm in a checkout of azure-rest-api-specs. Using the release-planner
155+
Using the release-planner
160156
flow, update the API spec pull request on an existing Contoso Widget
161157
Manager release plan. Here is the context you need:
162158
- release plan work item ID: "29262"
@@ -187,7 +183,7 @@ stimuli:
187183
source: C:/Users/gaoh/.vally-cache/azure-rest-api-specs
188184
ref: main
189185
prompt: |
190-
I'm in a checkout of azure-rest-api-specs. Using the release-planner
186+
Using the release-planner
191187
flow, refresh the SDK package-name details on an existing Contoso
192188
Widget Manager release plan from the on-disk TypeSpec emitter
193189
configuration. Here is the context you need:

0 commit comments

Comments
 (0)