fix: pass fingerprint metadata via config instead of tool args (#5216)

security_context was being injected into tool arguments by
_add_fingerprint_metadata(), causing Pydantic validation errors
(extra_forbidden) on MCP and integration tools with strict schemas.

Move fingerprint data to the `config` parameter that invoke/ainvoke
already accept, keeping it available to consumers without polluting
the tool args namespace.

Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
This commit is contained in:
Greyson LaLonde
2026-04-03 03:21:02 +08:00
committed by GitHub
parent 335130cb15
commit 4e46913045

View File

@@ -318,6 +318,8 @@ class ToolUsage:
if self.task: if self.task:
self.task.increment_delegations(coworker) self.task.increment_delegations(coworker)
fingerprint_config = self._build_fingerprint_config()
if calling.arguments: if calling.arguments:
try: try:
acceptable_args = tool.args_schema.model_json_schema()[ acceptable_args = tool.args_schema.model_json_schema()[
@@ -328,15 +330,16 @@ class ToolUsage:
for k, v in calling.arguments.items() for k, v in calling.arguments.items()
if k in acceptable_args if k in acceptable_args
} }
arguments = self._add_fingerprint_metadata(arguments) result = await tool.ainvoke(
result = await tool.ainvoke(input=arguments) input=arguments, config=fingerprint_config
)
except Exception: except Exception:
arguments = calling.arguments arguments = calling.arguments
arguments = self._add_fingerprint_metadata(arguments) result = await tool.ainvoke(
result = await tool.ainvoke(input=arguments) input=arguments, config=fingerprint_config
)
else: else:
arguments = self._add_fingerprint_metadata({}) result = await tool.ainvoke(input={}, config=fingerprint_config)
result = await tool.ainvoke(input=arguments)
if self.tools_handler: if self.tools_handler:
should_cache = True should_cache = True
@@ -550,6 +553,8 @@ class ToolUsage:
if self.task: if self.task:
self.task.increment_delegations(coworker) self.task.increment_delegations(coworker)
fingerprint_config = self._build_fingerprint_config()
if calling.arguments: if calling.arguments:
try: try:
acceptable_args = tool.args_schema.model_json_schema()[ acceptable_args = tool.args_schema.model_json_schema()[
@@ -560,15 +565,16 @@ class ToolUsage:
for k, v in calling.arguments.items() for k, v in calling.arguments.items()
if k in acceptable_args if k in acceptable_args
} }
arguments = self._add_fingerprint_metadata(arguments) result = tool.invoke(
result = tool.invoke(input=arguments) input=arguments, config=fingerprint_config
)
except Exception: except Exception:
arguments = calling.arguments arguments = calling.arguments
arguments = self._add_fingerprint_metadata(arguments) result = tool.invoke(
result = tool.invoke(input=arguments) input=arguments, config=fingerprint_config
)
else: else:
arguments = self._add_fingerprint_metadata({}) result = tool.invoke(input={}, config=fingerprint_config)
result = tool.invoke(input=arguments)
if self.tools_handler: if self.tools_handler:
should_cache = True should_cache = True
@@ -1008,23 +1014,16 @@ class ToolUsage:
return event_data return event_data
def _add_fingerprint_metadata(self, arguments: dict[str, Any]) -> dict[str, Any]: def _build_fingerprint_config(self) -> dict[str, Any]:
"""Add fingerprint metadata to tool arguments if available. """Build fingerprint metadata as a config dict for tool invocation.
Args: Returns the fingerprint data in a config dict rather than injecting it
arguments: The original tool arguments into tool arguments, so it doesn't conflict with strict tool schemas.
Returns: Returns:
Updated arguments dictionary with fingerprint metadata Config dictionary with security_context metadata.
""" """
# Create a shallow copy to avoid modifying the original security_context: dict[str, Any] = {}
arguments = arguments.copy()
# Add security metadata under a designated key
if "security_context" not in arguments:
arguments["security_context"] = {}
security_context = arguments["security_context"]
# Add agent fingerprint if available # Add agent fingerprint if available
if self.agent and hasattr(self.agent, "security_config"): if self.agent and hasattr(self.agent, "security_config"):
@@ -1048,4 +1047,4 @@ class ToolUsage:
except AttributeError: except AttributeError:
pass pass
return arguments return {"security_context": security_context} if security_context else {}