Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog AI Agents

MCP Server Design for a Digital Goods Storefront

Sean

Platform Writer

Jun 15, 2026
13 min read

An MCP server for a digital goods storefront is a long-lived JSON-RPC endpoint that AI shopping agents call to browse a catalog, manage a cart, and complete a purchase. The protocol is the easy part. The design pattern that doesn’t ship you to production hell is the hard part: pick a transport that matches your agents, design tools that map to real commerce actions, handle auth that doesn’t leak the catalog, and budget for the rate limits you will hit the moment a single agent decides your storefront is interesting.

This post is the design document I wish I had when I started building MCP servers for digital-goods storefronts. It covers the protocol decisions (transport, tool shape, error model), the commerce decisions (catalog, cart, checkout, fulfillment), the production decisions (auth, rate limiting, observability, schema versioning), and the four mistakes that turn an MCP server demo into a production incident. By the end you’ll have a pattern you can implement in a weekend, and a list of landmines to avoid.

MCP server design for a digital goods storefront: transport, tools, auth, and the four production mistakes

Table of contents

The one-paragraph answer

An MCP server for a digital goods storefront is a JSON-RPC 2.0 endpoint that accepts a small set of well-defined tool calls from AI shopping agents. The transport is HTTP/SSE for web agents, stdio for CLI agents, or a hosted transport for managed agents. The tool set is search_catalog, get_product, get_cart, update_cart, and complete_checkout, plus whatever domain-specific tools your storefront needs (download a purchased asset, fetch a license key, redeem a promo code). The auth is API key or OAuth bearer for the storefront, scoped to the operations the agent should be able to perform. The data layer is the existing product catalog and order management system, exposed read-mostly with a small number of write operations. The whole thing should fit in 1000–2000 lines of code and deploy in an afternoon.

The rest of this post is the part the MCP spec assumes you’ll figure out: which design decisions matter, which patterns repeat across successful implementations, and which mistakes are common enough to be worth pre-empting.

What the MCP spec actually gives you

The Model Context Protocol is JSON-RPC 2.0 over a transport. The spec defines the wire format (request, response, error), the transport options (stdio, HTTP with SSE, streamable HTTP), the tool discovery mechanism (list tools, describe tool), and the resource primitives (read resource, subscribe to resource updates). Everything else is your design decision.

For a digital goods storefront, the relevant primitives are:

  • Tools — actions the agent can invoke. search_catalog, get_product, add_to_cart, complete_checkout, download_purchase, get_license_key.
  • Resources — read-only data the agent can fetch. catalog://categories, storefront://policies, storefront://about.
  • Prompts — pre-built prompt templates the agent can use. recommend_gift, compare_products, find_alternative.

The spec is a contract, not a framework. It does not tell you how to implement an MCP server; it tells you what shape your implementation needs to have so that any compliant MCP client (Claude, ChatGPT, a custom agent) can talk to it. The implementation language is up to you; the data model is up to you; the rate limits and auth are up to you; the response shapes and error codes are up to you. You are not implementing a framework; you are building an API.

That distinction matters because it means the design decisions are the ones you would make for any long-lived API, with one addition: the consumer of the API is an AI agent, not a human with a browser. The agent will do things humans wouldn’t (call the same tool 1000 times per minute, send malformed requests, ignore soft errors), and the design has to handle that.

Transport: pick one, support it well

Three real options for an MCP server transport, and the choice depends on who is calling.

stdio. The original MCP transport, designed for local CLI agents. The MCP server runs as a child process; the agent communicates with it over stdin and stdout. Simple, fast, no network involved. The right choice for a CLI tool or a developer-local MCP server. The wrong choice for anything that needs to be reached over the network.

HTTP with Server-Sent Events (SSE). The standard transport for web agents. The agent opens a long-lived HTTP connection to the MCP server; the server pushes events to the agent over SSE and the agent posts tool calls as regular HTTP requests. Works through proxies and firewalls. The right choice for a hosted MCP server that any agent can reach. The downside is the long-lived connection, which doesn’t fit every hosting environment (most notably, traditional serverless functions don’t keep connections open across requests).

Streamable HTTP. The newer HTTP transport that replaces SSE for many use cases. The agent posts tool calls as regular HTTP POSTs; the server returns responses in a single request, with optional streaming for long operations. Doesn’t require a long-lived connection. Works in any hosting environment. The right choice for a hosted MCP server that needs to run on a serverless or edge platform.

For a digital goods storefront, the answer is streamable HTTP if you’re deploying to a modern platform, and HTTP+SSE if you have to. stdio is the right choice for a developer-local tool that helps you test the catalog, not for a production storefront endpoint.

The choice has consequences. Streamable HTTP lets you run on Vercel, Cloudflare Workers, and other serverless platforms. HTTP+SSE forces you onto a platform that supports long-lived connections, which is most container platforms. The decision should be made early; switching transport later is doable but disruptive.

Tool design: map to actions, not endpoints

The most common mistake in MCP server design is treating tools like API endpoints. An endpoint describes a resource or a collection; a tool describes an action. The MCP tools for a digital goods storefront should map to verbs an agent would actually do, not to URLs a developer would hit.

A reasonable starter tool set:

  • search_catalog(query, filters?, pagination?) — full-text search across the catalog. The agent calls this when a shopper says “find me a cookbook under $20.”
  • get_product(product_id) — fetch a single product by ID. The agent calls this when a shopper has selected a product and needs the details.
  • list_categories() — list the top-level categories. The agent calls this when it wants to show the shopper what’s in the store.
  • get_product_recommendations(product_id?, category_id?, limit?) — fetch related products. The agent calls this when a shopper has shown interest in one product and the store wants to surface adjacent products.
  • create_cart(items) — start a cart session. Returns a cart_id. The agent calls this when the shopper says “add this to my cart.”
  • get_cart(cart_id) — fetch the current contents of a cart. The agent calls this when the shopper wants to review what they’re buying.
  • update_cart_items(cart_id, line_updates) — change quantities or remove items. The agent calls this when the shopper says “actually, I only need two of those.”
  • get_checkout_options(cart_id) — fetch available shipping, tax, and payment options for the current cart. The agent calls this before completing checkout.
  • complete_checkout(cart_id, payment_method, shipping_address?) — finalize the purchase. Returns an order ID and a download URL or license key.
  • get_purchase(purchase_id) — fetch the status of a past purchase, including the download URL or license key.
  • get_storefront_policies() — return the store’s shipping, return, and refund policies. The agent calls this when a shopper asks “what’s your return policy.”

The shape of each tool is a JSON Schema for the input and a JSON Schema for the output. The agent’s job is to produce input that satisfies the input schema and to consume output that matches the output schema. The MCP server’s job is to enforce the schema, process the request, and return a response that matches the output schema.

Notice what is not in the list. There is no browse, no filter, no paginate_with_cursor, no add_to_wishlist, no leave_review, no contact_support. A digital goods storefront has many more surfaces than this; an MCP server for one should expose the surfaces the agent needs to complete a purchase, not every surface the store has. The principle is: every tool should be worth a real shopping action. A tool that exists because the underlying API has an endpoint is not worth a tool.

Schema: the part that survives a year

The single biggest reason MCP servers fail in production is a schema that changes without warning. The agent that worked yesterday calls a tool with the input shape it learned yesterday, and the MCP server returns a validation error because the tool’s input schema changed in a deploy last week. The agent’s behavior breaks. The store’s revenue breaks with it.

The fix is to version the tools, not just the server. Three rules that hold up:

Rule 1: Tools are immutable once published. If you publish search_catalog with a particular input shape, the input shape is a contract. Adding optional fields is fine. Removing fields, renaming fields, changing types, or making previously optional fields required is a breaking change. Breaking changes get a new tool name (search_catalog_v2) or a new server version. The agent’s code is written against the old tool; the new tool is opt-in.

Rule 2: Responses are additive. A response can grow new fields without breaking old clients. The agent’s code reads fields by name and ignores unknown fields, so adding a new field to a response is a non-breaking change. Removing or renaming a field is a breaking change and follows the same rules as tool schemas.

Rule 3: Errors are versioned separately. The error schema can change more aggressively than the success schema. A new error code can be added without breaking old clients; old error codes that are no longer generated can be deprecated over a version cycle. The error schema is a contract too, but a more forgiving one.

The pattern that holds up over years is the same pattern that holds up for any long-lived API: additive changes only within a major version, breaking changes only at a major version bump, and deprecation warnings for at least one major version before removal. The MCP server should expose a version field in its handshake so the agent knows which contract it is talking to.

Authentication: the part where most implementations fail

The biggest single mistake in MCP server design for digital goods is treating the storefront as if it has one auth model. It has at least three, and they don’t overlap.

Storefront-level auth. The storefront has credentials to talk to its own backend systems (the product database, the order management system, the payment processor). These credentials are configured in the MCP server at deploy time and are not exposed to the agent. The MCP server uses them as a server-to-server identity.

Agent-level auth. The agent has credentials to talk to the storefront on behalf of a specific shopper. The credentials are scoped to a specific shopping session and are passed by the agent in every request. The MCP server validates the credentials, scopes the request to the appropriate shopping session, and logs the agent’s identity for analytics.

Shopper-level auth. The shopper is the human on whose behalf the agent is acting. In some storefronts the shopper is implicit (the agent is acting on the storefront’s own behalf, like a price-comparison agent); in others the shopper is explicit (the agent is acting on a logged-in customer’s behalf, like a personal shopping assistant). The auth model depends on which.

A reasonable default for a digital goods storefront is API key for the agent, scoped to read-only catalog operations plus a small set of write operations (create cart, update cart, complete checkout) that the agent can perform on behalf of an unauthenticated shopper. If the storefront wants to support logged-in customers, the auth model is OAuth bearer with a refresh token, scoped to the customer’s profile, and the MCP server tracks the customer ID as part of the cart session.

The mistake to avoid: putting storefront credentials in the agent’s request, or putting agent credentials in the storefront’s deploy config. The two auth models serve different trust boundaries and should be kept separate.

Rate limiting: the part where your agent’s enthusiasm becomes your cost

An MCP server for a digital goods storefront is a public endpoint that any agent can call. Some of those agents will be well-behaved; some of them will be enthusiastic to the point of being a problem. A single agent that decides your storefront is interesting can call search_catalog a thousand times per minute, hammer the get_product endpoint for every product in the catalog, and overwhelm both your MCP server and the database it talks to.

The fix is rate limiting at three layers.

Per-IP rate limit. The cheapest defense. A single IP can make at most N requests per minute. Stops the simplest abuse and slows down the rest.

Per-API-key rate limit. The right defense against the well-behaved-but-too-enthusiastic agent. A single API key can make at most M requests per minute, with M lower than N. Allows legitimate agents to work but stops one agent from consuming the whole budget.

Per-tool rate limit. The right defense against a specific tool being expensive. The search_catalog tool can be called a few times per minute per session; the complete_checkout tool can be called once per minute per session. The expensive tools get tighter limits.

The rate limits are enforced in the MCP server, not in the agent. The agent receives a 429 Too Many Requests response when it exceeds a limit, with a Retry-After header telling it when to try again. The MCP server logs the rate limit event so the store’s operations team can see who’s hitting the limits.

A storefront that ships without rate limiting is a storefront that is one curious agent away from a production incident. The rate limits are not optional.

Observability: the part where the AI agent fails silently

An MCP server that is “working” in the sense that it returns responses is not the same as an MCP server that is “working” in the sense that agents are completing purchases. The first kind of working is what the MCP server logs say; the second kind of working is what the storefront’s revenue dashboard says. The gap between the two is the observability problem.

The fix is the same as for any API: structured logs, metrics, and traces. Specifically:

Structured logs. Every tool call gets a log line with the agent’s identity, the tool name, the input shape (not the full input, which can contain PII), the response code, the latency, and a request ID that propagates through the request. The logs are queryable (by agent, by tool, by response code, by latency bucket) so the store’s team can answer questions like “is agent X still working today” without grep.

Metrics. Counter for tool calls per agent per tool, histogram for tool latency per tool, counter for error responses per agent per tool, gauge for active shopping sessions. The metrics are the source of the storefront’s “are agents still buying from us” dashboard.

Traces. A sample of tool calls (1 in 100, configurable) gets a full distributed trace that follows the request from the MCP server through the underlying product database, the cart service, the checkout service. The traces are the source of the “why is complete_checkout slow today” answer.

The investment in observability pays back the first time something goes wrong. Without it, the storefront’s team is reading error reports from individual agents and trying to reconstruct what happened. With it, the team is reading a dashboard that shows the same data for every agent and every tool.

The four mistakes that turn a demo into a production incident

A short list, in the spirit of “things I have seen go wrong and would like to not see go wrong again.”

Mistake 1: Treating the MCP server as a thin wrapper over the existing API. This is the most common mistake. The team has a REST API for the storefront; they expose every endpoint as a tool; the agent gets overwhelmed by the surface area; the tool calls are inefficient; the schema doesn’t match how an agent would actually use the store. The fix is to design the MCP server’s tool surface from the agent’s perspective, not the existing API’s perspective. Some endpoints map cleanly to tools; some don’t; some new tools don’t exist as endpoints yet. The MCP server is its own product.

Mistake 2: Exposing write operations that should be read-only. A tool that lets the agent modify the storefront’s catalog directly from a customer-facing agent is a security incident waiting to happen. The agent’s credentials should not be able to add products, change prices, or modify inventory. The tool surface for the agent should be read-mostly with a small number of well-defined write operations (create cart, update cart, complete checkout). Anything that modifies the storefront itself should require a separate, more privileged credential.

Mistake 3: No idempotency on checkout. A network failure between the MCP server and the payment processor can result in a charge that succeeded but a response that didn’t get back to the agent. The agent retries. The MCP server processes the retry. The shopper gets charged twice. The fix is idempotency keys on complete_checkout: the agent sends a unique idempotency_key with each checkout request, the MCP server stores the result of the first request and returns the same result for any subsequent request with the same key. Stripe, Adyen, and Braintree all support this pattern; use it.

Mistake 4: No schema versioning. The MCP server is at version 1.0.0. The agent is integrated against the v1 tools. Six months later, the storefront needs to add a new field to get_product for pre-order inventory. The team makes the change and deploys. The agent’s code, which was written against the v1 schema, breaks. The fix is to ship the v1 schema unchanged, ship the v2 schema as a new tool (get_product_v2), and deprecate the v1 tool over a version cycle. The agent’s code is untouched; the new agents adopt v2 at their own pace.

These are not exotic mistakes. They are the mistakes every team makes on their first MCP server. The team that has built a few of them learns to avoid them; the team that is building their first one learns the hard way.

A worked example, end to end

A minimal MCP server for a digital goods storefront, in Python, with FastMCP. This is a real implementation; the tool names map to the catalog described above, the schema is versioned, the auth is API key, and the rate limits are enforced.

from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field
from typing import Optional, List
import os

mcp = FastMCP("Digital Goods Storefront")

# Rate limits are enforced at the API gateway, not here.
# Auth is API key, validated at the API gateway, identity passed via context.

class CartItem(BaseModel):
    product_id: str
    quantity: int = Field(gt=0, le=99)

class CartSummary(BaseModel):
    cart_id: str
    items: List[CartItem]
    subtotal_cents: int
    currency: str

@mcp.tool()
def search_catalog(query: str, limit: int = 20) -> dict:
    """Search the product catalog by free-text query."""
    # In real life, this would call the product database.
    # For this example, we return a structured empty result.
    return {
        "results": [],
        "total": 0,
        "query": query,
    }

@mcp.tool()
def get_product(product_id: str) -> dict:
    """Fetch a single product by ID."""
    return {
        "product_id": product_id,
        "title": "",
        "description": "",
        "price_cents": 0,
        "currency": "USD",
        "download_url": None,
    }

@mcp.tool()
def get_storefront_policies() -> dict:
    """Return the store's shipping, return, and refund policies."""
    return {
        "shipping": "Digital delivery only. No physical shipping.",
        "returns": "30-day refund on digital goods, no questions asked.",
        "license": "Personal and commercial use. Resale prohibited.",
    }

@mcp.tool()
def create_cart(items: List[CartItem]) -> dict:
    """Start a new cart session."""
    return {
        "cart_id": "cart_new",
        "items": [item.model_dump() for item in items],
        "subtotal_cents": 0,
        "currency": "USD",
    }

Deploy this with a single command: fastmcp run server.py:mcp. The server is live on stdio. For HTTP transport, wrap it with mcp.run(transport="streamable-http"). The agent discovers the tools, calls them, and gets responses.

The implementation is straightforward. The design decisions (which tools to expose, how to version the schema, what auth to use, how to rate limit, what to log) are the hard part. The rest is JSON Schema and a database connection.

The answer in 30 seconds

An MCP server for a digital goods storefront is a JSON-RPC endpoint that AI agents call to browse, buy, and download. The tools should be verbs the agent would actually perform (search, get, create cart, complete checkout), not endpoints the underlying API happens to have. The schema should be versioned immutably; the auth should be layered (storefront, agent, shopper); the rate limits should be per-IP, per-key, and per-tool; the checkout should be idempotent; the surface should be designed from the agent’s perspective, not the API’s. A well-built MCP server fits in 1000–2000 lines of code, deploys in an afternoon, and survives a year of production without breaking the agents that integrate with it.

The protocol is the easy part. The design is the work. Pick the design carefully and the rest is implementation.

Frequently asked questions

What is an MCP server for a digital goods storefront?

An MCP server for a digital goods storefront is a JSON-RPC endpoint that exposes a small set of tools (typically search_catalog, get_product, create_cart, update_cart, get_checkout_options, complete_checkout, and download-related tools) for AI shopping agents to call. The server handles auth, rate limiting, and observability; the agent handles the shopping logic. The protocol is the Model Context Protocol; the implementation is up to the storefront.

What transport should an MCP server use?

Streamable HTTP is the right choice for a hosted MCP server that any agent can reach over the network. HTTP+SSE is the right choice when the deployment environment requires a long-lived connection. stdio is the right choice for a developer-local MCP server that helps you test the catalog. The choice has consequences: streamable HTTP lets you run on serverless platforms, HTTP+SSE forces you onto a container platform.

How do I version an MCP server’s tools?

Tools are immutable once published. Adding optional fields is fine. Removing fields, renaming fields, changing types, or making previously optional fields required is a breaking change. Breaking changes get a new tool name (search_catalog_v2) or a new server version. The MCP server should expose a version field in its handshake so the agent knows which contract it is talking to. The version should follow semver; major version bumps are for breaking changes only.

How do I handle rate limiting on an MCP server?

At three layers: per-IP (the cheapest defense, stops simple abuse), per-API-key (the right defense against well-behaved-but-enthusiastic agents, scopes the budget to one agent), and per-tool (the right defense against specific expensive tools). The limits are enforced in the MCP server, not the agent. A 429 response with a Retry-After header is the correct error response when a limit is exceeded. The limits are logged so the store’s team can see who’s hitting them.

How do I keep checkout idempotent?

Use idempotency keys. The agent sends a unique idempotency_key with each complete_checkout request. The MCP server stores the result of the first request and returns the same result for any subsequent request with the same key. If the network fails between the MCP server and the payment processor, the agent retries with the same key, the MCP server returns the same response, and the shopper doesn’t get charged twice. Stripe, Adyen, and Braintree all support this pattern; use it.

Should the MCP server expose the existing REST API directly?

No. The MCP server’s tool surface should be designed from the agent’s perspective, not the existing API’s perspective. Some endpoints map cleanly to tools; some don’t; some new tools don’t exist as endpoints yet. The MCP server is its own product. A team that treats the MCP server as a thin wrapper over the existing API will end up with an MCP server that exposes too many tools, doesn’t match how an agent would actually use the store, and is hard to evolve.

How do I handle authentication for an MCP server for a digital goods storefront?

At three layers: storefront-level (the server’s own credentials, configured at deploy time, never exposed to the agent), agent-level (the agent’s API key or OAuth bearer, scoped to the operations the agent can perform), and shopper-level (when the storefront supports logged-in customers, the customer’s identity is part of the cart session, with the agent’s credentials scoped to that customer). The mistake to avoid: putting storefront credentials in the agent’s request, or putting agent credentials in the storefront’s deploy config. The two auth models serve different trust boundaries.

How this fits the rest of the stack

An MCP storefront server is a real piece of production infrastructure, and the cost should be modelled like any other backend service — runtime, memory, bandwidth, storage, and database for the catalog and orders. The team’s mental model for the storefront cost is the request rate, the catalog size, the order volume, and the bandwidth. The RunxBuild hosting calculator is the right place to model that — pick the runtime size, the database tier, the storage, and the expected traffic, and the calculator shows what the storefront costs at the team’s actual usage.

Useful related references:

#MCP Server#MCP#Model Context Protocol#Storefront#Digital Goods#AI Agents#Commerce#UCP