How Catalog Tools Work: Making E-Commerce Catalogs Accessible to AI
A deep dive into the catalog management system that enables AI assistants to manage Sitecore OrderCloud catalogs through natural language
Introduction: What Are Catalogs in E-Commerce?
In e-commerce, a catalog is a collection of products organized for a specific purpose. Think of it as a curated storefront. In Sitecore OrderCloud, catalogs are powerful organizational structures that:
- Group products into logical collections
- Control visibility by assigning catalogs to buyers, user groups, or individual users
- Enable multi-tenant commerce where different buyers see different product offerings
- Support B2B scenarios where different customer segments get different catalogs
For example, a company might have:
- A “Retail Catalog” for end consumers
- A “Wholesale Catalog” for business customers
- A “VIP Catalog” for premium customers
The Challenge: Making Catalogs AI-Accessible
The OrderCloud API provides comprehensive catalog management, but using it requires understanding REST API endpoints, knowing exact field names and data structures, handling authentication and error cases, and managing complex relationships.
Traditional API usage looks like this:
const catalog = {
Name: "Electronics Catalog",
Description: "Consumer electronics products",
Active: true
}
await axios.post('https://api.ordercloud.io/v1/catalogs', catalog, {
headers: { Authorization: `Bearer ${token}` }
})
Our goal was to make this accessible through natural language:
“Create a catalog called ‘Electronics Catalog’ with description ‘Consumer electronics products’ and make it active.”
The AI assistant handles everything automatically—no code required.
Architecture: Three-Layer Design
The catalog tools system follows a clean three-layer architecture:
Layer 1: Tool Registration – The AI Interface
The catalog-tools.ts file defines what AI assistants can do with catalogs. It’s the bridge between natural language and API calls.
How Tools Are Registered
Each tool follows this pattern:
server.registerTool(
"tool_name", // Unique identifier
{
title: "Human-Readable Title",
description: "What this tool does",
inputSchema: { // Zod schema for validation
// Parameter definitions
}
},
async (input) => { // Handler function
// Execute the operation
// Return formatted response
}
)
Example: The Create Catalog Tool
Let’s examine the complete implementation of the create_catalog tool:
server.registerTool(
"create_catalog",
{
title: "Create Catalog",
description: "Create a new catalog in OrderCloud",
inputSchema: {
id: z.string().optional(), // Optional custom ID
name: z.string(), // Required name
description: z.string().optional(), // Optional description
active: z.boolean().optional().default(true), // Defaults to active
xp: z.record(z.any()).optional(), // Extended properties
},
},
async (input) => {
try {
// Transform input to OrderCloud format
const catalog = {
...(input.id && { ID: input.id }),
Name: input.name,
...(input.description && { Description: input.description }),
Active: input.active,
...(input.xp && { xp: input.xp }),
}
// Call the client
const result = await orderCloudClient.catalogs.saveCatalogAssignment(assignment)
// Client Method
async saveCatalogAssignment(assignment: CatalogAssignment) {
this.ensureAuthenticated()
const response = await this.client.post<CatalogAssignment>(
"v1/catalogs/assignments",
assignment
)
return response.data
}
// API Call
POST https://sandboxapi.ordercloud.io/v1/catalogs/assignments
Body: {
"CatalogID": "summer-collection-001",
"BuyerID": "retail-buyer"
}
Step 5: Response to User
AI formats the response:
✅ Assigned catalog to Retail buyer
Advanced Features
1. Advanced Filtering and Search
The list_catalogs tool supports powerful query capabilities:
// Search by name
{
search: "Electronics",
searchOn: ["Name"]
}
// Multiple sort fields
{
sortBy: ["Name", "!ID"] // Sort by Name ascending, then ID descending
}
// Complex filters
{
filters: {
Active: true,
"CategoryCount": ">10" // More than 10 categories
}
}
The client transforms these into OrderCloud query parameters:
GET /v1/catalogs?search=Electronics&searchOn=Name&sortBy=Name,!ID&filters[Active]=true
2. Extended Properties (XP)
OrderCloud supports custom extended properties for storing additional metadata:
{
name: "Electronics Catalog",
xp: {
season: "Summer 2024",
region: "North America",
customField: "value",
promotions: {
enabled: true,
discountPercent: 15
}
}
}
These are preserved and passed through to the API, enabling custom metadata without schema changes.
3. Comprehensive Error Handling
The system implements error handling at multiple levels:
catch (error) {
return {
content: [{
type: "text",
text: `Error creating catalog: ${error.message}`
}],
isError: true
}
}
catch (error) {
const errorDetails = {
message: error.message,
response: {
status: error.response?.status,
data: error.response?.data
}
}
DebugLogger.log("createCatalog_error", { catalog, errorDetails }, undefined, error)
throw error
}
This provides:
- User-friendly error messages for the AI to communicate
- Detailed debugging information in logs
- HTTP status codes for troubleshooting
- Full API error responses
Real-World Use Cases
Use Case 1: Multi-Tenant Catalog Setup
Setting up catalogs for different customer segments:
// AI receives request:
// "Create three catalogs: 'Retail Catalog', 'Wholesale Catalog',
// and 'VIP Catalog'. Make them all active."
// AI executes three create_catalog calls:
await orderCloudClient.catalogs.create({
Name: "Retail Catalog",
Active: true
})
await orderCloudClient.catalogs.create({
Name: "Wholesale Catalog",
Active: true
})
await orderCloudClient.catalogs.create({
Name: "VIP Catalog",
Active: true
})
Use Case 2: Catalog Assignment Management
Assigning catalogs to multiple buyers:
// AI receives request:
// "Assign the 'Electronics Catalog' to the 'Retail Buyer' and
// the 'Wholesale Buyer'."
// AI executes two save_catalog_assignment calls:
await orderCloudClient.catalogs.saveCatalogAssignment({
CatalogID: "electronics-catalog",
BuyerID: "retail-buyer"
})
await orderCloudClient.catalogs.saveCatalogAssignment({
CatalogID: "electronics-catalog",
BuyerID: "wholesale-buyer"
})
Use Case 3: Bulk Catalog Operations
Updating multiple catalogs based on criteria:
// AI receives request:
// "Find all catalogs with 'Summer' in the name and set them to inactive."
// Step 1: List catalogs with filter
const catalogs = await orderCloudClient.catalogs.listCatalogs({
search: "Summer",
searchOn: ["Name"]
})
// Step 2: Patch each catalog
for (const catalog of catalogs.Items) {
await orderCloudClient.catalogs.patch(catalog.ID, {
Active: false
})
}
Use Case 4: Catalog Discovery with Complex Filtering
// AI receives request:
// "Show me all active catalogs that have more than 50 products."
const catalogs = await orderCloudClient.catalogs.listCatalogs({
filters: {
Active: true,
"CategoryCount": ">50"
},
sortBy: ["CategoryCount"],
pageSize: 100
})
// Returns structured data for AI to format
Design Patterns and Best Practices
1. Separation of Concerns
Each layer has a single, well-defined responsibility:
- Tools Layer – AI interface, validation, response formatting
- Client Layer – HTTP communication, data transformation
- API Layer – Data persistence
This separation makes the code:
- Easier to understand and maintain
- Simple to test in isolation
- Flexible to modify without ripple effects
2. Type Safety with TypeScript and Zod
Full TypeScript typing combined with Zod runtime validation ensures correctness:
interface Catalog {
ID?: string
Name: string
Description?: string
Active?: boolean
CategoryCount?: number
xp?: Record<string, any>
}
interface ListResponse<T> {
Items: T[]
Meta: {
Page: number
PageSize: number
TotalCount: number
TotalPages: number
}
}
const catalogSchema = {
id: z.string().optional(),
name: z.string().min(1, "Name is required"),
description: z.string().optional(),
active: z.boolean().optional().default(true),
xp: z.record(z.any()).optional()
}
This provides:
- Compile-time error checking
- IDE autocomplete and IntelliSense
- Self-documenting code
- Runtime validation to catch invalid data
3. Graceful Error Recovery
Errors are handled at multiple levels with context-appropriate responses:
// Level 1: Validation errors (caught early)
if (!input.name) {
throw new Error("Catalog name is required")
}
// Level 2: Client errors (network, auth)
try {
const response = await this.client.post(...)
} catch (error) {
if (error.response?.status === 401) {
throw new Error("Authentication failed. Please check credentials.")
}
throw error
}
// Level 3: API errors (business logic)
if (response.data.Errors) {
throw new Error(`API Error: ${response.data.Errors[0].Message}`)
}
4. Extensibility
The architecture makes it easy to add new capabilities:
// 1. Add client method
async getCatalogStats(id: string): Promise<CatalogStats> {
this.ensureAuthenticated()
const response = await this.client.get(`v1/catalogs/${id}/stats`)
return response.data
}
// 2. Register tool
server.registerTool(
"get_catalog_stats",
{
title: "Get Catalog Statistics",
description: "Get detailed statistics for a catalog",
inputSchema: {
catalogId: z.string()
}
},
async (input) => {
const stats = await orderCloudClient.catalogs.getCatalogStats(input.catalogId)
return {
content: [{ type: "text", text: JSON.stringify(stats, null, 2) }]
}
}
)
Testing and Debugging
Debug Tools
The system includes dedicated debug tools:
server.registerTool(
"get_debug_logs",
{
title: "Get Debug Logs",
description: "Retrieve debug logs for troubleshooting",
inputSchema: {
includeParams: z.boolean().optional(),
includeResults: z.boolean().optional(),
maxLogs: z.number().optional().default(20)
}
},
async (input) => {
const logs = DebugLogger.getLogs({
limit: input.maxLogs,
includeParams: input.includeParams,
includeResults: input.includeResults
})
return {
content: [{
type: "text",
text: JSON.stringify(logs, null, 2)
}]
}
}
)
server.registerTool(
"test_api_connection",
{
title: "Test API Connection",
description: "Test the connection to OrderCloud API",
inputSchema: {}
},
async () => {
try {
await orderCloudClient.catalogs.listCatalogs({ pageSize: 1 })
return {
content: [{
type: "text",
text: "✅ API connection successful"
}]
}
} catch (error) {
return {
content: [{
type: "text",
text: `❌ API connection failed: ${error.message}`
}],
isError: true
}
}
}
)
Common Issues and Solutions
| Issue | Symptom | Solution |
|---|---|---|
| Authentication Errors | “Not authenticated” errors | Ensure credentials are set in environment variables. Use test_api_connection tool. |
| Validation Errors | Zod validation failures | Check input schema requirements. Review error messages for missing/invalid fields. |
| API Errors | 400/404/500 errors | Check error response details in logs. Verify API endpoint and data format. |
| Network Errors | Connection timeouts | Check network connectivity. Verify API base URL is correct. |
Performance Considerations
Pagination
All list operations support pagination to prevent loading excessive data:
const catalogs = await orderCloudClient.catalogs.listCatalogs({
page: 1,
pageSize: 20 // Load 20 catalogs at a time
})
// Response includes metadata for pagination
console.log(catalogs.Meta)
// {
// Page: 1,
// PageSize: 20,
// TotalCount: 157,
// TotalPages: 8
// }
Request Optimization
The client uses efficient HTTP patterns:
- Request Interceptors – Automatic token injection eliminates redundant auth code
- Conditional Requests – Only send necessary parameters
- Type-Safe Responses – No runtime parsing overhead
const params: Record<string, any> = {}
// Only add parameters that have values
if (options?.search) params.search = options.search
if (options?.searchOn) params.searchOn = options.searchOn.join(",")
if (options?.sortBy) params.sortBy = options.sortBy.join(",")
// This prevents sending empty/undefined parameters
Future Enhancements
Potential improvements to the system:
1. Bulk Operations
// Create multiple catalogs in one call
async bulkCreate(catalogs: Catalog[]): Promise<Catalog[]> {
const results = await Promise.all(
catalogs.map(catalog => this.create(catalog))
)
return results
}
// Usage
const catalogs = await orderCloudClient.catalogs.bulkCreate([
{ Name: "Catalog 1", Active: true },
{ Name: "Catalog 2", Active: true },
{ Name: "Catalog 3", Active: true }
])
2. Catalog Templates
// Pre-configured catalog structures
const templates = {
retail: {
Name: "Retail Catalog",
Active: true,
xp: {
type: "retail",
allowsPublicAccess: true
}
},
wholesale: {
Name: "Wholesale Catalog",
Active: true,
xp: {
type: "wholesale",
requiresApproval: true,
minimumOrder: 1000
}
}
}
// Create from template
async createFromTemplate(
templateName: string,
overrides?: Partial<Catalog>
): Promise<Catalog> {
const template = templates[templateName]
return this.create({ ...template, ...overrides })
}
3. Catalog Cloning
async cloneCatalog(
sourceId: string,
newName: string,
includeAssignments: boolean = true
): Promise<Catalog> {
// Get source catalog
const source = await this.get(sourceId)
// Create new catalog
const newCatalog = await this.create({
Name: newName,
Description: source.Description,
Active: source.Active,
xp: source.xp
})
// Clone assignments if requested
if (includeAssignments) {
const assignments = await this.listCatalogAssignments({
catalogId: sourceId
})
for (const assignment of assignments.Items) {
await this.saveCatalogAssignment({
...assignment,
CatalogID: newCatalog.ID
})
}
}
return newCatalog
}
4. Catalog Analytics
interface CatalogAnalytics {
totalCatalogs: number
activeCatalogs: number
inactiveCatalogs: number
avgProductsPerCatalog: number
topCatalogsByProducts: Catalog[]
assignmentStats: {
totalAssignments: number
buyerAssignments: number
userGroupAssignments: number
userAssignments: number
}
}
async getCatalogAnalytics(): Promise<CatalogAnalytics> {
// Implementation would aggregate data across catalogs
}
Conclusion
The catalog tools system demonstrates a robust architecture for making complex e-commerce APIs accessible through AI. By implementing a clean three-layer design with proper separation of concerns, we’ve created a system that is:
- Type-Safe – Full TypeScript coverage with Zod validation
- Maintainable – Clear separation between AI interface, business logic, and API communication
- Debuggable – Comprehensive logging at every level
- Extensible – Easy to add new tools and capabilities
- User-Friendly – Natural language interface eliminates the learning curve
The system scales from simple single-catalog operations to complex multi-tenant scenarios, all while maintaining code quality and developer experience.
Quick Reference
Complete Tool List
// Basic CRUD Operations
list_catalogs(options) - List with filtering, search, sort
get_catalog(id) - Get by ID
create_catalog(catalog) - Create new
update_catalog(id, catalog) - Full update (PUT)
patch_catalog(id, catalog) - Partial update (PATCH)
delete_catalog(id) - Delete
// Catalog Assignments
list_catalog_assignments(options) - List assignments
save_catalog_assignment(assignment) - Create/update assignment
delete_catalog_assignment(catalogId) - Delete assignment
// Bundle Assignments
list_catalog_bundle_assignments(options)
save_catalog_bundle_assignment(assignment)
delete_catalog_bundle_assignment(catalogId, bundleId)
// Product Assignments
list_catalog_product_assignments(options)
save_catalog_product_assignment(assignment)
delete_catalog_product_assignment(catalogId, productId)
Example Code Snippets
// Create catalog with extended properties
await orderCloudClient.catalogs.create({
Name: "Electronics",
Description: "Consumer electronics",
Active: true,
xp: {
region: "North America",
season: "2024"
}
})
// Search with multiple criteria
await orderCloudClient.catalogs.listCatalogs({
search: "Electronics",
searchOn: ["Name"],
sortBy: ["Name"],
filters: { Active: true },
page: 1,
pageSize: 20
})
// Assign to buyer
await orderCloudClient.catalogs.saveCatalogAssignment({
CatalogID: "electronics-catalog",
BuyerID: "retail-buyer"
})
This technical documentation provides a comprehensive guide to understanding and implementing the catalog tools system. For source code, see src/tools/catalogs/catalog-tools.ts and src/tools/clients/catalog-client.ts.

