Adds the WebView2 native-to-SPA bridge and hardens it with origin validation, dispatcher marshaling for native posts, closed-window guards, sanitized bridge logging, and validated payload JSON.\n\nValidated locally with build.ps1, Shared tests, and Tray tests; GitHub CI is green.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add monitor-aware cascading submenu windows for tray flyouts, apply rounded HWND clipping so corners do not reveal the backing window, and reduce hover repaint flicker by reusing flyout windows with no-activate tool-window styling.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds the Windows tray local MCP HTTP server mode with loopback-only transport hardening, localized Settings UI, MCP-only startup support, and test coverage for the JSON-RPC bridge, HTTP security gates, cancellation, shutdown drain, and capability argument clamps.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two follow-ups from code review:
1. Hardcoded i18n strings in SettingsWindow are now resource-driven.
New x:Uid bindings cover the MCP toggle header, description,
Endpoint/Status labels, plus a new "Developer Mode" section header
above the toggle so the use case is visually scoped. Status text
("Disabled", "Listening", "Stopped", "Will start/stop on Save",
"Failed to start: ") routed through LocalizationHelper. Translations
added to en-us, fr-fr, nl-nl, zh-cn, and zh-tw. The diagnostic detail
appended after "Failed to start:" intentionally stays in English —
it embeds a literal `netsh http add urlacl` command that does not
localize meaningfully.
2. NodeService._capabilities is now guarded by _capabilitiesLock on
every read and write. The MCP bridge was previously snapshotting
_capabilities.ToArray() on threadpool threads while the UI thread
could be in the middle of RegisterCapabilities (Clear + adds). Low
probability but real — a tools/list mid-rebuild could observe an
empty or partial list. RegisterCapabilities now holds the lock for
the full sync rebuild; DisconnectAsync's Clear and the bridge
snapshot lambda also take it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per code review (GPT 5.5), tightens stability and operability of the
local MCP transport without changing the loopback-only trust model.
CR-003 — bound request and tool execution
* McpHttpServer: per-request linked CTS with 90s deadline; CT propagated
through ReadBodyAsync (slow-body defense) and bridge dispatch.
* McpToolBridge.HandleRequestAsync(body, ct) overload uses Task.WaitAsync
so a wedged tool surfaces as a "request timed out" tool error instead
of pinning a handler slot.
* SystemCapability.HandleRunAsync clamps timeoutMs <= 0 to the default
and caps at 10 minutes — closes the "wait forever" hole that
LocalCommandRunner.TimeoutMs <= 0 previously enabled.
CR-005 — drain in-flight handlers before disposal
* Track active handler tasks; StopAsync(drainTimeout) cancels CT, stops
the listener, awaits in-flight handlers (5s) before Dispose tears the
semaphore down. Removes the Task.Run(..., ct) cancellation race that
could leak a slot, and defensively swallows ObjectDisposedException
on semaphore release during shutdown.
CR-007 — clamp arguments at the capability boundary
* Screen / camera / canvas capabilities now clamp dimensions, quality,
monitor index, durations, fps, and window position to safe ranges
before any allocation. camera.clip floor-clamps duration so 0/-N can
no longer slip through the original Math.Min cap.
CR-008 — surface MCP startup status in Settings
* Categorize HttpListenerException by NativeErrorCode (5 = URL ACL
access denied with the exact netsh remediation; 32/183 = port in use).
* Settings status replaces the misleading "save and restart" wording
with "Will start/stop on Save", reflecting the OnSettingsSaved flow
that reinitializes services immediately.
Tests: 22 new test cases (timeout tool error, dispose-during-handler
hygiene, slow-body recovery, clamp behavior). Full Shared.Tests suite
remains green at 933 passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses review of the initial MCP HTTP server cut. Loopback bind alone
does not protect against browser-driven attacks — any page in the user's
browser is also on loopback. Adds a three-layer security gate, a body
size cap, a handler concurrency limit, and full HTTP-transport coverage.
Security
- McpHttpServer rejects requests with an Origin header (browsers always
set it; real MCP clients never do). Validates Host against loopback
names (DNS-rebinding pivot defense). Requires application/json on
POST so a cross-origin browser fetch must trigger a CORS preflight,
which we never satisfy.
- 4 MiB body cap with bounded streaming read (413 on overrun).
- 8-handler concurrency cap so a misbehaving local client cannot pin
every threadpool thread on long-running screen/camera calls.
- Settings migration: legacy McpOnlyMode=false no longer silently
inherits EnableNodeMode (would have flipped MCP on without consent).
Bridge
- _capabilities snapshotted to array per call (was racing concurrent
enumeration vs UI-thread mutation).
- Non-integer / out-of-range / string ids round-trip via GetRawText
instead of GetInt64 (used to strip the id from error responses on
fractional or big-int ids, breaking client correlation).
- tools/call validates 'arguments' is an object if present and rejects
empty 'name'.
- Generic 'internal error' on the wire for unhandled exceptions; full
exception with stack goes to the log via Error(string, Exception).
- Empty resources/list and prompts/list for Cursor compat (was
MethodNotFound).
- Non-object root → InvalidRequest.
Lifecycle
- Start() failure now disposes the half-constructed listener/CTS
instead of leaking the port reservation.
- _disposed guarded with Interlocked.Exchange (idempotent across
threads).
- App.xaml.cs warns when both modes are enabled but gateway
prerequisites are missing — silent fall-back to MCP-only was
confusing.
- NodeService.McpStartupError surfaces the actual failure to Settings
UI; status text shows 'Failed to start: <reason>' instead of the
misleading 'Stopped — save and restart to start'.
Refactor
- McpHttpServer moved from OpenClaw.Tray.WinUI/Services to
OpenClaw.Shared/Mcp (no WinUI deps; lets it be unit-tested).
Tests
- 10 new McpToolBridgeTests: non-object root, missing/non-string
params, non-object arguments, fractional/big-int/string ids,
empty resources/prompts, generic-message guarantee on internal
errors.
- New McpHttpServerTests (13): GET probe, valid POST, Origin reject,
rebind-Host reject, localhost-Host accept, text/plain reject,
json+charset accept, PUT reject, oversized body reject, notification
204, idempotent dispose, ctor null guards.
Docs
- MCP_MODE.md: rewritten Security Model section with the three-layer
model and curl-based 'verify the gate' examples; tool list updated
to reflect master (screen.snapshot, camera.clip, location.get).
All 715 tests pass (32 MCP, 20 integration, 663 other).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Document the tag-driven release process, versioning expectations, and retagging steps for OpenClaw Windows Hub releases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the new tray Support & Debug and Command Center labels into localized resources across all supported tray locales. Update tray markup coverage so the compact diagnostics menu keeps using resource-backed labels instead of English literals.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bring in the Windows tray parity work: Command Center support/debug entrypoints, diagnostics and summary copy surfaces, compact tray flyouts, Activity Stream history expansion, browser proxy guidance, manual update checks, SSH tunnel restart access, and refreshed Mission Control parity documentation.
Remaining parity blockers are intentionally documented: browser.proxy live smoke still needs matching gateway/browser-control auth or remote host setup, and Voice/TTS remains blocked pending a shared Mac/gateway contract and safer PR split.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Group recent activity previews and support file shortcuts into flyouts so the main tray menu stays compact while preserving all existing actions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>