arcade-mcp/toolkits/stripe/_generate.py
Eric Gustin c7fba25488
Add Stripe toolkit (#333)
| Name | Description | Package | Version |

|-----------------------------------|---------------------------------------------------------------------------|---------|---------|
| Stripe.CreateCustomer | This tool will create a customer in Stripe. |
Stripe | 0.0.1 |
| Stripe.ListCustomers | This tool will fetch a list of Customers from
Stripe. | Stripe | 0.0.1 |
| Stripe.CreateProduct | This tool will create a product in Stripe. |
Stripe | 0.0.1 |
| Stripe.ListProducts | This tool will fetch a list of Products from
Stripe. | Stripe | 0.0.1 |
| Stripe.CreatePrice | This tool will create a price in Stripe. If a
product has not already been | Stripe | 0.0.1 |
| Stripe.ListPrices | This tool will fetch a list of Prices from Stripe.
| Stripe | 0.0.1 |
| Stripe.CreatePaymentLink | This tool will create a payment link in
Stripe. | Stripe | 0.0.1 |
| Stripe.ListInvoices | This tool will list invoices in Stripe. | Stripe
| 0.0.1 |
| Stripe.CreateInvoice | This tool will create an invoice in Stripe. |
Stripe | 0.0.1 |
| Stripe.CreateInvoiceItem | This tool will create an invoice item in
Stripe. | Stripe | 0.0.1 |
| Stripe.FinalizeInvoice | This tool will finalize an invoice in Stripe.
| Stripe | 0.0.1 |
| Stripe.RetrieveBalance | This tool will retrieve the balance from
Stripe. It takes no input. | Stripe | 0.0.1 |
| Stripe.CreateRefund | This tool will refund a payment intent in
Stripe. | Stripe | 0.0.1 |
| Stripe.ListPaymentIntents | This tool will list payment intents in
Stripe. | Stripe | 0.0.1 |
| Stripe.CreateBillingPortalSession | This tool will create a billing
portal session. | Stripe | 0.0.1 |

-------------------------

This PR implements the tools in
[stripe-agent-toolkit](https://github.com/stripe/agent-toolkit)
verbatim. This means that the logic needed to implement these tools is
minimal since stripe has already done the heavy lifting.

The tools added in this toolkit are the same tools that are in the
stripe-agent-toolkit, but formatted as Arcade tools. This means that the
names of the tools, the parameter annotations, and tool docstrings are
taken from the stripe-agent-toolkit. I have omitted evals since the tool
definitions are taken from the stripe-agent-toolkit.

The tools in this PR are generated via a script. I have included this
script in the PR (`_generate.py`) so that if the stripe-agent-toolkit
ever adds more tools, then we can simply run that script to generate the
new tools.
2025-04-14 10:29:23 -07:00

110 lines
4.6 KiB
Python

import logging
from pathlib import Path
from typing import Union, get_args
from stripe_agent_toolkit.functions import * # noqa: F403
from stripe_agent_toolkit.prompts import * # noqa: F403
from stripe_agent_toolkit.schema import * # noqa: F403
from stripe_agent_toolkit.tools import tools
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def get_type_str(arg_type):
"""Extract type name, handling Optional/Union types."""
if hasattr(arg_type, "__origin__") and arg_type.__origin__ is Union:
non_none = [a for a in get_args(arg_type) if a is not type(None)]
if len(non_none) == 1:
return non_none[0].__name__
return arg_type.__name__ if hasattr(arg_type, "__name__") else str(arg_type)
def generate_stripe_tools(
output_file: Path = Path("arcade_stripe") / "tools" / "stripe.py",
) -> None:
"""
Generate the Arcade AI Stripe Toolkit file from the stripe agent toolkit definitions.
"""
logger.info("Generating stripe tools file at %s", output_file)
try:
output_file.touch(exist_ok=True)
with output_file.open("w") as f:
f.write("""import os
from typing import Annotated, Optional
from stripe_agent_toolkit.api import StripeAPI
from arcade.sdk import ToolContext, tool
def run_stripe_tool(context: ToolContext, method_name: str, params: dict) -> str:
\"\"\"
Helper function that retrieves the Stripe secret key, initializes the API,
and executes the specified method with the provided parameters.
\"\"\"
api_key = context.get_secret("STRIPE_SECRET_KEY")
stripe_api = StripeAPI(secret_key=api_key, context=None)
params = {k: v for k, v in params.items() if v is not None}
return stripe_api.run(method_name, **params) # type: ignore[no-any-return]
""")
# Generate each tool function from the stripe agent toolkit
for tool_info in tools:
method_name = tool_info["method"]
method = globals().get(method_name)
if not method:
logger.warning("Method %s not found.", method_name)
continue
args_schema = tool_info["args_schema"]
description = tool_info["description"].strip()
arg_names = list(args_schema.__annotations__.keys())
arg_types = [args_schema.__annotations__[field] for field in arg_names]
params_list = []
for name, arg_type in zip(arg_names, arg_types, strict=False):
field = args_schema.model_fields[name]
# Check if the type annotation already includes Optional (i.e. Union[..., None])
is_optional_type = (
hasattr(arg_type, "__origin__")
and arg_type.__origin__ is Union
and type(None) in get_args(arg_type)
)
if field.is_required:
if is_optional_type:
params_list.append(
f"{name}: Annotated[{get_type_str(arg_type)} | None, "
f'"{field.description}"] = None'
)
else:
params_list.append(
f"{name}: Annotated[{get_type_str(arg_type)}, "
f'"{field.description}"]'
)
else:
default_repr = "None" if field.default is None else repr(field.default)
params_list.append(
f"{name}: Annotated[Optional[{get_type_str(arg_type)}], "
f'"{field.description}"] = {default_repr}'
)
params_str = ", ".join(params_list)
dict_items = ", ".join([f'"{name}": {name}' for name in arg_names])
arcade_tool_code = (
f'@tool(requires_secrets=["STRIPE_SECRET_KEY"])\n'
f"def {method_name}(context: ToolContext, {params_str}) -> "
f'Annotated[str, "{description.splitlines()[0]}"]:\n'
f' """{description.splitlines()[0]}"""\n'
f' return run_stripe_tool(context, "{method_name}", '
+ "{"
+ dict_items
+ "})\n\n"
)
f.write(arcade_tool_code)
except Exception:
logger.exception("An error occurred while generating stripe tools")
raise
if __name__ == "__main__":
generate_stripe_tools()