|
@@ -0,0 +1,440 @@
|
|
|
|
|
+# MCP HTTP Servers for Webshop API Access
|
|
|
|
|
+
|
|
|
|
|
+## Overview
|
|
|
|
|
+
|
|
|
|
|
+This document describes the MCP (Model Context Protocol) HTTP servers implemented for accessing webshop orders and customers data in a GDPR-compliant manner. These servers allow LLMs to access personal data on-demand without storing it in the database.
|
|
|
|
|
+
|
|
|
|
|
+## Architecture
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+┌─────┐ ┌──────────────────┐ ┌────────────────┐ ┌──────────────┐
|
|
|
|
|
+│ LLM │───▶│ MCP HTTP Server │───▶│ Internal API │───▶│ Webshop │
|
|
|
|
|
+│ │ │ (Edge Function) │ │ Key Auth │ │ API │
|
|
|
|
|
+└─────┘ └──────────────────┘ └────────────────┘ └──────────────┘
|
|
|
|
|
+ │ │
|
|
|
|
|
+ │ │
|
|
|
|
|
+ ▼ ▼
|
|
|
|
|
+ ┌─────────────┐ ┌──────────────────┐
|
|
|
|
|
+ │ 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 Protocol
|
|
|
|
|
+
|
|
|
|
|
+### Endpoint: GET /tools
|
|
|
|
|
+
|
|
|
|
|
+Returns available tools (no authentication required):
|
|
|
|
|
+
|
|
|
|
|
+**Request:**
|
|
|
|
|
+```bash
|
|
|
|
|
+GET /functions/v1/mcp-shopify/tools
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**Response:**
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "tools": [
|
|
|
|
|
+ {
|
|
|
|
|
+ "name": "shopify_list_orders",
|
|
|
|
|
+ "description": "List orders from a Shopify store. Returns order details including customer info, items, status, and totals.",
|
|
|
|
|
+ "inputSchema": {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "shop_id": {
|
|
|
|
|
+ "type": "string",
|
|
|
|
|
+ "description": "The UUID of the Shopify store from the stores table"
|
|
|
|
|
+ },
|
|
|
|
|
+ "status": {
|
|
|
|
|
+ "type": "string",
|
|
|
|
|
+ "description": "Filter by order status: any (default), open, closed, cancelled",
|
|
|
|
|
+ "enum": ["any", "open", "closed", "cancelled"]
|
|
|
|
|
+ },
|
|
|
|
|
+ "limit": {
|
|
|
|
|
+ "type": "number",
|
|
|
|
|
+ "description": "Maximum number of orders to return (default: 50, max: 250)"
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ "required": ["shop_id"]
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Endpoint: POST /call
|
|
|
|
|
+
|
|
|
|
|
+Executes a tool (requires authentication):
|
|
|
|
|
+
|
|
|
|
|
+**Request:**
|
|
|
|
|
+```bash
|
|
|
|
|
+POST /functions/v1/mcp-shopify/call
|
|
|
|
|
+Authorization: Bearer int_shopcall_xxxxx
|
|
|
|
|
+Content-Type: application/json
|
|
|
|
|
+
|
|
|
|
|
+{
|
|
|
|
|
+ "name": "shopify_list_orders",
|
|
|
|
|
+ "arguments": {
|
|
|
|
|
+ "shop_id": "abc-123-def-456",
|
|
|
|
|
+ "status": "any",
|
|
|
|
|
+ "limit": 10
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**Success Response:**
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "content": [
|
|
|
|
|
+ {
|
|
|
|
|
+ "type": "text",
|
|
|
|
|
+ "text": "{\"count\":3,\"total\":10,\"orders\":[...]}"
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**Error Response:**
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "error": {
|
|
|
|
|
+ "code": "STORE_NOT_FOUND",
|
|
|
|
|
+ "message": "Shopify store not found"
|
|
|
|
|
+ },
|
|
|
|
|
+ "isError": true
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 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 Tool Discovery
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+curl https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1/mcp-shopify/tools
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Test Tool Execution
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+# Set your internal API key
|
|
|
|
|
+INTERNAL_API_KEY="int_shopcall_xxxxx"
|
|
|
|
|
+SHOP_ID="your-store-uuid"
|
|
|
|
|
+
|
|
|
|
|
+# List orders
|
|
|
|
|
+curl -X POST https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1/mcp-shopify/call \
|
|
|
|
|
+ -H "Authorization: Bearer $INTERNAL_API_KEY" \
|
|
|
|
|
+ -H "Content-Type: application/json" \
|
|
|
|
|
+ -d "{
|
|
|
|
|
+ \"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/call \
|
|
|
|
|
+ -H "Authorization: Bearer $INTERNAL_API_KEY" \
|
|
|
|
|
+ -H "Content-Type: application/json" \
|
|
|
|
|
+ -d "{
|
|
|
|
|
+ \"name\": \"shopify_list_orders\",
|
|
|
|
|
+ \"arguments\": {}
|
|
|
|
|
+ }"
|
|
|
|
|
+
|
|
|
|
|
+# Expected: MISSING_PARAMS error
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 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**: 1.0.0
|
|
|
|
|
+**Related Issue**: #76
|