[v3] MCP `call_bound_method` loads a second runtime copy and silently stops all frontend event listeners
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 36.3k
- Forks
- 1.9k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 33
Description
Wails version family
v3
Exact Wails version
v3.0.0-beta.16
Operating System
macOS
Description
In an app that bundles @wailsio/runtime from npm (the standard v3 setup), a single call_bound_method call permanently stops every Events.On handler in the page from firing. Nothing raises an error: Go reports the emit as successful, the page reports the event as dispatched, and the app simply stops reacting.
call_bound_method evaluates await import('/wails/runtime.js') inside the page (v3/pkg/application/mcp_tools_enabled.go, ~L460). That loads a second, independent copy of the runtime, with its own empty eventListeners map. Its module-scope side effect in v3/internal/runtime/desktop/@wailsio/runtime/src/events.ts:21 —
window._wails.dispatchWailsEvent = dispatchWailsEvent;
— overwrites the bundled copy's dispatcher, because it runs last. inlineEventJS then delivers every Go event into the copy that nobody subscribed to.
js_eval's description recommends the same import ("The Wails runtime is importable inside the page via await import('/wails/runtime.js')"), so a client following the docs triggers this by hand.
Suggested fix
Two independent changes; either one resolves the symptom.
call_bound_methodshouldn't touch the page's runtime. The MCP server runs in the Go process that owns the bindings, so it can usebindings.Get+BoundMethod.Call— the same pathmessageprocessor_call.gouses — with the resolved window in the context. That is also faster, and works when no window is rendering (a hidden or not-yet-loaded window times out on the eval today).events.tsshould chain rather than replace an existing dispatcher. Any second runtime instance (a plugin, a devtools helper, an iframe sharing the window) hits this.inline_event_shim.jsalready treats an installed dispatcher as something to preserve.
Happy to open a PR for either.
To Reproduce
- Build any v3 app that bundles the npm runtime with
-tags mcp, and subscribe to a custom event in the frontend. - Emit that event from Go. The frontend handler runs.
- Call
call_bound_methodonce, with any bound method. - Emit the same event again. The handler never runs. A second "Wails Runtime Loaded" line appears in the console.
A self-contained check from inside the page, with no app code involved:
// Register on the app's bundled runtime and dispatch through the window's function.
const off = Events.On('probe', () => console.log('hit'));
window._wails.dispatchWailsEvent({ name: 'probe', data: null }); // "hit"
await import('/wails/runtime.js');
window._wails.dispatchWailsEvent({ name: 'probe', data: null }); // silence
Expected behaviour
Calling a bound method through MCP should leave the app's event delivery working.
Testing an event-driven UI by calling a Go method and then checking that the page reacted is the pattern the tool invites — its own description is "Call a bound Go service method through the Wails runtime ... Returns the method's result." That is exactly the sequence that breaks, and it breaks silently.
Screenshots
No response
Attempted Fixes
Confirmed both code paths are unchanged on master as of filing.
Searched the tracker for dispatchWailsEvent, call_bound_method, runtime.js, _wails and MCP (title and body) and found no existing report. #4872 / #4925 concern the opposite failure (a missing dispatcher after reload, a load-order race).
Workaround in our test driver: install a property accessor on window._wails.dispatchWailsEvent that keeps every function ever assigned to it and fans each event out to all of them. The app's copy hears events again and the MCP's copy still works. This only helps a client that knows to install it.
System Details
# System
| Name | MacOS |
| Version | 26.6 |
| Platform | darwin |
| Architecture | arm64 |
| CPU | Apple M4 Max |
# Build Environment
| Wails CLI | v3.0.0-beta.16 |
| Go Version | go1.26.2 |
| GOARCH | arm64 |
| GOOS | darwin |
# Checking for issues
No issues found
(Dependencies and Signing sections omitted - not relevant; full output available on request.)
@wailsio/runtime npm package: 3.0.0-beta.16
Additional context
Found while driving a Wails app from Claude Code over MCP for end-to-end tests. Our launcher re-reads its project list when Go announces a change; after seeding a project through call_bound_method, the launcher sat on "No projects yet" over a project Go had already opened. A tap on window._wails.dispatchWailsEvent showed every event arriving at the window while the React handler never ran.
Filing here rather than in the MCP feedback thread (#5692) since it's a reproducible bug, but happy to move it.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in v3/pkg/application/mcp_tools_enabled.go around call_bound_method and compare its path with messageprocessor_call.go. Then inspect events.ts and inline_event_shim.js to understand dispatcher ownership, and reproduce the event-listener failure with the steps in the issue. Done means calling a bound method through MCP no longer prevents existing Events.On handlers from receiving later events.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, javascript, typescript
- Domain
- backend, desktop, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100