Source of Truth: docs/ARCHITECTURE.md
Status: Canonical repo cleanup aligned to the current VS MCP Bridge and BlogAI narrative as of 2026-05-16.
Why A VSIX Project Should Target .NET Framework 4.7.2
Host Constraints, Shared Code, And Stable Bridge Boundaries
When building a Visual Studio extension, one detail is easy to underestimate: an in-process VSIX is loaded by the Visual Studio shell. It is not a standalone desktop app, and it should not be treated like one.
In VS MCP Bridge, that is why VsMcpBridge.Vsix targets .NET Framework 4.7.2. The VSIX must align with the Visual Studio SDK and in-process extension hosting model, while the rest of the solution can use other target frameworks where they make sense.
Microsoft's in-process extension guidance summarizes the rule this way: in-process extensions must target the .NET version used by the Visual Studio version they run in. The relevant guidance is here: VisualStudio.Extensibility in-process extensions.
The VSIX Runs Inside Visual Studio
The VSIX host is different from the standalone app and different from the local MCP server.
The VSIX is loaded into the Visual Studio process. It uses the Visual Studio SDK, shell services, tool window infrastructure, MEF composition expectations, DTE/editor APIs, package loading behavior, and WPF UI hosted by Visual Studio.
That hosting model is the reason the extension project follows Visual Studio's in-process runtime constraints. Trying to force the VSIX itself to behave like a modern out-of-process .NET app would make loading, packaging, dependency resolution, and tool-window behavior harder to reason about.
The Current Solution Uses Targeting Deliberately
The target framework split is part of the architecture:
VsMcpBridge.Vsix targets .NET Framework 4.7.2 because it is the Visual Studio in-process extension host.
VsMcpBridge.Shared targets netstandard2.0 so shared contracts, tools, security seams, diagnostics, and orchestration logic can be reused across hosts.
VsMcpBridge.Shared.Wpf multi-targets so the reusable WPF surface can support both VSIX and standalone app hosts.
VsMcpBridge.App can target a modern Windows desktop runtime because it is not loaded into Visual Studio.
VsMcpBridge.McpServer can target a modern runtime because it runs out of process and communicates over stdio plus the local named pipe.
This is not accidental legacy layering. It is how the bridge keeps Visual Studio-specific constraints from infecting every project.
Host Code And Shared Logic Stay Separate
The VSIX owns Visual Studio-specific behavior:
- package initialization
- tool window creation
- Visual Studio service access
- DTE and editor interactions
- UI-thread switching
- VSIX-host logging and diagnostics
Shared infrastructure owns reusable bridge behavior:
- pipe message contracts and dispatch abstractions
- presenter/viewmodel orchestration
- proposal lifecycle contracts
- bridge tool descriptors, requests, results, catalog, and executor
- policy, approval, redaction, audit, capability, and secret-reference seams
- diagnostic patterns and correlation metadata
That separation lets the shared layer be tested without loading Visual Studio. It also lets the standalone app reuse the same core presentation and bridge concepts without pretending to be a VSIX.
Tool Windows Follow Visual Studio Lifecycle Rules
Visual Studio owns the lifecycle of extension components. Tool windows are created by the shell, not by normal application startup code.
That matters for dependency wiring and initialization. A VSIX should not assume that every object can be created with application-style constructor injection. Tool-window initialization belongs at the lifecycle points Visual Studio provides, including ToolWindowPane.OnToolWindowCreated() where appropriate.
This lifecycle constraint connects directly to the threading post: the VSIX must respect both Visual Studio object creation and Visual Studio UI-thread requirements.
Stable Pipe Integration Depends On Host Isolation
The local MCP server does not run inside Visual Studio. It speaks MCP over stdio to the AI client and communicates with the host through the local named pipe.
That boundary is important. The MCP server should not need to reference Visual Studio SDK assemblies, know about tool-window lifecycle rules, or switch to the Visual Studio UI thread. It should remain transport-focused and protocol-safe.
The VSIX side can then own the named-pipe server and host behavior. When a pipe-backed tool needs active document state, selected text, solution projects, error list data, or proposal UI behavior, the request crosses into the VSIX host, where Visual Studio-specific services are available.
This keeps the out-of-process server stable while letting the in-process extension follow Visual Studio's runtime rules.
Testing Benefits From The Split
Because shared infrastructure is not trapped inside the VSIX target framework, much of the bridge can be tested directly:
- shared tool execution tests can validate catalog, executor, policy, approval, audit, redaction, and correlation behavior
- proposal lifecycle tests can validate state transitions without starting Visual Studio
- shared WPF and presenter behavior can be exercised outside the VSIX host where appropriate
- VSIX-specific tests can focus on composition and host-specific service behavior
That is one reason the project can evolve safely. The VSIX target framework is a host constraint, not a reason to put all behavior into untestable host code.
Transport And Tool Execution Should Not Depend On VSIX Runtime Behavior
The bridge architecture intentionally prevents shared transport and tool execution concepts from depending on VSIX-only runtime behavior.
For example, BridgeToolExecutor owns shared tool policy, approval, redaction, audit, correlation, and structured results. It should not need to know whether the caller is the VSIX, the standalone app, or a test harness. Likewise, tool descriptors and request/result models should not depend on Visual Studio shell types.
When a tool genuinely needs Visual Studio, that should be represented as host-provided behavior behind the proper boundary. The shared contract should remain portable and observable.
What This Does Not Claim
This post is not a promise that the VSIX will move to a different framework. It is also not a claim that every project in the solution must target .NET Framework.
The practical rule is narrower:
- respect the runtime constraints of the Visual Studio in-process extension host
- keep Visual Studio-specific code in the VSIX host
- keep reusable bridge contracts and logic outside the VSIX where possible
- let out-of-process components use target frameworks appropriate to their own runtime
Related Mermaid Trace Sources
The following diagram sources help explain the host/runtime split:
Those .mmd files are the diagram source of truth. This post references them directly rather than embedding generated images.
Takeaway
Targeting .NET Framework 4.7.2 in the VSIX project is not just an old default. It is part of respecting the Visual Studio in-process hosting environment.
The maintainable design is to keep the VSIX host compatible with Visual Studio, keep shared logic portable and testable, keep the MCP server out of process, and let each boundary use the runtime model that fits its role.
That is what makes the bridge easier to build, validate, troubleshoot, and eventually evolve without turning Visual Studio hosting constraints into system-wide coupling.