Skip to content

MCP tool approval prompts show no arguments — just the tool name #1685

Description

@petabridge-netclaw

Problem

When an MCP server is configured with "requires approval" permission, the approval prompt shows only the tool name (Dropbox/upload) with zero argument context. The user has no idea what the tool is about to do — which path, which file, what payload.

Current approval prompt (Slack):

Tool: `Dropbox/upload`
Request: `Dropbox/upload`

This gives the approver no actionable information. The request line should surface the arguments (paths, targets, values) so the user can make an informed decision.

Root Cause

`DefaultApprovalMatcher.FormatForDisplay()` (`src/Netclaw.Security/IToolApprovalMatcher.cs:772-773`) receives the arguments dictionary but ignores it entirely:

public string FormatForDisplay(ToolName toolName, IDictionary<string, object?>? arguments)
    => toolName.Value;

Contrast with `ShellApprovalMatcher.FormatForDisplay()` at line 572, which does extract and format the command from arguments:

public string FormatForDisplay(ToolName toolName, IDictionary<string, object?>? arguments)
{
    var command = GetCommand(arguments);  // extracts "command" arg
    if (string.IsNullOrWhiteSpace(command))
        return "(empty command)";
    // ... formats multi-line commands ...
    return command;
}

Shell approval prompts show the full command text. MCP tool prompts show only the tool name.

Data Flow

The `displayText` flows through the entire pipeline at a single point:

  1. `ToolAccessPolicy.CheckApprovalGate()` (line 315): `displayText = matcher.FormatForDisplay(toolName, arguments)`
  2. → `ToolApprovalContext` → `ToolApprovalRequiredException` → `SessionToolExecutionPipeline` (line 467)
  3. → `ToolInteractionRequest.DisplayText`
  4. → Each channel renderer (Slack / Discord / Mattermost / CLI) renders `request.DisplayText` as-is

No renderer changes are needed — all channels already render whatever `DisplayText` contains.

Affected Channels

All channels are affected equally:

Channel Label What it renders
Slack `Request:` tool name only
Discord `Action:` tool name only
Mattermost `Action:` tool name only

Proposed Fix

Update `DefaultApprovalMatcher.FormatForDisplay()` to format the arguments into a readable summary:

public string FormatForDisplay(ToolName toolName, IDictionary<string, object?>? arguments)
{
    if (arguments is null || arguments.Count == 0)
        return toolName.Value;

    // Build: tool_name(arg1=value, arg2=value, ...)
    // Truncate long values, summarize binary content
    var parts = arguments
        .Select(kv => $"{kv.Key}={FormatArgumentValue(kv.Value)}")
        .Where(s => !string.IsNullOrEmpty(s));

    return $"{toolName.Value}({string.Join(", ", parts)})}";
}

With this fix, the approval prompt would show:

Tool: `Dropbox/upload`
Request: `Dropbox/upload(path=/docs/README.md, contents=(2.4 KB))`

Considerations

  • Value formatting: MCP arguments arrive as `JsonElement` values — need to handle strings, numbers, objects, arrays
  • Truncation: Long string values should be summarized (`(2.4 KB)`, `(15 lines)`) rather than dumped verbatim. The outer budget is handled by `ApprovalDisplayTextFormatter.Truncate()` (2500 chars for Slack, 1700 for Discord)
  • Binary detection: Skip or summarize base64/hex blobs
  • Key ordering: Sort alphabetically or surface important keys (paths, targets, URLs) first

Files to Change

  • `src/Netclaw.Security/IToolApprovalMatcher.cs` — `DefaultApprovalMatcher.FormatForDisplay()`
  • Add matching tests in `src/Netclaw.Security.Tests/`

Metadata

Metadata

Assignees

No one assigned

    Labels

    UX/DXUI / UX / DX friction issue or user-facing annoyances.mcpModel context protocol server / client issues.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions