Build your first SEA shopping agent with BuyWhere + Claude in 30 minutes
This buywhere quickstart shows the shortest path to a real shopping agent: Claude decides when to search, BuyWhere's hosted MCP server returns live catalog results, and your app turns that into a useful answer for a shopper in Southeast Asia. By the end, you will have a working TypeScript script, a Python alternative, and a clean starting point for a production AI shopping agent tutorial.
What you are building
You are building a small agent that answers prompts like "find running shoes under 180 SGD for a shopper in Singapore." Claude uses BuyWhere's search_products MCP tool, your code executes the hosted MCP request at https://api.buywhere.ai/mcp, and the app prints the returned results plus Claude's summary.
This guide stays focused on search_products because it is the most stable first integration point and enough to prove your end-to-end loop. Once that works, you can add comparison, deals, and product detail flows.
Prerequisites
- Node.js 18+ or Python 3.10+
- An Anthropic API key
- A BuyWhere API key
As of April 16, 2026, BuyWhere's public MCP guide documents a hosted HTTP endpoint. You do not need to install a local MCP server for this quickstart.
Step 1: Get a BuyWhere API key
Register an agent and receive a key:
curl -X POST https://api.buywhere.ai/auth/register \
-H "Content-Type: application/json" \
-d '{
"agent_name": "sea-shopping-agent",
"contact": "you@example.com",
"use_case": "Claude shopping agent for SEA product discovery"
}'
The response includes api_key, tier, and rate_limit. Export the key along with your Anthropic key:
export BUYWHERE_API_KEY="bw_free_your_key_here"
export ANTHROPIC_API_KEY="sk-ant-your_key_here"
BuyWhere's MCP guide currently lists bw_free_*, bw_live_*, and bw_partner_* key tiers. For a local prototype, bw_free_* is enough.
Step 2: Connect the BuyWhere MCP server
If you want BuyWhere to appear directly inside Claude Desktop, add this to claude_desktop_config.json:
{
"mcpServers": {
"buywhere": {
"url": "https://api.buywhere.ai/mcp",
"headers": {
"Authorization": "Bearer bw_free_your_key_here"
}
}
}
}
Restart Claude Desktop and the BuyWhere tools will be available through the hosted MCP server.
For this tutorial, we will call the same MCP endpoint directly from code. That keeps the flow easy to test in a terminal, CI, or your own backend.
Before wiring Claude into your app, it is worth confirming the live tool catalog:
curl -X POST https://api.buywhere.ai/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
On April 16, 2026, that response includes search_products, get_product, compare_products, get_deals, and list_categories. For this quickstart, we only need search_products.
Step 3: Write a Claude tool-use query that calls BuyWhere search
Install the TypeScript dependency:
npm install @anthropic-ai/sdk
Create agent.ts:
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const BUYWHERE_API_KEY = process.env.BUYWHERE_API_KEY!;
const MCP_URL = "https://api.buywhere.ai/mcp";
async function callBuyWhereTool(name: string, args: Record<string, unknown>) {
const response = await fetch(MCP_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${BUYWHERE_API_KEY}`
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: { name, arguments: args }
})
});
const payload = await response.json();
if (!response.ok || payload.error) {
throw new Error(JSON.stringify(payload));
}
return payload.result;
}
const tools = [
{
name: "search_products",
description: "Search the BuyWhere product catalog by keyword.",
input_schema: {
type: "object",
properties: {
q: { type: "string", description: "Keyword search query" },
region: { type: "string", description: "Region filter such as sea" },
country_code: {
type: "string",
enum: ["SG", "US", "VN", "TH", "MY"],
description: "ISO country code"
},
min_price: { type: "number" },
max_price: { type: "number" },
limit: { type: "integer", default: 5 },
offset: { type: "integer", default: 0 }
},
required: ["q"]
}
}
];
async function run() {
const userPrompt =
"Find running shoes under 180 SGD for a shopper in Singapore. " +
"Use BuyWhere search and keep the answer short.";
const firstTurn = await anthropic.messages.create({
model: "claude-sonnet-4-6-20250514",
max_tokens: 1024,
tools,
messages: [{ role: "user", content: userPrompt }]
});
const toolUse = firstTurn.content.find((block) => block.type === "tool_use");
if (!toolUse || toolUse.type !== "tool_use") {
throw new Error("Claude did not request search_products.");
}
const toolResult = await callBuyWhereTool(toolUse.name, toolUse.input);
const rawToolText = (toolResult.content ?? [])
.filter((block: { type?: string }) => block.type === "text")
.map((block: { text: string }) => block.text)
.join("\n");
const finalTurn = await anthropic.messages.create({
model: "claude-sonnet-4-6-20250514",
max_tokens: 1024,
tools,
messages: [
{ role: "user", content: userPrompt },
{ role: "assistant", content: firstTurn.content },
{
role: "user",
content: [
{
type: "tool_result",
tool_use_id: toolUse.id,
content: rawToolText
}
]
}
]
});
console.log("\nRaw BuyWhere result:\n");
console.log(rawToolText);
const summary = finalTurn.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("\n");
console.log("\nClaude summary:\n");
console.log(summary);
}
run().catch((error) => {
console.error(error);
process.exit(1);
});
Run it with:
npx tsx agent.ts
This is the key pattern: Claude chooses the tool, your code executes the MCP call, and then you pass the result back to Claude for the final answer.
Step 4: Parse the response and display results
The hosted MCP server uses JSON-RPC, but the tool payload itself is returned in MCP content blocks. For a first build, do not overfit to a guessed product schema. Print the raw text from the tool response first, then let Claude summarize it. That gives you a working developer loop immediately and keeps debugging simple when the catalog output changes.
For a SEA-focused shopping flow, these search_products fields matter most:
qfor the shopper's search stringregion: "sea"when you want Southeast Asia resultscountry_code: "SG"when you want Singapore defaults and pricing contextmax_pricewhen the shopper gives you a budgetlimitto keep tool output small while you iterate
If you prefer Python, install the dependencies:
pip install anthropic requests
Then use this version:
import os
import requests
from anthropic import Anthropic
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
BUYWHERE_API_KEY = os.environ["BUYWHERE_API_KEY"]
MCP_URL = "https://api.buywhere.ai/mcp"
TOOLS = [{
"name": "search_products",
"description": "Search the BuyWhere product catalog by keyword.",
"input_schema": {
"type": "object",
"properties": {
"q": {"type": "string"},
"region": {"type": "string"},
"country_code": {"type": "string", "enum": ["SG", "US", "VN", "TH", "MY"]},
"min_price": {"type": "number"},
"max_price": {"type": "number"},
"limit": {"type": "integer", "default": 5},
"offset": {"type": "integer", "default": 0}
},
"required": ["q"]
}
}]
def call_buywhere_tool(name, arguments):
response = requests.post(
MCP_URL,
headers={
"Authorization": f"Bearer {BUYWHERE_API_KEY}",
"Content-Type": "application/json"
},
json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {"name": name, "arguments": arguments}
},
timeout=30
)
response.raise_for_status()
payload = response.json()
if "error" in payload:
raise RuntimeError(payload["error"])
return payload["result"]
prompt = "Find wireless headphones under 250 SGD for a shopper in Singapore."
first_turn = client.messages.create(
model="claude-sonnet-4-6-20250514",
max_tokens=1024,
tools=TOOLS,
messages=[{"role": "user", "content": prompt}]
)
tool_use = next(block for block in first_turn.content if block.type == "tool_use")
tool_result = call_buywhere_tool(tool_use.name, tool_use.input)
print(tool_result)
The Python example stops after the tool call on purpose. Start by verifying the raw BuyWhere response, then add the second Claude turn once you are happy with the search behavior.
What to build next
After search_products, the next useful additions are:
get_productto fetch details for a product the shopper selectscompare_productsafter you have a shortlist of IDs from searchget_dealsfor discount-first agents and daily digestslist_categoriesfor browse-first shopping flows
If you are building a production agent, the next layer is usually preference memory plus stricter prompt rules around budget, shipping geography, and result ranking.
Troubleshooting
Invalid API key: make sure you are sendingAuthorization: Bearer <key>tohttps://api.buywhere.ai/mcpinvalid_params: calltools/listfirst and compare your arguments to the live schema- Poor results: add
country_code, set a realisticmax_price, and keep the prompt explicit - Rate-limited responses: BuyWhere's guide recommends exponential backoff at 2s, 4s, then 8s
That is enough to get a real shopping agent running on BuyWhere: register a key, point Claude at the hosted MCP endpoint, execute search_products, and keep the raw MCP output visible while you tighten the experience.