@markstiles/sitecore-search-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AUTHENTICATION.md +337 -0
- package/LICENSE +21 -0
- package/README.md +606 -0
- package/dist/client/base-client.d.ts +34 -0
- package/dist/client/base-client.d.ts.map +1 -0
- package/dist/client/base-client.js +117 -0
- package/dist/client/base-client.js.map +1 -0
- package/dist/client/events-client.d.ts +77 -0
- package/dist/client/events-client.d.ts.map +1 -0
- package/dist/client/events-client.js +32 -0
- package/dist/client/events-client.js.map +1 -0
- package/dist/client/ingestion-client.d.ts +76 -0
- package/dist/client/ingestion-client.d.ts.map +1 -0
- package/dist/client/ingestion-client.js +73 -0
- package/dist/client/ingestion-client.js.map +1 -0
- package/dist/client/search-client.d.ts +97 -0
- package/dist/client/search-client.d.ts.map +1 -0
- package/dist/client/search-client.js +18 -0
- package/dist/client/search-client.js.map +1 -0
- package/dist/config.d.ts +99 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +75 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +312 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/events/track-event.d.ts +394 -0
- package/dist/tools/events/track-event.d.ts.map +1 -0
- package/dist/tools/events/track-event.js +80 -0
- package/dist/tools/events/track-event.js.map +1 -0
- package/dist/tools/events/validate-event.d.ts +94 -0
- package/dist/tools/events/validate-event.d.ts.map +1 -0
- package/dist/tools/events/validate-event.js +51 -0
- package/dist/tools/events/validate-event.js.map +1 -0
- package/dist/tools/ingestion/check-status.d.ts +50 -0
- package/dist/tools/ingestion/check-status.d.ts.map +1 -0
- package/dist/tools/ingestion/check-status.js +27 -0
- package/dist/tools/ingestion/check-status.js.map +1 -0
- package/dist/tools/ingestion/create-document.d.ts +50 -0
- package/dist/tools/ingestion/create-document.d.ts.map +1 -0
- package/dist/tools/ingestion/create-document.js +27 -0
- package/dist/tools/ingestion/create-document.js.map +1 -0
- package/dist/tools/ingestion/delete-document.d.ts +50 -0
- package/dist/tools/ingestion/delete-document.d.ts.map +1 -0
- package/dist/tools/ingestion/delete-document.js +27 -0
- package/dist/tools/ingestion/delete-document.js.map +1 -0
- package/dist/tools/ingestion/ingest-from-source.d.ts +234 -0
- package/dist/tools/ingestion/ingest-from-source.d.ts.map +1 -0
- package/dist/tools/ingestion/ingest-from-source.js +48 -0
- package/dist/tools/ingestion/ingest-from-source.js.map +1 -0
- package/dist/tools/ingestion/update-document.d.ts +62 -0
- package/dist/tools/ingestion/update-document.d.ts.map +1 -0
- package/dist/tools/ingestion/update-document.js +34 -0
- package/dist/tools/ingestion/update-document.js.map +1 -0
- package/dist/tools/search/ai-search.d.ts +132 -0
- package/dist/tools/search/ai-search.d.ts.map +1 -0
- package/dist/tools/search/ai-search.js +65 -0
- package/dist/tools/search/ai-search.js.map +1 -0
- package/dist/tools/search/basic-search.d.ts +98 -0
- package/dist/tools/search/basic-search.d.ts.map +1 -0
- package/dist/tools/search/basic-search.js +51 -0
- package/dist/tools/search/basic-search.js.map +1 -0
- package/dist/tools/search/faceted-search.d.ts +114 -0
- package/dist/tools/search/faceted-search.d.ts.map +1 -0
- package/dist/tools/search/faceted-search.js +52 -0
- package/dist/tools/search/faceted-search.js.map +1 -0
- package/dist/tools/search/recommendations.d.ts +62 -0
- package/dist/tools/search/recommendations.d.ts.map +1 -0
- package/dist/tools/search/recommendations.js +50 -0
- package/dist/tools/search/recommendations.js.map +1 -0
- package/dist/utils/auth-manager.d.ts +60 -0
- package/dist/utils/auth-manager.d.ts.map +1 -0
- package/dist/utils/auth-manager.js +184 -0
- package/dist/utils/auth-manager.js.map +1 -0
- package/dist/utils/errors.d.ts +18 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +58 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +50 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/schema.d.ts +6 -0
- package/dist/utils/schema.d.ts.map +1 -0
- package/dist/utils/schema.js +14 -0
- package/dist/utils/schema.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# Authentication Implementation Guide
|
|
2
|
+
|
|
3
|
+
This document provides a detailed explanation of how authentication works in the Sitecore Search MCP Server.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The MCP server supports two authentication methods as per Sitecore Search API documentation:
|
|
8
|
+
|
|
9
|
+
1. **Subdomain Authentication** (Recommended) - No explicit authentication needed
|
|
10
|
+
2. **API Key Authentication** - Using API keys and automatically managed access tokens
|
|
11
|
+
|
|
12
|
+
## Authentication Flow
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
┌─────────────┐
|
|
16
|
+
│ API Key │ (52-character secret from Sitecore)
|
|
17
|
+
└──────┬──────┘
|
|
18
|
+
│
|
|
19
|
+
▼
|
|
20
|
+
┌─────────────────┐
|
|
21
|
+
│ AuthManager │ Manages tokens and authentication
|
|
22
|
+
└────────┬────────┘
|
|
23
|
+
│
|
|
24
|
+
├──► Generate Access Token (POST to auth endpoint)
|
|
25
|
+
│ └──► Valid for 1 day (configurable)
|
|
26
|
+
│
|
|
27
|
+
├──► Refresh Access Token (PUT to auth endpoint)
|
|
28
|
+
│ └──► Using refresh token when access token expires
|
|
29
|
+
│
|
|
30
|
+
└──► Clear & Re-authenticate when refresh token expires
|
|
31
|
+
└──► Valid for 7 days (configurable)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Key Components
|
|
35
|
+
|
|
36
|
+
### 1. AuthManager (`src/utils/auth-manager.ts`)
|
|
37
|
+
|
|
38
|
+
The `AuthManager` class handles all authentication logic:
|
|
39
|
+
|
|
40
|
+
**Responsibilities:**
|
|
41
|
+
- Stores API key and authentication scopes
|
|
42
|
+
- Generates access tokens from API key
|
|
43
|
+
- Refreshes access tokens using refresh tokens
|
|
44
|
+
- Tracks token expiry and auto-refreshes before expiration
|
|
45
|
+
- Provides authentication headers for API requests
|
|
46
|
+
|
|
47
|
+
**Key Methods:**
|
|
48
|
+
- `getAuthHeader()`: Returns authentication header (API key or Bearer token)
|
|
49
|
+
- `generateTokensFromApiKey()`: POST request to get access & refresh tokens
|
|
50
|
+
- `refreshAccessToken()`: PUT request to refresh expired access token
|
|
51
|
+
- `clearTokens()`: Manually clear stored tokens
|
|
52
|
+
|
|
53
|
+
**Token Expiry:**
|
|
54
|
+
- Access token: 1 day default (86400000 ms)
|
|
55
|
+
- Refresh token: 7 days default (604800000 ms)
|
|
56
|
+
- 1-minute buffer before access token expiry to prevent race conditions
|
|
57
|
+
|
|
58
|
+
### 2. BaseClient (`src/client/base-client.ts`)
|
|
59
|
+
|
|
60
|
+
Updated to support authentication via `AuthManager`:
|
|
61
|
+
|
|
62
|
+
**Changes:**
|
|
63
|
+
- Constructor now accepts optional `AuthManager` instance
|
|
64
|
+
- Request interceptor calls `authManager.getAuthHeader()` before each request
|
|
65
|
+
- Headers are automatically injected into all API requests
|
|
66
|
+
|
|
67
|
+
### 3. Client Classes
|
|
68
|
+
|
|
69
|
+
All client classes now accept and pass `AuthManager`:
|
|
70
|
+
|
|
71
|
+
- `SearchClient` - For Search & Recommendation API
|
|
72
|
+
- `IngestionClient` - For Ingestion API
|
|
73
|
+
- `EventsClient` - For Events API
|
|
74
|
+
|
|
75
|
+
### 4. Configuration (`src/config.ts`)
|
|
76
|
+
|
|
77
|
+
Extended to support authentication settings:
|
|
78
|
+
|
|
79
|
+
**New Configuration Fields:**
|
|
80
|
+
- `apiKey`: The 52-character API key from Sitecore
|
|
81
|
+
- `authScopes`: Array of scopes (`discover`, `event`, `ingestion`)
|
|
82
|
+
- `accessTokenExpiry`: Access token validity in milliseconds
|
|
83
|
+
- `refreshTokenExpiry`: Refresh token validity in milliseconds
|
|
84
|
+
|
|
85
|
+
## API Key and Token Usage
|
|
86
|
+
|
|
87
|
+
### Getting Your API Key
|
|
88
|
+
|
|
89
|
+
1. Log in to Sitecore Search Customer Engagement Console (CEC)
|
|
90
|
+
2. Navigate to **Developer Resources > API Access**
|
|
91
|
+
3. Copy your 52-character API key
|
|
92
|
+
- Format: `01-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
|
|
93
|
+
|
|
94
|
+
### Authentication Scopes
|
|
95
|
+
|
|
96
|
+
Each API key has assigned scopes that determine which APIs you can access:
|
|
97
|
+
|
|
98
|
+
- **`discover`** - Search and Recommendation API
|
|
99
|
+
- **`event`** - Events API
|
|
100
|
+
- **`ingestion`** - Ingestion API
|
|
101
|
+
|
|
102
|
+
If you call an API without the proper scope, you'll receive an HTTP 4XX error.
|
|
103
|
+
|
|
104
|
+
### How Tokens Are Used
|
|
105
|
+
|
|
106
|
+
#### Search & Recommendation API
|
|
107
|
+
```
|
|
108
|
+
Authorization: Bearer <access-token>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Events API
|
|
112
|
+
```
|
|
113
|
+
Authorization: Bearer <access-token>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### Ingestion API (Special Case)
|
|
117
|
+
```
|
|
118
|
+
Authorization: <api-key>
|
|
119
|
+
```
|
|
120
|
+
**Note:** The Ingestion API always uses the API key directly (no Bearer prefix), per Sitecore documentation.
|
|
121
|
+
|
|
122
|
+
## Token Lifecycle
|
|
123
|
+
|
|
124
|
+
### 1. Initial Request
|
|
125
|
+
```typescript
|
|
126
|
+
// User makes first API call
|
|
127
|
+
searchClient.search(domainId, request)
|
|
128
|
+
↓
|
|
129
|
+
// AuthManager checks for valid access token (none exists)
|
|
130
|
+
authManager.getAuthHeader()
|
|
131
|
+
↓
|
|
132
|
+
// Generates new tokens from API key
|
|
133
|
+
POST https://api.rfksrv.com/account/1/access-token
|
|
134
|
+
Headers: { 'x-api-key': '01-...' }
|
|
135
|
+
Body: {
|
|
136
|
+
"scope": ["discover", "event", "ingestion"],
|
|
137
|
+
"accessExpiry": 86400000,
|
|
138
|
+
"refreshExpiry": 604800000
|
|
139
|
+
}
|
|
140
|
+
↓
|
|
141
|
+
// Response contains tokens
|
|
142
|
+
{
|
|
143
|
+
"accessToken": "167dgw2vyy733",
|
|
144
|
+
"refreshToken": "1n555d88sss448",
|
|
145
|
+
"accessTokenExpiry": 86400000,
|
|
146
|
+
"refreshTokenExpiry": 604800000
|
|
147
|
+
}
|
|
148
|
+
↓
|
|
149
|
+
// Tokens stored in memory with expiry timestamps
|
|
150
|
+
// Access token used in Authorization header
|
|
151
|
+
Authorization: Bearer 167dgw2vyy733
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 2. Subsequent Requests (Token Valid)
|
|
155
|
+
```typescript
|
|
156
|
+
// User makes another API call
|
|
157
|
+
searchClient.search(domainId, request)
|
|
158
|
+
↓
|
|
159
|
+
// AuthManager checks for valid access token (exists and not expired)
|
|
160
|
+
authManager.getAuthHeader()
|
|
161
|
+
↓
|
|
162
|
+
// Returns existing access token
|
|
163
|
+
Authorization: Bearer 167dgw2vyy733
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 3. Access Token Expiration
|
|
167
|
+
```typescript
|
|
168
|
+
// User makes API call after ~1 day
|
|
169
|
+
searchClient.search(domainId, request)
|
|
170
|
+
↓
|
|
171
|
+
// AuthManager detects expired access token
|
|
172
|
+
authManager.getAuthHeader()
|
|
173
|
+
↓
|
|
174
|
+
// Refreshes access token using refresh token
|
|
175
|
+
PUT https://api.rfksrv.com/account/1/access-token
|
|
176
|
+
Headers: { 'Authorization': 'Bearer 1n555d88sss448' }
|
|
177
|
+
↓
|
|
178
|
+
// Response contains new access token
|
|
179
|
+
{
|
|
180
|
+
"accessToken": "187dgb6vmy733"
|
|
181
|
+
}
|
|
182
|
+
↓
|
|
183
|
+
// New access token stored and used
|
|
184
|
+
Authorization: Bearer 187dgb6vmy733
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 4. Refresh Token Expiration
|
|
188
|
+
```typescript
|
|
189
|
+
// User makes API call after ~7 days
|
|
190
|
+
searchClient.search(domainId, request)
|
|
191
|
+
↓
|
|
192
|
+
// AuthManager detects expired refresh token
|
|
193
|
+
authManager.getAuthHeader()
|
|
194
|
+
↓
|
|
195
|
+
// Clears token storage and generates new tokens from API key
|
|
196
|
+
// (Returns to step 1)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Configuration Examples
|
|
200
|
+
|
|
201
|
+
### Basic Configuration (Subdomain)
|
|
202
|
+
```env
|
|
203
|
+
SITECORE_DOMAIN_ID=my-domain
|
|
204
|
+
# No API key needed - subdomain handles authentication
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### API Key Authentication
|
|
208
|
+
```env
|
|
209
|
+
SITECORE_DOMAIN_ID=my-domain
|
|
210
|
+
SITECORE_API_KEY=01-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Custom Token Expiry
|
|
214
|
+
```env
|
|
215
|
+
SITECORE_DOMAIN_ID=my-domain
|
|
216
|
+
SITECORE_API_KEY=01-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
217
|
+
SITECORE_ACCESS_TOKEN_EXPIRY=43200000 # 12 hours
|
|
218
|
+
SITECORE_REFRESH_TOKEN_EXPIRY=259200000 # 3 days
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Limited Scopes
|
|
222
|
+
```env
|
|
223
|
+
SITECORE_DOMAIN_ID=my-domain
|
|
224
|
+
SITECORE_API_KEY=01-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
225
|
+
SITECORE_AUTH_SCOPES=discover,event # Only Search and Events APIs
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Multiple Domains
|
|
229
|
+
```env
|
|
230
|
+
SITECORE_DOMAINS_JSON={
|
|
231
|
+
"production": {
|
|
232
|
+
"apiKey": "01-prod-key...",
|
|
233
|
+
"authScopes": ["discover", "event", "ingestion"]
|
|
234
|
+
},
|
|
235
|
+
"staging": {
|
|
236
|
+
"apiKey": "01-stage-key...",
|
|
237
|
+
"authScopes": ["discover"],
|
|
238
|
+
"accessTokenExpiry": 3600000
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
SITECORE_DEFAULT_DOMAIN=production
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Request Flow in Code
|
|
245
|
+
|
|
246
|
+
### Location: `src/index.ts`
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// 1. Server creates AuthManager for each domain
|
|
250
|
+
getAuthManager(domainId) {
|
|
251
|
+
return new AuthManager(
|
|
252
|
+
apiKey,
|
|
253
|
+
authScopes,
|
|
254
|
+
accessTokenExpiry,
|
|
255
|
+
refreshTokenExpiry
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 2. AuthManager passed to clients
|
|
260
|
+
getSearchClient(domainId) {
|
|
261
|
+
const authManager = this.getAuthManager(domainId);
|
|
262
|
+
return new SearchClient(baseUrl, authManager);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 3. Client uses AuthManager in interceptor
|
|
266
|
+
// src/client/base-client.ts
|
|
267
|
+
axios.interceptors.request.use(async (config) => {
|
|
268
|
+
const authHeaders = await authManager.getAuthHeader();
|
|
269
|
+
// Headers injected into request
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Debugging Authentication
|
|
274
|
+
|
|
275
|
+
### Check Token Status
|
|
276
|
+
```typescript
|
|
277
|
+
const authManager = new AuthManager(apiKey, scopes);
|
|
278
|
+
const status = authManager.getTokenStatus();
|
|
279
|
+
console.log(status);
|
|
280
|
+
// {
|
|
281
|
+
// hasTokens: true,
|
|
282
|
+
// accessTokenValid: true,
|
|
283
|
+
// refreshTokenValid: true
|
|
284
|
+
// }
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Clear Tokens Manually
|
|
288
|
+
```typescript
|
|
289
|
+
authManager.clearTokens();
|
|
290
|
+
// Forces re-authentication on next request
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Enable Debug Logging
|
|
294
|
+
```env
|
|
295
|
+
DEBUG=true
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
This will log:
|
|
299
|
+
- Token generation requests
|
|
300
|
+
- Token refresh requests
|
|
301
|
+
- Token expiry warnings
|
|
302
|
+
- Authentication header injection
|
|
303
|
+
|
|
304
|
+
## Security Considerations
|
|
305
|
+
|
|
306
|
+
1. **API Key Storage**: Never commit API keys to version control
|
|
307
|
+
2. **Environment Variables**: Use `.env` file (gitignored) for local development
|
|
308
|
+
3. **Production**: Use secure environment variable management (e.g., Azure Key Vault, AWS Secrets Manager)
|
|
309
|
+
4. **Token Storage**: Tokens are stored in memory only (not persisted to disk)
|
|
310
|
+
5. **Token Lifetime**: Use shorter expiry times for sensitive environments
|
|
311
|
+
6. **Scope Limitation**: Only assign necessary scopes to each API key
|
|
312
|
+
|
|
313
|
+
## Troubleshooting
|
|
314
|
+
|
|
315
|
+
### 401 Unauthorized
|
|
316
|
+
- Verify API key is correct (52 characters)
|
|
317
|
+
- Check that API key has required scopes
|
|
318
|
+
- Ensure tokens haven't expired (check logs)
|
|
319
|
+
|
|
320
|
+
### 403 Forbidden
|
|
321
|
+
- API key doesn't have permission for requested operation
|
|
322
|
+
- Contact Sitecore support to adjust API key scopes
|
|
323
|
+
|
|
324
|
+
### Token Refresh Failures
|
|
325
|
+
- Refresh token may have expired (>7 days)
|
|
326
|
+
- Server will automatically re-authenticate with API key
|
|
327
|
+
- Check network connectivity to auth endpoint
|
|
328
|
+
|
|
329
|
+
### Ingestion API Not Working
|
|
330
|
+
- Ingestion API requires `ingestion` scope
|
|
331
|
+
- Ensure API key is being used (not access token)
|
|
332
|
+
|
|
333
|
+
## References
|
|
334
|
+
|
|
335
|
+
- [Sitecore Search API Authentication](https://doc.sitecore.com/search/en/developers/search-developer-guide/api-authentication-and-authorization.html)
|
|
336
|
+
- [Get Access & Refresh Tokens](https://doc.sitecore.com/search/en/developers/search-developer-guide/get-an-access-token-and-a-refresh-token.html)
|
|
337
|
+
- [Add API Key or Token to Request](https://doc.sitecore.com/search/en/developers/search-developer-guide/add-an-api-key-or-access-token-to-a-request-header.html)
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mark Stiles
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|