Allow for this session now sticks
Agent
On a trust: untrusted MCP server
Session and Always stop the next prompt for that tool. One-shot prompts offer only Once and Deny.
On an untrusted MCP server, choosing Allow for this session, or Always, now actually stops the next prompt for that tool. Prompts that can only cover one request now offer just Allow Once and Deny. It's a salvage of an earlier fix, rebuilt on current main, and it closes two issues.
The next call asked again
Agent
Pressing Session allowed one call, and the next call asked again
Turn 1
notes · write_note
Runs
Turn 2, same tool
notes · write_note
Asks again
Every write-capable call to an untrusted MCP server asks first, with Once, Session, Always and Deny. You press Session on the first call, and the tool runs. Then the next call to the same tool asks again. Session and Always behaved exactly like Once, on the gateway, the CLI and the TUI.
Why every yes covered one call
Agent
The consent helper turned every yes into a one-call accept
Recorded nothing
tools/approval_prompt.py
request_elicitation_consent mapped Once, Session and Always to the same single accept.
Offered everything
gateway/run_turn_runner.py · tui_gateway/server.py
The gateway request left out allow_session and allow_permanent, so both scopes defaulted to on.
The trust gate asks through request elicitation consent, in the approval prompt module. That helper turned every yes into a one-call accept, and recorded nothing. Its gateway request also left out both scope flags, so the gateway runner and the TUI server defaulted them to on, and showed buttons the helper never honoured.
An oversight, not a design choice
Agent
The helper's comment, written for server-initiated elicitations
# allow_permanent=False: elicitation is a per-call confirmation — no pattern to remember.
A trust-gate call does have something to remember: one (server, tool) pair.
The trust gate reused a helper written for server-initiated elicitations, before the gate existed. Its comment says per-call confirmation, no pattern to remember. A trust-gate call does have something to remember: one server and tool pair. So the broken Session and Always buttons were an oversight.
One key per server and tool
Agent
The gate keys each grant on one server and one tool
Key for the notes server's write tool
def _trust_approval_key(server_name, tool_name):
return f"mcp:{server_name}/{tool_name}"
def _trust_granted(server_name, tool_name):
return is_approved(
get_current_session_key(),
_trust_approval_key(server_name, tool_name))
gated = (
trust == _core._TRUST_UNTRUSTED
and not _tool_is_read_only(server_name, tool_name)
and not _trust_granted(server_name, tool_name))
In the MCP tool handlers, each answer is kept under a key made of the server name and the tool name, so allowing one write tool never covers the server's others. Before it asks, the gate checks whether that key is already approved in this session. A granted tool skips the prompt, and the spend limit check still runs.
Session and Always are stored
Agent
A key turns Session and Always into stored answers
def _remembered_consent(session_key: str, choice,
remember_key: str | None,
unresolved: str) -> str:
if remember_key is not None:
from tools import approval as _a
_a._persist_choice(session_key, choice,
[remember_key])
return _consent(choice, unresolved)
command_allowlistrequest elicitation consent gains one keyword argument, remember key. With a key, the answer goes through the existing persist choice, the same path command approvals use. Session covers that tool for the rest of this chat. Always writes the key into the active profile's command allowlist, so other chats skip the prompt too.
One-shot prompts offer Once and Deny
Agent
Prompts with nothing to remember offer only Once and Deny
Without a key, the helper sends both scope flags as false. Every surface then shows only Allow Once and Deny. That covers MCP elicitation, sampling, payment card fill, and spend limit prompts. A stale or typed broader answer still covers just that one call.
Over the limit, every call asks
Agent
An over-limit call asks every time, even after Always
# Over the limit the answer covers this call only:
# a remembered trust grant must not pre-approve
# the next over-limit call.
remember_key = (
None if over
else _trust_approval_key(server_name, tool_name))
scope = (
"once" if over
else "once, for this session, or always")
A call that would take the chat past its spend limit carries no key. So it offers only Once and Deny, and the description says so. An earlier Session or Always answer for that tool doesn't silence it.
Revoking Always
Agent
Review found a gap in revoking Always, and the docs now cover it
“Deleting the command_allowlist entry does not revoke an Always answer in the chat that granted it.”
greptile-apps · mcp-config-reference.md
“Deleting the entry revokes it for every other chat, and the chat that answered Always keeps it until it ends.”
teknium1 · fixed in f3414ff1dd6
The automated reviewer raised a real concern about revocation. Persist choice also keeps an Always answer in the granting chat's session, so deleting the allowlist entry leaves that chat approved. The author agreed, since command approvals work the same way, and fixed the docs to say the granting chat keeps it until it ends.
Live repro on a real gateway
Agent
On a real gateway, Session and Always stopped the second prompt
A hermes gateway run child, real inline-button presses, a stdio server notes with trust: untrusted
| Pressed on turn 1 | Base · turn 2 | Head · turn 2 | command_allowlist on head |
|---|---|---|---|
| Session | New prompt | No prompt, note two written | none |
| Always | New prompt | No prompt, note two written | ['mcp:notes/write_note'] |
| Once | Prompt | Prompt | none |
The live repro ran a real gateway process, a Telegram stand-in pressing real inline buttons, and an untrusted notes server. On the base commit, Session or Always on turn one still prompted on turn two. On this branch, turn two wrote the second note without a prompt, and Always left the key in the allowlist. Once, the control, asks both times.
Tests fail on main, pass here
Agent
Three new test cases fail on main and pass here
On origin/main
Driven through the real gateway approval queue
existing MCP and approval tests passed
Same fix seen live on
The new tests drive the real gateway approval queue. On main, all three cases fail, with three prompts where two were expected, and a missing allow session flag. All three pass here. More than 140 existing MCP and approval tests still pass, and the classic CLI and the TUI backend showed the same fix with real processes.
What it leaves alone
Agent
Still one-shot
Remembering elicitation answers per server request is a separate feature, in #142803.
No adapter changes
Telegram, Discord, Slack, Matrix, Teams and Feishu buttons, /approve, TUI and Desktop, the API server and ACP already honour the flags.
Revoke an Always answer
then end the chat that answered Always.
Allow for this session now sticks.
Elicitation answers still cover one request each. Remembering them per server request is a separate feature. The adapters stay as they were, because the messaging buttons, the TUI, Desktop, the API server and ACP already honour these flags. To take back an Always answer, delete its key from the command allowlist, then end the chat that granted it. Allow for this session now sticks, one tool at a time.












