MCP Tools for Commerce: The Complete Guide
June 11, 2026 · Victor Young
MCP tools for commerce require different design principles than tools for search or information retrieval. A failed search can be retried with no consequences. A failed checkout call might result in a charge with no order, or an order with no tracking. This guide covers what makes a commerce MCP tool robust, deep-dives the five Firestarter tools with example call JSON, and explains how MCP differs from standard function calling for consequential actions.
Why Commerce Tools Need Different Design
The Model Context Protocol gives models a standard way to call external tools. The protocol itself is tool-agnostic — the hard design work is in the tools themselves.
For read-only tools (search, lookup, status), the main design concern is response shape: give the model well-structured output it can reason over. For consequential tools — tools that move money, place orders, or trigger irreversible actions — four additional properties matter:
1. Idempotency where possible. Status polling should be safe to call repeatedly. If a model retries a status check because it didn't get a clean response, nothing bad should happen.
2. Non-idempotent actions must be explicit. Approve and execute are not idempotent. The tool schema should make this visible, and the tool description should tell the model when not to retry blindly.
3. Approval gates separate research from commitment. A commerce tool that goes straight from "here's what I found" to "I charged your card" is unsafe by design. There must be an explicit step where a human (or an authorized autonomous rule) provides approval.
4. Exception handling returns to a defined state. Every tool call should leave the execution in a known status — even on failure. "Unknown" is not a valid post-call state for a purchasing tool.
These principles are reflected in the Firestarter MCP tool design. See /mcp for the full manifest and /use-cases/mcp-commerce-tool for the design rationale.
MCP vs Function Calling for Commerce
Both MCP and function calling (in the OpenAI/Anthropic sense) let models invoke external tools. For commerce, the differences matter:
Tool discovery. With function calling, you define the tools per-request in the API call. With MCP, tools are declared in a manifest the client fetches at connection time. This means the model always has an up-to-date view of available tools — including any new capabilities added to the server — without you rebuilding your prompt.
Consequential action metadata. MCP tool definitions can carry metadata fields that hint to the client how to handle a tool. Firestarter marks approve and execute calls as consequential, which clients like Claude use to display confirmation prompts before calling.
Persistent server connection. An MCP server maintains state across a session. For a multi-step purchase flow — execute, refine, approve, track — the execution ID is threaded through the session without you manually tracking it in your prompt context.
Standardized auth. MCP auth is configured once at install time (--header "Authorization: Bearer ..."), not injected into every API call. This means the key never appears in prompt context.
The practical difference: for one-off integrations, function calling is simpler to set up. For a commerce capability you want available across Claude sessions and projects, MCP is the right abstraction. See also /openapi for the function-calling-compatible OpenAPI spec.
The Five Firestarter MCP Tools
firestarter_execute
What it does: Creates a new purchase execution from a natural-language request. Firestarter parses the request, queries its seller network, scores options, and returns the top matches. The execution is created in awaiting_approval status by default — nothing is charged.
When to call it: When the user asks to buy, order, source, or find a product with intent to purchase.
Input schema:
{
"name": "firestarter_execute",
"description": "Create a new purchase execution. Returns execution ID and ranked options. Does not charge until approve is called.",
"inputSchema": {
"type": "object",
"properties": {
"request": {
"type": "string",
"description": "Natural language description of what to buy"
},
"budget": {
"type": "object",
"properties": {
"max_total": { "type": "number" }
}
}
},
"required": ["request"]
}
}
Example call:
{
"tool": "firestarter_execute",
"arguments": {
"request": "ergonomic office chair under $400, lumbar support required",
"budget": { "max_total": 400 }
}
}
Example response:
{
"id": "exec_01jcm...",
"status": "awaiting_approval",
"options": [
{
"rank": 1,
"title": "Autonomous ErgoChair Pro",
"price": 349.00,
"seller_rating": 4.6,
"estimated_delivery": "3–5 days",
"notes": "Adjustable lumbar, 4D armrests"
},
{
"rank": 2,
"title": "Branch Ergonomic Chair",
"price": 329.00,
"seller_rating": 4.7,
"estimated_delivery": "5–7 days",
"notes": "Dynamic lumbar support"
}
],
"approval_required": true
}
firestarter_status
What it does: Returns the current state of an execution. Idempotent — safe to call any number of times without side effects. Returns status, selected item details (post-approval), tracking information (post-shipment), and receipt URL (post-delivery).
When to call it: After approval to check checkout progress; whenever the user asks for an order update; to verify final delivery.
Example call:
{
"tool": "firestarter_status",
"arguments": {
"execution_id": "exec_01jcm..."
}
}
Example response (post-shipment):
{
"id": "exec_01jcm...",
"status": "shipped",
"selected": {
"title": "Autonomous ErgoChair Pro",
"price": 349.00
},
"tracking_number": "EZ1234567890",
"tracking_url": "https://track.easypost.com/EZ1234567890",
"estimated_delivery": "2026-06-16",
"receipt_url": "https://api.firestarter.network/receipts/exec_01jcm..."
}
Status values follow a defined lifecycle: awaiting_approval → processing → shipped → delivered (or cancelled, exception). No status is a dead end — exception transitions to awaiting_action with a structured description of what's needed.
firestarter_approve
What it does: Triggers checkout for an execution in awaiting_approval state. Initiates the Stripe charge (held in escrow) and EasyPost label generation. Not idempotent — do not retry on timeout without first calling firestarter_status to check whether the approval completed.
When to call it: Only after presenting options to the user and receiving explicit confirmation. The tool description marks this as a consequential action.
Example call:
{
"tool": "firestarter_approve",
"arguments": {
"execution_id": "exec_01jcm...",
"selected_option_rank": 1
}
}
Important: If firestarter_approve returns a timeout or network error, call firestarter_status before retrying. Calling approve twice on the same execution returns an error (the execution is no longer in awaiting_approval state), but it's better to check than to handle the error.
firestarter_cancel
What it does: Cancels an execution. If called before checkout completes, releases the escrow hold. If called after shipment, initiates the return flow. Returns the execution in cancelled status with any applicable refund details.
When to call it: When the user explicitly asks to cancel; if an exception state cannot be resolved; as a cleanup step in error handling.
Example call:
{
"tool": "firestarter_cancel",
"arguments": {
"execution_id": "exec_01jcm...",
"reason": "User changed mind before shipment"
}
}
firestarter_message
What it does: Sends a follow-up message to an in-progress execution to refine requirements. Can be called in awaiting_approval state to change constraints before checkout. Returns an updated options list.
When to call it: When the user wants to change color, size, budget, or delivery speed after an execution has started but before approval; when an exception state requires clarification.
Example call:
{
"tool": "firestarter_message",
"arguments": {
"execution_id": "exec_01jcm...",
"message": "Need delivery by end of week — prioritize fastest shipping option even if slightly over budget"
}
}
This returns an updated options array without creating a new execution. The execution ID is preserved, which is important for audit continuity — the full refinement history is attached to the single execution record.
Designing the Approval Gate in Practice
The approval gate is where MCP tool design meets human-computer interaction. The model can gather all the information needed for a purchase decision and surface it cleanly — but the decision itself should involve a human.
A well-designed agent flow using these tools:
firestarter_execute— gather options- Present options clearly with price, delivery time, and seller rating
- Ask the user to confirm a specific option
firestarter_approveonly after explicit confirmationfirestarter_statusto report the outcome
What to avoid: calling firestarter_approve as part of the same response that presents options ("Here are two choices — I went ahead and ordered the first one"). The approval step should be a separate model turn, after user input.
The server-side approval checkpoint is a safety net, not a substitute for good client-side design. See /use-cases/agent-approval-audit-api for the full control plane design.
Getting Started
Install the MCP server:
claude mcp add firestarter \
--transport sse \
--url https://api.firestarter.network/mcp \
--header "Authorization: Bearer fs_live_YOUR_KEY"
Full manifest: https://api.firestarter.network/.well-known/mcp.json
API key and free tier at /developers. See also /scenarios for example use cases, and /compare/firestarter-vs-rye for how the tool set compares to other commerce API approaches.
FAQ
Can I use these tools with OpenAI models instead of Claude?
The MCP server uses SSE transport per the MCP spec. OpenAI doesn't yet have native MCP client support, but you can use the Firestarter OpenAPI spec as function definitions with any OpenAI-compatible API. See /openapi.
What's the difference between firestarter_execute and just sending a message to firestarter_message?
firestarter_execute creates a new execution with a new ID. firestarter_message refines an existing execution without creating a new one. Use message when you want to iterate on an open execution; use execute to start fresh.
How do I handle the exception status?
Call firestarter_status to get the structured exception description, surface it to the user, and either call firestarter_message with a resolution instruction or firestarter_cancel to stop the execution and release the escrow hold.
Are there rate limits on status polling?
Standard polling limits apply (see /docs). For high-frequency status checks in autonomous pipelines, use webhook callbacks rather than polling — configure a webhook_url in the execution request body.
How does the seller network work?
Sellers list catalogs via the Firestarter seller API. When an execution is created, the network is queried in real time. Sellers pay a 3% commission on completed sales only — no listing fee. Buyers pay no transaction fees.