Skip to main content

Commerce MCP Server

5.1 Tool Design for an E-Commerce MCP Server

A complete e-commerce MCP Server typically includes the following tools:
Tool NameFunctionInputOutput
search_productsSearch productsquery, category, priceRange, limitProduct list
get_productGet product detailsproductId or skuFull product information
check_inventoryCheck inventorysku, warehouse (optional)Inventory quantity and location
get_priceGet pricesku, quantity, couponCode (optional)Real-time price with discounts
get_orderGet orderorderIdOrder status and details
list_ordersList ordersuserId, status (optional), limitUser’s order list
create_returnInitiate returnorderId, items, reasonReturn request number
get_shippingTrack shippingorderId or trackingNumberShipping status and tracking
get_categoriesGet categoriesparentId (optional)Category tree
get_promotionsGet promotionscategory (optional)Current promotion list

5.2 Tool Design Principles

Single Responsibility

Each tool should do one thing. Do not combine search and ordering in the same tool. The AI model will automatically compose multiple tools based on context.

Clear Descriptions

A tool’s description directly affects when the AI model chooses to use it. Be explicit about:
  • What the tool does
  • When it should be used
  • What type of data it returns
// Good description
"Search the product catalog with support for keyword, category, and price range filters. Returns product name, price, and stock status."

// Poor description
"Search products"

Sensible Parameter Design

  • Keep required parameters minimal (lower the barrier to use)
  • Provide defaults for optional parameters
  • Write specific parameter descriptions

Leverage structuredContent

For return data that needs programmatic processing (such as price calculations, inventory comparisons), provide both content (for LLM reading) and structuredContent (for programmatic processing):
return {
  content: [{
    type: "text",
    text: "Nike Air Max (SHOE-001): $89.90, In Stock\nAdidas Ultra Boost (SHOE-002): $129.90, Out of Stock"
  }],
  structuredContent: {
    products: [
      { name: "Nike Air Max", sku: "SHOE-001", price: 89.90, inStock: true },
      { name: "Adidas Ultra Boost", sku: "SHOE-002", price: 129.90, inStock: false }
    ],
    total: 42,
    page: 1
  }
};

Use isError to Distinguish Business Errors

For business issues encountered during tool execution (e.g., product not found, insufficient inventory), use isError: true instead of throwing an exception:
if (!product) {
  return {
    content: [{ type: "text", text: `SKU ${sku} not found, please check the code` }],
    isError: true
  };
}

5.3 Complete E-Commerce MCP Server Example

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new McpServer({
  name: "my-store-server",
  version: "1.0.0"
});

// ---- Product Search ----
server.tool(
  "search_products",
  "Search products. Supports keyword and category filtering. Returns product name, price, and stock status.",
  {
    query: { type: "string", description: "Search keyword" },
    category: { type: "string", description: "Category name (optional)" },
    minPrice: { type: "number", description: "Minimum price (optional)" },
    maxPrice: { type: "number", description: "Maximum price (optional)" },
    limit: { type: "number", description: "Number of results, default 10, max 50" }
  },
  async ({ query, category, minPrice, maxPrice, limit = 10 }) => {
    const filters: any = {};
    if (category) filters.category = category;
    if (minPrice) filters.price = { $gte: minPrice };
    if (maxPrice) filters.price = { ...filters.price, $lte: maxPrice };

    const results = await db.products.search(query, filters, Math.min(limit, 50));

    return {
      content: [{
        type: "text",
        text: JSON.stringify({
          results: results.map(p => ({
            name: p.name,
            sku: p.sku,
            price: p.price,
            currency: "USD",
            inStock: p.inventory > 0,
            category: p.category,
            url: `https://mystore.com/product/${p.slug}`
          })),
          total: results.totalCount,
          query
        }, null, 2)
      }]
    };
  }
);

// ---- Product Details ----
server.tool(
  "get_product",
  "Get full details for a single product, including description, specifications, ratings, and inventory.",
  {
    sku: { type: "string", description: "Product SKU code" }
  },
  async ({ sku }) => {
    const product = await db.products.findBySku(sku);
    if (!product) {
      return {
        content: [{ type: "text", text: `SKU not found: ${sku}` }],
        isError: true
      };
    }

    return {
      content: [{
        type: "text",
        text: JSON.stringify({
          name: product.name,
          sku: product.sku,
          description: product.description,
          price: product.price,
          currency: "USD",
          brand: product.brand,
          category: product.category,
          inStock: product.inventory > 0,
          inventory: product.inventory,
          rating: product.averageRating,
          reviewCount: product.reviewCount,
          specs: product.specifications,
          images: product.images,
          url: `https://mystore.com/product/${product.slug}`
        }, null, 2)
      }]
    };
  }
);

// ---- Order Query ----
server.tool(
  "get_order",
  "Query order status and details. Requires order ID.",
  {
    orderId: { type: "string", description: "Order ID" }
  },
  async ({ orderId }) => {
    const order = await db.orders.findById(orderId);
    if (!order) {
      return {
        content: [{ type: "text", text: `Order not found: ${orderId}` }],
        isError: true
      };
    }

    return {
      content: [{
        type: "text",
        text: JSON.stringify({
          id: order.id,
          status: order.status,
          createdAt: order.createdAt,
          items: order.items.map(i => ({
            name: i.name, quantity: i.quantity, price: i.price
          })),
          total: order.total,
          shipping: {
            method: order.shippingMethod,
            trackingNumber: order.trackingNumber,
            estimatedDelivery: order.estimatedDelivery
          }
        }, null, 2)
      }]
    };
  }
);

// ---- OTR Trust Query (ORBEXA Integration) ----
server.tool(
  "otr_verify",
  "Query a merchant website's OTR trust score. Returns six-dimensional trust assessment results.",
  {
    domain: { type: "string", description: "Merchant domain, e.g. example.com" }
  },
  async ({ domain }) => {
    const response = await fetch(
      `https://${domain}/.well-known/otr/verify`
    );
    if (!response.ok) {
      return {
        content: [{ type: "text", text: `${domain} has not deployed the OTR protocol` }],
        isError: true
      };
    }
    const otr = await response.json();
    return {
      content: [{
        type: "text",
        text: JSON.stringify({
          domain: otr.domain,
          trustScore: otr.trustScore,
          dimensions: otr.dimensions,
          badges: otr.badges,
          lastScan: otr.lastScan
        }, null, 2)
      }]
    };
  }
);

// Start
const transport = new StdioServerTransport();
await server.connect(transport);

5.4 Commerce Resource Design

Beyond tools, an e-commerce Server can also expose static context as Resources, enabling AI agents to understand your business:
// Return policy
server.resource(
  "commerce://policies/return",
  "Return Policy",
  "text/markdown",
  async () => {
    return "## Return Policy\n\n- 7-day no-questions-asked returns\n- Original packaging must be intact\n- Custom products are non-returnable";
  }
);

// Shipping zones
server.resource(
  "commerce://shipping/zones",
  "Shipping Zones",
  "application/json",
  async () => {
    const zones = await db.shippingZones.getAll();
    return JSON.stringify(zones, null, 2);
  }
);
When an AI agent answers questions like “Do you accept returns?”, it automatically reads the relevant resource as context.

5.5 Permissions and Security

Commerce Servers require special attention to access control:
ToolPermission RequiredNotes
search_productsNo authenticationPublic information
get_productNo authenticationPublic information
check_inventoryOptional authenticationRough inventory is public; precise inventory requires authentication
get_orderAuthentication requiredUsers can only view their own orders
create_returnAuthentication requiredInvolves refund operations
otr_verifyNo authenticationOTR is a public protocol
For remote deployment, use OAuth 2.1 authentication to ensure correct user identity and permissions. For tools that require authentication, you can request user login via Elicitation, or rely on the Host application’s existing OAuth integration.

5.6 Commerce MCP Server and Its Relationship to Other Protocols

ProtocolPurposeRelationship to MCP
UCPAI agent product discoveryMCP Server can expose UCP data as a Resource
ACPAI agent ordering and paymentMCP Server can wrap ACP workflows as a Tool
OTRMerchant trust assessmentMCP Server can integrate OTR queries as a Tool
In a complete Agentic Commerce system, MCP serves as the “connection layer” between AI applications and your commerce systems, while UCP/ACP/OTR handle the specific commerce protocol logic.
Next Chapter: Resources and Prompts — Deep dive into Resources and Prompts