Skip to content

[TS-11328] feat: add runtime interactive tool approval (client, server, MCP)#39

Merged
masudahiroto merged 11 commits intomainfrom
masudahiroto/tool-runtime-interactive-approval
Mar 9, 2026
Merged

[TS-11328] feat: add runtime interactive tool approval (client, server, MCP)#39
masudahiroto merged 11 commits intomainfrom
masudahiroto/tool-runtime-interactive-approval

Conversation

@masudahiroto
Copy link
Copy Markdown
Contributor

@masudahiroto masudahiroto commented Mar 5, 2026

Add the ability to request user approval mid-execution of a tool, allowing approval logic to depend on runtime parameters.

Implemented across all three tool execution contexts:

  • Client-side & Server-side tools: A ctx argument is passed to the tool handler. Calling ctx.requestApproval({ message, metadata }) pauses execution, shows an approval dialog, and resumes based on the user's response.
  • MCP tools: Since MCP tools run on a remote server, mid-execution intervention is not possible. Instead, the tool returns a _use_ai_internal response. The server intercepts this, shows the approval dialog, and if approved, re-calls the same tool with the original args merged with additional_columns (e.g. a one-time token). See apps/example-nest-mcp-server/src/tools.service.ts for a clear example of this pattern.

Example pages and E2E tests added for all three patterns in apps/example/.

masudahiroto and others added 8 commits March 4, 2026 18:04
Enable tools to dynamically request user approval mid-execution based
on runtime conditions (e.g., API endpoint, input values), complementing
the existing static destructiveHint annotation flow.

- Add ToolExecutionContext with requestApproval() to client defineTool
- Add message/metadata fields to ToolApprovalRequestEvent (core types)
- Wire runtime approval resolvers in useToolSystem (client-side flow)
- Add requestApproval to ServerToolContext and serverToolExecutor
- Pass events to createServerToolExecutor in AISDKAgent
- Display runtime approval messages in ToolApprovalDialog
- Export ToolExecutionContext from client package
- Add unit tests for both client and server requestApproval flows

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ation

MCP tools can now request user confirmation by returning a JSON response
with `confirmation_required: true`. The server intercepts this, shows
the approval dialog, and if approved, calls the specified execution tool
on the same MCP endpoint (phase 2).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add RuntimeApprovalPage (client) and ServerRuntimeApprovalPage (server)
demo pages, and register serverTransfer tool with ctx.requestApproval()
for amounts over $1,000.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add transfer and confirm_transfer tools to the MCP test server that
demonstrate the two-phase confirmation pattern. Update the example page
to be an interactive demo instead of documentation-only.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace separate transfer + confirm_transfer tools with a single transfer
tool using a token parameter. The token is server-generated, one-time use,
parameter-bound, and expires after 5 minutes — preventing AI from bypassing
the approval flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The token never appears in the AI's context, so complex validation
(UUID generation, parameter binding, expiry) is unnecessary. A simple
fixed internal token suffices for bypass prevention.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dd E2E tests

Replace old confirmation_required/execute_on_approval schema with _use_ai_internal/_use_ai_type/_use_ai_metadata
to avoid namespace collisions with user data. Server now re-calls the same tool with original args merged with
additional_columns instead of requiring redundant tool name and full args in the response.

Also rename client-side transfer tool to clientTransfer for disambiguation, and add 9 E2E tests covering
approve/deny/small-transfer flows across client, server, and MCP approval types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…_ai_ dispatch

Split type guard into two layers:
- isUseAIInternalResponse: validates envelope (_use_ai_internal, _use_ai_type, _use_ai_metadata)
- isMcpConfirmationResponse: narrows to confirmation_required variant

AISDKAgent now uses isUseAIInternalResponse + switch on _use_ai_type,
making it easy to add new internal response types in the future.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@masudahiroto masudahiroto changed the title feat: add runtime interactive tool approval (client, server, MCP) [TS-11328] feat: add runtime interactive tool approval (client, server, MCP) Mar 5, 2026
@notion-workspace
Copy link
Copy Markdown

masudahiroto and others added 2 commits March 5, 2026 14:03
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use exact button text match to avoid ambiguous 'Server Tools' click
matching 'Runtime Approval (Server)'. Also fix h1 text to match
actual page heading.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@masudahiroto masudahiroto marked this pull request as ready for review March 5, 2026 07:32
/** Sentinel — must be `true` */
_use_ai_internal: true;
/** Discriminator — determines how the server handles this response */
_use_ai_type: string;
Copy link
Copy Markdown
Contributor

@mm-zacharydavison mm-zacharydavison Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we create types for the possible combinations of _use_ai_type and _use_ai_metadata`?

it's difficult to know how this can be used, and i don't think we should permit all types
maybe we need to share in core package

so then UseAIInternalResponse would be more like

export type UseAIInternalResponse = McpConfirmationResponse | UseAISomeOtherResponse

@masudahiroto
Copy link
Copy Markdown
Contributor Author

Thanks for the feedback! Addressed your comment.

@masudahiroto masudahiroto merged commit 6bb2757 into main Mar 9, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants