Skip to main content
Glama

Agent Commerce SDK

Install our SDK on a Shopify merchant, connect Razorpay, and expose the merchant's commerce capabilities through MCP so AI agents can shop and transact on behalf of users under explicit authorization and policy.

This is a control plane, not another storefront, chatbot, or payment gateway. Shopify remains the system of record for commerce; Razorpay remains the system of record for payments. This SDK owns orchestration, identity, delegated authorization, deterministic policy, approval, reconciliation, and audit — see docs/ARCHITECTURE.md for the full design and the boundaries between what Shopify/Razorpay/our SDK each own.

Architecture

AI AGENT (Local, or any external MCP client)
        |
        v
UNIFIED MCP SERVER  (/mcp -- official `mcp` SDK, streamable-HTTP)
        |
        v
mcp.tools.execute_tool  <-- the single dispatch choke point
        |
        v
AGENT COMMERCE SDK (sdk/agent_commerce/)
  commerce/  identity/  policy/  audit/  security/
        |                    |
        v                    v
  shopify/ (ShopifyPort)   razorpay/ (RazorpayPort)
        |                    |
        v                    v
  Shopify commerce state   Razorpay payment state

Full component/data-flow diagrams, the database ERD, the Shopify/Razorpay integration boundaries, and every ambiguity resolved during the build are in docs/ARCHITECTURE.md. The REST API contract both the backend and the dashboard build against is in docs/API_CONTRACT.md.

Related MCP server: Commerce MCP Server

Repository layout

sdk/agent_commerce/   the SDK -- all business logic lives here
  core/                shared types, errors, Protocols (ShopifyPort/RazorpayPort)
  shopify/             Admin API GraphQL client, webhooks, demo-mode fallback
  razorpay/            orders, payments, verification, webhooks, reconciliation
  identity/            users, agents, delegated grants, scopes
  policy/              deterministic rules, evaluator, versioning
  commerce/            catalog, cart, checkout, orders, fingerprinting
  agent/               Ollama qwen2.5:7b bounded tool-calling loop
  mcp/                 the unified MCP server + tool dispatcher
  audit/               append-only ledger + evidence-backed decision records
  security/            input validation, idempotency, signatures, encryption
  client.py            the `AgentCommerce` SDK facade (see below)
api/app/               FastAPI REST routes + app wiring; mounts the MCP server
frontend/              Next.js merchant dashboard
tests/                 unit, security, shopify, razorpay, ai, e2e, mcp, integration
scripts/               seed_demo.py

Prerequisites

  • Python 3.12+

  • PostgreSQL (via Docker Compose, or your own instance)

  • Ollama, running locally, with qwen2.5:7b pulled:

    ollama pull qwen2.5:7b

    The application never substitutes another model — OLLAMA_MODEL is hard-validated to equal exactly qwen2.5:7b at startup.

  • Node 20+ (for the dashboard)

  • A Razorpay account in TEST MODE for real payment testing (optional — see "Running without credentials" below)

  • A Shopify development store for real catalog/order testing (optional — see below)

1. Install the SDK

python -m venv .venv
# Windows: .venv\Scripts\activate    macOS/Linux: source .venv/bin/activate
pip install -e ./sdk
pip install -r api/requirements.txt
pip install -r requirements-dev.txt   # adds pytest, ruff for development

2. Configure environment variables

cp .env.example .env

Fill in what you have; the application runs with partial configuration (see "Running without credentials"). Key variables:

Variable

Required for

Notes

DATABASE_URL

everything

defaults to local Postgres

APP_SECRET_KEY

dashboard sessions, credential encryption

set a real random value outside development

OLLAMA_BASE_URL, OLLAMA_MODEL

the agent runtime

model must be exactly qwen2.5:7b

SHOPIFY_SHOP_DOMAIN, SHOPIFY_CLIENT_ID, SHOPIFY_CLIENT_SECRET

real Shopify catalog/orders

the app mints + refreshes its own token; see step 4

RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET, RAZORPAY_WEBHOOK_SECRET

real payments

test mode only — see step 5

3. Start Postgres and run migrations

docker compose up -d postgres
alembic upgrade head

4. Connect a Shopify store (optional)

Without this step, the SDK runs against a dynamically generated demo catalog (shopify/demo_data.py) so the full search → cart → checkout → policy → approval flow is usable immediately. Real Shopify activates automatically the moment credentials are set — nothing else changes.

To connect a real development store:

  1. In the Shopify Dev Dashboard, create an app (custom-app creation moved out of store admin in 2026).

  2. Grant Admin API scopes: read_products, read_inventory, read_orders, write_draft_orders, read_customers — plus write_discounts and write_orders if you want the growth agent's campaigns and synthetic seeding. Scopes take effect only once you release a version and the store approves it.

  3. Install the app on your store.

  4. Set SHOPIFY_SHOP_DOMAIN (e.g. your-store.myshopify.com) plus SHOPIFY_CLIENT_ID and SHOPIFY_CLIENT_SECRET in .env. Leave SHOPIFY_ACCESS_TOKEN empty — see below.

  5. Sync the catalog via the dashboard's onboarding flow, or POST /api/merchants/{id}/shopify/sync.

You do not paste an access token. Shopify stopped issuing non-expiring tokens for custom apps in January 2026 — every token now lasts about 24 hours, so a pasted one dies overnight (it surfaces as a 401 mid-session). Instead the app mints its own from SHOPIFY_CLIENT_ID/SHOPIFY_CLIENT_SECRET via the client-credentials grant, caches it in-process, refreshes it five minutes before expiry, and re-mints once automatically if a request is rejected with a 401 — see sdk/agent_commerce/shopify/tokens.py. Setting SHOPIFY_ACCESS_TOKEN still works and takes precedence, but a pinned token is never refreshed.

One prerequisite worth knowing: the client-credentials grant only works when the app and the store belong to the same Shopify organization (a store created from the Dev Dashboard's Dev stores page). A store created from Shopify admin sits outside the org and the grant is refused regardless of how correct the credentials are.

The SDK uses Shopify's GraphQL Admin API exclusively — never storefront HTML scraping. Checkout is built on Shopify Draft Orders (not Storefront Cart/Checkout), since payment capture happens through Razorpay rather than Shopify's native checkout; see docs/ARCHITECTURE.md for why.

5. Connect Razorpay Test Mode (optional)

Without this, every read/browse/cart/checkout/policy/approval tool call still works — only complete_checkout (the one tool that moves money) fails with a clear "Razorpay Test Mode integration is ready to wire" message rather than faking a payment result. Money movement is never simulated.

  1. Razorpay Dashboard → Settings → API Keys, generate a Test Mode key pair.

  2. Set RAZORPAY_KEY_ID and RAZORPAY_KEY_SECRET in .env.

  3. For webhook-driven payment reconciliation, add a webhook endpoint pointing at POST /api/webhooks/razorpay subscribed to payment.captured and payment.failed, and set RAZORPAY_WEBHOOK_SECRET from the webhook configuration.

Never use live/production Razorpay credentials with this project.

6. Start the backend

python run_api.py --reload

This serves the REST API under /api/* and mounts the unified MCP server at /mcp in the same process (see api/app/main.py for how the two ASGI apps' lifespans are combined).

Use run_api.py, not uvicorn directly — especially on Windows. psycopg's async mode cannot run on Windows' ProactorEventLoop, and uvicorn hardcodes that loop on win32 via an explicit loop_factory, which bypasses the asyncio event-loop policy entirely (so setting the policy has no effect). run_api.py passes a SelectorEventLoop factory through uvicorn's supported loop= hook. Running uvicorn api.app.main:app directly on Windows starts fine but fails every database request with InterfaceError: Psycopg cannot use the 'ProactorEventLoop'.

Verify it's up:

curl http://localhost:8000/api/health

Interactive API docs: http://localhost:8000/docs

7. Start the dashboard

cd frontend
npm install
npm run dev

Visit http://localhost:3000. The onboarding flow walks through connecting Shopify, connecting Razorpay, configuring policy, and confirming MCP is live.

8. Connect an MCP-compatible agent

Fastest path — one command creates a user, merchant, policy, agent, and grant, syncs the catalog, and prints a ready-to-use bearer token:

python scripts/seed_demo.py

(Or do the same through the dashboard's onboarding flow.) Then point any MCP client (Claude Desktop, a custom client, etc.) at:

http://localhost:8000/mcp
Authorization: Bearer <session token from the grant>

The token is shown exactly once — only its SHA-256 hash is stored.

9. Perform a test transaction

With the dashboard's chat interface (backed by agent/runtime.py and real Ollama inference) or any connected MCP client:

"Find me a black hoodie under ₹2,000."
  -> search_catalog (real Shopify or demo catalog)
"Choose the best one."
  -> agent selects from actual returned products
"Buy it."
  -> create_cart -> create_checkout -> policy evaluation
     -> ALLOW: proceeds automatically
     -> REQUIRES_APPROVAL: approve via the dashboard's Purchase Approval screen
  -> complete_checkout -> real Razorpay order (test mode) -> webhook -> reconciliation

Running without credentials

Per the project's design, missing external credentials never block development:

  • No Shopify credentialsshopify/demo_data.py provides a dynamically generated (Faker-backed, never hardcoded) catalog satisfying the exact same ShopifyPort interface the real client does.

  • No Razorpay credentials → every tool up through request_purchase_approval works; complete_checkout raises a clear, actionable configuration error instead of faking success.

Accounts: admin and customer

Two roles share one login form. The role is a property of the stored account, decided at registration — it is never sent in a request body or picked on the login screen, because a self-selected role would be a bypassable boundary.

  • The first account to register becomes the admin. They connect Shopify and Razorpay, register agents, edit policy, and see Transactions/Audit.

  • Every account after that is a customer. They get their own spending policy (inherited from the admin's active policy), and can chat with an agent an admin has assigned to them, approving their own purchases.

Assigning is one click: Agents → Assign customer. Mechanically it issues an AgentGrant whose user_id is the customer, which is what that column already means — identity/grants.py:resolve_request_context sets RequestContext.user_id = grant.user_id, so the customer's carts, checkouts, approvals, and daily-spend tracking are all their own. One agent can therefore serve many customers, each isolated from the others by the same require_ownership checks that protect any two users.

Enforcement lives in api/app/deps.py:current_admin (403 for a customer), not in the UI — hiding a nav item is presentation, not security.

Customers are also mirrored into Shopify as Customer records. That needs the write_customers scope and protected-customer-data approval on your app; if Shopify refuses, registration logs a warning and continues, and the local customer works fully regardless.

Security model

  • The LLM never authorizes payment. Qwen2.5:7B selects tools and explains results; it never computes prices, evaluates policy, or resolves identity. Every tool call is treated as untrusted input.

  • Deterministic policy engine. policy/evaluator.py is pure, rule-based logic (scope → inventory → category → max transaction → daily limit → approval threshold). No LLM involvement, ever.

  • Checkout fingerprinting. Every checkout is hashed from its trusted commerce facts (merchant, currency, total, line items). An approval is bound to that exact fingerprint; if it changes before payment (e.g. a price change), the approval is invalidated and a new one is required.

  • Ownership enforcement. Every resource load checks identity/grants.py:require_ownership — one user's agent cannot read or mutate another user's cart, checkout, or order.

  • Explicit tool allowlist. security/validation.py defines a strict Pydantic schema per tool (extra="forbid"); there is no raw GraphQL, raw SQL, or arbitrary HTTP passthrough anywhere in the tool surface.

  • Prompt-injection resistance. Product descriptions/tags are treated as untrusted data; the agent's system prompt explicitly instructs it never to follow instructions embedded in tool output. See tests/security/.

  • Idempotency everywhere it matters. Webhook processing, payment execution, and approval creation are all deduplicated via security/idempotency.py so a retried request with the same key replays its prior result rather than double-charging, double-creating, or double-applying state. Reusing a key with a different payload raises a conflict instead of silently applying the new payload.

  • Signed webhooks only. Shopify (X-Shopify-Hmac-Sha256, base64) and Razorpay (X-Razorpay-Signature, hex) webhooks are verified via HMAC-SHA256 before any processing; an invalid or missing signature is rejected.

Full details, including the exact evidence-backed Decision Record format and the audit event schema, are in docs/ARCHITECTURE.md.

MCP tools

Exposed by the unified MCP server — the agent never talks to Shopify or Razorpay directly.

Tool

Risk

Scope

search_catalog, get_product, get_inventory

READ

catalog:read / inventory:read

get_cart, get_checkout, get_order, get_payment_status, get_order_history, get_customer_profile

READ

various *:read

create_cart, add_to_cart, remove_from_cart, update_cart

LOW_WRITE

cart:write

create_checkout, refresh_checkout

MONEY / LOW_WRITE

checkout:create

request_purchase_approval

MONEY

payment:request

complete_checkout

MONEY

payment:request

list_approvals, get_policy

READ

approval:read

decide_approval

MONEY

approval:decide — opt-in, see below

Read-only MCP resources (resources/read): merchant://catalog, merchant://policies, user://orders/{id}, checkout://{id}, transaction://{id}.

Connecting Claude / ChatGPT

The /mcp endpoint is a standard streamable-HTTP MCP server, so Claude Desktop, Claude Code, ChatGPT (Developer Mode, paid plan) and any other MCP client can drive the full user-side surface: browse, cart, checkout, request approval, pay via Razorpay, read orders and the audit timeline. Setup steps per client — including the ngrok tunnel ChatGPT needs, since it cannot reach localhost — are in docs/MCP_CLIENTS.md.

approval:decide is the one scope to think about before granting. It lets a client approve its own pending purchase from inside the chat instead of the dashboard — convenient for an agent you drive personally, but it means the agent that proposed a purchase can approve it, turning the approval threshold into a speed bump rather than a two-party gate. It is in no default scope set, and POST /api/agents/{id}/assign refuses it outright so an agent assigned to a customer can never hold it. Ownership, stale-price invalidation, and an audit record marked source="mcp" all still apply when it is granted.

Merchant companion agent (growth/revenue insights for admins)

A second agent persona, admin-facing rather than shopper-facing: it reads a merchant's real Shopify order/customer data, surfaces growth insights (e.g. "revenue is up 12% this month, largely from N repeat customers"), and can propose campaigns like a loyalty discount for repeat buyers — but a proposed campaign only takes effect on Shopify after an admin approves it on the dashboard. Built on the same MCP-tool/scope architecture as the shopper agent, so it's pluggable the same way: create one via POST /api/agents {"kind": "merchant_companion"}, issue it a grant with analytics:read/campaign:write/data:seed_write scopes, then chat with it through the same /api/chat/{agent_id} endpoint used for shopping.

Tool

Risk

Scope

get_growth_insights, get_customer_segments

READ

analytics:read

propose_campaign

LOW_WRITE (drafts only — never touches Shopify)

analytics:read

launch_campaign

HIGH_WRITE (creates a real discount code)

campaign:write

seed_synthetic_data

HIGH_WRITE

data:seed_write

These scopes are disjoint from the shopper scopes above: a shopper-scoped grant can never call a growth tool, and a growth-scoped grant can never move money. Full flow (insights → propose → admin approves → launch → metrics snapshot) and the campaign state machine are in docs/ARCHITECTURE.md "Merchant companion agent"; the REST surface is in docs/API_CONTRACT.md "Growth & Campaigns".

Example: using the SDK directly

from agent_commerce import AgentCommerce

commerce = AgentCommerce(session=session, shopify=shopify, razorpay=razorpay, ctx=ctx)

products = await commerce.catalog.search(query="hoodie", max_price=Decimal("2000"))
cart = await commerce.cart.create(lines=[{"variant_id": "...", "quantity": 1}])
checkout = await commerce.checkout.create(cart_id=cart["cart_id"])
if checkout["next_step"] == "request_purchase_approval":
    await commerce.checkout.request_approval(checkout["checkout_id"])
    # ... user approves via the dashboard ...
payment = await commerce.checkout.complete(checkout["checkout_id"])

Both this facade and the MCP transport route through the identical mcp.tools.execute_tool dispatcher — business logic exists in exactly one place.

Testing

pytest tests/unit tests/security          # fast, no external dependencies
pytest tests/mcp                          # MCP protocol conformance (tools/call, resources/read)
pytest tests/razorpay                     # real Razorpay test-mode API (needs credentials)
pytest tests/shopify                      # real Shopify Admin API (needs credentials; skips cleanly without)
pytest tests/ai tests/e2e                 # real Ollama qwen2.5:7b inference, no mocking (slower)

tests/ai/test_real_ollama.py and tests/e2e/test_real_agent_shopify_flow.py never mock the LLM, the policy engine, or the commerce layer — they exercise real inference against a real (demo or seeded) catalog and real PostgreSQL, per the project's testing philosophy: prefer real integrations over mocked demonstrations. tests/shopify/ and tests/integration/ are reserved for real-Shopify-credential coverage and cross-module integration tests respectively — currently unpopulated (no test files yet).

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/PR-HARIHARAN/agent_commerce'

If you have feedback or need assistance with the MCP directory API, please join our Discord server