SAP Commerce · AI-native tooling

commerce-mcp

Make your SAP Commerce (Hybris) estate legible to AI agents — the type system, FlexibleSearch, ImpEx and order traces, exposed as safe, typed, read-only tools over the Model Context Protocol.

The problem

SAP Commerce is powerful but expertise-gated and opaque.

Everything needs an expert

Inspecting types, writing correct ImpEx, tracing an order — all require deep tribal knowledge and manual HAC/Backoffice clicking.

Onboarding takes months

The combinatorial business-flow matrix and sprawling type system mean new engineers are slow for a long time.

AI can't help — yet

LLMs have no safe, structured way into the platform. So the biggest cost driver on every program stays untouched.

The solution

A hardened MCP server that turns the platform into tools an agent can call safely.

        LLM / Agent (Claude, Copilot, …)
                    │  MCP (stdio)
        ┌───────────▼───────────┐
        │   commerce-mcp server  │
        ├────────────────────────┤
        │   read-only tool catalogue     (zod-typed)
        ├────────────────────────┤
        │   CommerceConnector    │
        │   ├─ MockConnector  (offline, zero-config)
        │   └─ LiveConnector  (HAC / OCC / read-only DB) *planned
        └────────────────────────┘
                    │
          SAP Commerce Cloud (Hybris)
ToolWhat the agent can do
describe_typeGet the full definition of any item type.
list_typesDiscover the type system.
run_flexible_searchRun a read-only, row-capped SELECT.
validate_impexCheck ImpEx against the live model before it's applied.

Why it's useful

Collapses the expertise barrier

Junior engineers and agents get expert-level access to the estate — the single biggest cost lever on a program.

Safe by construction

Read-only tools, SELECT-only enforcement, row caps, planned PII redaction & audit logging. Security is a feature.

Offline-first

A fixture-backed mock connector runs the whole surface with zero config — demos and CI need no live instance.

The integration hub

Once agents speak to Commerce through one safe layer, every other tool (upgrades, tracing, migration) can build on it.

Get started

npm install
npm run build
npm start   # MCP server on stdio, backed by the offline mock connector
npm test

Early scaffold: the tool surface, safety model and mock connector are real and tested. The live Hybris connector is a documented extension point on the roadmap.

Usage example

Distilled from the runnable examples/ tutorials, driving the offline MockConnector. The output below is the real captured stdout.

MockConnector + buildTools() — describe, query, and the read-only guard

import { MockConnector } from "commerce-mcp/src/mock-connector.js";
import { buildTools } from "commerce-mcp/src/tools/index.js";

const connector = new MockConnector();
const tools = buildTools(connector);
const call = (name: string) => tools.find((t) => t.name === name)!;

console.log("tools:", tools.map((t) => t.name).join(", "));

// describe_type: full type definition (parent, deployment table, attributes).
const product = await call("describe_type").handler({ code: "Product" }) as any;
console.log(`describe_type Product: extends=${product.extends}, table=${product.deploymentTable}, attrs=[${product.attributes.map((a: any) => a.qualifier).join(", ")}]`);

// run_flexible_search: a read-only SELECT returns capped fixture rows.
const res = await call("run_flexible_search").handler({ query: "SELECT {code},{name} FROM {Product}", maxRows: 10 }) as any;
console.log("run_flexible_search:", JSON.stringify(res));

// SAFETY: a mutating statement is rejected before it reaches the backend.
try {
  await call("run_flexible_search").handler({ query: "DELETE FROM {Product}", maxRows: 10 });
} catch (err) {
  console.log(`DELETE rejected: ${(err as Error).name}: ${(err as Error).message}`);
}

Output:

tools: describe_type, list_types, run_flexible_search, validate_impex
describe_type Product: extends=GenericItem, table=products, attrs=[code, name, catalogVersion]
run_flexible_search: {"columns":["code","name"],"rows":[{"code":"PROD-001","name":"Sample Product"},{"code":"PROD-002","name":"Another Product"}],"rowCount":2,"capped":false,"tookMs":3}
DELETE rejected: ReadOnlyViolationError: Only SELECT queries are permitted through this tool.