- Notifications
You must be signed in to change notification settings - Fork836
Comments
feat: fast DevServer startup with convention-based discovery, instant MCP, and hot reconnection#22704
feat: fast DevServer startup with convention-based discovery, instant MCP, and hot reconnection#22704carldebilly wants to merge 56 commits intomasterfrom
Conversation
Replace MSBuild-based add-in discovery (2x dotnet build, 10-30s) with direct .targets XML parsing from the NuGet cache (~110ms).- Add TargetsAddInResolver that parses packages.json and buildTransitive/*.targets- Fix NUGET_PACKAGES="" producing dangerous relative paths in GetNuGetCachePaths- Add --addins flag to Host to skip MSBuild discovery when paths are pre-resolved- Forward --addins from controller to child server process- Resolve add-ins in CLI start and MCP paths before launching the host- Add --addins-only flag to disco command for pipe-friendly output- Show add-ins section with discovery method and duration in disco output- Add 18 unit tests for TargetsAddInResolver
…fixes- Add DotNetVersionCache to avoid repeated `dotnet --version` subprocess calls during startup (disk cache with 24h TTL + global.json sdk.version invalidation key)- Integrate cache into UnoToolsLocator and register in DI- Add EntryPoint regression tests to lock VS extension reflection-probed type names and constructor signatures- Fix MCP notification name in dev-server.md (tools/list_changed)- Add missing tools to using-the-uno-mcps.md (uno_app_start, Business tier)- Add --mcp-wait-tools-list to Antigravity setup guide- Add antigravity to ClientsWithoutListUpdateSupport in McpProxy- Create devserver-agent.md and reference it in AGENTS.md
… diagnostics- Enable tool cache unconditionally in --mcp-app mode for < 1s first list_tools- Add 30s bounded timeout to list_tools and EnsureRootsInitialized (no more infinite blocking)- Fix McpClientProxy TCS completion on connection error (prevents permanent deadlock)- Fix DisposeAsync hang by canceling TCS before awaiting client disposal- Remove incorrect ResourceUpdatedNotificationParams deserialization for ToolListChanged- Always invoke toolListChanged callback even with 0 tools to unblock waiters- Return structured MCP error responses for premature tool calls instead of exceptions- Add uno_health built-in tool with HealthReport model for startup diagnostics- Add 17 unit tests covering serialization, timeout patterns, and cache integration
…overyAdd 17 unit tests (addins flag parsing, controller forwarding, NuGetcache path guard) and 5 PowerShell integration test functions (discoJSON output, addins-only, cleanup, Host --addins flag, package content).
Upgrade ModelContextProtocol SDK from 0.4.1 to 0.8.0. Add uno://healthMCP resource, HealthReport fields (UnoSdkVersion, DiscoveryDurationMs),cache invalidation metadata (workspace hash, SDK version), and atomiccache writes.
- Link AmbientRegistry and CsprojUserGenerator sources into CLI project- Check AmbientRegistry for existing DevServer before launching- Write .csproj.user files CLI-side before server launch- Launch Host directly without --command start (2-process chain)- Manage long-running server process lifecycle and cleanup- Call StopMonitoringAsync on MCP proxy shutdown- Add Phase 1b unit tests for AmbientRegistry and direct launch args- Document IDEChannel single-connection limitation
- TCS resettable via ResetConnectionAsync (bugs 1+4)- Monitor loop continues after ServerStarted, raises ServerCrashed (bug 2)- Notification handler awaits async callback (bug 3)- Add 11 regression and bug-exposing tests
Extract health reporting into a dedicated HealthService class:- BuildHealthReport(), BuildHealthToolResponse()- HealthTool static Tool definition- HealthResourceUri constantMcpProxy now delegates all health logic to HealthService via DI.
Extract tool caching and list operations into ToolListManager:- GetCachedTools(), PersistToolCacheIfNeeded(), RefreshCachedToolsFromUpstreamAsync()- ListToolsWithTimeoutAsync(), FetchToolsFromUpstreamAsync(), AppendBuiltInTools()- InitializeToolCachePath(), MarkShouldRefresh()HealthService now uses ToolListManager.CachedToolCount directly.
Pure rename for clarity — McpUpstreamClient better describes its roleas the connection to the upstream DevServer MCP host.
Extract MCP Host building and handler registration into McpStdioServer:- BuildHost() constructs the stdio MCP server with all 4 handlers- Lifecycle-specific behavior injected via delegates- McpProxy now calls BuildHost() then wires callbacks and runs
McpProxy now only contains lifecycle orchestration (roots, DevServerstartup, cache priming, event wiring). The new name reflects its role.
- Split Given_McpProxy into 5 test classes matching production classes (Given_HealthService, Given_ToolCacheFile, Given_McpUpstreamClient, Given_DevServerMonitor, Given_McpStdioServer)- Add XML doc comments on test classes with <see cref> to tested class- Add MSTest [Description] on non-trivial test methods- Rename mcpClientProxy parameter to mcpUpstreamClient in all classes- Replace "MCP proxy" with "MCP bridge" / "stdio server" in docs and logs- Remove Phase 1b/1c-1 references from code comments- Add [Retry(2)] on intermittent DevServer process tests
Add observational state machine to ProxyLifecycleManager that tracksConnectionState transitions driven by DevServerMonitor events:- ServerStarted -> Connecting- toolListChanged -> Connected (resets reconnection counter)- ServerCrashed -> Reconnecting (with ResetConnectionAsync) or Degraded after 3 failed reconnection attempts- ServerFailed -> DegradedMcpStdioServer now returns a contextual error message when tools arecalled during reconnection vs initial startup.
Cover TCS reset pattern (crash -> reset -> reconnect cycle), reconnectioncounter reset on success, max attempts leading to Degraded state,Degraded not being terminal, and HealthReport roundtrip withReconnecting/Degraded ConnectionState.
Add ASCII state diagram in ProxyLifecycleManager next to the statefields, showing all transitions with their triggering events and thetwo separate retry counters.Enrich ConnectionState.cs with detailed <remarks> block describingevent-to-state mapping, counter semantics, and per-member XML summarieson each enum value.
ServerLaunching event fires after process start, before health-check wait.Shutdown state is set in the finally block for guaranteed cleanup transition.
Configure ServerInfo with name="uno-devserver" and assembly versionso clients see a named server in the initialize response.
Remove stale ClientsWithoutListUpdateSupport blacklist. Instead, waitfor upstream tools when no cache exists (universal), return cached toolsimmediately when available. Add HasCachedTools property to ToolListManager.
Align ServerInfo and HealthReport on the same versioning pattern asCliManager.ShowBanner, using AssemblyInformationalVersionAttributeto get the SemVer version instead of the 4-part assembly version.
Cover Launching/Shutdown roundtrips in HealthReport, ServerInfoversion extraction pattern, and ServerLaunching event sequence.
Refactor RunMonitor() to use DiscoverAsync() directly instead ofResolveHostExecutableAsync(), storing the full DiscoveryInfo forhealth reporting. StartProcess() now reuses cached add-ins fromthe discovery result instead of re-calling DiscoverAsync().
Static mapper that converts DiscoveryInfo nullable fields intoValidationIssue[] for health reports. Maps GlobalJsonNotFound,UnoSdkNotInGlobalJson, SdkNotInCache, PackagesJsonNotFound,DevServerPackageNotCached, HostBinaryNotFound, DotNetNotFound.
unodevops commentedFeb 20, 2026
🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here:https://unowasmprstaging.z20.web.core.windows.net/pr-22704/wasm-skia-net9/index.html |
unodevops commentedFeb 20, 2026
🤖 Your Docs stage site is ready! Visit it here:https://unodocsprstaging.z13.web.core.windows.net/pr-22704/docs/index.html |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Pull request overview
Copilot reviewed 77 out of 78 changed files in this pull request and generated 3 comments.
💡Add Copilot custom instructions for smarter, more guided reviews.Learn how to get started.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
unodevops commentedFeb 20, 2026
unodevops commentedFeb 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Pull request overview
Copilot reviewed 78 out of 80 changed files in this pull request and generated no new comments.
💡Add Copilot custom instructions for smarter, more guided reviews.Learn how to get started.
unodevops commentedFeb 20, 2026
🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here:https://unowasmprstaging.z20.web.core.windows.net/pr-22704/wasm-skia-net9/index.html |
unodevops commentedFeb 20, 2026
🤖 Your Docs stage site is ready! Visit it here:https://unodocsprstaging.z13.web.core.windows.net/pr-22704/docs/index.html |
unodevops commentedFeb 20, 2026
🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here:https://unowasmprstaging.z20.web.core.windows.net/pr-22704/wasm-skia-net9/index.html |
unodevops commentedFeb 20, 2026
🤖 Your Docs stage site is ready! Visit it here:https://unodocsprstaging.z13.web.core.windows.net/pr-22704/docs/index.html |
Summary
Problem: Every DevServer startup pays a 10-30s MSBuild evaluation tax for add-in discovery. In MCP mode, AI tools take 15-40s to become available.
Solution: Convention-based
.targetsparsing replaces MSBuild evaluation (<200ms). MCP server starts instantly with cached tool definitions (<1s). Hot reconnection survives Host crashes without MCP client restart.Scope: 73 files changed, ~12.9K lines added across discovery, MCP proxy, tests, and specs.
Performance Gains
list_toolsWhat's Changed
Convention-Based Add-In Discovery
TargetsAddInResolver: Direct.targetsXML parsing from NuGet cache — replaces 10-30s MSBuild evaluation with <200ms resolution. Handles MSBuild property substitution ($(TargetFramework),$(MSBuildThisFileDirectory), etc.)ManifestAddInResolver: Forward-compatibledevserver-addin.jsonmanifest lookup (priority 1 in resolution chain) with version gatingProjectAssetsParser: Third-party add-in discovery viaproject.assets.json— finds transitive NuGet packages that ship DevServer add-insDotNetVersionCache: Cacheddotnet --versionwith 24h TTL andglobal.jsonfile-hash invalidation--addinsflag on Host for pre-resolved add-in paths (backward-compatible: absence = MSBuild fallback).targets→ MSBuild fallbackInstant MCP Startup
Direct Host Launch & Instance Reuse
AmbientRegistryreuse: detects existing DevServer instances by port/PID, connects instead of spawning duplicates.csproj.usergeneration moved CLI-sideHot Reconnection
ConnectionStatestate machine:Initializing → Discovering → Launching → Connecting → Connected, plusReconnecting,Degraded,ShutdownDegradedstate (MCP stays alive with health info)Health & Diagnostics
uno_healthbuilt-in tool +uno://healthMCP resource — always available, even before Host is readyDiscoveryIssueMapper: maps discovery failures to structuredValidationIssuewith codes, severity, and remediation hintsServerInfodeclaration for protocol complianceReliability & Race Condition Fixes
Volatile.Read/Interlocked.Exchangeon upstream connectionStartOnceGuardfor failed startup retry (Interlocked.CompareExchange)DisposeAndClearProcesshelperMCP SDK Upgrade
Architecture Overview
ConnectionState Machine
Add-In Discovery Chain
Key New Components
ProxyLifecycleManagerMcpUpstreamClientMcpStdioServerDevServerMonitorHealthServiceToolListManagerMonitorDecisionsStartOnceGuardUnoToolsLocatorTargetsAddInResolver.targetsfile parsing with MSBuild property resolutionManifestAddInResolverdevserver-addin.jsonresolution with version gatingProjectAssetsParserproject.assets.jsonparsing for transitive add-insDiscoveryIssueMapperDotNetVersionCachedotnet --versionwith 24h TTLAmbientRegistryRefactoring
McpProxysplit into 5 focused classes:ProxyLifecycleManager,McpUpstreamClient,McpStdioServer,HealthService,ToolListManagerMcpClientProxyrenamed toMcpUpstreamClientfor clarity[Description]attributesTest Coverage
TargetsAddInResolver,ManifestAddInResolver,ProjectAssetsParser,McpUpstreamClient,DevServerMonitor,HealthService,ConnectionState,DiscoveryIssueMapper,MonitorDecisions,ToolCacheFile,EntryPointregression,DotNetVersionCache,NuGetCacheHelper,ConfigurationExtensionsrun-devserver-compat-tests.ps1)Backward Compatibility
start,stop,list,disco,login)Specification
specs/001-fast-devserver-startup/spec.mdwith 8 appendices (A–H)specs/001-fast-devserver-startup/known-limitations.mdTest plan
dotnet teston DevServer.Tests)uno-devserver startlaunches Host with--addinsflaglist_toolsreturns cached tools in <1suno_healthtool returns structured health info