Add 5 critical MCP tools: capabilities, back, key, gesture, batch#115
Conversation
Add maui_capabilities to AgentTools — discover agent-supported features. Add maui_back to NavigationTools — system back button navigation. Add maui_key to InteractionTools — keyboard input (enter, backspace, text). Add maui_gesture to InteractionTools — swipe, tap, longpress gestures with input validation for supported types and required parameters. Create BatchTools with maui_batch — atomic multi-action sequences with strict JSON validation and descriptive error messages. Profiler tools tracked in #112, WebView tools tracked in #113. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds missing Model Context Protocol (MCP) tools to the maui devflow mcp surface so AI agents can access newer DevFlow AgentClient capabilities.
Changes:
- Added new MCP tools:
maui_capabilities,maui_back,maui_key,maui_gesture. - Introduced
maui_batchtool (newBatchTools.cs) and registered it inMcpServerHost. - Implemented basic validation + JSON output formatting for new tool responses.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Cli/Microsoft.Maui.Cli/DevFlow/Mcp/Tools/NavigationTools.cs | Adds maui_back tool wrapping AgentClient.BackAsync(). |
| src/Cli/Microsoft.Maui.Cli/DevFlow/Mcp/Tools/InteractionTools.cs | Adds maui_key and maui_gesture tools for keyboard and gesture actions. |
| src/Cli/Microsoft.Maui.Cli/DevFlow/Mcp/Tools/BatchTools.cs | New tool maui_batch that parses action JSON and calls AgentClient.BatchAsync(). |
| src/Cli/Microsoft.Maui.Cli/DevFlow/Mcp/Tools/AgentTools.cs | Adds maui_capabilities tool wrapping AgentClient.GetCapabilitiesAsync(). |
| src/Cli/Microsoft.Maui.Cli/DevFlow/Mcp/McpServerHost.cs | Registers BatchTools with the MCP server host. |
| The 'actionsJson' parameter must be a JSON array of action objects. | ||
| Each action object must have an "action" field specifying the operation. | ||
|
|
||
| Supported actions and their fields: | ||
| - {"action":"tap", "elementId":"<id>"} | ||
| - {"action":"fill", "elementId":"<id>", "text":"<value>"} | ||
| - {"action":"clear", "elementId":"<id>"} | ||
| - {"action":"key", "key":"enter", "elementId":"<id>"} | ||
| - {"action":"focus", "elementId":"<id>"} | ||
| - {"action":"scroll", "elementId":"<id>", "deltaX":0, "deltaY":200} | ||
| - {"action":"gesture", "type":"swipe", "elementId":"<id>", "direction":"up"} | ||
| - {"action":"navigate", "route":"//page"} | ||
| - {"action":"back"} |
There was a problem hiding this comment.
The tool description and validation messaging say each action must have an "action" field, but the backend and existing tests accept either action or type (and AgentClient’s BatchAsync examples use type). This mismatch will confuse callers and can lead to sending objects with only action that may not behave as expected for some operations (e.g., gesture). Please align the schema documentation and error message with the actual accepted field(s) (e.g., require type or explicitly document action/type aliases and when each is needed).
- Fix BatchTools: use pattern match instead of AsArray() to avoid
InvalidOperationException on non-array JSON (e.g., {})
- Fix BatchTools: remove misleading 'atomic' wording, clarify sequential
non-transactional behavior
- Fix BatchTools: document 'action'/'type' field aliases and when each
is needed (gesture actions use both)
- Fix AgentTools: guard GetCapabilitiesAsync against Undefined JsonElement
when agent is unreachable
- Fix InteractionTools: clarify maui_key description — omitting elementId
may result in no action; update success message accordingly
- Fix InteractionTools: normalize gesture type (accept 'long-press' alias),
validate swipe direction against supported set (up/down/left/right)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| return $"Invalid action at index {i}: expected a JSON object, got {parsed[i]?.GetValueKind().ToString() ?? "null"}."; | ||
|
|
||
| if (obj["action"] == null && obj["type"] == null) | ||
| return $"Invalid action at index {i}: must have an 'action' field (e.g., 'tap', 'fill', 'navigate')."; |
There was a problem hiding this comment.
The validation allows an action object to have either an action or type field, but this error message says only action is required. Update the message (or the validation) so it matches the accepted schema.
| return $"Invalid action at index {i}: must have an 'action' field (e.g., 'tap', 'fill', 'navigate')."; | |
| return $"Invalid action at index {i}: must have an 'action' or 'type' field (e.g., 'tap', 'fill', 'navigate')."; |
| [McpServerTool(Name = "maui_batch"), Description(""" | ||
| Execute multiple UI actions in a single request. Actions run sequentially and are not transactional. | ||
| Earlier actions are applied even if a later action fails. | ||
| The 'actionsJson' parameter must be a JSON array of action objects. |
There was a problem hiding this comment.
PR description calls maui_batch “atomic”, but the tool description explicitly says actions are not transactional and earlier actions may be applied even if later ones fail. Please align the PR description and tool behavior/description so “atomic” isn’t misleading.
| [McpServerTool(Name = "maui_key"), Description("Send a key press to an element. Supported keys for Entry/Editor/SearchBar: 'enter' (submit or newline), 'backspace' (delete last character). Use 'text' parameter to type characters. For reliable behavior, provide an element ID; omitting it may have no effect depending on the agent/platform implementation.")] | ||
| public static async Task<string> Key( | ||
| McpAgentSession session, | ||
| [Description("Key to press: 'enter', 'return', 'backspace', 'delete'")] string key, | ||
| [Description("Target element ID. Optional, but omitting it may result in no action; provide an element ID for reliable behavior.")] string? elementId = null, |
There was a problem hiding this comment.
maui_key requires a non-empty key, but the agent endpoint supports either key or text. With the current signature, “type-only” input isn’t possible without a dummy/empty key, and passing both key and text can result in text being ignored for keys like enter/return. Consider making key nullable/optional and validating that at least one of key or text is provided (and ideally reject conflicting combinations).
| [McpServerTool(Name = "maui_gesture"), Description("Perform a touch gesture on the app. Supported gesture types: 'swipe' (requires direction), 'tap', 'longpress', and 'long-press'. Use maui_tap for simple taps — this tool is for advanced gestures like swiping.")] | ||
| public static async Task<string> Gesture( | ||
| McpAgentSession session, | ||
| [Description("Gesture type: 'swipe', 'tap', 'longpress', or 'long-press'")] string type, | ||
| [Description("Target element ID (optional)")] string? elementId = null, |
There was a problem hiding this comment.
For maui_gesture, elementId is typed/described as optional, but tap/longpress are implemented by delegating to the tap action, which requires elementId. Consider failing fast when type is tap/longpress and elementId is missing, or update the parameter description to clarify it’s required for those gesture types.
| [Description("Swipe direction: 'up', 'down', 'left', or 'right' (required for swipe)")] string? direction = null, | ||
| [Description("Swipe distance in pixels (optional, uses default if omitted)")] double? distance = null, | ||
| [Description("Gesture duration in milliseconds (optional)")] int? durationMs = null, | ||
| [Description("Agent HTTP port (optional if only one agent connected)")] int? agentPort = null) |
There was a problem hiding this comment.
distance/durationMs default to null, but the agent request model uses non-nullable double Distance/int DurationMs with defaults. Sending JSON null for these fields can cause deserialization to fail on the agent. Consider passing explicit defaults when omitted (matching the agent defaults), or ensure null values aren’t serialized/sent.
| [McpServerTool(Name = "maui_gesture"), Description("Perform a touch gesture on the app. Supported gesture types: 'swipe' (requires direction), 'tap', 'longpress', and 'long-press'. Use maui_tap for simple taps — this tool is for advanced gestures like swiping.")] | ||
| public static async Task<string> Gesture( | ||
| McpAgentSession session, | ||
| [Description("Gesture type: 'swipe', 'tap', 'longpress', or 'long-press'")] string type, |
There was a problem hiding this comment.
The maui_gesture description implies longpress is a distinct gesture, but the agent currently handles longpress by delegating to the tap action. Consider clarifying in the tool description (or restricting supported types) so agents don’t assume a true long-press interaction is performed.
| [McpServerTool(Name = "maui_gesture"), Description("Perform a touch gesture on the app. Supported gesture types: 'swipe' (requires direction), 'tap', 'longpress', and 'long-press'. Use maui_tap for simple taps — this tool is for advanced gestures like swiping.")] | |
| public static async Task<string> Gesture( | |
| McpAgentSession session, | |
| [Description("Gesture type: 'swipe', 'tap', 'longpress', or 'long-press'")] string type, | |
| [McpServerTool(Name = "maui_gesture"), Description("Perform a touch gesture on the app. Supported gesture types: 'swipe' (requires direction) and 'tap'. 'longpress' and 'long-press' are accepted for compatibility but currently use tap behavior rather than performing a true long-press interaction. Use maui_tap for simple taps — this tool is primarily for advanced gestures like swiping.")] | |
| public static async Task<string> Gesture( | |
| McpAgentSession session, | |
| [Description("Gesture type: 'swipe' or 'tap'. 'longpress' and 'long-press' are accepted aliases but currently behave the same as 'tap', not a true long press.")] string type, |
This comment has been minimized.
This comment has been minimized.
🔍 Expert Code Review — Adversarial ConsensusMethodology: 3 independent reviewers with adversarial consensus. Disputed findings were cross-examined by the other reviewers. Only findings with ≥2/3 agreement are included. CI Status: ✅ All 7 checks passed (macOS build, Windows build, CLA, agent tests, etc.) 🟡 MODERATE —
|
| Finding | Flagged By | Verdict |
|---|---|---|
| Batch validation should enforce semantic rules per action type | Reviewer 3 | Discarded — Reviewers 1 & 2 confirmed this follows the established lightweight-validation pattern; backend returns structured per-action errors |
maui_key success message omits text parameter feedback |
Reviewer 1 | Discarded — single reviewer |
maui_key success message "may have had no effect" is misleading |
Reviewer 2 | Discarded — single reviewer |
maui_gesture description lists long-press as distinct type |
Reviewer 1 | Discarded — single reviewer |
Batch error message casing inconsistency ("Null" vs "null") |
Reviewer 2 | Discarded — single reviewer |
Summary
| Severity | Count | Key Theme |
|---|---|---|
| 🟡 MODERATE | 3 | Exception safety, serialization bug, API design |
| 🟢 MINOR | 1 | Error message accuracy |
Overall: Clean, well-structured PR that follows established MCP tool patterns. The most impactful issue is the gesture serialization bug (nullable → non-nullable deserialization failure) which causes valid default-parameter calls to fail silently. The batch exception handling gap is straightforward to fix. The key parameter design could benefit from making key optional to support text-only scenarios.
Test coverage: No new unit tests added for the 5 new tools. The PR notes 260 existing tests are unaffected. Consider adding tests for the batch JSON validation logic (valid/invalid arrays, edge cases) and gesture parameter handling in a follow-up.
Note
🔒 Integrity filter blocked 1 item
The following item were blocked because they don't meet the GitHub integrity level.
- #100
list_pull_requests: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
To allow these resources, lower min-integrity in your GitHub frontmatter:
tools:
github:
min-integrity: approved # merged | approved | unapproved | noneGenerated by Expert Code Review · ● 15.4M · ◷
…' fields The validation logic accepts either 'action' or 'type' on each action object, but the error message only mentioned 'action', which would confuse AI agents. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Follow-up items for @rmarinho (non-blocking, from the expert review):
None of these are the tools follow established patterns and CI is green.blocking |
jfversluis
left a comment
There was a problem hiding this comment.
Reviewed with multi-model consensus (Opus 4.5 + Sonnet 4) cross-referenced with the expert adversarial review. Pushed fix for the batch error message. Remaining items noted as non-blocking follow-ups. CI green, tools follow MCP conventions, descriptions are AI-agent-friendly.
… message (#144) * Harden MCP tools: batch exception handling, gesture null params, capabilities message - Wrap BatchAsync in try/catch for HttpRequestException, TaskCanceledException, and JsonException so batch tool returns a friendly error instead of crashing - Omit null distance/durationMs from gesture JsonObject to prevent agent-side deserialization failure on non-nullable DTO properties - Improve capabilities error message to distinguish unreachable agents from older agents that lack the capabilities endpoint Follow-up to #115. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback: ScrollAsync nulls, OperationCanceledException, error messages - Fix ScrollAsync to omit null itemIndex/groupIndex/scrollToPosition/elementId from JsonObject (same pattern as the GestureAsync fix) - Widen batch catch filter from TaskCanceledException to OperationCanceledException (the base class) - Improve batch and capabilities error messages to mention both connectivity and version/feature support Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sus (#35111) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Summary Adds a `/review` slash command that triggers a 3-model adversarial code review on any PR. ## How It Works 1. A maintainer comments `/review` on a PR 2. The orchestrator (Opus) dispatches 3 parallel sub-agents (Opus, Sonnet, Codex) to independently review the PR 3. Findings go through adversarial consensus — 3/3 include, 2/3 include, 1/3 gets challenged by the other 2 models 4. Results posted as inline review comments on diff lines + a COMMENT review summary ## Files | File | Purpose | |------|---------| | `.github/workflows/review.agent.md` | `/review` slash command trigger + workflow_dispatch for testing | | `.github/workflows/shared/review-shared.md` | Shared orchestration (multi-model dispatch, consensus, posting) | | `.github/workflows/review.agent.lock.yml` | Auto-generated compiled workflow | | `.github/aw/actions-lock.json` | Pinned action versions (adds v0.71.0, preserves existing entries) | ## Design Decisions - **`/review` only** — no auto-review-on-open to avoid cost on every PR in a large repo - **COMMENT-only reviews** — `allowed-events: [COMMENT]` prevents stale blocking reviews that cannot be dismissed ([gh-aw#27655](github/gh-aw#27655)) - **Inline + summary** — `create_pull_request_review_comment` for diff-line annotations, `submit_pull_request_review` for summary, `add_comment` as fallback - **Gated to write+ roles** — `roles: [admin, maintainer, write]` - **Token-optimized** — orchestrator delegates file reading to sub-agents, caps follow-ups at 2 models and 3 disputed findings - **Sub-agents use `.github/skills/code-review/SKILL.md`** — existing MAUI code review skill with 345 lines of maintainer-sourced review rules ## Trial Run Validated end-to-end via `gh aw trial`: - [PureWeen/gh-aw-trial run](https://github.com/PureWeen/gh-aw-trial/actions/runs/24992602411) — all 6 jobs passed (pre_activation, activation, agent, detection, safe_outputs, conclusion) - Compiled with 0 errors, 0 warnings at gh-aw v0.71.0 ## Provenance Ported from [dotnet/maui-labs PR #118](dotnet/maui-labs#118), iteratively tested and refined across: - [dotnet/maui-labs PR #115](dotnet/maui-labs#115 (comment)) (add_comment path verified) - [PureWeen/PolyPilot PR #656](PureWeen/PolyPilot#656) (inline review comments verified) - [dotnet/maui-labs PR #123](dotnet/maui-labs#123) (inline + summary verified) --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sus (dotnet#35111) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Summary Adds a `/review` slash command that triggers a 3-model adversarial code review on any PR. ## How It Works 1. A maintainer comments `/review` on a PR 2. The orchestrator (Opus) dispatches 3 parallel sub-agents (Opus, Sonnet, Codex) to independently review the PR 3. Findings go through adversarial consensus — 3/3 include, 2/3 include, 1/3 gets challenged by the other 2 models 4. Results posted as inline review comments on diff lines + a COMMENT review summary ## Files | File | Purpose | |------|---------| | `.github/workflows/review.agent.md` | `/review` slash command trigger + workflow_dispatch for testing | | `.github/workflows/shared/review-shared.md` | Shared orchestration (multi-model dispatch, consensus, posting) | | `.github/workflows/review.agent.lock.yml` | Auto-generated compiled workflow | | `.github/aw/actions-lock.json` | Pinned action versions (adds v0.71.0, preserves existing entries) | ## Design Decisions - **`/review` only** — no auto-review-on-open to avoid cost on every PR in a large repo - **COMMENT-only reviews** — `allowed-events: [COMMENT]` prevents stale blocking reviews that cannot be dismissed ([gh-aw#27655](github/gh-aw#27655)) - **Inline + summary** — `create_pull_request_review_comment` for diff-line annotations, `submit_pull_request_review` for summary, `add_comment` as fallback - **Gated to write+ roles** — `roles: [admin, maintainer, write]` - **Token-optimized** — orchestrator delegates file reading to sub-agents, caps follow-ups at 2 models and 3 disputed findings - **Sub-agents use `.github/skills/code-review/SKILL.md`** — existing MAUI code review skill with 345 lines of maintainer-sourced review rules ## Trial Run Validated end-to-end via `gh aw trial`: - [PureWeen/gh-aw-trial run](https://github.com/PureWeen/gh-aw-trial/actions/runs/24992602411) — all 6 jobs passed (pre_activation, activation, agent, detection, safe_outputs, conclusion) - Compiled with 0 errors, 0 warnings at gh-aw v0.71.0 ## Provenance Ported from [dotnet/maui-labs PR dotnet#118](dotnet/maui-labs#118), iteratively tested and refined across: - [dotnet/maui-labs PR dotnet#115](dotnet/maui-labs#115 (comment)) (add_comment path verified) - [PureWeen/PolyPilot PR dotnet#656](PureWeen/PolyPilot#656) (inline review comments verified) - [dotnet/maui-labs PR dotnet#123](dotnet/maui-labs#123) (inline + summary verified) --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Closes the critical gap between DevFlow's AgentClient API and the MCP tool surface. After the v1 spec alignment (2c05e68), several new AgentClient methods had no MCP tools.
New MCP Tools
maui_capabilitiesGetCapabilitiesAsyncmaui_backBackAsyncmaui_keyKeyAsyncmaui_gestureGestureAsyncmaui_batchBatchAsyncDesign Decisions
maui_gesturevalidates types to the supported backend set (swipe,tap,longpress) and requiresdirectionfor swipes — fails fast instead of silently succeedingmaui_keydocuments actual backend behavior (enter/backspace work on Entry/Editor/SearchBar; other keys may be no-ops)maui_batchaccepts a JSON string with strict validation (array check, object check, action field check) with descriptive error messagesmaui_capabilitiesplaced in AgentTools since it's agent metadata, not UI interactionRemaining Gaps (tracked in issues)
Testing
dotnet build src/Cli/Microsoft.Maui.Cli/)maui devflow mcpwith a running MAUI app, or the equivalent CLI commands