npm package: @markstiles/sitecore-cdp-mcp
Description: Model Context Protocol (MCP) server for Sitecore Customer Data Platform APIs
Author: Mark Stiles
Latest version: 1.0.0

# Sitecore CDP MCP Server

A Model Context Protocol (MCP) server for the Sitecore Customer Data Platform (CDP) APIs. This package enables AI assistants like Claude to interact with Sitecore CDP for guest management, order tracking, and audience exports.

[![npm version](https://badge.fury.io/js/%40markstiles%2Fsitecore-cdp-mcp.svg)](https://www.npmjs.com/package/@markstiles/sitecore-cdp-mcp)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Features

- **Guest Management**: Create, search, retrieve, update, and patch guest profiles
- **Type-Safe**: Full TypeScript support with exported types
- **Validation**: Input validation using Zod schemas matching OpenAPI specs
- **Error Handling**: Comprehensive error handling with retry logic
- **Pagination**: Support for paginated results with user control
- **Environment-Based**: Configuration via environment variables

## Installation

```bash
npm install @markstiles/sitecore-cdp-mcp
```

## Configuration

### Environment Variables

Create a `.env` file in your project root:

```env
# Required: Your CDP instance base URL
CDP_BASE_URL=https://api-engage-us.sitecorecloud.io

# Required: Basic Auth credentials (for Guest and Order APIs)
CDP_CLIENT_KEY=your_client_key
CDP_API_TOKEN=your_api_token

# Optional: OAuth 2.0 credentials (for Audience Export API)
# Note: Only required if using Audience Export features
# These are different from Basic Auth credentials above
CDP_OAUTH_KEY=your_oauth_client_id
CDP_OAUTH_SECRET=your_oauth_client_secret

# Optional: Retry configuration
CDP_RETRY_MAX_ATTEMPTS=3
CDP_RETRY_DELAY_MS=1000
```

### Finding Your Credentials

#### Basic Auth Credentials (Guest & Order APIs)

In Sitecore CDP:
1. Navigate to **Settings** > **API Access**
2. **Client Key** = Your username for Basic Auth (`CDP_CLIENT_KEY`)
3. **API Token** = Your password for Basic Auth (`CDP_API_TOKEN`)

#### OAuth 2.0 Credentials (Audience Export API)

For Audience Export functionality, you need separate OAuth credentials:
1. Navigate to **Settings** > **API Access** > **OAuth**
2. Create or use existing OAuth client credentials
3. **OAuth Client ID** = `CDP_OAUTH_KEY`
4. **OAuth Client Secret** = `CDP_OAUTH_SECRET`

**Note**: OAuth tokens are managed automatically by the server with in-memory caching (24-hour expiry with 5-minute refresh buffer).

## MCP Server Setup

### Claude Desktop Configuration

Add to your Claude Desktop configuration file:

**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`

**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "sitecore-cdp": {
      "command": "node",
      "args": [
        "/path/to/node_modules/@markstiles/sitecore-cdp-mcp/dist/index.js"
      ],
      "env": {
        "CDP_BASE_URL": "https://api-engage-us.sitecorecloud.io",
        "CDP_CLIENT_KEY": "your_client_key",
        "CDP_API_TOKEN": "your_api_token",
        "CDP_OAUTH_KEY": "your_oauth_client_id",
        "CDP_OAUTH_SECRET": "your_oauth_client_secret"
      }
    }
  }
}
```

**Note**: OAuth credentials are only required if you plan to use Audience Export features. Guest and Order management only require Basic Auth credentials.

Or if installed globally:

```json
{
  "mcpServers": {
    "sitecore-cdp": {
      "command": "sitecore-cdp-mcp",
      "env": {
        "CDP_BASE_URL": "https://api-engage-us.sitecorecloud.io",
        "CDP_CLIENT_KEY": "your_client_key",
        "CDP_API_TOKEN": "your_api_token",
        "CDP_OAUTH_KEY": "your_oauth_client_id",
        "CDP_OAUTH_SECRET": "your_oauth_client_secret"
      }
    }
  }
}
```

## Available MCP Tools

### Phase 1: Guest Management

#### `cdp_guest_create`

Create a new guest profile in Sitecore CDP.

**Important**: Always search for existing guests by email before creating to avoid duplicates.

```typescript
// Example usage in Claude
{
  "guestType": "customer",
  "firstName": "John",
  "lastName": "Doe",
  "email": "john.doe@example.com",
  "phoneNumbers": ["+353161123345"],
  "city": "Dublin",
  "country": "IE"
}
```

**Parameters**:
- `guestType` (required): `"visitor"`, `"customer"`, or `"traveller"`
- `firstName`: Guest's first name
- `lastName`: Guest's last name
- `email`: Primary email address
- `emails`: Array of email addresses
- `phoneNumbers`: Array of phone numbers (E.164 format)
- `dateOfBirth`: ISO 8601 date-time (must be in the past)
- `gender`: `"male"`, `"female"`, or `"unknown"`
- `title`: Title (e.g., "Mr", "Mrs", "Dr")
- `city`, `state`, `country`, `postCode`, `street`: Address fields
- `nationality`, `passportNumber`, `passportExpiry`: Travel fields
- `identifiers`: Array of `{provider, id}` objects for external system linking

#### `cdp_guest_search`

Search for guests by email, identifiers, or order information.

```typescript
{
  "email": "john.doe@example.com",
  "limit": 10,
  "offset": 0
}
```

**Parameters**:
- `email`: Search by email address
- `identifiers`: Search by identifier (format: `"provider:id"`)
- `orderReferenceId`: Search by order reference ID
- `orderRef`: Search by order reference
- `offset`: Pagination offset (default: 0)
- `limit`: Results per page (default: 10, max: 100)
- `sort`: Sort field (e.g., `"createdAt"`)
- `expand`: Include related data (default: false)

#### `cdp_guest_get`

Retrieve a guest profile by reference ID.

```typescript
{
  "guestRef": "guest-reference-id",
  "expand": false
}
```

**Parameters**:
- `guestRef` (required): The guest reference ID
- `expand`: Include data extensions and related data

#### `cdp_guest_update`

Update a guest profile with full replacement (PUT). All fields must be provided.

```typescript
{
  "guestRef": "guest-reference-id",
  "guestType": "customer",
  "firstName": "John",
  "lastName": "Doe",
  "email": "john.doe@example.com",
  "city": "London"
}
```

**Note**: This is a full replacement. Use `cdp_guest_patch` for partial updates.

#### `cdp_guest_patch`

Update specific fields of a guest profile (PATCH). Only provided fields are updated.

```typescript
{
  "guestRef": "guest-reference-id",
  "city": "London",
  "country": "GB"
}
```

**Parameters**: Same as create/update, but all fields are optional except `guestRef`.

### Phase 2: Order Management

#### `cdp_order_create`

Create a new order in Sitecore CDP.

```typescript
{
  "referenceId": "ORDER-12345",
  "orderedAt": "2024-01-15T10:30:00Z",
  "currencyCode": "USD",
  "price": 99.99,
  "status": "PURCHASED",
  "channel": "WEB"
}
```

**Required Parameters**:
- `referenceId`: Unique order identifier from your system
- `orderedAt`: ISO 8601 timestamp when order was placed
- `currencyCode`: ISO 4217 3-letter currency code (e.g., USD, EUR, GBP)
- `price`: Order amount

**Optional Parameters**:
- `status`: Order status - `PENDING`, `CONFIRMED`, `PURCHASED`, `CANCELLED`, etc.
- `channel`: Sales channel - `WEB`, `MOBILE_APP`, `CALL_CENTER`, `BRANCH`, etc.
- `pointOfSale`: Point of sale location
- `total`: Total including taxes and shipping
- `subtotal`: Subtotal before taxes
- `paymentType`: Payment method
- `cardType`: Card type if applicable

#### `cdp_order_get`

Retrieve an order by reference.

```typescript
{
  "orderRef": "order-reference-id",
  "expand": true
}
```

#### `cdp_order_search`

Search for orders by reference ID or guest.

```typescript
{
  "guestRef": "guest-reference-id",
  "limit": 10,
  "offset": 0
}
```

**Parameters**:
- `referenceId`: Filter by order reference ID
- `guestRef`: Filter by guest reference
- `offset`, `limit`, `sort`, `expand`: Pagination options

#### `cdp_order_update`

Update an order (full replacement - all fields required).

#### `cdp_order_patch`

Partially update an order (only provided fields updated).

#### `cdp_order_item_add`

Add an item to an existing order.

```typescript
{
  "orderRef": "order-reference-id",
  "type": "product",
  "name": "Premium Widget",
  "price": 49.99,
  "currencyCode": "USD",
  "status": "CONFIRMED",
  "productId": "WIDGET-001",
  "referenceId": "ITEM-12345",
  "quantity": 2
}
```

**Required Parameters**:
- `orderRef`: The order to add the item to
- `type`: Item type (e.g., `product`, `service`)
- `name`: Item name/description
- `price`: Item price
- `currencyCode`: Currency code
- `status`: Item status (e.g., `CONFIRMED`)
- `productId`: Product identifier
- `referenceId`: Item reference

#### `cdp_order_contact_create`

Create an order contact to link an order to a guest.

```typescript
{
  "orderRef": "order-reference-id",
  "guestRef": "guest-reference-id",
  "firstName": "John",
  "lastName": "Doe",
  "emails": ["john.doe@example.com"]
}
```

**Parameters**:
- `orderRef` (required): The order reference
- `guestRef` (optional): Link to existing guest
- Contact fields: `firstName`, `lastName`, `emails`, `phoneNumbers`, `street`, `city`, etc.

## Programmatic Usage

You can also use this package programmatically in your Node.js applications:

```typescript
import { GuestClient } from '@markstiles/sitecore-cdp-mcp';

const client = new GuestClient();

// Create a guest
const result = await client.createGuest({
  guestType: 'customer',
  firstName: 'Jane',
  lastName: 'Smith',
  email: 'jane.smith@example.com',
});

console.log('Guest created:', result.ref);

// Search for guests
const searchResults = await client.searchGuests({
  email: 'jane.smith@example.com',
});

console.log('Found guests:', searchResults.items.length);

// Get guest details
const guest = await client.getGuest(result.ref);
console.log('Guest details:', guest);

// Update guest
const updated = await client.patchGuest(result.ref, {
  city: 'Dublin',
  country: 'IE',
});

// Delete guest
await client.deleteGuest(result.ref);
```

### Order Management

```typescript
import { OrderClient, GuestClient } from '@markstiles/sitecore-cdp-mcp';

const orderClient = new OrderClient();
const guestClient = new GuestClient();

// Create an order
const order = await orderClient.createOrder({
  referenceId: 'ORDER-12345',
  orderedAt: new Date().toISOString(),
  currencyCode: 'USD',
  price: 149.99,
  status: 'PURCHASED',
  channel: 'WEB',
});

console.log('Order created:', order.ref);

// Add items to the order
const item = await orderClient.createOrderItem(order.ref, {
  type: 'product',
  name: 'Premium Widget',
  price: 49.99,
  currencyCode: 'USD',
  status: 'CONFIRMED',
  productId: 'WIDGET-001',
  referenceId: 'ITEM-001',
  quantity: 3,
});

// Link order to a guest via order contact
const contact = await orderClient.createOrderContact(
  order.ref,
  {
    firstName: 'Jane',
    lastName: 'Smith',
    emails: ['jane.smith@example.com'],
  },
  guestRef // Optional: link to existing guest
);

// Search for guest's orders
const guestOrders = await orderClient.searchOrders({
  guestRef: guestRef,
  limit: 10,
});

console.log('Guest orders:', guestOrders.items.length);
```

## Type Exports

All TypeScript types are exported for use in your applications:

```typescript
import {
  // Guest types
  Guest,
  GuestCreateRequest,
  GuestUpdateRequest,
  GuestPatchRequest,
  GuestSearchParams,
  
  // Order types
  Order,
  OrderCreateRequest,
  OrderUpdateRequest,
  OrderPatchRequest,
  OrderSearchParams,
  OrderItem,
  OrderItemCreateRequest,
  OrderContact,
  OrderContactCreateRequest,
  
  // Common types
  PaginatedResponse,
  CdpError,
  Identifier,
  GuestType,
} from '@markstiles/sitecore-cdp-mcp';
```

## Testing

### Running Integration Tests

Integration tests run against your real CDP environment:

1. Set up your `.env` file with test credentials
2. Run tests:

```bash
npm test
```

**Note**: Integration tests create and delete real data in your CDP instance. Use a test/development environment.

### Test Coverage

```bash
npm run test:coverage
```

### Watch Mode

```bash
npm run test:watch
```

## Development

### Building

```bash
npm run build
```

### Development Mode

```bash
npm run dev
```

## Error Handling

All errors from the CDP API are transformed into structured `CdpError` objects:

```typescript
interface CdpError {
  status: number;        // HTTP status code
  code: number;          // CDP error code
  message: string;       // User-friendly message
  developerMessage: string; // Technical details
  moreInfoUrl?: string;  // Support documentation link
}
```

### Retry Logic

The client automatically retries failed requests for:
- 5xx server errors
- 429 rate limiting errors

Retry behavior is configurable via environment variables:
- `CDP_RETRY_MAX_ATTEMPTS`: Maximum retry attempts (default: 3)
- `CDP_RETRY_DELAY_MS`: Initial delay in milliseconds (default: 1000)

Exponential backoff is applied: `delay * 2^retryCount`

## Regional Endpoints

Sitecore CDP is available in multiple regions. Set your base URL accordingly:

- **US**: `https://api-engage-us.sitecorecloud.io`
- **EU**: `https://api-engage-eu.sitecorecloud.io`
- **AP**: `https://api-engage-ap.sitecorecloud.io`
- **JP**: `https://api-engage-jpe.sitecorecloud.io`

## Security & Credential Management

### Current Architecture (Environment Variables)

This MCP server uses **environment variables** for credential management, which is the most secure approach for several reasons:

1. **Credentials are never exposed in code or logs**
2. **MCP clients (like Claude Desktop) securely pass environment variables to the server process**
3. **Credentials are never transmitted over the network or stored in chat history**
4. **Each user's credentials remain on their local machine**

### Alternative Approaches Considered

#### Option 1: Tool-Level Parameters (Not Recommended)
Passing credentials as parameters to each tool call would:
- ❌ Expose credentials in chat history and logs
- ❌ Require credentials to be provided with every request
- ❌ Create security risks if the conversation is shared

#### Option 2: MCP Server Prompts (Future Enhancement)
The MCP protocol supports prompting users for input. A future enhancement could:
- ✅ Prompt for credentials on first tool use
- ✅ Cache credentials in memory for the session
- ✅ Provide better UX for non-technical users
- ⚠️ Requires MCP client support for prompts

**Current Status**: Environment variables provide the best balance of security, simplicity, and compatibility across MCP clients.

### VS Code Extension Integration

For VS Code extensions that provide MCP servers, the `resolveMcpServerDefinition` method can handle authentication dynamically:

```typescript
resolveMcpServerDefinition(server, token) {
  // Extension can prompt for credentials here
  // Can integrate with VS Code's authentication API
  return {
    ...server,
    env: {
      CDP_CLIENT_KEY: await getCredential('clientKey'),
      CDP_API_TOKEN: await getCredential('apiToken')
    }
  };
}
```

This approach is useful for shared/team extensions but requires VS Code-specific implementation.

## Roadmap

### ✅ Phase 1: Guest Management (Completed)
- Create, search, retrieve, update, and patch guest profiles
- Full CRUD operations with validation
- Pagination and error handling

### ✅ Phase 2: Order Management (Completed)
- Create and manage orders
- Link orders to guests via order contacts
- Add order items with full product details
- Search orders by guest or reference ID

### Phase 3: Data Extensions (Coming Soon)
- Guest data extensions
- Order data extensions
- Order item data extensions
- Custom field management

### Phase 4: Audience Export (Coming Soon)
- List and retrieve audience export jobs
- Download exported audience data
- OAuth 2.0 authentication with automatic token refresh

## Resources

- [Sitecore CDP Developer Documentation](https://doc.sitecore.com/cdp/en/developers/api/index-en.html)
- [Model Context Protocol Documentation](https://modelcontextprotocol.io)
- [Claude Desktop Documentation](https://claude.ai/desktop)

## Publishing to npm

This section is for maintainers who want to publish the package to npm.

### Prerequisites

1. **npm Account**: Create an account at [npmjs.com](https://www.npmjs.com/signup)

2. **Login to npm**:
   ```bash
   npm login
   ```
   Enter your username, password, and email when prompted.

3. **Verify Login**:
   ```bash
   npm whoami
   ```

### First-Time Setup

If publishing a scoped package (like `@markstiles/sitecore-cdp-mcp`), decide on access level:

**Option 1: Public Package (Free)**
```bash
npm publish --access public
```

**Option 2: Private Package (Requires paid npm account)**
```bash
npm publish
```

### Publishing Process

1. **Update Version**

   Follow [Semantic Versioning](https://semver.org/):
   
   ```bash
   # Patch release (1.0.0 → 1.0.1) - Bug fixes
   npm version patch
   
   # Minor release (1.0.0 → 1.1.0) - New features, backward compatible
   npm version minor
   
   # Major release (1.0.0 → 2.0.0) - Breaking changes
   npm version major
   ```

   This automatically:
   - Updates version in package.json
   - Creates a git commit
   - Creates a git tag

2. **Build the Package**

   ```bash
   npm run build
   ```
   
   The `prepublishOnly` script will run this automatically, but it's good to verify first.

3. **Test the Package Locally (Optional)**

   ```bash
   # Create a tarball
   npm pack
   
   # This creates @markstiles-sitecore-cdp-mcp-1.0.0.tgz
   # Test install in another project:
   cd /path/to/test-project
   npm install /path/to/cdp2/@markstiles-sitecore-cdp-mcp-1.0.0.tgz
   ```

4. **Run Tests**

   ```bash
   npm test
   ```
   
   Make sure all tests pass before publishing.

5. **Publish to npm**

   ```bash
   # For scoped packages (first time)
   npm publish --access public
   
   # For subsequent releases
   npm publish
   ```

6. **Push Git Changes**

   ```bash
   git push origin main --follow-tags
   ```

### Package Contents

The published package includes (configured in `package.json` `files` array):
- `dist/` - Compiled JavaScript and TypeScript declarations
- `README.md` - Package documentation
- `LICENSE` - MIT license file

**Excluded** (via `.npmignore`):
- Source TypeScript files (`src/`)
- Tests (`test/`)
- Environment files (`.env*`)
- Development configs

### Post-Publishing

1. **Verify on npm**: Visit `https://www.npmjs.com/package/@markstiles/sitecore-cdp-mcp`

2. **Test Installation**:
   ```bash
   npm install @markstiles/sitecore-cdp-mcp
   ```

3. **Create GitHub Release** (Optional):
   - Go to your GitHub repository
   - Create a release from the version tag
   - Add release notes describing changes

### Version Management Strategy

**Recommended approach for this project:**

- **Patch (1.0.x)**: Bug fixes, documentation updates, test improvements
- **Minor (1.x.0)**: New tools, new API endpoints, new features (backward compatible)
  - Example: Adding Phase 3 (Data Extensions) → 1.1.0
  - Example: Adding Phase 4 (Audience Export) → 1.2.0
- **Major (x.0.0)**: Breaking changes to public API, removed tools, changed interfaces
  - Example: Changing tool names or parameters → 2.0.0
  - Example: Requiring different authentication → 2.0.0

### Troubleshooting

**Error: "You do not have permission to publish"**
- Make sure you're logged in: `npm whoami`
- For scoped packages, use `--access public`
- Verify the package name is available

**Error: "Version already published"**
- Update version: `npm version patch`
- Each version can only be published once

**Error: "Package name too similar to existing package"**
- Choose a different name or add a scope
- Scoped packages (@username/package) are unique to your account

### Automated Publishing with GitHub Actions

For automated releases, create `.github/workflows/publish.yml`:

```yaml
name: Publish to npm

on:
  release:
    types: [created]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
          registry-url: 'https://registry.npmjs.org'
      - run: npm ci
      - run: npm test
      - run: npm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
```

Then add your npm token to GitHub repository secrets as `NPM_TOKEN`.

## License

MIT License - see [LICENSE](LICENSE) file for details

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Support

For issues and questions:
- GitHub Issues: [https://github.com/markstiles/sitecore-cdp-mcp/issues](https://github.com/markstiles/sitecore-cdp-mcp/issues)
- Sitecore CDP Support: [https://support.sitecore.com](https://support.sitecore.com)

## Author

Mark Stiles

## Acknowledgments

Built with:
- [Model Context Protocol SDK](https://github.com/modelcontextprotocol/sdk)
- [Axios](https://axios-http.com/)
- [Zod](https://zod.dev/)
- [TypeScript](https://www.typescriptlang.org/)