# MCP SSE Servers for Webshop API Access

## Overview

This document describes the MCP (Model Context Protocol) SSE (Server-Sent Events) servers implemented for accessing webshop orders and customers data in a GDPR-compliant manner. These servers allow LLMs (like n8n workflows) to access personal data on-demand without storing it in the database.

**Protocol**: MCP over HTTP with Server-Sent Events (2024-11-05)
**Transport**: HTTP POST with SSE streaming responses
**Format**: JSON-RPC 2.0

## Architecture

```
┌─────────┐   POST (JSON-RPC)   ┌──────────────────┐    ┌────────────────┐    ┌──────────────┐
│   n8n   │────────────────────▶│  MCP SSE Server  │───▶│ Internal API   │───▶│   Webshop    │
│   LLM   │◀────────────────────│ (Edge Function)  │    │ Key Auth       │    │   API        │
└─────────┘   SSE Stream         └──────────────────┘    └────────────────┘    └──────────────┘
                                        │                           │
                                        │                           │
                                        ▼                           ▼
                                   ┌─────────────┐          ┌──────────────────┐
                                   │   Store     │          │ internal_api_keys│
                                   │ Permissions │          │     table        │
                                   └─────────────┘          └──────────────────┘
```

## Implemented MCP Servers

### 1. mcp-shopify
- **Endpoint**: `/functions/v1/mcp-shopify`
- **Purpose**: Access Shopify orders and customers
- **Tools**: 3 (list_orders, list_customers, get_customer_orders)

### 2. mcp-woocommerce
- **Endpoint**: `/functions/v1/mcp-woocommerce`
- **Purpose**: Access WooCommerce orders and customers
- **Tools**: 3 (list_orders, list_customers, get_customer_orders)

### 3. mcp-shoprenter
- **Endpoint**: `/functions/v1/mcp-shoprenter`
- **Purpose**: Access ShopRenter orders and customers
- **Tools**: 3 (list_orders, list_customers, get_customer_orders)

## Authentication

### Internal API Keys

All MCP servers require authentication using internal API keys stored in the `internal_api_keys` table:

```sql
-- Example internal API key structure
{
  "id": "uuid",
  "api_key": "int_shopcall_xxxxx...",
  "permissions": {
    "read_orders": true,
    "read_customers": true,
    "all_webshops": true
  },
  "is_active": true,
  "expires_at": null
}
```

### Authorization Header

```
Authorization: Bearer int_shopcall_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

### Rate Limiting

- **Limit**: 200 requests per minute per API key
- **Headers**:
  - `X-RateLimit-Limit: 200`
  - `X-RateLimit-Remaining: 150`
  - `X-RateLimit-Reset: 2025-01-15T10:30:00Z`

## MCP SSE Protocol

### Single Endpoint: POST /

All MCP communication happens through a single POST endpoint using Server-Sent Events.

**URL**: `https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1/mcp-{platform}/`

**Method**: POST

**Authentication**: Required (Bearer token in Authorization header)

**Protocol**: JSON-RPC 2.0 over SSE

### Supported JSON-RPC Methods

#### 1. `initialize` - Initialize MCP connection

**Request:**
```bash
POST /functions/v1/mcp-shopify
Authorization: Bearer int_shopcall_xxxxx
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "roots": { "listChanged": true }
    },
    "clientInfo": {
      "name": "n8n",
      "version": "1.0.0"
    }
  }
}
```

**Response (SSE Stream):**
```
data: {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{"listChanged":false}},"serverInfo":{"name":"mcp-shopify","version":"1.0.0"}}}

```

#### 2. `tools/list` - Get available tools

**Request:**
```bash
POST /functions/v1/mcp-shopify
Authorization: Bearer int_shopcall_xxxxx
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list",
  "params": {}
}
```

**Response (SSE Stream):**
```
data: {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"shopify_list_orders","description":"List orders from a Shopify store...","inputSchema":{...}}]}}

```

#### 3. `tools/call` - Execute a tool

**Request:**
```bash
POST /functions/v1/mcp-shopify
Authorization: Bearer int_shopcall_xxxxx
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "shopify_list_orders",
    "arguments": {
      "shop_id": "abc-123-def-456",
      "status": "any",
      "limit": 10
    }
  }
}
```

**Success Response (SSE Stream):**
```
data: {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"{\"count\":3,\"total\":10,\"orders\":[...]}"}]}}

```

**Error Response (SSE Stream):**
```
data: {"jsonrpc":"2.0","id":3,"error":{"code":-32603,"message":"Shopify store not found"}}

```

### JSON-RPC Error Codes

| Code | Name | Description |
|------|------|-------------|
| -32700 | Parse error | Invalid JSON was received |
| -32600 | Invalid Request | The JSON sent is not a valid Request object |
| -32601 | Method not found | The method does not exist / is not available |
| -32602 | Invalid params | Invalid method parameter(s) |
| -32603 | Internal error | Internal JSON-RPC error |

## Store Permissions

Each store has a `data_access_permissions` field that controls access:

```json
{
  "allow_product_access": true,
  "allow_customer_access": true,
  "allow_order_access": true
}
```

MCP servers check these permissions before accessing data:
- **Orders**: Requires `allow_order_access: true`
- **Customers**: Requires `allow_customer_access: true`
- **Customer Orders**: Requires both permissions

## LLM-Friendly Data Format

All responses use compact, clear formats optimized for LLM consumption:

### Customer Format

```typescript
interface LlmCustomer {
  id: string;
  name: string;              // "John Doe"
  email: string;             // "john@example.com"
  phone?: string;            // "+1234567890"
  ordersCount?: number;      // 5
  totalSpent?: string;       // "1250.00 USD"
}
```

### Order Format

```typescript
interface LlmOrder {
  id: string;
  orderNumber: string;       // "#1001"
  customer: {
    name: string;
    email: string;
    phone?: string;
  };
  status: string;            // "paid/fulfilled"
  total: string;             // "250.00"
  currency?: string;         // "USD"
  items: Array<{
    name: string;            // "Product A"
    quantity: number;        // 2
    price: string;           // "100.00"
  }>;
  createdAt: string;         // ISO 8601 timestamp
}
```

## Error Codes

| Code | HTTP Status | Description |
|------|-------------|-------------|
| `MISSING_API_KEY` | 401 | No Authorization header provided |
| `INVALID_FORMAT` | 401 | API key format is invalid |
| `NOT_FOUND` | 401 | API key not found in database |
| `INACTIVE` | 401 | API key is inactive |
| `EXPIRED` | 401 | API key has expired |
| `INSUFFICIENT_PERMISSIONS` | 401 | Missing required permission |
| `RATE_LIMIT_EXCEEDED` | 429 | Too many requests |
| `MISSING_PARAMS` | 400 | Required parameters missing |
| `STORE_NOT_FOUND` | 404 | Store not found or wrong platform |
| `PERMISSION_DENIED` | 403 | Store permissions deny access |
| `FETCH_ERROR` | 500 | Error fetching from webshop API |
| `UNKNOWN_TOOL` | 404 | Tool name not recognized |
| `INTERNAL_ERROR` | 500 | Server error |

## Deployment

### Prerequisites

1. Supabase project with Edge Functions enabled
2. Environment variables configured:
   - `SUPABASE_URL`
   - `SUPABASE_SERVICE_ROLE_KEY`
   - Platform-specific credentials (Shopify, WooCommerce, ShopRenter)

### Deploy via CLI

```bash
cd supabase

# Deploy individual MCP servers
supabase functions deploy mcp-shopify --project-ref ztklqodcdjeqpsvhlpud
supabase functions deploy mcp-woocommerce --project-ref ztklqodcdjeqpsvhlpud
supabase functions deploy mcp-shoprenter --project-ref ztklqodcdjeqpsvhlpud

# Or deploy all at once
supabase functions deploy mcp-shopify mcp-woocommerce mcp-shoprenter --project-ref ztklqodcdjeqpsvhlpud
```

### Verify Deployment

```bash
# Check if function is live
curl https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1/mcp-shopify/tools

# Should return JSON with tools array
```

## Testing

### Test with curl

```bash
# Set your internal API key
INTERNAL_API_KEY="int_shopcall_xxxxx"
SHOP_ID="your-store-uuid"

# Test initialize
curl -X POST https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1/mcp-shopify \
  -H "Authorization: Bearer $INTERNAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": {
        "name": "test-client",
        "version": "1.0.0"
      }
    }
  }'

# Test tools/list
curl -X POST https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1/mcp-shopify \
  -H "Authorization: Bearer $INTERNAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list",
    "params": {}
  }'

# Test tools/call (list orders)
curl -X POST https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1/mcp-shopify \
  -H "Authorization: Bearer $INTERNAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"id\": 3,
    \"method\": \"tools/call\",
    \"params\": {
      \"name\": \"shopify_list_orders\",
      \"arguments\": {
        \"shop_id\": \"$SHOP_ID\",
        \"limit\": 5
      }
    }
  }"
```

### Test Error Handling

```bash
# Test missing shop_id
curl -X POST https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1/mcp-shopify \
  -H "Authorization: Bearer $INTERNAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 4,
    "method": "tools/call",
    "params": {
      "name": "shopify_list_orders",
      "arguments": {}
    }
  }'

# Expected: JSON-RPC error with missing parameters
```

## n8n Integration

### Configuring MCP Servers in n8n

1. **Add MCP Server Credential in n8n:**
   - Type: `HTTP Streamable`
   - URL: `https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1/mcp-woocommerce`
     - Or use `/mcp-shopify` or `/mcp-shoprenter` based on your needs
   - Authentication: `Bearer Token`
   - Add custom header:
     - Header: `Authorization`
     - Value: `Bearer int_shopcall_[your-api-key]`

2. **Using MCP Tools in n8n Workflow:**
   - Add an "AI Agent" or "MCP Tool" node
   - Select your MCP server credential
   - The agent will automatically discover available tools
   - Call tools with required parameters (always include `shop_id`)

3. **Example n8n Tool Call:**
   ```javascript
   // In n8n AI Agent node, the MCP client will call:
   {
     "name": "woocommerce_list_orders",
     "arguments": {
       "shop_id": "uuid-of-woocommerce-store",
       "status": "completed",
       "per_page": 10
     }
   }
   ```

### Troubleshooting n8n Integration

**Issue: "No tools found"**
- Check that the URL ends with the function name (e.g., `/mcp-woocommerce`)
- Verify the Bearer token is correctly set in headers
- Check Supabase Edge Function logs for 401 errors

**Issue: "Connection timeout"**
- Ensure the Edge Function is deployed and active
- Check network connectivity to Supabase
- Verify CORS headers are present

**Issue: "401 Unauthorized"**
- Verify the internal API key is active in the database
- Check that the API key has `read_orders` permission
- Ensure the Bearer token format is: `Bearer int_shopcall_...`

**Issue: "Tools executing but returning errors"**
- Check that `shop_id` parameter is a valid UUID
- Verify the store exists and matches the platform (e.g., don't use a Shopify store ID with mcp-woocommerce)
- Check store permissions allow order/customer access

## Security Best Practices

1. **API Key Management**
   - Store internal API keys securely
   - Rotate keys periodically
   - Set expiration dates when appropriate
   - Monitor `last_used_at` for anomalies

2. **Rate Limiting**
   - Default: 200 req/min per key
   - Adjust based on usage patterns
   - Monitor for abuse

3. **Store Permissions**
   - Review permissions regularly
   - Audit permission changes via `store_permission_audit` table
   - Default to least privilege

4. **Logging**
   - Monitor Edge Function logs for errors
   - Track tool usage patterns
   - Alert on suspicious activity

## GDPR Compliance

### How MCP Servers Ensure GDPR Compliance

1. **No Data Storage**
   - Personal data is fetched on-demand from webshop APIs
   - Data is never stored in Supabase database
   - Data exists only in memory during request processing

2. **Access Control**
   - Strict authentication via internal API keys
   - Permission checks at store level
   - Audit trail of all data access

3. **Data Minimization**
   - Only requested data is fetched
   - LLM-friendly format includes only necessary fields
   - No unnecessary data retention

4. **Right to be Forgotten**
   - Deleting store credentials immediately prevents access
   - No cached personal data to delete
   - All data access is real-time from source

## Troubleshooting

### Issue: "API key not found"

**Cause**: Internal API key doesn't exist in database

**Solution**:
```sql
-- Check if key exists
SELECT * FROM internal_api_keys WHERE api_key = 'int_shopcall_xxxxx';

-- Create new key if needed (see INTERNAL_API_KEYS.md)
```

### Issue: "Rate limit exceeded"

**Cause**: Too many requests in 1 minute window

**Solution**: Wait until reset time shown in error message or increase limit

### Issue: "Store not found"

**Cause**: Invalid shop_id or wrong platform

**Solution**:
```sql
-- Verify store exists and check platform
SELECT id, platform_name, store_name FROM stores WHERE id = 'shop-uuid';
```

### Issue: "Permission denied"

**Cause**: Store's `data_access_permissions` deny access

**Solution**:
```sql
-- Check store permissions
SELECT data_access_permissions FROM stores WHERE id = 'shop-uuid';

-- Update if needed
UPDATE stores
SET data_access_permissions = '{"allow_order_access": true, "allow_customer_access": true}'::jsonb
WHERE id = 'shop-uuid';
```

## File Structure

```
supabase/functions/
├── _shared/
│   ├── mcp-types.ts          # MCP type definitions
│   ├── mcp-helpers.ts        # Helper functions
│   ├── shopify-client.ts     # Shopify API client
│   ├── woocommerce-client.ts # WooCommerce API client
│   ├── shoprenter-client.ts  # ShopRenter API client
│   └── internal-api-key-auth.ts # Auth middleware
├── mcp-shopify/
│   └── index.ts              # Shopify MCP server
├── mcp-woocommerce/
│   └── index.ts              # WooCommerce MCP server
└── mcp-shoprenter/
    └── index.ts              # ShopRenter MCP server
```

## Related Documentation

- [INTERNAL_API_KEYS.md](../INTERNAL_API_KEYS.md) - Managing internal API keys
- [CLAUDE.md](../CLAUDE.md) - Project overview and architecture
- [Supabase Edge Functions Docs](https://supabase.com/docs/guides/functions)
- [MCP Protocol Specification](https://modelcontextprotocol.io)

## Support

For issues or questions:
1. Check Supabase Edge Function logs
2. Review error codes and troubleshooting section
3. Contact: ferenc.szontagh@smartbotics.ai

---

**Last Updated**: 2025-01-12
**Version**: 2.0.0 (SSE Protocol)
**Related Issue**: #76
**Protocol**: MCP over HTTP with SSE (2024-11-05)
