Give your CrewAI multi-agent crews access to Agent Bazaar's paid tool marketplace. Every tool call is priced, metered, and receipted — each crew member pays only for what it uses.
pip install crewai
Bazaar runs each provider's MCP server for you behind one HTTPS proxy, so your crew needs no MCP adapter or extra packages.
export NOUI_API_KEY="bz_your_key_here" export OPENAI_API_KEY="sk-..." # CrewAI default LLM
Save this as bazaar_client.py. It lists tools from GET /api/bazaar/catalog and runs them through POST /api/bazaar/proxy:
# bazaar_client.py — no dependencies beyond the standard library
import json
import os
import re
import urllib.error
import urllib.request
BAZAAR_URL = "https://noui.bot/api/bazaar"
API_KEY = os.environ["NOUI_API_KEY"]
def list_bazaar_tools() -> list[dict]:
"""Every active tool in the public catalog, following pagination."""
tools: list[dict] = []
while True:
url = f"{BAZAAR_URL}/catalog?limit=100&offset={len(tools)}"
with urllib.request.urlopen(url, timeout=30) as resp:
page = json.load(resp)
tools += page["tools"]
if not page["tools"] or len(tools) >= page["total"]:
return tools
def tool_function_name(tool: dict) -> str:
"""LLM function names allow only letters, digits, _ and - (max 64)."""
return re.sub(r"[^A-Za-z0-9_-]", "_", tool["tool_name"])[:64]
def tool_description(tool: dict) -> str:
summary = tool["description"] or tool["display_name"]
return (
f"{summary} (price: {tool['pricing']['price']}). "
"Pass the tool's parameters as a JSON object in 'arguments'."
)
def call_bazaar_tool(tool_id: str, arguments: dict | None) -> str:
"""Run one tool through the metered proxy.
Failures (402 balance, 429 rate limit, provider errors) come back as text
so the agent can read them instead of crashing the run.
"""
request = urllib.request.Request(
f"{BAZAAR_URL}/proxy",
data=json.dumps({"tool_id": tool_id, "input": arguments or {}}).encode(),
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=30) as resp:
body = json.load(resp)
except urllib.error.HTTPError as err:
try:
error = json.load(err)
except ValueError:
error = {}
return f"Bazaar error {err.code} {error.get('code', '')}: {error.get('message', err.reason)}"
return json.dumps(body["result"])Save this as crew.py next to the client and run it. Only the researcher gets paid tools; the writer just reasons:
from crewai import Agent, Crew, Task
from crewai.tools import BaseTool
from bazaar_client import (
call_bazaar_tool,
list_bazaar_tools,
tool_description,
tool_function_name,
)
class BazaarTool(BaseTool):
tool_id: str
def _run(self, arguments: dict) -> str:
return call_bazaar_tool(self.tool_id, arguments)
bazaar_tools = [
BazaarTool(
name=tool_function_name(t),
description=tool_description(t),
tool_id=t["id"],
)
for t in list_bazaar_tools()
]
print(f"Loaded {len(bazaar_tools)} Bazaar tools")
researcher = Agent(
role="Research Analyst",
goal="Gather data using the available Bazaar tools",
backstory="You call paid tools through Agent Bazaar and report raw results.",
tools=bazaar_tools,
verbose=True,
)
writer = Agent(
role="Report Writer",
goal="Turn research into a clear summary",
backstory="You turn raw data into actionable insights.",
verbose=True,
)
research_task = Task(
description="Use the available tools to get the current weather in Paris.",
expected_output="Raw data from tool calls",
agent=researcher,
)
report_task = Task(
description="Write a 3-sentence summary of the findings.",
expected_output="A concise summary report",
agent=writer,
)
crew = Crew(agents=[researcher, writer], tasks=[research_task, report_task], verbose=True)
result = crew.kickoff()
print(result)The catalog does not publish parameter schemas yet, so each tool takes one arguments object and the model reads the expected fields from the tool description.
You don't have to give every agent every tool. Filter by catalog name to control costs and scope:
# Only give the researcher specific tools
allowed = {"weather_forecast", "web_search"}
filtered_tools = [t for t in bazaar_tools if t.name in allowed]
researcher = Agent(
role="Research Analyst",
goal="Gather weather and search data",
backstory="You have access to specific Bazaar tools.",
tools=filtered_tools,
)Every tool call through Bazaar is metered transparently:
What happens on each tool call:
# Check your balance anytime curl https://noui.bot/api/v1/bazaar/balance \ -H "Authorization: Bearer bz_your_key" # View usage and spend curl https://noui.bot/api/v1/bazaar/usage/summary \ -H "Authorization: Bearer bz_your_key"
Check balance before and after each crew.kickoff(). Multi-agent crews can make many tool calls — monitor costs during development.
Assign only the tools each agent needs. A report writer rarely needs API tools — restrict expensive tools to specialized agents.
Enable verbose=True on agents during development to see which tools are called and how often. Helps catch runaway tool usage.