← Back to PRs

#23573: feat: add programmatic tool calling (PTC) support for Anthropic

by syumpx open 2026-02-22 13:10 View on GitHub →
agents size: M
## Summary Adds [Programmatic Tool Calling (PTC)](https://docs.anthropic.com/en/docs/build-with-claude/tool-use/computer-use#programmatic-tool-calling) support for Anthropic models. PTC lets Claude write Python code that calls custom tools as async functions inside a sandboxed container — instead of N inference passes for N tool calls, the model writes one script, and only `stdout` enters context (~37% token savings). ### What this PR does - **Config**: Adds `tools.ptc` with `enabled`, `tools` (optional allowlist), and `version` fields - **Tool injection**: `createPTCWrapper()` stream wrapper injects `code_execution_20260120` tool definition and sets `allowed_callers` on eligible custom tools - **Content blocks**: Adds `code_execution_tool_result` to `ANTHROPIC_SERVER_CONTENT_BLOCK_TYPES` so results from code execution aren't treated as client tool calls - **Helpers**: `isProgrammaticToolCall()` detects tool_use blocks with `caller` field; `extractCodeExecutionResult()` extracts stdout/stderr/returnCode from results - **Zod validation**: PTC config schema with strict mode ### Configuration ```json5 // Enable PTC for all tools { "tools": { "ptc": { "enabled": true } } } // Restrict to specific tools { "tools": { "ptc": { "enabled": true, "tools": ["exec", "web_search"] } } } ``` ### How it works 1. When `tools.ptc.enabled` is true and provider is Anthropic, the wrapper injects `code_execution_20260120` tool def into the API request 2. Custom tools get `allowed_callers: ["code_execution_20260120"]` added (or filtered by `tools.ptc.tools` allowlist) 3. Claude writes Python code that calls tools as functions — Anthropic executes the code server-side 4. Tool calls from within the code appear as `tool_use` blocks with a `caller` field — the existing SDK tool execution loop handles these identically to direct tool calls 5. `code_execution_tool_result` blocks (stdout/stderr) are preserved as server content ### What's NOT in this PR - **Container reuse**: Marked as TODO — requires wrapping the AsyncIterable response to extract `container.id` from response metadata - **Non-Anthropic providers**: PTC config is silently ignored for OpenAI/OpenRouter/etc. ## Relationship to other PRs - **Builds on #23469** (Anthropic server tools by @jayy-77) — uses the same `onPayload` wrapper pattern and extends `AnthropicServerToolId` - **Refs #20102** (PTC feature request) ## Test plan - [x] Unit tests: `isProgrammaticToolCall()` and `extractCodeExecutionResult()` with 15 test cases - [x] Existing `isAnthropicServerContentBlock` tests updated for `code_execution_tool_result` - [x] Existing extra-params tests (cache retention, Z.AI tool stream) still pass - [x] Schema validation and regression tests pass - [x] TypeScript type checking passes with no errors - [ ] Integration test: configure PTC enabled, verify `allowed_callers` appears in API payload - [ ] E2E test: send multi-tool prompt, verify model writes code that calls tools - [ ] Fallback test: disable PTC, verify standard tool calling unchanged 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds Programmatic Tool Calling (PTC) support for Anthropic models, enabling Claude to write Python code that calls custom tools via a sandboxed code execution container. Also introduces Anthropic server tool injection (web_search, web_fetch, code_execution) and deduplicates client-side tools when server equivalents are configured. - **PTC wrapper** (`createPTCWrapper`) injects the `code_execution` tool definition and adds `allowed_callers` to eligible custom tools so they can be invoked from code execution - **Server tools wrapper** (`createAnthropicServerToolsWrapper`) injects Anthropic server tool definitions into API requests - **Client tool deduplication** skips client-side `web_search`/`web_fetch` tools when Anthropic server equivalents are configured via `serverTools` - **Helper functions** in `content-blocks.ts` for detecting PTC tool calls, server content blocks, and extracting code execution results, with comprehensive tests - **Config & schema** adds `serverTools`, `ptc` fields to `ToolsConfig` with Zod validation - **Bug**: When both `serverTools` includes a `code_execution_*` entry and PTC is enabled without explicit `version`, the `allowed_callers` set on custom tools can reference a code execution version that doesn't exist in the API request (version mismatch between the injected server tool and the PTC default) <h3>Confidence Score: 3/5</h3> - This PR is generally safe but has a logic bug in the PTC/serverTools interaction that should be fixed before merge. - The overall code quality is good with clean helper functions, comprehensive tests, proper defensive coding, and backward-compatible changes. However, there's a version mismatch bug in createPTCWrapper where allowed_callers can reference a code_execution version not present in the request when both serverTools and PTC are configured. This would cause PTC to silently not work for affected configurations. The integration and E2E tests are also still unchecked per the PR description. - Pay close attention to `src/agents/pi-embedded-runner/extra-params.ts` — the `createPTCWrapper` function has a version mismatch bug in the interaction between serverTools and PTC allowed_callers. <sub>Last reviewed commit: 37a1b04</sub> <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs