feat: Add support for Anthropic advanced tool use beta features

Add support for the new advanced-tool-use-2025-11-20 beta features:

## Tool Search Tool
- New tool types: tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
- defer_loading property on tools for on-demand discovery
- MCPToolset type for MCP server tool configuration

## Programmatic Tool Calling
- New tool type: code_execution_20250825
- allowed_callers property to enable tools for programmatic execution
- ToolUseCaller interface for tracking call context in responses
- CodeExecutionToolResultBlock for code execution results

## Tool Use Examples
- input_examples property on tools for demonstrating usage patterns

## Implementation
- Auto-detect advanced features and add beta header automatically
- Support in both /messages and /v1/chat/completions endpoints
- Anthropic direct API, Bedrock, and Vertex AI providers updated
- Response types updated for new content blocks

Fixes type errors in Google providers caused by optional function property.
This commit is contained in:
Rohit Agarwal
2025-11-24 21:08:55 -08:00
parent da1a1d04fe
commit 2660d6b2a1
12 changed files with 493 additions and 23 deletions
+1
View File
@@ -146,3 +146,4 @@ plugins/**/.creds.json
plugins/**/creds.json
plugins/**/.parameters.json
src/handlers/tests/.creds.json
.cursor/
+57 -3
View File
@@ -1,4 +1,53 @@
import { ProviderAPIConfig } from '../types';
import { Params } from '../../types/requestBody';
// Beta header for advanced tool use features
const ADVANCED_TOOL_USE_BETA = 'advanced-tool-use-2025-11-20';
// Tool types that require the advanced tool use beta
const ADVANCED_TOOL_TYPES = [
'tool_search_tool_regex_20251119',
'tool_search_tool_bm25_20251119',
'code_execution_20250825',
'mcp_toolset',
];
/**
* Check if the request uses advanced tool use features that require the beta header.
*/
function requiresAdvancedToolUseBeta(gatewayRequestBody?: Params): boolean {
if (!gatewayRequestBody?.tools) return false;
return gatewayRequestBody.tools.some((tool) => {
// Check for advanced tool types
if (tool.type && ADVANCED_TOOL_TYPES.includes(tool.type)) {
return true;
}
// Check for advanced tool use properties
if (
tool.defer_loading !== undefined ||
tool.allowed_callers ||
tool.input_examples
) {
return true;
}
return false;
});
}
/**
* Combine beta headers, avoiding duplicates.
*/
function combineBetaHeaders(
existingBeta: string,
additionalBeta: string
): string {
const existing = existingBeta.split(',').map((s) => s.trim());
if (existing.includes(additionalBeta)) {
return existingBeta;
}
return [...existing, additionalBeta].join(',');
}
const AnthropicAPIConfig: ProviderAPIConfig = {
getBaseURL: () => 'https://api.anthropic.com/v1',
@@ -15,8 +64,8 @@ const AnthropicAPIConfig: ProviderAPIConfig = {
'X-API-Key': apiKey,
};
// Accept anthropic_beta and anthropic_version in body to support enviroments which cannot send it in headers.
const betaHeader =
// Accept anthropic_beta and anthropic_version in body to support environments which cannot send it in headers.
let betaHeader =
providerOptions?.['anthropicBeta'] ??
gatewayRequestBody?.['anthropic_beta'] ??
'messages-2023-12-15';
@@ -25,7 +74,12 @@ const AnthropicAPIConfig: ProviderAPIConfig = {
gatewayRequestBody?.['anthropic_version'] ??
'2023-06-01';
if (fn === 'chatComplete') {
// Add advanced tool use beta if needed
if (requiresAdvancedToolUseBeta(gatewayRequestBody)) {
betaHeader = combineBetaHeaders(betaHeader, ADVANCED_TOOL_USE_BETA);
}
if (fn === 'chatComplete' || fn === 'messages') {
headers['anthropic-beta'] = betaHeader;
}
headers['anthropic-version'] = version;
+35
View File
@@ -45,6 +45,20 @@ interface AnthropicTool extends PromptCache {
display_width_px?: number;
display_height_px?: number;
display_number?: number;
/**
* When true, this tool is not loaded into context initially.
* Claude discovers it via Tool Search Tool on-demand.
*/
defer_loading?: boolean;
/**
* List of tool types that can call this tool programmatically.
* E.g., ["code_execution_20250825"] enables Programmatic Tool Calling.
*/
allowed_callers?: string[];
/**
* Example inputs demonstrating how to use this tool.
*/
input_examples?: Record<string, any>[];
}
interface AnthropicToolResultContentItem {
@@ -370,8 +384,19 @@ export const AnthropicChatCompleteConfig: ProviderConfig = {
...(tool.cache_control && {
cache_control: { type: 'ephemeral' },
}),
// Advanced tool use properties
...(tool.defer_loading !== undefined && {
defer_loading: tool.defer_loading,
}),
...(tool.allowed_callers && {
allowed_callers: tool.allowed_callers,
}),
...(tool.input_examples && {
input_examples: tool.input_examples,
}),
});
} else if (tool.type) {
// Handle special tool types (tool_search, code_execution, mcp_toolset, etc.)
const toolOptions = tool[tool.type];
tools.push({
...(toolOptions && { ...toolOptions }),
@@ -380,6 +405,16 @@ export const AnthropicChatCompleteConfig: ProviderConfig = {
...(tool.cache_control && {
cache_control: { type: 'ephemeral' },
}),
// Advanced tool use properties for special tools
...(tool.defer_loading !== undefined && {
defer_loading: tool.defer_loading,
}),
...(tool.allowed_callers && {
allowed_callers: tool.allowed_callers,
}),
...(tool.input_examples && {
input_examples: tool.input_examples,
}),
});
}
});
+26
View File
@@ -108,6 +108,32 @@ export interface BedrockMessagesParams extends MessageCreateParamsBase {
anthropic_version?: string;
countPenalty?: number;
}
/**
* Tool parameter interface for Bedrock Messages API.
* Extends standard tool definition with advanced tool use properties.
*/
export interface BedrockMessagesToolParam {
name: string;
description?: string;
input_schema?: Record<string, any>;
type?: string;
cache_control?: { type: string };
/**
* When true, this tool is not loaded into context initially.
* Claude discovers it via Tool Search Tool on-demand.
*/
defer_loading?: boolean;
/**
* List of tool types that can call this tool programmatically.
* E.g., ["code_execution_20250825"] enables Programmatic Tool Calling.
*/
allowed_callers?: string[];
/**
* Example inputs demonstrating how to use this tool.
*/
input_examples?: Record<string, any>[];
}
export interface BedrockChatCompletionResponse {
metrics: {
latencyMs: number;
+62 -4
View File
@@ -110,6 +110,40 @@ export const transformAdditionalModelRequestFields = (
return additionalModelRequestFields;
};
// Beta header for advanced tool use features
const ADVANCED_TOOL_USE_BETA = 'advanced-tool-use-2025-11-20';
// Tool types that require the advanced tool use beta
const ADVANCED_TOOL_TYPES = [
'tool_search_tool_regex_20251119',
'tool_search_tool_bm25_20251119',
'code_execution_20250825',
'mcp_toolset',
];
/**
* Check if the request uses advanced tool use features that require the beta header.
*/
function requiresAdvancedToolUseBeta(tools?: Tool[]): boolean {
if (!tools) return false;
return tools.some((tool) => {
// Check for advanced tool types
if (tool.type && ADVANCED_TOOL_TYPES.includes(tool.type)) {
return true;
}
// Check for advanced tool use properties
if (
tool.defer_loading !== undefined ||
tool.allowed_callers ||
tool.input_examples
) {
return true;
}
return false;
});
}
export const transformAnthropicAdditionalModelRequestFields = (
params: BedrockConverseAnthropicChatCompletionsParams
) => {
@@ -132,15 +166,29 @@ export const transformAnthropicAdditionalModelRequestFields = (
if (params['thinking']) {
additionalModelRequestFields['thinking'] = params['thinking'];
}
// Handle anthropic_beta header, adding advanced tool use beta if needed
let betaHeaders: string[] = [];
if (params['anthropic_beta']) {
if (typeof params['anthropic_beta'] === 'string') {
additionalModelRequestFields['anthropic_beta'] = [
params['anthropic_beta'],
];
betaHeaders = [params['anthropic_beta']];
} else {
additionalModelRequestFields['anthropic_beta'] = params['anthropic_beta'];
betaHeaders = params['anthropic_beta'];
}
}
// Add advanced tool use beta if features are used
if (
requiresAdvancedToolUseBeta(params.tools) &&
!betaHeaders.includes(ADVANCED_TOOL_USE_BETA)
) {
betaHeaders.push(ADVANCED_TOOL_USE_BETA);
}
if (betaHeaders.length) {
additionalModelRequestFields['anthropic_beta'] = betaHeaders;
}
if (params.tools && params.tools.length) {
const anthropicTools: any[] = [];
params.tools.forEach((tool: Tool) => {
@@ -153,6 +201,16 @@ export const transformAnthropicAdditionalModelRequestFields = (
...(tool.cache_control && {
cache_control: { type: 'ephemeral' },
}),
// Advanced tool use properties
...(tool.defer_loading !== undefined && {
defer_loading: tool.defer_loading,
}),
...(tool.allowed_callers && {
allowed_callers: tool.allowed_callers,
}),
...(tool.input_examples && {
input_examples: tool.input_examples,
}),
});
}
});
+77 -11
View File
@@ -1,4 +1,44 @@
import { BedrockMessagesParams } from '../types';
import { ToolUnion } from '../../../types/MessagesRequest';
// Beta header for advanced tool use features
const ADVANCED_TOOL_USE_BETA = 'advanced-tool-use-2025-11-20';
// Tool types that require the advanced tool use beta
const ADVANCED_TOOL_TYPES = [
'tool_search_tool_regex_20251119',
'tool_search_tool_bm25_20251119',
'code_execution_20250825',
'mcp_toolset',
];
/**
* Check if the request uses advanced tool use features that require the beta header.
*/
function requiresAdvancedToolUseBeta(tools?: ToolUnion[]): boolean {
if (!tools) return false;
return tools.some((tool) => {
// Check for advanced tool types
if (tool.type && ADVANCED_TOOL_TYPES.includes(tool.type)) {
return true;
}
// Check for advanced tool use properties (only present on Tool type)
const toolWithAdvanced = tool as {
defer_loading?: boolean;
allowed_callers?: string[];
input_examples?: Record<string, unknown>[];
};
if (
toolWithAdvanced.defer_loading !== undefined ||
toolWithAdvanced.allowed_callers ||
toolWithAdvanced.input_examples
) {
return true;
}
return false;
});
}
export const transformInferenceConfig = (params: BedrockMessagesParams) => {
const inferenceConfig: Record<string, any> = {};
@@ -34,15 +74,29 @@ export const transformAnthropicAdditionalModelRequestFields = (
if (params['thinking']) {
additionalModelRequestFields['thinking'] = params['thinking'];
}
// Handle anthropic_beta header, adding advanced tool use beta if needed
let betaHeaders: string[] = [];
if (params['anthropic_beta']) {
if (typeof params['anthropic_beta'] === 'string') {
additionalModelRequestFields['anthropic_beta'] = [
params['anthropic_beta'],
];
betaHeaders = [params['anthropic_beta']];
} else {
additionalModelRequestFields['anthropic_beta'] = params['anthropic_beta'];
betaHeaders = params['anthropic_beta'];
}
}
// Add advanced tool use beta if features are used
if (
requiresAdvancedToolUseBeta(params.tools) &&
!betaHeaders.includes(ADVANCED_TOOL_USE_BETA)
) {
betaHeaders.push(ADVANCED_TOOL_USE_BETA);
}
if (betaHeaders.length) {
additionalModelRequestFields['anthropic_beta'] = betaHeaders;
}
return additionalModelRequestFields;
};
@@ -69,13 +123,25 @@ export const transformToolsConfig = (params: BedrockMessagesParams) => {
if (params.tools) {
for (const tool of params.tools) {
if (tool.type === 'custom' || !tool.type) {
tools.push({
toolSpec: {
name: tool.name,
inputSchema: { json: tool.input_schema },
description: tool.description,
},
});
const toolSpec: Record<string, any> = {
name: tool.name,
inputSchema: { json: tool.input_schema },
description: tool.description,
};
// Add advanced tool use properties
if (tool.defer_loading !== undefined) {
toolSpec.defer_loading = tool.defer_loading;
}
if (tool.allowed_callers) {
toolSpec.allowed_callers = tool.allowed_callers;
}
if (tool.input_examples) {
toolSpec.input_examples = tool.input_examples;
}
tools.push({ toolSpec });
if (tool.cache_control) {
tools.push({
cachePoint: {
@@ -280,7 +280,7 @@ export const VertexGoogleChatCompleteConfig: ProviderConfig = {
const functionDeclarations: any = [];
const tools: any = [];
params.tools?.forEach((tool) => {
if (tool.type === 'function') {
if (tool.type === 'function' && tool.function) {
// these are not supported by google
recursivelyDeleteUnsupportedParameters(tool.function?.parameters);
delete tool.function?.strict;
+5 -1
View File
@@ -743,6 +743,9 @@ export const googleTools = [
export const transformGoogleTools = (tool: Tool) => {
const tools: any = [];
// This function is called only when tool.function exists
if (!tool.function) return tools;
if (['googleSearch', 'google_search'].includes(tool.function.name)) {
const timeRangeFilter = tool.function.parameters?.timeRangeFilter;
tools.push({
@@ -773,7 +776,8 @@ export const buildGoogleSearchRetrievalTool = (tool: Tool) => {
const googleSearchRetrievalTool: GoogleSearchRetrievalTool = {
googleSearchRetrieval: {},
};
if (tool.function.parameters?.dynamicRetrievalConfig) {
// This function is called only when tool.function exists
if (tool.function?.parameters?.dynamicRetrievalConfig) {
googleSearchRetrievalTool.googleSearchRetrieval.dynamicRetrievalConfig =
tool.function.parameters.dynamicRetrievalConfig;
}
+1 -1
View File
@@ -384,7 +384,7 @@ export const GoogleChatCompleteConfig: ProviderConfig = {
const functionDeclarations: any = [];
const tools: any = [];
params.tools?.forEach((tool) => {
if (tool.type === 'function') {
if (tool.type === 'function' && tool.function) {
// these are not supported by google
recursivelyDeleteUnsupportedParameters(tool.function?.parameters);
delete tool.function?.strict;
+127 -1
View File
@@ -451,6 +451,27 @@ export interface Tool {
description?: string;
type?: 'custom' | null;
/**
* When true, this tool is not loaded into context initially.
* Claude discovers it via Tool Search Tool on-demand.
* Part of Anthropic's advanced tool use beta (advanced-tool-use-2025-11-20).
*/
defer_loading?: boolean;
/**
* List of tool types that can call this tool programmatically.
* E.g., ["code_execution_20250825"] enables Programmatic Tool Calling.
* Part of Anthropic's advanced tool use beta (advanced-tool-use-2025-11-20).
*/
allowed_callers?: string[];
/**
* Example inputs demonstrating how to use this tool.
* Helps Claude understand usage patterns beyond JSON schema.
* Part of Anthropic's advanced tool use beta (advanced-tool-use-2025-11-20).
*/
input_examples?: Array<Record<string, unknown>>;
}
export interface ToolBash20250124 {
@@ -566,12 +587,117 @@ export interface TextEditor20250429 {
cache_control?: CacheControlEphemeral | null;
}
/**
* Tool Search Tool with regex-based search.
* Enables Claude to discover tools on-demand instead of loading all upfront.
* Part of Anthropic's advanced tool use beta (advanced-tool-use-2025-11-20).
*/
export interface ToolSearchToolRegex {
/**
* Name of the tool search tool.
*/
name: string;
type: 'tool_search_tool_regex_20251119';
/**
* Create a cache control breakpoint at this content block.
*/
cache_control?: CacheControlEphemeral | null;
}
/**
* Tool Search Tool with BM25-based search.
* Enables Claude to discover tools on-demand instead of loading all upfront.
* Part of Anthropic's advanced tool use beta (advanced-tool-use-2025-11-20).
*/
export interface ToolSearchToolBM25 {
/**
* Name of the tool search tool.
*/
name: string;
type: 'tool_search_tool_bm25_20251119';
/**
* Create a cache control breakpoint at this content block.
*/
cache_control?: CacheControlEphemeral | null;
}
/**
* Code Execution Tool for Programmatic Tool Calling.
* Allows Claude to invoke tools from within a code execution environment.
* Part of Anthropic's advanced tool use beta (advanced-tool-use-2025-11-20).
*/
export interface CodeExecutionTool {
/**
* Name of the code execution tool.
*/
name: string;
type: 'code_execution_20250825';
/**
* Create a cache control breakpoint at this content block.
*/
cache_control?: CacheControlEphemeral | null;
}
/**
* Configuration for individual tools within an MCP toolset.
*/
export interface MCPToolConfig {
/**
* When true, this tool is not loaded into context initially.
*/
defer_loading?: boolean;
/**
* List of tool types that can call this tool programmatically.
*/
allowed_callers?: string[];
}
/**
* MCP Toolset for connecting MCP servers.
* Allows deferring loading for entire servers while keeping specific tools loaded.
* Part of Anthropic's advanced tool use beta (advanced-tool-use-2025-11-20).
*/
export interface MCPToolset {
type: 'mcp_toolset';
/**
* Name of the MCP server to connect to.
*/
mcp_server_name: string;
/**
* Default configuration applied to all tools in this MCP server.
*/
default_config?: MCPToolConfig;
/**
* Per-tool configuration overrides, keyed by tool name.
*/
configs?: Record<string, MCPToolConfig>;
/**
* Create a cache control breakpoint at this content block.
*/
cache_control?: CacheControlEphemeral | null;
}
export type ToolUnion =
| Tool
| ToolBash20250124
| ToolTextEditor20250124
| TextEditor20250429
| WebSearchTool20250305;
| WebSearchTool20250305
| ToolSearchToolRegex
| ToolSearchToolBM25
| CodeExecutionTool
| MCPToolset;
/**
* How the model should use the provided tools. The model can use a specific tool,
+82
View File
@@ -73,6 +73,23 @@ export interface TextBlock {
type: 'text';
}
/**
* Indicates the tool was called from within a code execution context.
* Present when using Programmatic Tool Calling.
* Part of Anthropic's advanced tool use beta (advanced-tool-use-2025-11-20).
*/
export interface ToolUseCaller {
/**
* The type of caller (e.g., "code_execution_20250825").
*/
type: string;
/**
* The ID of the server tool use block that initiated this call.
*/
tool_id: string;
}
export interface ToolUseBlock {
id: string;
@@ -81,6 +98,12 @@ export interface ToolUseBlock {
name: string;
type: 'tool_use';
/**
* Present when this tool was invoked from within a code execution context.
* Part of Anthropic's advanced tool use beta (advanced-tool-use-2025-11-20).
*/
caller?: ToolUseCaller;
}
export interface ServerToolUseBlock {
@@ -128,6 +151,64 @@ export interface WebSearchToolResultBlock {
type: 'web_search_tool_result';
}
/**
* Error codes for code execution tool results.
*/
export type CodeExecutionToolResultErrorCode =
| 'invalid_tool_input'
| 'unavailable'
| 'too_many_requests'
| 'execution_time_exceeded';
/**
* Error result from code execution.
*/
export interface CodeExecutionToolResultError {
error_code: CodeExecutionToolResultErrorCode;
type: 'code_execution_tool_result_error';
}
/**
* Output file from code execution.
*/
export interface CodeExecutionOutputBlock {
file_id: string;
type: 'code_execution_output';
}
/**
* Successful result from code execution.
*/
export interface CodeExecutionResultBlock {
content: Array<CodeExecutionOutputBlock>;
return_code: number;
stderr: string;
stdout: string;
type: 'code_execution_result';
}
export type CodeExecutionToolResultBlockContent =
| CodeExecutionToolResultError
| CodeExecutionResultBlock;
/**
* Result block from Programmatic Tool Calling code execution.
* Part of Anthropic's advanced tool use beta (advanced-tool-use-2025-11-20).
*/
export interface CodeExecutionToolResultBlock {
content: CodeExecutionToolResultBlockContent;
tool_use_id: string;
type: 'code_execution_tool_result';
}
export interface ThinkingBlock {
signature: string;
@@ -147,6 +228,7 @@ export type ContentBlock =
| ToolUseBlock
| ServerToolUseBlock
| WebSearchToolResultBlock
| CodeExecutionToolResultBlock
| ThinkingBlock
| RedactedThinkingBlock;
+19 -1
View File
@@ -370,7 +370,25 @@ export interface Tool extends PromptCache {
/** The name of the function. */
type: string;
/** A description of the function. */
function: Function;
function?: Function;
/**
* When true, this tool is not loaded into context initially.
* Claude discovers it via Tool Search Tool on-demand.
* Part of Anthropic's advanced tool use beta features.
*/
defer_loading?: boolean;
/**
* List of tool types that can call this tool programmatically.
* E.g., ["code_execution_20250825"] enables Programmatic Tool Calling.
* Part of Anthropic's advanced tool use beta features.
*/
allowed_callers?: string[];
/**
* Example inputs demonstrating how to use this tool.
* Helps Claude understand usage patterns beyond JSON schema.
* Part of Anthropic's advanced tool use beta features.
*/
input_examples?: Record<string, any>[];
// this is used to support tools like computer, web_search, etc.
[key: string]: any;
}