feat: tag platform action tools with provider and provider_id

This commit is contained in:
Renato Nitta
2026-05-03 22:58:34 -03:00
parent 05c6cd2e44
commit c8ce37c7c3
4 changed files with 132 additions and 1 deletions

View File

@@ -26,6 +26,8 @@ class CrewAIPlatformActionTool(BaseTool):
description: str,
action_name: str,
action_schema: dict[str, Any],
provider: str | None = None,
provider_id: str | None = None,
):
parameters = action_schema.get("function", {}).get("parameters", {})
@@ -48,6 +50,12 @@ class CrewAIPlatformActionTool(BaseTool):
)
self.action_name = action_name
self.action_schema = action_schema
# Private metadata used by enterprise tooling to recover the canonical
# tool_id (e.g. "crewai_oauth:google_drive" or "paragon:<uuid>") for
# ACP rule evaluation. Set by CrewaiPlatformToolBuilder; remains None
# for direct construction.
self._provider = provider
self._provider_id = provider_id
def _run(self, **kwargs: Any) -> str:
try:

View File

@@ -75,6 +75,7 @@ class CrewaiPlatformToolBuilder:
),
"parameters": action.get("parameters", {}),
"app": app,
"provider": action.get("provider"),
}
}
self._actions_schema[action_name] = action_schema
@@ -91,6 +92,8 @@ class CrewaiPlatformToolBuilder:
description=description,
action_name=action_name,
action_schema=action_schema,
provider=function_details.get("provider"),
provider_id=function_details.get("app"),
)
tools.append(tool)

View File

@@ -0,0 +1,120 @@
"""Tests for the _provider / _provider_id metadata attached to platform tools.
These attributes are private metadata used by enterprise tooling to recover
the canonical tool_id (e.g. ``crewai_oauth:google_drive`` or
``paragon:<uuid>``) for ACP rule evaluation.
"""
from unittest.mock import Mock, patch
from crewai_tools.tools.crewai_platform_tools import (
CrewAIPlatformActionTool,
CrewaiPlatformToolBuilder,
)
class TestActionToolProviderAttrs:
def setup_method(self):
self.action_schema = {
"function": {
"name": "test_action",
"parameters": {"type": "object", "properties": {}},
}
}
def test_defaults_to_none_when_not_provided(self):
tool = CrewAIPlatformActionTool(
description="x",
action_name="test_action",
action_schema=self.action_schema,
)
assert tool._provider is None
assert tool._provider_id is None
def test_stores_explicit_values(self):
tool = CrewAIPlatformActionTool(
description="x",
action_name="test_action",
action_schema=self.action_schema,
provider="crewai_oauth",
provider_id="google_drive",
)
assert tool._provider == "crewai_oauth"
assert tool._provider_id == "google_drive"
class TestBuilderProviderThreading:
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
@patch(
"crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get"
)
def test_builder_threads_provider_and_app_into_each_tool(self, mock_get):
mock_api_response = {
"actions": {
"google_drive": [
{
"name": "create_file",
"description": "Create a file",
"parameters": {"type": "object", "properties": {}},
"provider": "crewai_oauth",
}
],
"1b5f2395-65a5-4da8-9b2f-c10eafc83a0b": [
{
"name": "send_invoice",
"description": "Send an invoice",
"parameters": {"type": "object", "properties": {}},
"provider": "paragon",
}
],
}
}
mock_response = Mock()
mock_response.raise_for_status.return_value = None
mock_response.json.return_value = mock_api_response
mock_get.return_value = mock_response
builder = CrewaiPlatformToolBuilder(
apps=["google_drive", "1b5f2395-65a5-4da8-9b2f-c10eafc83a0b"]
)
tools = builder.tools()
by_action = {tool.action_name: tool for tool in tools}
oauth_tool = by_action["create_file"]
assert oauth_tool._provider == "crewai_oauth"
assert oauth_tool._provider_id == "google_drive"
paragon_tool = by_action["send_invoice"]
assert paragon_tool._provider == "paragon"
assert paragon_tool._provider_id == "1b5f2395-65a5-4da8-9b2f-c10eafc83a0b"
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
@patch(
"crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get"
)
def test_builder_handles_response_without_provider_field(self, mock_get):
# Older crewai-plus versions return actions without a "provider" key.
# The builder must remain compatible: provider_id is set, provider is None.
mock_api_response = {
"actions": {
"github": [
{
"name": "create_issue",
"description": "Create issue",
"parameters": {"type": "object", "properties": {}},
}
]
}
}
mock_response = Mock()
mock_response.raise_for_status.return_value = None
mock_response.json.return_value = mock_api_response
mock_get.return_value = mock_response
builder = CrewaiPlatformToolBuilder(apps=["github"])
tools = builder.tools()
assert len(tools) == 1
assert tools[0]._provider is None
assert tools[0]._provider_id == "github"

View File

@@ -153,7 +153,7 @@ class MCPToolResolver:
try:
tools, clients = self._resolve_native(mcp_server_config)
for tool in tools:
tool._amp_slug = slug
tool._amp_slug = slug # type: ignore[attr-defined]
resolved_cache[slug] = (tools, clients)
all_clients.extend(clients)
except Exception as e: