Skip to content

feat(tools): add Visual Studio terminal tools - #106

Merged
CalvinAllen merged 1 commit into
mainfrom
feat/tools/terminal-tools
Sep 3, 2026
Merged

feat(tools): add Visual Studio terminal tools#106
CalvinAllen merged 1 commit into
mainfrom
feat/tools/terminal-tools

Conversation

@CalvinAllen

Copy link
Copy Markdown
Contributor

Resolves #103

Adds six MCP tools over the Visual Studio integrated terminal.

Tool Behavior
terminal_run Run a command in a new terminal, inside the VS developer environment
terminal_create Open an empty terminal using the user's default profile
terminal_list List open terminal identifiers
terminal_show Bring a terminal into view
terminal_close Close one terminal
terminal_close_all Close every terminal

Assembly access turned out better than expected

My comment on #103 assumed the whole brokered-service stack would need reflection. It doesn't. IBrokeredServiceContainer, IServiceBroker, ServiceRpcDescriptor and ServiceActivationOptions all come from Microsoft.ServiceHub.Framework, a real NuGet package the VSIX already pulls in transitively. I verified with a compile probe before designing.

Only three types need reflection — ITerminalService, ProfileConfig and TerminalServiceDescriptors, from Microsoft.VisualStudio.Terminal.dll, which is subject to the same constraint documented on TestExplorerInterop: VS-install-only, absent from NuGet and the SDK metapackage, and $(DevEnvDir) undefined under dotnet build.

The assembly is loaded by strong name at 17.0.0.0. Both VS 2022 and VS 2026 ship a binding redirect covering 0.0.0.0 through the installed version plus a codeBase entry pointing at CommonExtensions\Microsoft\Terminal, so one version number resolves on both. An already-loaded copy is preferred when present, which avoids depending on binding policy at all once the terminal window has been used.

Design notes

Commands run via ProfileConfig, not a PTY stream. ProfileConfig exposes Location and Arguments, so a command can be launched by creating a terminal configured to run it — no duplex-stream plumbing, no AttachTerminalStreamAsync. Much less machinery than the issue anticipated.

Developer environment by default. When VsDevCmd.bat can be located from VSAPPIDDIR, it's chained in ahead of the command, so msbuild, vstest.console and dotnet-coverage are on PATH. Plain cmd.exe would not have them — and the OpenCover-style use case in #95 is exactly why that matters. Falls back cleanly when the script isn't found, and reports which happened via DeveloperEnvironment.

Working directory defaults to the solution directory, not devenv's own current directory, which points into the Visual Studio install.

Output is deliberately not captured. The terminal is a raw PTY: no exit code, no command boundaries. Rather than return text an agent might misread as success, the tool descriptions state the limitation and point at redirecting to a file and reading it back with document_read. This is the limitation I flagged on #95 and it is inherent to the API, not to this implementation.

Testing

dotnet build -c Release clean, 0 warnings. 50/50 tests pass (14 added).

TerminalInterop takes an assembly resolver so tests can supply the test assembly, whose stand-ins are declared in the genuine Microsoft.VisualStudio.Terminal namespace. That runs the real path end to end: type resolution, descriptor retrieval, the generic GetProxyAsync<T> invocation including its ValueTask unwrapping, overload selection, and profile construction.

The overload selection is worth calling out — CreateTerminalAsync has four overloads, and the three-argument one would silently drop the working directory. The stand-in throws if that overload is picked, so the test fails loudly rather than quietly passing with a wrong working directory.

Not covered automatically: an actual VS terminal opening, which needs a live instance.

Security note

terminal_run executes commands on the machine. The README now flags that under Settings, next to the Binding Address option, since combining it with a non-localhost binding exposes command execution to the network.

Adds six MCP tools over the integrated terminal: terminal_run,
terminal_create, terminal_list, terminal_show, terminal_close and
terminal_close_all.

The brokered service plumbing is ordinary compile-time code.
IBrokeredServiceContainer, IServiceBroker, ServiceRpcDescriptor and
ServiceActivationOptions all come from Microsoft.ServiceHub.Framework, a real
NuGet package the VSIX already references.

Microsoft.VisualStudio.Terminal.dll is the part that cannot be referenced. It
supplies ITerminalService, ProfileConfig and TerminalServiceDescriptors, ships
only inside the Visual Studio installation, is absent from NuGet and from the
Microsoft.VisualStudio.SDK metapackage, and $(DevEnvDir) is undefined under
dotnet build. TerminalInterop loads it by strong name at version 17.0.0.0,
which the binding redirect and codeBase entry shipped by both VS 2022 and
VS 2026 resolve correctly, and reaches those three types reflectively.

Commands run through cmd.exe with /k so the window stays readable. When
VsDevCmd.bat can be located from VSAPPIDDIR it is chained in first, so msbuild,
vstest.console and dotnet-coverage are on PATH, which is what the original
request in #95 needed.

Output is deliberately not captured. The terminal is a raw PTY with no exit
code and no command boundaries, so inferring success from its text would be
unreliable. The tool descriptions say so and point at redirecting to a file and
reading it back with document_read.

Working directory defaults to the open solution's directory rather than
devenv's own, which points into the Visual Studio install.
@CalvinAllen
CalvinAllen merged commit 91fb380 into main Sep 3, 2026
2 checks passed
@CalvinAllen
CalvinAllen deleted the feat/tools/terminal-tools branch September 3, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

feat(tools): add visual studio terminal tools

1 participant