Skip to content

feat(tools): add dialog tools to answer blocking modal dialogs - #116

Merged
CalvinAllen merged 1 commit into
mainfrom
feat/tools/dialog-tools
Oct 1, 2026
Merged

CalvinAllen merged 1 commit into
mainfrom
feat/tools/dialog-tools

Conversation

@CalvinAllen

Copy link
Copy Markdown
Contributor

Description

Adds dialog_list and dialog_respond, so an agent can see and answer the modal dialogs that block Visual Studio, such as "The source code is different from the original version" or "Hot Reload changes not supported".

A modal dialog holds the VS UI thread. The tool call that raised it doesn't return, and every other tool that needs that thread waits behind it. Until now nothing could see the dialog, let alone answer it.

  • dialog_list returns each dialog VS is waiting on: an id, the title, the message text and the buttons.
  • dialog_respond clicks a button by name. Matching ignores case, & access-key markers and trailing ellipses. If the name matches nothing, matches two buttons, or the button is disabled, the tool refuses and lists the real buttons. The result says whether the dialog closed and which dialogs are still open, since one answer can raise another prompt.

How it works

  • Neither tool touches the UI thread. StreamJsonRpc dispatches requests concurrently, so these calls are served while the blocked call is still waiting on the dialog.
  • Finding dialogs uses Win32 window enumeration, which reads window state without sending messages, so it can't hang. A window counts as a blocking dialog when it is visible and enabled and its owner has been disabled; that's how both Win32 and WPF implement modality. An unowned native dialog also counts. Windows in other processes count when a VS window owns them.
  • Reading and clicking use UI Automation, which handles message boxes, task dialogs and WPF dialogs alike. These calls run on a dedicated background thread with a timeout, so a dialog that stops responding is still listed by title instead of hanging the tool.
  • Where it lives: Services/DialogAutomation.cs is in the extension, not the server. The server ships as a self-contained single-file exe and would otherwise have to bundle the whole Windows Desktop runtime.

Tests

DialogAutomationTests contains:

  • Unit tests for the dialog-detection rules, choosing a dialog by id, and button matching.
  • End-to-end tests that open a real Win32 message box, a real WPF dialog and an unresponsive dialog in the test process, then list and answer them through UI Automation from another thread.

The first WPF window in a fresh process takes about 8 s to start, so the test's wait for a dialog to appear is 60 s. Only a failing test waits that long.

Type of Change

  • feat - New feature
  • fix - Bug fix
  • docs - Documentation only
  • refactor - Code change that neither fixes a bug nor adds a feature
  • test - Adding or updating tests
  • chore - Maintenance tasks
  • ci - CI/CD changes

Related Issues

Related to #112. This needs testing inside Visual Studio before the issue can be closed.

Checklist

  • My code follows the project's code style
  • I have tested my changes locally (automated tests pass; not yet tried inside a running Visual Studio, see below)
  • I have updated documentation if needed
  • My commits follow the conventional commit format
  • This PR has a descriptive title using conventional commit format

Additional Notes

Still to test inside Visual Studio. The automated tests use stand-in dialogs in the test process, not VS's own. Worth checking each dialog from the issue:

  • "The source code is different from the original version": edit a file after building, then set a breakpoint in it during a debug session
  • "Hot Reload changes not supported": make an edit Hot Reload can't apply while debugging
  • "Unable to marshal host object to interpreter space": where this comes from is still unknown; it's covered only if it's a regular dialog owned by VS
  • dialog_list answers while the call that raised the dialog is still waiting
  • dialog_respond closes the dialog, and the blocked call then completes

Known limitation. The call that raised the dialog still waits until the MCP client's own timeout; it doesn't fail early. The tool descriptions tell agents to try dialog_list when a call hangs or times out. Making blocked calls return early would be a separate change.

@CalvinAllen CalvinAllen changed the title feat(tools): add dialog tools to answer modal dialogs blocking Visual Studio feat(tools): add dialog tools to answer blocking modal dialogs Oct 1, 2026
… Studio

Adds dialog_list and dialog_respond. A modal dialog such as "The source
code is different from the original version" or "Hot Reload changes not
supported" holds the Visual Studio UI thread, so the tool call that raised
it never returns and every other tool that needs that thread waits behind
it. An agent had no way to see the dialog or answer it.

Neither tool touches the UI thread. Discovery is Win32 window enumeration,
which reads window state without sending messages and so cannot hang. A
window is a blocking dialog when it is visible and enabled and its owner
has been disabled, which is how Win32 and WPF both implement modality, or
when it is an unowned native dialog. Out-of-process windows owned by a VS
window are included.

Reading a dialog and clicking a button use UI Automation, which covers
message boxes, task dialogs and WPF dialogs alike. Those calls run on a
dedicated MTA thread under a timeout, so a dialog that stops responding is
still listed by title rather than hanging the tool. Buttons are matched by
name, ignoring case, access key markers and trailing ellipses; ambiguous,
missing and disabled buttons are refused with the list of real buttons.

StreamJsonRpc dispatches requests concurrently, so these calls are served
while the call that raised the dialog is still waiting on it.

UI Automation lives in the extension rather than the server because the
server is a self-contained single-file publish and would otherwise have to
carry the whole Windows Desktop runtime.
@CalvinAllen
CalvinAllen force-pushed the feat/tools/dialog-tools branch 2 times, most recently from 503302b to 525a460 Compare October 1, 2026 20:32
@CalvinAllen
CalvinAllen merged commit 3cafffb into main Oct 1, 2026
2 checks passed
@CalvinAllen
CalvinAllen deleted the feat/tools/dialog-tools branch October 1, 2026 20:39
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.

1 participant