Files
2026-02-11 13:17:05 -05:00

169 lines
5.0 KiB
Python

"""Stripe Agent Toolkit for Strands."""
import asyncio
import json
from typing import List, Optional, Dict, Any, Callable, Awaitable
from strands.tools.tools import PythonAgentTool as StrandTool
from ..shared.toolkit_core import ToolkitCore
from ..shared.mcp_client import McpTool
from ..configuration import Configuration
def create_strand_tool(
run_tool: Callable[..., Awaitable[str]],
mcp_tool: McpTool
) -> "StrandTool":
"""Create a Strand tool from MCP tool definition."""
tool_name = mcp_tool.get("name", "")
# Prepare parameters schema
input_schema = mcp_tool.get("inputSchema") or {}
parameters: Dict[str, Any] = dict(input_schema)
parameters["additionalProperties"] = False
parameters["type"] = "object"
# Clean up schema
for key in ["description", "title"]:
parameters.pop(key, None)
properties = parameters.get("properties")
if isinstance(properties, dict):
for prop in properties.values():
for key in ["title", "default"]:
if isinstance(prop, dict):
prop.pop(key, None)
def callback_wrapper(tool_input: Any, **kwargs: Any) -> Dict[str, Any]:
"""Wrapper to handle additional parameters from strands framework."""
# Extract toolUseId for the response
tool_use_id = None
actual_params: Dict[str, Any] = {}
if isinstance(tool_input, dict) and "toolUseId" in tool_input:
tool_use_id = tool_input["toolUseId"]
# Extract the actual parameters from the nested input structure
actual_params = tool_input.get("input", {})
elif isinstance(tool_input, str):
# Parse JSON string input
try:
parsed = json.loads(tool_input)
tool_use_id = parsed.get("toolUseId")
actual_params = parsed.get("input", parsed)
except json.JSONDecodeError:
actual_params = {}
elif isinstance(tool_input, dict):
actual_params = tool_input.copy()
# Call the MCP client (need to run async in sync context)
loop = asyncio.get_event_loop()
if loop.is_running():
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor() as pool:
future = pool.submit(
asyncio.run,
run_tool(tool_name, actual_params)
)
result = future.result()
else:
result = loop.run_until_complete(
run_tool(tool_name, actual_params)
)
# Return in the format expected by strands
response: Dict[str, Any] = {
"content": [{"text": result}]
}
if tool_use_id:
response["toolUseId"] = tool_use_id
return response
return StrandTool(
tool_name=tool_name,
tool_spec={
"name": tool_name,
"description": mcp_tool.get("description", tool_name),
"inputSchema": {
"json": parameters
}
},
callback=callback_wrapper
)
class StripeAgentToolkit(ToolkitCore[List[StrandTool]]):
"""
Stripe Agent Toolkit for Strands.
Example:
toolkit = await create_stripe_agent_toolkit(
secret_key='rk_test_...',
)
tools = toolkit.get_tools()
await toolkit.close()
"""
def __init__(
self,
secret_key: str,
configuration: Optional[Configuration] = None
):
super().__init__(secret_key, configuration)
def _empty_tools(self) -> List[StrandTool]:
"""Return empty list of tools."""
return []
def _convert_tools(
self,
mcp_tools: List[McpTool]
) -> List[StrandTool]:
"""Convert MCP tools to Strands StrandTool instances."""
return [
create_strand_tool(self.run_tool, t)
for t in mcp_tools
]
@property
def tools(self) -> List[StrandTool]:
"""
The tools available in the toolkit.
.. deprecated::
Access tools via get_tools() after calling initialize().
"""
return self._get_tools_with_warning()
async def create_stripe_agent_toolkit(
secret_key: str,
configuration: Optional[Configuration] = None
) -> StripeAgentToolkit:
"""
Factory function to create and initialize a StripeAgentToolkit.
This is the recommended way to create a toolkit as it handles
async initialization automatically.
Example:
toolkit = await create_stripe_agent_toolkit(
secret_key='rk_test_...',
)
tools = toolkit.get_tools()
await toolkit.close()
Args:
secret_key: Stripe API key (rk_* strongly recommended over sk_*)
configuration: Optional configuration for context
Returns:
Initialized StripeAgentToolkit ready to use
"""
toolkit = StripeAgentToolkit(secret_key, configuration)
await toolkit.initialize()
return toolkit