generated from amazon-archives/__template_Apache-2.0
-
Notifications
You must be signed in to change notification settings - Fork 256
feat: Add programmatic tool caller #387
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mkmeral
wants to merge
15
commits into
strands-agents:main
Choose a base branch
from
mkmeral:feat/programmatic-tool-caller
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
feat: Add programmatic tool caller #387
mkmeral
wants to merge
15
commits into
strands-agents:main
from
mkmeral:feat/programmatic-tool-caller
+799
−0
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This tool enables programmatic/code-based tool invocation for Strands Agents,
inspired by Anthropic's Programmatic Tool Calling feature. It allows an agent
to write Python code that calls other tools as functions.
Key features:
- Tools exposed as callable methods via 'tools.<tool_name>(**kwargs)'
- Supports complex orchestration with loops, conditionals, data processing
- Captures stdout/stderr from executed code
- Records all tool calls for transparency
- Validates code for potentially dangerous patterns
- User confirmation required unless BYPASS_TOOL_CONSENT is set
Example usage:
result = agent.tool.programmatic_tool_caller(
code='''
result = tools.calculator(expression="2 + 2")
print(f"Result: {result}")
'''
)
The tool integrates with Strands' DecoratedFunctionTool pattern, calling
tools directly with keyword arguments and handling both string and dict
return values.
Includes comprehensive unit tests covering:
- ToolProxy functionality
- Code validation
- Tool execution
- Integration with real tools
- Edge cases and error handling
Changes: - Use tool_context via @tool(context=True) instead of agent parameter - Handle multiple content blocks in tool results (combine all text) - Remove allowed_tools parameter (let agent decide which tools to use) - Add comprehensive integration tests with real tools - Fix test assertions and add more edge case coverage Test coverage: - 43 unit tests - 10 integration tests - All tests passing
- Add tool entry to the tools table - Add usage example section with code sample - Note that tool does not work on Windows (uses exec)
Major changes: - Remove ToolProxy class, inject tools directly as functions - Tools exposed as both async (tool_name) and sync (tool_name_sync) - Only return print() output, not tool call summary or execution time - Support async tool calls via asyncio This aligns with Anthropic's design where: - Tools are callable as async functions: await tool_name(...) - Only print() output is captured and returned to agent - Tool results stay in code execution context, don't enter agent messages
- Remove sync functions, only expose async (await tool_name(...)) - Auto-wrap user code in async function - no boilerplate needed - Support asyncio.gather() for parallel execution - Simplified implementation and tests
- Add Executor abstract base class for custom execution environments - LocalAsyncExecutor as default (local exec with asyncio) - Custom executors can be set via: programmatic_tool_caller.executor = MyExecutor() - Add PROGRAMMATIC_TOOL_CALLER_ALLOWED_TOOLS env var to control exposed tools - Tests for executor swapping and env var filtering
This reverts commit ca41d0f.
be93226 to
717566e
Compare
Use agent.tool.<name>() instead of directly calling tool_impl() from registry. This properly handles all tool types including MCP tools which are not directly callable but work through the ToolExecutor._stream() mechanism. - Changed _execute_tool to use getattr(agent.tool, tool_name)() - Added record_direct_tool_call=False to prevent polluting message history - Handle AttributeError for tool not found case
717566e to
b273bd6
Compare
mkmeral
commented
Feb 10, 2026
Contributor
Author
mkmeral
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We MUST handle interrupts!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
Adds
programmatic_tool_callertool that enables code-based tool invocation. Agents can write Python code that calls other tools as async functions, reducing API round-trips and enabling complex orchestration patterns like loops, parallel execution, and chaining.Note: Does not work on Windows.
Key Features
await tool_name(...)- code runs in async context automaticallyprint()output returned: Tool results stay in code execution context, don't enter agent's context window unless explicitly printedExecutorimplementations for sandboxed environments (Docker, Lambda, etc.)BYPASS_TOOL_CONSENT), configurable allowed toolsExample Usage
Environment Variables
BYPASS_TOOL_CONSENT"true"PROGRAMMATIC_TOOL_CALLER_ALLOWED_TOOLSCustom Executors
Related Issues
Type of Change
New Tool
Testing
asyncio.gather, custom executors, user cancellationChecklist