crewai - ✅(Solved) Fix [BUG] Bedrock LLM Claude Sonnet returning empty Input Params on Tools calling with MCP Server. [2 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
crewAIInc/crewAI#4470Fetched 2026-04-08 00:41:55
View on GitHub
Comments
1
Participants
2
Timeline
9
Reactions
0
Timeline (top)
referenced ×3cross-referenced ×2commented ×1labeled ×1

Hi, I have running a Crewai script using Amazon Bedrock LLM Using model Bedrock Claude Model. I'm able to fetch the tools details using MCPServerAdapter module, but when calling the Tool using the LLM, the LLM returns empty Input Parameters. The Same when I use Gemini LLM, it works fine.

Error Message

except Exception as e: print(f"Error starting MCP Server Adapter. {e}") except Exception as e: print(f"Error stoppting MCP Server Adapter. {e}") except Exception as e: print(f"Error during crew kickoff: {e}")

Root Cause

Hi, I have running a Crewai script using Amazon Bedrock LLM Using model Bedrock Claude Model. I'm able to fetch the tools details using MCPServerAdapter module, but when calling the Tool using the LLM, the LLM returns empty Input Parameters. The Same when I use Gemini LLM, it works fine.

Fix Action

Fix / Workaround

def patched_schema(*args, __orig=original_schema_fn, **kwargs): raw = __orig(*args, **kwargs) return sanitize_schema(raw)

tool.args_schema.model_json_schema = patched_schema

PR fix notes

PR #4471: fix: resolve empty tool input params for Bedrock tool calls (#4470)

Description (problem / solution / changelog)

fix: resolve empty tool input params for Bedrock tool calls (#4470)

Summary

Fixes #4470 — Bedrock LLM (Claude Sonnet) returning empty input params on tool calls.

Root cause: In _handle_native_tool_calls (line 737), when a Bedrock-style dict tool call arrives (with name/input keys, no function key), the expression func_info.get("arguments", "{}") returns the default string "{}" which is truthy, causing the or to short-circuit and never reach tool_call.get("input", {}). The tool arguments are lost and become empty.

Fix: One-line change — remove the truthy default so None is returned when the key is absent, allowing fallthrough:

# Before (broken):
func_args = func_info.get("arguments", "{}") or tool_call.get("input", {})
# After (fixed):
func_args = func_info.get("arguments") or tool_call.get("input") or {}

This matches the pattern already used in extract_tool_call_info() in agent_utils.py.

Review & Testing Checklist for Human

  • Verify OpenAI-style tool calls still work: The changed expression also handles OpenAI dicts (where function.arguments is a JSON string). Confirm that removing the "{}" default doesn't regress when arguments key is present with a valid string value, or absent entirely.
  • Test against real Bedrock endpoint: The new tests use mocks only. If possible, verify with an actual Bedrock Claude model that tool input params are now populated correctly (the reproducer in the issue can be used).
  • Unused imports in tests: test_bedrock_tool_call_dict_without_function_key imports CrewAgentExecutor, Mock, and json but only uses inline dict logic — these unused imports may trigger lint warnings in CI.

Notes

Changed files

  • lib/crewai/src/crewai/agents/crew_agent_executor.py (modified, +1/-1)
  • lib/crewai/tests/llms/bedrock/test_bedrock.py (modified, +105/-0)

PR #5024: fix(bedrock): handle MCP tools with inputSchema in extract_tool_info

Description (problem / solution / changelog)

Description

MCP servers advertise tools using inputSchema (not parameters) in the function definition:

{
  "type": "function",
  "function": {
    "name": "list_resources",
    "description": "...",
    "inputSchema": {
      "type": "object",
      "properties": { "filter": { "type": "string" } }
    }
  }
}

extract_tool_info() (in llms/providers/utils/common.py) only looked for the parameters key, so all MCP tools arrived at Bedrock with an empty inputSchema: {}, causing Claude Sonnet to ignore all tool inputs and return empty param responses.

Root Cause

# Before fix — only parameters key is checked:
parameters = function_info.get("parameters", {})  # MCP tools return {} here

Fix

Fall back to inputSchema when parameters is absent or empty, in both the nested function-wrapper format and the direct tool format. parameters still takes precedence when both keys are present:

parameters = function_info.get("parameters") or function_info.get("inputSchema") or {}

The fix is applied in extract_tool_info() which is shared by all native LLM providers.

Testing

Added tests/llms/providers/utils/test_common.py with 6 tests covering:

  • Core regression: MCP tool with inputSchema inside function wrapper → non-empty params
  • MCP tool in direct format with inputSchema
  • parameters takes precedence over inputSchema when both present
  • Standard OpenAI-format tools still work correctly
  • safe_tool_conversion end-to-end with MCP tool

Fixes #4470

Changed files

  • lib/crewai/src/crewai/llms/providers/utils/common.py (modified, +4/-2)
  • lib/crewai/tests/llms/providers/utils/test_common.py (added, +119/-0)
RAW_BUFFERClick to expand / collapse

Description

Hi, I have running a Crewai script using Amazon Bedrock LLM Using model Bedrock Claude Model. I'm able to fetch the tools details using MCPServerAdapter module, but when calling the Tool using the LLM, the LLM returns empty Input Parameters. The Same when I use Gemini LLM, it works fine.

Steps to Reproduce

from crewai import Agent, Task, Crew, Process, LLM from crewai_tools import MCPServerAdapter from dotenv import load_dotenv import os

load_dotenv()

llm = LLM( model="bedrock/anthropic.claude-3-sonnet-20240229-v1:0", )

''' llm = LLM( model="gemini/gemini-2.5-flash", # or gemini/gemini-2.0-flash api_key=os.getenv("GEMINI_API_KEY"), ) '''

server_params = { "url": "http://10.10.10.10/mcp", "transport": "streamable-http", }

mcp_server_adapter = MCPServerAdapter(server_params) mcp_tools = mcp_server_adapter.tools print(f"Available Tools: {[tool.name for tool in mcp_tools]}")

''' for tool in mcp_tools: print(tool.name) print(tool) '''

def sanitize_schema(schema: dict) -> dict: """ Strict sanitizer for Bedrock Claude tool schemas. Produces minimal JSON Schema Draft-7 subset. """

clean = {
    "type": "object",
    "properties": {},
    "required": list(schema.get("required", [])),
    "additionalProperties": False,  # Important for Claude
}

for name, prop in schema.get("properties", {}).items():

    prop_type = prop.get("type", "string")

    clean_prop = {
        "type": prop_type,
        "description": (
            prop.get("description")
            if prop.get("description")
            else f"Required parameter '{name}'. This field MUST be provided."
        ),
    }

    # Keep enum only if valid non-empty list
    if isinstance(prop.get("enum"), list) and prop["enum"]:
        clean_prop["enum"] = prop["enum"]

    # Handle arrays safely
    if prop_type == "array":
        items = prop.get("items", {})
        clean_prop["items"] = {"type": items.get("type", "string")}

    # Handle nested object safely (flat only)
    if prop_type == "object" and prop.get("properties"):
        nested_props = {}
        for nested_name, nested_prop in prop["properties"].items():
            nested_props[nested_name] = {
                "type": nested_prop.get("type", "string"),
                "description": nested_prop.get("description")
                or f"Required parameter '{nested_name}'.",
            }
        clean_prop["properties"] = nested_props
        clean_prop["required"] = list(prop.get("required", nested_props.keys()))
        clean_prop["additionalProperties"] = False

    clean["properties"][name] = clean_prop

return clean

for tool in mcp_tools: if hasattr(tool, "args_schema"): original_schema_fn = tool.args_schema.model_json_schema

    def patched_schema(*args, __orig=original_schema_fn, **kwargs):
        raw = __orig(*args, **kwargs)
        return sanitize_schema(raw)

    tool.args_schema.model_json_schema = patched_schema

print(tool.args_schema.model_json_schema()) print(tool.description)

def start_mcp_adapter(): try: print("Starting MCP Server Adapter...") mcp_server_adapter.start() print("MCP Server Adapter started.") except Exception as e: print(f"Error starting MCP Server Adapter. {e}")

def stop_mcp_adapter(): try: print("Stopping MCP Server Adapter...") mcp_server_adapter.stop() print("MCP Server Adapter stopped.") except Exception as e: print(f"Error stoppting MCP Server Adapter. {e}")

my_agent = Agent( role="Firewall Configuration Analyst", goal="Use MCP tools to analyze and optimize firewall configurations.", backstory="An AI agent that specializes in firewall management using MCP tools.", tools=mcp_tools, verbose=True, llm=llm, max_iter=3, # Limit to 3 iterations )

my_task = Task( name="Firewall Analysis Task", description=( "{input} - From the User's input, gather info about the firewall hostname/IP address" "And identify what is required from firewall and get the appropriate Fortigate cli commands." "and execute it using the tool. " "You MUST call use the appropriate MCP Tools with:\n" "- hostname: the firewall IP address\n" "- command: the CLI command string (optional)\n" "Show what is being sent to MCP Tool\n" "Do not respond without calling the tool.\n" ), expected_output="Detailed Analysis of the firewall configuration based on the User's input and the results from the MCP tools.", agent=my_agent, ) crew = Crew( name="Firewall Configuration Crew", agents=[my_agent], tasks=[my_task], verbose=True, process=Process.sequential, )

start_mcp_adapter() try: result = crew.kickoff( inputs={ "input": "Firewall IP: 10.10.10.10. I want to check the current firewall status." } ) print(result) except Exception as e: print(f"Error during crew kickoff: {e}") finally: stop_mcp_adapter()

Expected behavior

ool execute_fortigate_command executed with result (from cache): 2 validation errors for call[execute_fortigate_command] hostname Missing required argument [type=missing_argument, input_value={}, input_type=dict] For further information visit https://errors.p...

Screenshots/Code snippets

....

Operating System

Ubuntu 20.04

Python Version

3.12

crewAI Version

1.9.3

crewAI Tools Version

1.9.3

Virtual Environment

Venv

Evidence

None

Possible Solution

None

Additional context

None

extent analysis

Fix Plan

The issue seems to be related to the input parameters not being passed correctly to the LLM. To fix this, we need to modify the my_task function to pass the input parameters correctly.

Here are the steps to fix the issue:

  • Modify the my_task function to pass the input parameters correctly.
  • Update the crew.kickoff function to pass the input parameters to the my_task function.

Code Changes

my_task = Task(
    name="Firewall Analysis Task",
    description=(
        "{input} - From the User's input, gather info about the firewall hostname/IP address"
        "And identify what is required from firewall and get the appropriate Fortigate cli commands."
        "and execute it using the tool. "
        "You MUST call use the appropriate MCP Tools with:\n"
        "- hostname: the firewall IP address\n"
        "- command: the CLI command string (optional)\n"
        "Show what is being sent to MCP Tool\n"
        "Do not respond without calling the tool.\n"
    ),
    expected_output="Detailed Analysis of the firewall configuration based on the User's input and the results from the MCP tools.",
    agent=my_agent,
    input_params=["hostname", "command"]  # Add input parameters
)

# Update the crew.kickoff function to pass the input parameters
result = crew.kickoff(
    inputs={
        "input": "Firewall IP: 10.10.10.10. I want to check the current firewall status.",
        "hostname": "10.10.10.10",  # Pass the hostname as an input parameter
        "command": "get firewall status"  # Pass the command as an input parameter
    }
)

Verification

To verify that the fix worked, run the updated code and check if the input parameters are being passed correctly to the LLM. You can do this by printing the input parameters in the my_task function.

my_task = Task(
    ...
    def execute(self, inputs):
        print("Input parameters:", inputs)
        # Rest of the code
)

This will print the input parameters being passed to the my_task function. If the input parameters are being passed correctly, you should see the hostname and command parameters being printed.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

ool execute_fortigate_command executed with result (from cache): 2 validation errors for call[execute_fortigate_command] hostname Missing required argument [type=missing_argument, input_value={}, input_type=dict] For further information visit https://errors.p...

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING