google-gemini / google-gemini/gemini-cli

ACP: no tool_call session update before request_permission

Open
#21,783 5 comments 0 reactions 0 assignees View on GitHub
area/core effort/small kind/bug priority/p1 status/bot-triaged
Dominant language
TypeScript
Stars
107k
Forks
14.6k
Avg merge
2d 3h
Merged PRs (30d)
45

Description

### What happened?

In ACP mode, when a tool requires confirmation, `runTool()` sends `session/request_permission` but never sends a preceding `tool_call` session update. The [if/else in runTool](https://github.com/google-gemini/gemini-cli/blob/1fd42802be781c79e08f62da8056fef0863a7c50/packages/cli/src/acp/acpClient.ts#L888-L958) treats them as mutually exclusive:

- If `confirmationDetails` is truthy: sends `request_permission` only (no `tool_call`)
- If `confirmationDetails` is falsy: sends `tool_call` only (no `request_permission`)

After permission is granted, it sends `tool_call_update` (completed), but the client never saw a `tool_call` (pending) to associate it with.

This is easiest to reproduce with MCP tools from `session/new`. Since ACP doesn't pass `trust` to `MCPServerConfig`, MCP tools always need confirmation via the policy engine. The debug log shows the expected path:

```
[MESSAGE_BUS] publish: {"type":"tool-confirmation-request",...}
[PolicyEngine.check] toolCall.name: mcp_lookup_get_code, stringifiedArgs: {}
[PolicyEngine.check] NO MATCH - using default decision: ask_user
```

But the client only sees:

```json
{"sessionUpdate": "tool_call_update", "status": "completed", "toolCallId": "mcp_lookup_get_code-..."}
```

No `tool_call` with status pending or in_progress is ever emitted.

### What did you expect to happen?

Per the [ACP tool calls spec](https://agentclientprotocol.com/protocol/tool-calls#creating), the Agent SHOULD report a `tool_call` session update when the model requests a tool invocation. This should happen before [requesting permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission), so the client can track the tool call lifecycle:

```
tool_call (pending) -> request_permission -> tool_call_update (completed)
```

The fix is to send `tool_call` in both branches of the if/else, not just the else. Something like:

```typescript
// Always notify the client about the tool call first
await this.sendUpdate({
sessionUpdate: 'tool_call',
toolCallId: callId,
status: confirmationDetails ? 'pending' : 'in_progress',
title: invocation.getDescription(),
content: [],
locations: invocation.toolLocations(),
kind: toAcpToolKind(tool.kind),
});

if (confirmationDetails) {
// then request permission...
}
```

Note: #14957 shows `tool_call` with `in_progress` was being emitted for MCP tools in earlier versions, so this may be a regression from the confirmation refactor.

### Client information

Client Information

Built from source, commit 1fd42802b.

Platform: macOS (Darwin, aarch64)

### Login information

Google account.

### Anything else we need to know?

How to validate:
1. Start gemini in ACP mode with an MCP server via `session/new`
2. Prompt to use an MCP tool
3. Observe: client receives `session/request_permission` but no preceding `tool_call` session update
4. After approval, client receives `tool_call_update` (completed) with no matching `tool_call`

Related: #14957 (tool_call title issue, which shows MCP tools were emitting tool_call in earlier versions), #17952 (30s confirmation delay, same code path)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.