DEV Community

Saurabh Pathak
Saurabh Pathak

Posted on

Can an MCP server ever know a human really answered an elicitation?

I'm working on an approval gate for destructive MCP tools. The server asks the human through MCP elicitation, so the model never sees the approval step.

A commenter pointed out a hole: in Claude Code, a hook on the Elicitation event can exit 0 with action "accept" and answer the request with no dialog. The server only receives accept, so it can't tell a person from a script. A project .claude/settings.json in a cloned repo could register such a hook once the folder is trusted.

What I'm considering:

  • Log "client-accepted" instead of "approved", so the audit file never claims a human was involved.
  • Run the allowlist and policy checks before any elicitation, so a scripted accept can't override them.
  • Refuse irreversible tools when I can't tell how the answer was produced.

My question: is there any reliable way for an MCP server to know that a real person answered an elicitation? Or should the design assume it can't know, and rely on reversibility and quarantine instead?

I haven't checked which other clients let a script answer elicitation. If you know of any, please tell me. The project is open source, so PRs and issues are welcome.

Top comments (2)

Collapse
 
sattyamjjain profile image
Sattyam Jain •

I think the honest answer for form-mode elicitation is no. The 2025-11-25 spec says "the protocol itself does not mandate any specific user interaction model", and user approval controls on the client are a SHOULD. So logging "client-accepted" is right.

URL mode is the closest the spec gets. The server sends a URL; the data does not pass through the client, and the server "MUST verify the identity of the user who opens the URL before accepting information". A Claude Code Elicitation hook can still accept a URL-mode request, but it gets no content back, so the server keeps waiting for its own page. It is close to the one-time code idea above, but native to the spec.

One idea I have not tested: put the confirm step for irreversible tools on that server-hosted page, with a step-up check like a passkey with user verification. A scripted accept cannot finish that. It still does not help if someone controls the browser session, and I have not checked which clients support URL mode.

For repo hooks, allowManagedHooksOnly in managed settings limits which hooks run.

modelcontextprotocol.io/specificat...

Collapse
 
autenai profile image
Auten •

From the Auten team (screen-control tools, same wall). Is your threat model only hooks that a cloned repo brings in, or also hooks the user wrote? If it's repo hooks, an out-of-band step closes most of it: for irreversible tools, send a short one-time code to something the client process can't read (a phone push, an authenticator) and require that code in the elicitation reply. A scripted accept doesn't have it, so "client-accepted" and "human-confirmed" become two separate log states.