Model Context Protocol (MCP) support with new use case (#42)

* initial mcp

* food ordering with mcp

* prompt eng

* splitting out goals and updating docs

* a diff so I can get tests from codex

* a diff so I can get tests from codex

* oops, missing files

* tests, file formatting

* readme and setup updates

* setup.md link fixes

* readme change

* readme change

* readme change

* stripe food setup script

* single agent mode default

* prompt engineering for better multi agent performance

* performance should be greatly improved

* Update goals/finance.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update activities/tool_activities.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* co-pilot PR suggested this change, and now fixed it

* stronger wording around json format response

* formatting

* moved docs to dir

* moved image assets under docs

* cleanup env example, stripe guidance

* cleanup

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Steve Androulakis
2025-06-09 16:39:57 -07:00
committed by GitHub
parent 1811e4cf59
commit 5d55a9fe80
49 changed files with 3268 additions and 279 deletions

View File

@@ -8,6 +8,7 @@ from .fin.get_account_balances import get_account_balance
from .fin.move_money import move_money
from .fin.submit_loan_application import submit_loan_application
from .find_events import find_events
from .food.add_to_cart import add_to_cart
from .give_hint import give_hint
from .guess_location import guess_location
from .hr.book_pto import book_pto
@@ -66,5 +67,7 @@ def get_handler(tool_name: str):
return give_hint
if tool_name == "GuessLocation":
return guess_location
if tool_name == "AddToCart":
return add_to_cart
raise ValueError(f"Unknown tool: {tool_name}")

View File

@@ -1,17 +1,14 @@
import os
import stripe
from dotenv import load_dotenv
load_dotenv(override=True) # Load environment variables from a .env file
stripe.api_key = os.getenv("STRIPE_API_KEY")
def ensure_customer_exists(
customer_id: str = None, email: str = "default@example.com"
) -> str:
"""Ensure a Stripe customer exists; create one if not."""
import stripe
if customer_id:
try:
stripe.Customer.retrieve(customer_id)
@@ -27,6 +24,12 @@ def ensure_customer_exists(
def create_invoice(args: dict) -> dict:
"""Create and finalize a Stripe invoice."""
import stripe
# Load environment variables and configure stripe
load_dotenv(override=True)
stripe.api_key = os.getenv("STRIPE_API_KEY")
# If an API key exists in the env file, find or create customer
if stripe.api_key is not None and stripe.api_key != "":
customer_id = ensure_customer_exists(

33
tools/food/add_to_cart.py Normal file
View File

@@ -0,0 +1,33 @@
def add_to_cart(args: dict) -> dict:
"""
Simple stateless cart tool for demo purposes.
In production, this would use proper session storage or database.
"""
customer_email = args.get("customer_email")
item_name = args.get("item_name")
item_price = float(args.get("item_price", 0))
quantity = int(args.get("quantity", 1))
stripe_product_id = args.get("stripe_product_id")
# Basic validation
if not customer_email:
return {"error": "Customer email is required"}
if not item_name:
return {"error": "Item name is required"}
if item_price <= 0:
return {"error": "Item price must be greater than 0"}
if quantity <= 0:
return {"error": "Quantity must be greater than 0"}
# For demo purposes, just acknowledge the addition
# In a real system, this would store to session/database
return {
"status": "success",
"message": f"Added {quantity} x {item_name} (${item_price}) to cart for {customer_email}",
"item_added": {
"name": item_name,
"price": item_price,
"quantity": quantity,
"stripe_product_id": stripe_product_id,
},
}

View File

@@ -0,0 +1,48 @@
import os
from dotenv import load_dotenv
def delete_food_ordering_products():
"""Archive all Stripe products with metadata use_case = food_ordering_demo (since products with prices cannot be deleted)."""
import stripe
# Load environment variables and configure stripe
load_dotenv(override=True)
stripe.api_key = os.getenv("STRIPE_API_KEY")
if not stripe.api_key:
print("Error: STRIPE_API_KEY not found in environment variables")
return
try:
# Search for products with food_ordering_demo use_case
products = stripe.Product.search(
query="metadata['use_case']:'food_ordering_demo'", limit=100
)
if not products.data:
print("No products found with use_case = food_ordering_demo")
return
archived_count = 0
for product in products.data:
try:
# Archive the product (set active=False)
stripe.Product.modify(product.id, active=False)
print(f"Archived product: {product.name} (ID: {product.id})")
archived_count += 1
except Exception as e:
print(
f"Error archiving product {product.name} (ID: {product.id}): {str(e)}"
)
print(f"\nSuccessfully archived {archived_count} products")
except Exception as e:
print(f"Error searching for products: {str(e)}")
if __name__ == "__main__":
delete_food_ordering_products()

View File

@@ -0,0 +1,92 @@
import json
import os
from dotenv import load_dotenv
def create_stripe_products():
"""Create Stripe products and prices from the stripe_pizza_products.json file."""
import stripe
# Load environment variables and configure stripe
load_dotenv(override=True)
stripe.api_key = os.getenv("STRIPE_API_KEY")
if not stripe.api_key:
print("Error: STRIPE_API_KEY not found in environment variables")
return
# Load the products data
current_dir = os.path.dirname(__file__)
products_file = os.path.join(current_dir, "stripe_pizza_products.json")
with open(products_file, "r") as f:
products_data = json.load(f)
# Filter for food ordering demo products only
food_products = [
p
for p in products_data
if p.get("metadata", {}).get("use_case") == "food_ordering_demo"
]
created_products = []
for product_data in food_products:
try:
# Create the product with relevant fields
product = stripe.Product.create(
name=product_data["name"],
description=product_data.get("description"),
images=product_data.get("images", []),
metadata=product_data.get("metadata", {}),
type=product_data.get("type", "service"),
active=product_data.get("active", True),
)
# Create price for the product if price_info exists
price_info = product_data.get("price_info")
if price_info:
price_amount = price_info.get("amount")
currency = price_info.get("currency", "usd")
price = stripe.Price.create(
currency=currency, unit_amount=price_amount, product=product.id
)
# Set this price as the default price for the product
stripe.Product.modify(product.id, default_price=price.id)
print(
f"Created product: {product.name} (ID: {product.id}) with default price ${price_amount/100:.2f}"
)
created_products.append(
{
"name": product.name,
"id": product.id,
"price_id": price.id,
"price_amount": price_amount,
"original_id": product_data["id"],
}
)
else:
print(
f"Created product: {product.name} (ID: {product.id}) - No price defined"
)
created_products.append(
{
"name": product.name,
"id": product.id,
"original_id": product_data["id"],
}
)
except Exception as e:
print(f"Error creating product {product_data['name']}: {str(e)}")
print(f"\nSuccessfully created {len(created_products)} products with prices")
return created_products
if __name__ == "__main__":
create_stripe_products()

View File

@@ -0,0 +1,188 @@
[
{
"id": "prod_SSWirxxS5A8gcT",
"object": "product",
"active": true,
"attributes": [],
"created": 1749360061,
"default_price": "price_1RXbfGKVZbzw7QA57Mj1akGI",
"description": "A large size bottle of cola.",
"images": [
"https://files.stripe.com/links/MDB8YWNjdF8xTkJPTHVLVlpienc3UUE1fGZsX3Rlc3RfbDJxckJKMDRnT1dDc253OHlZNWNkZkY5006Xg07kHT"
],
"livemode": false,
"marketing_features": [],
"metadata": {
"use_case": "food_ordering_demo"
},
"name": "Soda",
"price_info": {
"amount": 349,
"currency": "usd"
},
"package_dimensions": null,
"shippable": null,
"statement_descriptor": null,
"tax_code": null,
"type": "service",
"unit_label": null,
"updated": 1749360062,
"url": null
},
{
"id": "prod_SSWhxv3tUy1YOG",
"object": "product",
"active": true,
"attributes": [],
"created": 1749359978,
"default_price": "price_1RXbdvKVZbzw7QA5ARomQvaf",
"description": "Our warm, crusty bread is generously spread with a savory garlic butter and toasted to golden perfection. It's the ideal aromatic and flavorful side to accompany your main course.",
"images": [
"https://files.stripe.com/links/MDB8YWNjdF8xTkJPTHVLVlpienc3UUE1fGZsX3Rlc3RfWTdIZTBkUjNZNFQ1ZEhSVG9nRnduY1pS00XVgLRRZD"
],
"livemode": false,
"marketing_features": [],
"metadata": {
"use_case": "food_ordering_demo"
},
"name": "Garlic Bread",
"price_info": {
"amount": 799,
"currency": "usd"
},
"package_dimensions": null,
"shippable": null,
"statement_descriptor": null,
"tax_code": null,
"type": "service",
"unit_label": null,
"updated": 1749360084,
"url": null
},
{
"id": "prod_SSWgXa5bwUFCJs",
"object": "product",
"active": true,
"attributes": [],
"created": 1749359922,
"default_price": "price_1RXbd0KVZbzw7QA5Nq36vdLW",
"description": "A tribute to Italian simplicity, this pizza is topped with fresh mozzarella, a vibrant tomato sauce, and fragrant basil leaves. Each bite delivers a clean and authentic taste of Italy's most famous flavors.",
"images": [
"https://files.stripe.com/links/MDB8YWNjdF8xTkJPTHVLVlpienc3UUE1fGZsX3Rlc3RfamdmTXBFbzY0TW9rS2N0c2g0Tml2SERL00Evl60Ttq"
],
"livemode": false,
"marketing_features": [],
"metadata": {
"use_case": "food_ordering_demo"
},
"name": "Margherita Pizza",
"price_info": {
"amount": 1699,
"currency": "usd"
},
"package_dimensions": null,
"shippable": null,
"statement_descriptor": null,
"tax_code": null,
"type": "service",
"unit_label": null,
"updated": 1749359998,
"url": null
},
{
"id": "prod_SSWf738UqIJzzi",
"object": "product",
"active": true,
"attributes": [],
"created": 1749359845,
"default_price": "price_1RXbbmKVZbzw7QA53EkjV2nB",
"description": "A timeless classic featuring a generous layer of savory pepperoni over rich tomato sauce and melted mozzarella cheese. It's the perfect choice for those who love a bold, meaty flavor on a perfectly baked crust.",
"images": [
"https://files.stripe.com/links/MDB8YWNjdF8xTkJPTHVLVlpienc3UUE1fGZsX3Rlc3RfcGRHc0c4cEZYWmR2bm0zOHBOa0FWMk5t008QmCJoWr"
],
"livemode": false,
"marketing_features": [],
"metadata": {
"use_case": "food_ordering_demo"
},
"name": "Pepperoni Pizza",
"price_info": {
"amount": 2299,
"currency": "usd"
},
"package_dimensions": null,
"shippable": null,
"statement_descriptor": null,
"tax_code": null,
"type": "service",
"unit_label": null,
"updated": 1749359846,
"url": null
},
{
"id": "prod_SGMXBnatLlkJ4d",
"object": "product",
"active": true,
"attributes": [],
"created": 1746554502,
"default_price": "price_1RLpoJKVZbzw7QA5ra76Fk6g",
"description": null,
"images": [],
"livemode": false,
"marketing_features": [],
"metadata": {},
"name": "ACME Scooter Token",
"package_dimensions": null,
"shippable": null,
"statement_descriptor": null,
"tax_code": null,
"type": "service",
"unit_label": null,
"updated": 1746554503,
"url": null
},
{
"id": "prod_NxJPcqTWzXk45K",
"object": "product",
"active": true,
"attributes": [],
"created": 1684961969,
"default_price": null,
"description": "$12/Month subscription",
"images": [],
"livemode": false,
"marketing_features": [],
"metadata": {},
"name": "Starter Subscription",
"package_dimensions": null,
"shippable": null,
"statement_descriptor": null,
"tax_code": null,
"type": "service",
"unit_label": null,
"updated": 1684961969,
"url": null
},
{
"id": "prod_NxJ4KvyENd0uUu",
"object": "product",
"active": true,
"attributes": [],
"created": 1684960731,
"default_price": null,
"description": "Created with the Stripe CLI",
"images": [],
"livemode": false,
"marketing_features": [],
"metadata": {},
"name": "Temporal Money Transfer",
"package_dimensions": null,
"shippable": null,
"statement_descriptor": null,
"tax_code": null,
"type": "service",
"unit_label": null,
"updated": 1684960731,
"url": null
}
]

View File

@@ -2,7 +2,7 @@ import os
from typing import List
import tools.tool_registry as tool_registry
from models.tool_definitions import AgentGoal
from models.tool_definitions import AgentGoal, MCPServerDefinition
# Turn on Silly Mode - this should be a description of the persona you'd like the bot to have and can be a single word or a phrase.
# Example if you want the bot to be a specific person, like Mario or Christopher Walken, or to describe a specific tone:
@@ -311,7 +311,7 @@ goal_fin_check_account_balances = AgentGoal(
)
# this tool checks account balances, and uses ./data/customer_account_data.json as dummy data
# it also uses a separate workflow/tool, see ./SETUP.md for details
# it also uses a separate workflow/tool, see ./setup.md for details
goal_fin_move_money = AgentGoal(
id="goal_fin_move_money",
category_tag="fin",
@@ -351,7 +351,7 @@ goal_fin_move_money = AgentGoal(
)
# this starts a loan approval process
# it also uses a separate workflow/tool, see ./SETUP.md for details
# it also uses a separate workflow/tool, see ./setup.md for details
goal_fin_loan_application = AgentGoal(
id="goal_fin_loan_application",
category_tag="fin",
@@ -455,6 +455,126 @@ goal_ecomm_list_orders = AgentGoal(
),
)
# ----- MCP Integrations -----
goal_mcp_stripe = AgentGoal(
id="goal_mcp_stripe",
category_tag="mcp-integrations",
agent_name="Stripe MCP Agent",
agent_friendly_description="Manage Stripe operations via MCP",
tools=[], # Will be populated dynamically
mcp_server_definition=MCPServerDefinition(
name="stripe-mcp",
command="npx",
args=[
"-y",
"@stripe/mcp",
"--tools=all",
f"--api-key={os.getenv('STRIPE_API_KEY')}",
],
env=None,
included_tools=["list_customers", "list_products"],
),
description="Help manage Stripe operations for customer and product data by using the customers.read and products.read tools.",
starter_prompt="Welcome! I can help you read Stripe customer and product information.",
example_conversation_history="\n ".join(
[
"agent: Welcome! I can help you read Stripe customer and product information. What would you like to do first?",
"user: what customers are there?",
"agent: I'll check for customers now.",
"user_confirmed_tool_run: <user clicks confirm on customers.read tool>",
'tool_result: { "customers": [{"id": "cus_abc", "name": "Customer A"}, {"id": "cus_xyz", "name": "Customer B"}] }',
"agent: I found two customers: Customer A and Customer B. Can I help with anything else?",
"user: what products exist?",
"agent: Let me get the list of products for you.",
"user_confirmed_tool_run: <user clicks confirm on products.read tool>",
'tool_result: { "products": [{"id": "prod_123", "name": "Gold Plan"}, {"id": "prod_456", "name": "Silver Plan"}] }',
"agent: I found two products: Gold Plan and Silver Plan.",
]
),
)
# ----- Food Ordering Goal -----
goal_food_ordering = AgentGoal(
id="goal_food_ordering",
category_tag="food",
agent_name="Food Ordering Assistant",
agent_friendly_description="Order food from Tony's Pizza Palace using Stripe for payment processing. Browse menu, add items to your order, and check out securely. Please ensure context carries over between tool runs.",
tools=[tool_registry.food_add_to_cart_tool],
mcp_server_definition=MCPServerDefinition(
name="stripe-mcp",
command="npx",
args=[
"-y",
"@stripe/mcp",
"--tools=all",
f"--api-key={os.getenv('STRIPE_API_KEY')}",
],
env=None,
included_tools=[
"list_products",
"list_prices",
"create_customer",
"create_invoice",
"create_invoice_item",
"finalize_invoice",
],
),
description="The user wants to order food from Tony's Pizza Palace. "
"First, help the user browse the menu by calling list_products. "
"When they express interest in items, get pricing using list_prices. "
"Add items to their cart using AddToCart as they decide - the order doesn't matter, multiple items can be added. "
"After they're done selecting items, get their customer details and create a Stripe customer. "
"For checkout: 1) create_invoice, 2) create_invoice_item for each individual item (IMPORTANT: create_invoice_item does NOT accept quantity parameter - call it once per item, so if user wants 2 pizzas, call create_invoice_item twice with the same price), "
"3) finalize_invoice. The finalized invoice will contain a hosted_invoice_url for payment.",
starter_prompt=starter_prompt_generic,
example_conversation_history="\n ".join(
[
"user: I want to order food.",
"agent: Welcome to Tony's Pizza Palace! I can help you browse our menu, add items to your cart, and process payment. Let me show you what we have available.",
"user_confirmed_tool_run: <user clicks confirm on list_products tool with limit=100 and product.metadata.use_case == 'food_ordering_demo'>",
'tool_result: {"products": [{"id": "prod_pepperoni", "name": "Pepperoni Pizza", "description": "A timeless classic featuring pepperoni"}, {"id": "prod_margherita", "name": "Margherita Pizza", "description": "Fresh mozzarella and basil"}, {"id": "prod_garlic_bread", "name": "Garlic Bread", "description": "Warm crusty bread with garlic butter"}, {"id": "prod_soda", "name": "Soda", "description": "A large bottle of cola"}]}',
"agent: We have Pepperoni Pizza, Margherita Pizza, Garlic Bread, and Soda available. What would you like to order?",
"user: I'd like 2 pepperoni pizzas and a soda.",
"agent: Excellent! Let me get the pricing for the pepperoni pizzas first so you know the cost.",
"user_confirmed_tool_run: <user clicks confirm on list_prices tool with product='prod_pepperoni'>",
'tool_result: {"prices": [{"id": "price_pepperoni_large", "product": "prod_pepperoni", "unit_amount": 2299, "currency": "usd"}]}',
"agent: Pepperoni Pizza is $22.99 each. Now let me check the soda pricing:",
"user_confirmed_tool_run: <user clicks confirm on list_prices tool with product='prod_soda'>",
'tool_result: {"prices": [{"id": "price_soda", "product": "prod_soda", "unit_amount": 250, "currency": "usd"}]}',
"agent: And Soda is $2.50. So 2 Pepperoni Pizzas ($22.99 each) plus 1 Soda ($2.50) = $48.48 total. What's your email address to add these items to your cart?",
"user: jane.smith@example.com",
"agent: Perfect! Adding 2 Pepperoni Pizzas to your cart for jane.smith@example.com. Note: I'm using quantity=2 since you want 2 pizzas.",
"user_confirmed_tool_run: <user clicks confirm on AddToCart tool with customer_email='jane.smith@example.com', item_name='Pepperoni Pizza', item_price=22.99, quantity=2, stripe_product_id='prod_pepperoni'>",
'tool_result: {"status": "success", "message": "Added 2 x Pepperoni Pizza ($22.99) to cart for jane.smith@example.com", "item_added": {"quantity": 2, "name": "Pepperoni Pizza"}}',
"agent: Great! Added 2 Pepperoni Pizzas to your cart. Now adding the Soda (quantity=1):",
"user_confirmed_tool_run: <user clicks confirm on AddToCart tool with customer_email='jane.smith@example.com', item_name='Soda', item_price=2.50, quantity=1, stripe_product_id='prod_soda'>",
'tool_result: {"status": "success", "message": "Added 1 x Soda ($2.50) to cart for jane.smith@example.com", "item_added": {"quantity": 1, "name": "Soda"}}',
"agent: Excellent! Your cart now contains: 2 Pepperoni Pizzas ($22.99 each) and 1 Soda ($2.50). Ready to checkout? I'll need your full name.",
"user: Yes, let's proceed with payment. My name is Jane Smith.",
"agent: Thank you Jane! Creating your Stripe customer profile with name and email.",
"user_confirmed_tool_run: <user clicks confirm on create_customer tool with name='Jane Smith' and email='jane.smith@example.com'>",
'tool_result: {"customer": {"id": "cus_ExAmPlE12345", "name": "Jane Smith", "email": "jane.smith@example.com"}}',
"agent: Customer profile created! Now I'll create a draft invoice for your order.",
"user_confirmed_tool_run: <user clicks confirm on create_invoice tool with customer='cus_ExAmPlE12345'>",
'tool_result: {"invoice": {"id": "in_InvOicE54321", "status": "draft", "customer": "cus_ExAmPlE12345"}}',
"agent: Invoice created! Now adding your first Pepperoni Pizza to the invoice.",
"user_confirmed_tool_run: <user clicks confirm on create_invoice_item tool with customer='cus_ExAmPlE12345', invoice='in_InvOicE54321', price='price_pepperoni_large'>",
'tool_result: {"status": "success", "invoice_item": {"id": "ii_pizza1"}}',
"agent: First pizza added! Now adding your second Pepperoni Pizza to the invoice.",
"user_confirmed_tool_run: <user clicks confirm on create_invoice_item tool with customer='cus_ExAmPlE12345', invoice='in_InvOicE54321', price='price_pepperoni_large'>",
'tool_result: {"status": "success", "invoice_item": {"id": "ii_pizza2"}}',
"agent: Both pizzas added! Now adding your Soda to the invoice.",
"user_confirmed_tool_run: <user clicks confirm on create_invoice_item tool with customer='cus_ExAmPlE12345', invoice='in_InvOicE54321', price='price_soda'>",
'tool_result: {"status": "success", "invoice_item": {"id": "ii_soda"}}',
"agent: All items added to invoice! Now finalizing to generate the payment link.",
"user_confirmed_tool_run: <user clicks confirm on finalize_invoice tool with invoice='in_InvOicE54321'>",
'tool_result: {"invoice": {"id": "in_InvOicE54321", "status": "open", "hosted_invoice_url": "https://invoice.stripe.com/i/acct_1NBOLuKVZbzw7QA5/test_YWNjdF8xTkJPTHVLVlpienc3UUE1LF9SbGJlZ2xLMjhlc3lxNzFkVVBXbkswOUlFa2Rna1RxLDEzMDAwMTY4Nw0200RKeJRMlw?s=ap", "amount_due": 4848}}',
"agent: Perfect! Your order is complete with 2 Pepperoni Pizzas and 1 Soda. Total: $48.48. Pay securely here: https://invoice.stripe.com/i/acct_1NBOLuKVZbzw7QA5/test_YWNjdF8xTkJPTHVLVlpienc3UUE1LF9SbGJlZ2xLMjhlc3lxNzFkVVBXbkswOUlFa2Rna1RxLDEzMDAwMTY4Nw0200RKeJRMlw?s=ap\\n\\nThank you for ordering from Tony's Pizza Palace!",
]
),
)
# Add the goals to a list for more generic processing, like listing available agents
goal_list: List[AgentGoal] = []
goal_list.append(goal_choose_agent_type)
@@ -469,19 +589,21 @@ goal_list.append(goal_fin_move_money)
goal_list.append(goal_fin_loan_application)
goal_list.append(goal_ecomm_list_orders)
goal_list.append(goal_ecomm_order_status)
goal_list.append(goal_mcp_stripe)
goal_list.append(goal_food_ordering)
# for multi-goal, just set list agents as the last tool
first_goal_value = os.getenv("AGENT_GOAL")
if first_goal_value is None:
multi_goal_mode = True # default if unset
multi_goal_mode = False # default to single agent mode if unset
elif (
first_goal_value is not None
and first_goal_value.lower() != "goal_choose_agent_type"
and first_goal_value.lower() == "goal_choose_agent_type"
):
multi_goal_mode = False
else:
multi_goal_mode = True
else:
multi_goal_mode = False
if multi_goal_mode:
for goal in goal_list:

View File

@@ -1,6 +1,6 @@
import os
import tools.goal_registry as goals
import goals
def list_agents(args: dict) -> dict:

View File

@@ -1,6 +1,7 @@
import http.client
import json
import os
import random
import urllib.parse
from dotenv import load_dotenv
@@ -174,45 +175,166 @@ def search_flights_real_api(
}
def generate_smart_flights(origin: str, destination: str) -> list:
"""
Generate realistic flight options with smart pricing based on origin and destination.
"""
# Common airlines for different regions
airlines_by_region = {
"domestic_us": [
{"name": "American Airlines", "code": "AA"},
{"name": "United Airlines", "code": "UA"},
{"name": "Delta Airlines", "code": "DL"},
{"name": "Southwest Airlines", "code": "WN"},
],
"us_international": [
{"name": "American Airlines", "code": "AA"},
{"name": "United Airlines", "code": "UA"},
{"name": "Delta Airlines", "code": "DL"},
{"name": "Virgin Atlantic", "code": "VS"},
],
"australia_nz": [
{"name": "Qantas", "code": "QF"},
{"name": "Jetstar", "code": "JQ"},
{"name": "Virgin Australia", "code": "VA"},
{"name": "Air New Zealand", "code": "NZ"},
],
"international": [
{"name": "American Airlines", "code": "AA"},
{"name": "United Airlines", "code": "UA"},
{"name": "Delta Airlines", "code": "DL"},
{"name": "Air New Zealand", "code": "NZ"},
{"name": "Qantas", "code": "QF"},
{"name": "Singapore Airlines", "code": "SQ"},
],
}
# Determine route type and base pricing
origin_lower = origin.lower()
dest_lower = destination.lower()
# Australia/NZ cities
anz_cities = [
"sydney",
"melbourne",
"syd",
"mel",
"auckland",
"akl",
"wellington",
"wlg",
"brisbane",
"bne",
"perth",
"per",
]
# US cities
us_cities = [
"los angeles",
"lax",
"san francisco",
"sfo",
"new york",
"nyc",
"jfk",
"chicago",
"ord",
"miami",
"mia",
]
is_origin_anz = any(city in origin_lower for city in anz_cities)
is_dest_anz = any(city in dest_lower for city in anz_cities)
is_origin_us = any(city in origin_lower for city in us_cities)
is_dest_us = any(city in dest_lower for city in us_cities)
# Determine airline pool and base price
if (is_origin_us and is_dest_anz) or (is_origin_anz and is_dest_us):
# Trans-Pacific routes
airline_pool = airlines_by_region["international"]
base_price = random.randint(1200, 1800)
elif is_origin_anz and is_dest_anz:
# Australia/NZ domestic
airline_pool = airlines_by_region["australia_nz"]
base_price = random.randint(300, 600)
elif is_origin_us and is_dest_us:
# US domestic
airline_pool = airlines_by_region["domestic_us"]
base_price = random.randint(200, 800)
else:
# General international
airline_pool = airlines_by_region["international"]
base_price = random.randint(800, 1500)
# Generate 3-4 flight options
num_flights = random.randint(3, 4)
results = []
used_airlines = set()
for i in range(num_flights):
# Pick unique airline
available_airlines = [a for a in airline_pool if a["name"] not in used_airlines]
if not available_airlines:
available_airlines = airline_pool # Reset if we run out
airline = random.choice(available_airlines)
used_airlines.add(airline["name"])
# Generate flight numbers
outbound_num = random.randint(100, 999)
return_num = random.randint(100, 999)
# Price variation (cheaper airlines get lower prices)
price_multiplier = 1.0
if "Southwest" in airline["name"] or "Jetstar" in airline["name"]:
price_multiplier = 0.7
elif "Virgin" in airline["name"]:
price_multiplier = 0.85
elif "Singapore" in airline["name"]:
price_multiplier = 1.2
# Add some random variation
price_variation = random.uniform(0.9, 1.1)
final_price = round(base_price * price_multiplier * price_variation, 2)
results.append(
{
"operating_carrier": airline["name"],
"outbound_flight_code": f"{airline['code']}{outbound_num}",
"price": final_price,
"return_flight_code": f"{airline['code']}{return_num}",
"return_operating_carrier": airline["name"],
}
)
# Sort by price
results.sort(key=lambda x: x["price"])
return results
def search_flights(args: dict) -> dict:
"""
Returns example flight search results in the requested JSON format.
Search for flights. Uses real API if RAPIDAPI_KEY is available, otherwise generates smart mock data.
"""
load_dotenv(override=True)
api_key = os.getenv("RAPIDAPI_KEY")
origin = args.get("origin")
destination = args.get("destination")
if not origin or not destination:
return {"error": "Both origin and destination are required"}
# If API key is available, use the real API
if api_key and api_key != "YOUR_DEFAULT_KEY":
return search_flights_real_api(args)
# Otherwise, generate smart mock data
results = generate_smart_flights(origin, destination)
return {
"currency": "USD",
"destination": f"{destination}",
"origin": f"{origin}",
"results": [
{
"operating_carrier": "American Airlines",
"outbound_flight_code": "AA203",
"price": 1262.51,
"return_flight_code": "AA202",
"return_operating_carrier": "American Airlines",
},
{
"operating_carrier": "Air New Zealand",
"outbound_flight_code": "NZ488",
"price": 1396.00,
"return_flight_code": "NZ527",
"return_operating_carrier": "Air New Zealand",
},
{
"operating_carrier": "United Airlines",
"outbound_flight_code": "UA100",
"price": 1500.00,
"return_flight_code": "UA101",
"return_operating_carrier": "United Airlines",
},
{
"operating_carrier": "Delta Airlines",
"outbound_flight_code": "DL200",
"price": 1600.00,
"return_flight_code": "DL201",
"return_operating_carrier": "Delta Airlines",
},
],
"destination": destination,
"origin": origin,
"results": results,
}

View File

@@ -1,3 +1,5 @@
from typing import Dict, List
from models.tool_definitions import ToolArgument, ToolDefinition
# ----- System tools -----
@@ -397,3 +399,75 @@ ecomm_track_package = ToolDefinition(
),
],
)
# ----- Food Ordering Use Case Tools -----
food_add_to_cart_tool = ToolDefinition(
name="AddToCart",
description="Add a menu item to the customer's cart using item details from Stripe.",
arguments=[
ToolArgument(
name="customer_email",
type="string",
description="Email address of the customer",
),
ToolArgument(
name="item_name",
type="string",
description="Name of the menu item (e.g., 'Margherita Pizza', 'Caesar Salad')",
),
ToolArgument(
name="item_price",
type="number",
description="Price of the item in dollars (e.g., 14.99)",
),
ToolArgument(
name="quantity",
type="number",
description="Quantity of the item to add (defaults to 1)",
),
ToolArgument(
name="stripe_product_id",
type="string",
description="Stripe product ID for reference (optional)",
),
],
)
# MCP Integration Functions
def create_mcp_tool_definitions(
mcp_tools_info: Dict[str, Dict],
) -> List[ToolDefinition]:
"""Convert MCP tool info to ToolDefinition objects"""
tool_definitions = []
for tool_name, tool_info in mcp_tools_info.items():
# Extract input schema properties
input_schema = tool_info.get("inputSchema", {})
properties = (
input_schema.get("properties", {}) if isinstance(input_schema, dict) else {}
)
# Convert properties to ToolArgument objects
arguments = []
for param_name, param_info in properties.items():
if isinstance(param_info, dict):
arguments.append(
ToolArgument(
name=param_name,
type=param_info.get("type", "string"),
description=param_info.get("description", ""),
)
)
# Create ToolDefinition
tool_def = ToolDefinition(
name=tool_info["name"],
description=tool_info.get("description", ""),
arguments=arguments,
)
tool_definitions.append(tool_def)
return tool_definitions