Skip to content

Conversation

@vinitkadam03
Copy link
Contributor

Description

Summary

This PR introduces support for client-executed tools that are intended to be executed by the client/caller rather than by Prism.

Motivation

Client-executed tools enable scenarios where tool execution must happen on the client side, such as:

  • Interactive user input - Rendering forms, confirmations, or option selectors based on tool call params passed by llm, then continuing the conversation with the user's selection (similar to how AI coding assistants ask clarifying questions during agentic workflows)
  • Browser automation - Controlling UI elements, clicking buttons, or navigating pages
  • Frontend-only operations - Accessing browser APIs, local storage, or device capabilities
  • Any tool where the server should not (or cannot) execute the logic

Changes

Core Implementation

src/Tool.php

  • Added clientExecuted() method to explicitly mark a tool as client-executed
  • Added isClientExecuted() method that returns true when no handler function is defined ($this->fn === null)
public function clientExecuted(): self
{
    $this->fn = null;

    return $this;
}

public function isClientExecuted(): bool
{
    return $this->fn === null;
}

src/Concerns/CallsTools.php

  • Modified callToolsAndYieldEvents() to filter out client-executed tools from execution

Behavior:

  • Client-executed tools are skipped during tool execution
  • Server-executed tools in the same request are still executed normally
  • When client-executed tools are detected, execution stops and control is returned to the caller
  • The LLM is not called for the next turn, allowing the client to execute the tool and continue the conversation
  • Response/stream ends with FinishReason::ToolCalls

Usage Example

use Prism\Prism\Facades\Tool;

// Explicit declaration (recommended)
$clientTool = Tool::as('browser_action')
    ->for('Perform an action in the user\'s browser')
    ->withStringParameter('action', 'The action to perform')
    ->clientExecuted();

// Implicit declaration (also works - omit using())
$clientTool = Tool::as('browser_action')
    ->for('Perform an action in the user\'s browser')
    ->withStringParameter('action', 'The action to perform');

Breaking Changes

None. This is a backward-compatible addition. Existing tools with handlers continue to work exactly as before.

@sixlive
Copy link
Contributor

sixlive commented Jan 27, 2026

I was just working on implementing a mechanism for "tool approvals" and I think this might be it.

@vinitkadam03
Copy link
Contributor Author

vinitkadam03 commented Jan 28, 2026

I was just working on implementing a mechanism for "tool approvals" and I think this might be it.

that is next thing that can be added. Hence I have added the hasPendingTools variable which will be true if there are tool needing approval in llm response.

a new helper requiresApproval will be added in tool which will emit tool approval required events. frontend will render tool approval ui and add tool approval response and then prism will execute approved tools or add approval denied output and continue llm flow.

https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling#tool-execution-approval

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants