ebay-mcp-remote-edition 4.4.0 → 4.6.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/README.md CHANGED
@@ -16,23 +16,21 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server providi
16
16
 
17
17
  ## Overview
18
18
 
19
- This project extends [Yosef Hayim's eBay MCP](https://github.com/YosefHayim/ebay-mcp) with a full hosted, multi-user deployment mode while keeping the original local STDIO mode intact. The key additions are:
19
+ This project extends [Yosef Hayim's eBay MCP](https://github.com/YosefHayim/ebay-mcp) with hosted, multi-user deployment while preserving local STDIO mode. Key additions:
20
20
 
21
- - **Hosted Streamable HTTP mode** — deploy to Render (or any Node.js host) and serve multiple users from one instance
22
- - **MCP OAuth 2.1 authorization server** — full browser-based eBay login with automatic token management; Cline and other OAuth-aware clients connect with zero manual token pasting
23
- - **Environment-scoped route trees** — `/sandbox/mcp` and `/production/mcp` hard-bind their eBay environment; no query param needed
24
- - **Cloudflare KV / Upstash Redis** token and session storage for persistent multi-user auth
21
+ - **Hosted Streamable HTTP** — deploy anywhere, serve multiple users from one instance
22
+ - **MCP OAuth 2.1** — browser-based eBay login with automatic token management; OAuth-aware clients (Cline) connect without manual token pasting
23
+ - **Environment-scoped routes** — `/sandbox/mcp` and `/production/mcp` hard-bind their eBay environment
24
+ - **Cloudflare KV / Upstash Redis** — persistent multi-user token and session storage with TTL-aligned expiry
25
25
  - **Admin session management** — inspect, revoke, or delete sessions via authenticated endpoints
26
- - **TTL-aligned records** — every stored record (OAuth state, auth code, session, user token) carries an `expiresAt` timestamp and a matching KV/Redis TTL so storage and application expiry are always in sync
27
- - **Env-selected eBay Research session persistence** — the first-party research bootstrap/runtime can persist Playwright storage state to Cloudflare KV, Upstash KV, or explicit filesystem mode via `EBAY_RESEARCH_SESSION_STORE`
28
- - **QStash-triggered Telegram alerts for eBay Research session expiry** — bootstrap can schedule version-aware expiry callbacks that notify operators before first-party research auth silently degrades
29
- - **Alert-safe scheduling guardrails** — expiry callbacks are only scheduled when the callback URL is externally reachable and the research session store supports shared alert locks (`upstash-redis` or `filesystem`)
26
+ - **eBay Research session persistence** — Playwright storage state to KV/Redis/filesystem via `EBAY_RESEARCH_SESSION_STORE`
27
+ - **QStash-triggered Telegram alerts** — expiry callbacks notify operators before research auth degrades
30
28
 
31
29
  ---
32
30
 
33
31
  ## ⚠️ Disclaimer
34
32
 
35
- This is an open-source project provided "as is" without warranty of any kind. The authors accept **no responsibility or liability** for any damages arising from use of this software. This project is **not affiliated with, endorsed by, or sponsored by eBay Inc.** Test thoroughly in eBay's sandbox before using in production.
33
+ This is an open-source project provided "as is" without warranty of any kind. Not affiliated with, endorsed by, or sponsored by eBay Inc. Test thoroughly in sandbox before production.
36
34
 
37
35
  ---
38
36
 
@@ -41,21 +39,8 @@ This is an open-source project provided "as is" without warranty of any kind. Th
41
39
  - [Choose a runtime mode](#choose-a-runtime-mode)
42
40
  - [Prerequisites](#prerequisites)
43
41
  - [Local mode setup](#local-mode-setup)
44
- - [Install](#install)
45
- - [Configure credentials](#configure-credentials)
46
- - [Run the setup wizard](#run-the-setup-wizard)
47
- - [Local client configuration](#local-client-configuration)
48
42
  - [Hosted mode setup](#hosted-mode-setup)
49
- - [Environment variables](#hosted-environment-variables)
50
- - [Secret file](#secret-file)
51
- - [Deploy to Render](#deploy-to-render)
52
- - [OAuth flows](#oauth-flows)
53
- - [MCP endpoints](#mcp-endpoints)
54
- - [Validation architecture](#validation-architecture)
55
- - [Validation endpoints and auth model](#validation-endpoints-and-auth-model)
56
- - [Diagnostics and health endpoints](#diagnostics-and-health-endpoints)
57
- - [Validation provider behavior and limitations](#validation-provider-behavior-and-limitations)
58
- - [Remote client configuration](#remote-client-configuration)
43
+ - [Tool discovery for agents](#tool-discovery-for-agents)
59
44
  - [Available tools](#available-tools)
60
45
  - [Development](#development)
61
46
  - [Testing & validation](#testing--validation)
@@ -66,12 +51,12 @@ This is an open-source project provided "as is" without warranty of any kind. Th
66
51
 
67
52
  ## Choose a runtime mode
68
53
 
69
- | Mode | Transport | Best for |
70
- |------|-----------|----------|
71
- | **Local STDIO** | stdin/stdout | Single-user local AI client (Claude Desktop, Cline, Cursor, etc.) |
72
- | **Hosted HTTP** | Streamable HTTP | Multi-user server deployment; remote MCP clients |
54
+ | Mode | Command | Transport | Best for | Authorization model |
55
+ |------|---------|-----------|----------|---------------------|
56
+ | **Local STDIO** | `pnpm start` / `pnpm run dev` | stdin/stdout | Single-user local AI client (Claude Desktop, Cline, Cursor, etc.) | The local process reads eBay credentials and optional `EBAY_USER_REFRESH_TOKEN` from environment variables. |
57
+ | **Hosted HTTP** | `pnpm run start:http` / `pnpm run dev:http` | Streamable HTTP | Multi-user server deployment; remote MCP clients | Users normally authorize through the browser OAuth flow and then call MCP with `Authorization: Bearer <session-token>`. |
73
58
 
74
- Both modes use the same eBay tools. The local mode reads credentials from environment variables or a `.env` file. The hosted mode handles multi-user OAuth server-side and authenticates clients with session tokens.
59
+ Both modes use the same eBay tool registry. Local STDIO is best when one trusted local client owns the eBay credentials. Hosted HTTP runs an Express server with OAuth 2.1 discovery, environment-scoped route trees, server-side token/session storage, and admin-only operational endpoints.
75
60
 
76
61
  ---
77
62
 
@@ -82,16 +67,16 @@ Both modes use the same eBay tools. The local mode reads credentials from enviro
82
67
  - [pnpm](https://pnpm.io/) (or npm — `npm install -g pnpm`)
83
68
  - An [eBay Developer Account](https://developer.ebay.com/)
84
69
 
85
- **Getting eBay credentials:**
86
- 1. Log in to the [eBay Developer Portal](https://developer.ebay.com/my/keys)
87
- 2. Create an application and copy your **App ID (Client ID)** and **Cert ID (Client Secret)**
88
- 3. Under **User Tokens → Add RuName**, register your OAuth callback URL and copy the generated **RuName** string
70
+ **Getting credentials:**
71
+ 1. Log in to [eBay Developer Portal](https://developer.ebay.com/my/keys)
72
+ 2. Create an application, copy **App ID (Client ID)** and **Cert ID (Client Secret)**
73
+ 3. Under **User Tokens → Add RuName**, register your public HTTPS OAuth callback URL and copy the generated **RuName** string
89
74
 
90
- > **`EBAY_RUNAME` is the RuName string eBay generates, not the callback URL itself.** It looks like `YourApp-YourApp-SBX-abcdefghi`. The callback URL is set separately (see below).
75
+ > **`EBAY_RUNAME` and the public Redirect URL are distinct.** `EBAY_RUNAME` is the eBay-generated RuName string used as eBay's OAuth `redirect_uri` identifier (for example, `YourApp-YourApp-SB-abcdefghi`). The public Redirect URL is the real browser callback URL you register in eBay, such as `https://your-server.com/oauth/callback` or `https://ebay-local.test:3000/oauth/callback`; it is derived from `PUBLIC_BASE_URL` in this project. Do not use either value as a fallback for the other.
91
76
 
92
77
  ### HTTPS callback URL (required by eBay)
93
78
 
94
- eBay requires an HTTPS callback URL for OAuth. For local development, use [mkcert](https://github.com/FiloSottile/mkcert):
79
+ eBay requires HTTPS for OAuth callbacks. For local dev, use [mkcert](https://github.com/FiloSottile/mkcert):
95
80
 
96
81
  ```bash
97
82
  brew install mkcert nss
@@ -100,7 +85,7 @@ mkcert ebay-local.test
100
85
  echo "127.0.0.1 ebay-local.test" | sudo tee -a /etc/hosts
101
86
  ```
102
87
 
103
- Register `https://ebay-local.test:3000/oauth/callback` in the eBay Developer Portal as your Accept URL. Then add to `.env`:
88
+ Register `https://ebay-local.test:3000/oauth/callback` in the Developer Portal. Add to `.env`:
104
89
 
105
90
  ```bash
106
91
  PUBLIC_BASE_URL=https://ebay-local.test:3000
@@ -108,17 +93,15 @@ EBAY_LOCAL_TLS_CERT_PATH=/path/to/ebay-local.test.pem
108
93
  EBAY_LOCAL_TLS_KEY_PATH=/path/to/ebay-local.test-key.pem
109
94
  ```
110
95
 
111
- #### ⚠️ Trust the mkcert CA in Node.js (required for MCP clients like Cline)
96
+ #### Trust mkcert CA in Node.js
112
97
 
113
- VS Code's extension host (where Cline runs) uses Node.js for outbound HTTPS requests. Node.js does **not** automatically read macOS's system keychain, so the `ebay-local.test` certificate is not trusted by default. This causes the OAuth token exchange (`POST /sandbox/token`) to fail silently — the browser flow completes, the "Open in VS Code" page appears, but Cline never receives a session token.
114
-
115
- **Fix — run these two commands once, then fully quit and reopen VS Code:**
98
+ VS Code's extension host uses Node.js, which doesn't read macOS system keychain by default. Trust the cert:
116
99
 
117
100
  ```bash
118
- # 1. Set for the current macOS session (affects all Dock/Spotlight-launched apps):
101
+ # Current session (Dock/Spotlight-launched apps):
119
102
  launchctl setenv NODE_EXTRA_CA_CERTS "$(mkcert -CAROOT)/rootCA.pem"
120
103
 
121
- # 2. Create a LaunchAgent so it persists across reboots:
104
+ # Persist across reboots:
122
105
  cat > ~/Library/LaunchAgents/com.local.mkcert-node-trust.plist <<'EOF'
123
106
  <?xml version="1.0" encoding="UTF-8"?>
124
107
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -137,23 +120,13 @@ cat > ~/Library/LaunchAgents/com.local.mkcert-node-trust.plist <<'EOF'
137
120
  EOF
138
121
  launchctl load ~/Library/LaunchAgents/com.local.mkcert-node-trust.plist
139
122
 
140
- # 3. For terminal-launched VS Code — add to ~/.zshrc:
123
+ # Terminal-launched apps — add to ~/.zshrc:
141
124
  echo 'export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem"' >> ~/.zshrc
142
125
  ```
143
126
 
144
- > Replace `YOUR_USERNAME` with your actual macOS username in the plist, or use the full path printed by `mkcert -CAROOT`.
145
-
146
- After running these commands and **fully quitting VS Code (Cmd+Q on macOS)** and reopening it, Cline's extension host will trust the `ebay-local.test` certificate and the MCP OAuth flow will complete successfully.
147
-
148
- **Verify the fix works (without restarting VS Code):**
149
- ```bash
150
- NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem" node -e "
151
- require('https').get('https://ebay-local.test:3000/health', r => console.log('TLS OK — status:', r.statusCode)).on('error', e => console.error('TLS FAIL:', e.message));
152
- "
153
- # Expected: TLS OK — status: 200
154
- ```
127
+ Then fully quit and reopen VS Code.
155
128
 
156
- For hosted deployments, register your server's public HTTPS URL instead (e.g. `https://your-server.com/oauth/callback`).
129
+ For hosted deployments, register your server's public HTTPS URL instead.
157
130
 
158
131
  ---
159
132
 
@@ -161,64 +134,48 @@ For hosted deployments, register your server's public HTTPS URL instead (e.g. `h
161
134
 
162
135
  ### Install
163
136
 
164
- **Option A — pnpm global install (no build step):**
165
-
166
137
  ```bash
138
+ # Option A — global install (no build step):
167
139
  pnpm install -g ebay-mcp-remote-edition
168
- ```
169
-
170
- **Option B — clone and build (for contributors or self-hosting):**
171
140
 
172
- ```bash
141
+ # Option B — clone and build (contributors):
173
142
  git clone https://github.com/mrnajiboy/ebay-mcp-remote-edition.git
174
143
  cd ebay-mcp-remote-edition
175
- pnpm install
176
- pnpm run build
144
+ pnpm install && pnpm run build
177
145
  ```
178
146
 
179
147
  ### Configure credentials
180
148
 
181
- Create a `.env` file in the project root (see `.env.example`):
149
+ Create `.env` (see `.env.example`):
182
150
 
183
151
  ```bash
184
152
  EBAY_CLIENT_ID=your_client_id
185
153
  EBAY_CLIENT_SECRET=your_client_secret
186
- EBAY_RUNAME=your_runame_string
154
+ EBAY_RUNAME=your_runame_string # eBay-generated RuName, not a URL
155
+ EBAY_REDIRECT_URI= # legacy env name; do not set to the public callback URL
187
156
  EBAY_ENVIRONMENT=sandbox # or production
188
157
  EBAY_MARKETPLACE_ID=EBAY_US # optional, defaults to EBAY_US
189
158
  EBAY_CONTENT_LANGUAGE=en-US # optional, defaults to en-US
190
-
191
- # Populated by the setup wizard:
192
- EBAY_USER_REFRESH_TOKEN=
159
+ EBAY_USER_REFRESH_TOKEN= # populated by setup wizard
193
160
  ```
194
161
 
195
162
  **Authentication tiers:**
196
163
 
197
164
  | Method | Rate limit | How |
198
165
  |--------|-----------|-----|
199
- | Client credentials (default) | 1,000 req/day | Just set `EBAY_CLIENT_ID` + `EBAY_CLIENT_SECRET` |
200
- | User tokens (recommended) | 10,000–50,000 req/day | Run the setup wizard to complete OAuth and populate `EBAY_USER_REFRESH_TOKEN` |
166
+ | Client credentials (default) | 1,000 req/day | Set `EBAY_CLIENT_ID` + `EBAY_CLIENT_SECRET` |
167
+ | User tokens (recommended) | 10,000–50,000 req/day | Run `pnpm run setup` to complete OAuth |
201
168
 
202
169
  ### Run the setup wizard
203
170
 
204
- The interactive wizard guides you through environment selection, credential entry, OAuth login, and MCP client configuration:
205
-
206
171
  ```bash
207
- pnpm run setup
172
+ pnpm run setup # interactive: env selection, credentials, OAuth, client config
173
+ pnpm run setup --quick # skip optional steps
174
+ pnpm run setup --diagnose # connectivity and token checks only
208
175
  ```
209
176
 
210
- Options:
211
- - `--quick` — skip optional steps
212
- - `--diagnose` — run connectivity and token checks only
213
-
214
- After completing OAuth, the wizard writes `EBAY_USER_REFRESH_TOKEN` to `.env` and optionally configures Claude Desktop automatically.
215
-
216
177
  ### Local client configuration
217
178
 
218
- For direct STDIO usage, configure your MCP client to launch the server as a subprocess. All clients use the same JSON pattern:
219
-
220
- **Using npm (no clone needed):**
221
-
222
179
  ```json
223
180
  {
224
181
  "mcpServers": {
@@ -230,6 +187,7 @@ For direct STDIO usage, configure your MCP client to launch the server as a subp
230
187
  "EBAY_CLIENT_SECRET": "YOUR_CLIENT_SECRET",
231
188
  "EBAY_ENVIRONMENT": "sandbox",
232
189
  "EBAY_RUNAME": "YOUR_RUNAME",
190
+ "EBAY_REDIRECT_URI": "",
233
191
  "EBAY_USER_REFRESH_TOKEN": "YOUR_REFRESH_TOKEN"
234
192
  }
235
193
  }
@@ -237,35 +195,14 @@ For direct STDIO usage, configure your MCP client to launch the server as a subp
237
195
  }
238
196
  ```
239
197
 
240
- **Using a local build:**
241
-
242
- ```json
243
- {
244
- "mcpServers": {
245
- "ebay": {
246
- "command": "node",
247
- "args": ["/absolute/path/to/ebay-mcp-remote-edition/build/index.js"],
248
- "env": {
249
- "EBAY_CLIENT_ID": "YOUR_CLIENT_ID",
250
- "EBAY_CLIENT_SECRET": "YOUR_CLIENT_SECRET",
251
- "EBAY_ENVIRONMENT": "sandbox",
252
- "EBAY_RUNAME": "YOUR_RUNAME",
253
- "EBAY_USER_REFRESH_TOKEN": "YOUR_REFRESH_TOKEN"
254
- }
255
- }
256
- }
257
- }
258
- ```
259
-
260
- **Config file locations by client:**
198
+ **Config file locations:**
261
199
 
262
200
  | Client | Config file |
263
201
  |--------|-------------|
264
202
  | Cline | `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` |
265
203
  | Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
266
204
  | Claude Desktop (Windows) | `%APPDATA%\Claude\claude_desktop_config.json` |
267
- | Cursor (global) | `~/.cursor/mcp.json` |
268
- | Cursor (project) | `.cursor/mcp.json` |
205
+ | Cursor | `~/.cursor/mcp.json` or `.cursor/mcp.json` |
269
206
 
270
207
  Zed, Windsurf, Continue.dev, Roo Code, and Amazon Q follow the same `mcpServers` JSON shape.
271
208
 
@@ -273,543 +210,334 @@ Zed, Windsurf, Continue.dev, Roo Code, and Amazon Q follow the same `mcpServers`
273
210
 
274
211
  ## Hosted mode setup
275
212
 
276
- The hosted HTTP server exposes environment-scoped MCP and OAuth endpoints, handles eBay OAuth server-side, and issues session tokens to MCP clients.
213
+ ### Environment variables
277
214
 
278
- ### Hosted environment variables
215
+ Hosted HTTP reads the same eBay credential variables as local STDIO plus the HTTP, storage, and security variables below. The start script is `pnpm run start:http`; development uses `pnpm run dev:http`.
279
216
 
280
217
  ```bash
281
- # Server
282
- PORT=3000
283
- MCP_HOST=0.0.0.0
218
+ # Required for hosted OAuth URLs and eBay callback registration
284
219
  PUBLIC_BASE_URL=https://your-server.com
285
220
 
286
- # eBay credentials (prefer secret file below instead of raw env)
287
- EBAY_DEFAULT_ENVIRONMENT=production # sandbox or production
288
-
289
- # Persistent token/session storage backend (required for multi-user hosted mode)
290
- EBAY_TOKEN_STORE_BACKEND=cloudflare-kv # cloudflare-kv | upstash-redis | memory
291
-
292
- # Cloudflare KV (when EBAY_TOKEN_STORE_BACKEND=cloudflare-kv)
293
- CLOUDFLARE_ACCOUNT_ID=
294
- CLOUDFLARE_KV_NAMESPACE_ID=
295
- CLOUDFLARE_API_TOKEN=
296
-
297
- # Upstash Redis (when EBAY_TOKEN_STORE_BACKEND=upstash-redis)
298
- UPSTASH_REDIS_REST_URL=
299
- UPSTASH_REDIS_REST_TOKEN=
300
-
301
- # eBay Research session-expiry alerts (optional but recommended when using
302
- # first-party research in hosted mode)
303
- TELEGRAM_BOT_TOKEN=
304
- TELEGRAM_CHAT_ID=1574052684
305
- QSTASH_URL=
306
- QSTASH_TOKEN=
307
- QSTASH_CURRENT_SIGNING_KEY=
308
- QSTASH_NEXT_SIGNING_KEY=
309
- EBAY_RESEARCH_SESSION_ALERTS_ENABLED=true
310
- EBAY_RESEARCH_SESSION_ALERT_WINDOW_24H=true
311
- EBAY_RESEARCH_SESSION_ALERT_WINDOW_6H=true
312
- EBAY_RESEARCH_SESSION_ALERT_ON_EXPIRED=true
313
- EBAY_RESEARCH_SESSION_ALERT_CALLBACK_URL=
314
-
315
- # Alert scheduling additionally requires:
316
- # - PUBLIC_BASE_URL or EBAY_RESEARCH_SESSION_ALERT_CALLBACK_URL to be externally reachable
317
- # - EBAY_RESEARCH_SESSION_STORE=upstash-redis or filesystem
318
-
319
- # Security
320
- ADMIN_API_KEY= # required for admin session endpoints
321
- OAUTH_START_KEY= # optional; protects /oauth/start with a shared secret
221
+ # Required eBay credentials unless EBAY_CONFIG_FILE provides them
222
+ EBAY_CLIENT_ID=
223
+ EBAY_CLIENT_SECRET=
224
+ EBAY_RUNAME=
225
+ EBAY_REDIRECT_URI= # legacy env name; not the public callback URL
226
+ # Recommended when serving both environments from one host
227
+ EBAY_PRODUCTION_CLIENT_ID=
228
+ EBAY_PRODUCTION_CLIENT_SECRET=
229
+ EBAY_PRODUCTION_RUNAME=
230
+ EBAY_PRODUCTION_REDIRECT_URI=
231
+ EBAY_SANDBOX_CLIENT_ID=
232
+ EBAY_SANDBOX_CLIENT_SECRET=
233
+ EBAY_SANDBOX_RUNAME=
234
+ EBAY_SANDBOX_REDIRECT_URI=
235
+
236
+ # Required for hosted multi-user token/session persistence
237
+ EBAY_TOKEN_STORE_BACKEND=upstash-redis # cloudflare-kv | upstash-redis | memory
238
+ UPSTASH_REDIS_REST_URL= # required when backend is upstash-redis
239
+ UPSTASH_REDIS_REST_TOKEN= # required when backend is upstash-redis
240
+ CLOUDFLARE_ACCOUNT_ID= # required when backend is cloudflare-kv
241
+ CLOUDFLARE_KV_NAMESPACE_ID= # required when backend is cloudflare-kv
242
+ CLOUDFLARE_API_TOKEN= # required when backend is cloudflare-kv
243
+
244
+ # Required for admin/validation endpoints and privileged MCP bypass
245
+ ADMIN_API_KEY=
246
+
247
+ # Optional HTTP/server behavior
248
+ PORT=3000 # defaults to 3000; many hosts inject this
249
+ MCP_HOST=0.0.0.0 # defaults to 0.0.0.0
250
+ EBAY_ENVIRONMENT=production # root/legacy default selector
251
+ EBAY_DEFAULT_ENVIRONMENT=production # fallback when EBAY_ENVIRONMENT is unset
252
+ SESSION_TTL_SECONDS=2592000 # default: 30 days
253
+ OAUTH_START_KEY= # optional gate for /oauth/start
254
+ EBAY_CONFIG_FILE=/etc/secrets/ebay-config.json
255
+ EBAY_MARKETPLACE_ID=EBAY_US
256
+ EBAY_CONTENT_LANGUAGE=en-US
257
+ EBAY_LOG_LEVEL=info
322
258
 
323
- # Validation runner identity (required for hosted /validation/* routes)
324
- # Reuses a stored refresh-token-backed user in the existing multi-user auth store.
325
- # Use the env-specific values when sandbox and production need different runner users.
259
+ # Optional validation runner identity
326
260
  VALIDATION_RUNNER_USER_ID=
327
261
  VALIDATION_RUNNER_USER_ID_SANDBOX=
328
262
  VALIDATION_RUNNER_USER_ID_PRODUCTION=
329
263
 
330
- # Temporary sold-data enrichment provider for validation.
331
- # This is an interim external abstraction and will be replaced by an internal
332
- # sales-data implementation without changing the validation orchestration route.
264
+ # Optional validation/research providers and alerts
333
265
  SOLD_ITEMS_API_URL=
334
266
  SOLD_ITEMS_API_KEY=
335
-
336
- # Future orchestration-side historical research provider.
337
- # Currently only used to enable the placeholder research contract.
338
267
  PERPLEXITY_API_KEY=
339
-
340
- # Optional phase-1 social-signal providers used by hosted validation.
341
- # These signals are supportive only and should not be treated as authoritative
342
- # automated buy triggers on their own.
343
268
  TWITTER_BEARER_TOKEN=
344
269
  YOUTUBE_API_KEY=
345
270
  REDDIT_CLIENT_ID=
346
271
  REDDIT_CLIENT_SECRET=
347
272
  REDDIT_USER_AGENT=
348
-
349
- # Session TTL (optional; default 30 days)
350
- SESSION_TTL_SECONDS=2592000
351
-
352
- # Logging
353
- EBAY_LOG_LEVEL=info
354
- EBAY_MARKETPLACE_ID=EBAY_US
355
- EBAY_CONTENT_LANGUAGE=en-US
356
-
357
- # Path to secret file (see below)
358
- EBAY_CONFIG_FILE=/etc/secrets/ebay-config.json
273
+ TELEGRAM_BOT_TOKEN=
274
+ TELEGRAM_CHAT_ID=
275
+ QSTASH_URL=
276
+ QSTASH_TOKEN=
277
+ QSTASH_CURRENT_SIGNING_KEY=
278
+ QSTASH_NEXT_SIGNING_KEY=
279
+ EBAY_RESEARCH_SESSION_ALERTS_ENABLED=true
280
+ EBAY_RESEARCH_SESSION_ALERT_CALLBACK_URL=
359
281
  ```
360
282
 
361
- > Use `EBAY_TOKEN_STORE_BACKEND=memory` only for local development or tests. All OAuth state, session tokens, and user tokens are lost on restart.
283
+ > `EBAY_TOKEN_STORE_BACKEND` defaults to Cloudflare KV when unset or unrecognized. Use `memory` only for tests or throwaway local development because hosted sessions and tokens are lost on restart.
362
284
 
363
285
  ### Secret file
364
286
 
365
- Store eBay credentials in a mounted secret file rather than raw environment variables. On Render, create a **Secret File** named `ebay-config.json` mounted at `/etc/secrets/ebay-config.json`:
287
+ Mount a JSON file with credentials (e.g., Render Secret File at `/etc/secrets/ebay-config.json`):
366
288
 
367
289
  ```json
368
290
  {
369
291
  "production": {
370
292
  "clientId": "PROD_CLIENT_ID",
371
293
  "clientSecret": "PROD_CLIENT_SECRET",
372
- "redirectUri": "YOUR_PRODUCTION_RUNAME"
294
+ "redirectUri": "YOUR_PRODUCTION_RUNAME",
295
+ "ruName": "YOUR_PRODUCTION_RUNAME"
373
296
  },
374
297
  "sandbox": {
375
298
  "clientId": "SANDBOX_CLIENT_ID",
376
299
  "clientSecret": "SANDBOX_CLIENT_SECRET",
377
- "redirectUri": "YOUR_SANDBOX_RUNAME"
300
+ "redirectUri": "YOUR_SANDBOX_RUNAME",
301
+ "ruName": "YOUR_SANDBOX_RUNAME"
378
302
  }
379
303
  }
380
304
  ```
381
305
 
382
- ### Deploy to Render / Railway / other Nixpacks hosts
306
+ ### Deploy to Render / Railway / Coolify
383
307
 
384
- 1. Connect your repo to Render as a **Web Service**
385
- 2. Set **Build command:**
386
- ```bash
387
- pnpm install && pnpm run build
388
- ```
389
- 3. Set **Start command:**
390
- ```bash
391
- pnpm run start:http
392
- ```
393
- 4. Add the environment variables listed above
394
- 5. Add the `ebay-config.json` secret file
308
+ 1. Connect your repo as a **Web Service**
309
+ 2. **Build:** `pnpm install && pnpm run build`
310
+ 3. **Start:** `pnpm run start:http`
311
+ 4. Add environment variables + secret file
395
312
 
396
- The server starts on the port Render assigns via `$PORT` and logs the active KV backend on startup.
397
-
398
- For Nixpacks-based platforms such as Railway and Coolify:
399
-
400
- - The repository now includes `nixpacks.toml` so the generated image uses `pnpm install --frozen-lockfile`, runs `pnpm run build`, installs Chromium for the Playwright-backed validation paths, and starts with `pnpm run start:http`.
401
- - Runtime secrets should be configured in the platform dashboard as runtime environment variables or mounted secret files. Do not bake secrets into Docker build arguments or `ENV` layers.
402
- - Keep `pnpm-lock.yaml` committed and in sync with `package.json`; Nixpacks installs with a frozen lockfile.
313
+ For Nixpacks-based platforms (Railway, Coolify): `nixpacks.toml` handles `pnpm`, build, Chromium install, and start automatically.
403
314
 
404
315
  ### OAuth flows
405
316
 
406
- eBay requires a single registered callback URL per application. The hosted server registers `/oauth/callback` at the root and recovers the environment from the stored OAuth state record.
407
-
408
- **Start an OAuth flow (browser):**
409
-
410
317
  ```
411
- GET /sandbox/oauth/start # always sandbox
412
- GET /production/oauth/start # always production
318
+ GET /sandbox/oauth/start # sandbox browser login
319
+ GET /production/oauth/start # production browser login
413
320
  ```
414
321
 
415
- If `OAUTH_START_KEY` is set, include it as a query parameter or header:
416
- ```
417
- GET /sandbox/oauth/start?key=YOUR_OAUTH_START_KEY
418
- # or header: X-OAuth-Start-Key: YOUR_OAUTH_START_KEY
419
- ```
322
+ If `OAUTH_START_KEY` is set, start URLs require either `?key=YOUR_KEY` or the `X-OAuth-Start-Key: YOUR_KEY` header. The server also includes this key as `key` in generated `authorization_url` values for unauthenticated MCP requests.
420
323
 
421
- After a successful login, the callback page displays your **session token**, **eBay access token**, and **eBay refresh token** with one-click copy buttons.
324
+ After login, the callback page shows your **session token** with copy buttons.
422
325
 
423
- **Session token TTL schedule:**
326
+ **Session TTL schedule:**
424
327
 
425
- | Record | `expiresAt` field | Backend TTL |
426
- |--------|-------------------|-------------|
427
- | OAuth state | 15 minutes | 15 minutes |
428
- | MCP auth code | 10 minutes | 10 minutes |
429
- | Session | 30 days (configurable) | Matches `SESSION_TTL_SECONDS` |
430
- | User token record | eBay refresh token expiry (fallback: 18 months) | Matches token expiry |
328
+ | Record | TTL |
329
+ |--------|-----|
330
+ | OAuth state | 15 minutes |
331
+ | MCP auth code | 10 minutes |
332
+ | Session | 30 days (configurable) |
333
+ | User token | eBay refresh token expiry (fallback: 18 months) |
431
334
 
432
335
  ### MCP endpoints
433
336
 
434
337
  **Environment-scoped (recommended):**
435
-
436
338
  ```
437
339
  POST/GET/DELETE /sandbox/mcp
438
340
  POST/GET/DELETE /production/mcp
439
341
  ```
440
342
 
441
- Each scoped path includes its own OAuth 2.1 discovery document:
442
- ```
443
- GET /sandbox/.well-known/oauth-authorization-server
444
- GET /production/.well-known/oauth-authorization-server
445
- ```
446
-
447
- **Legacy auto-detect (backward-compatible):**
448
- ```
449
- POST/GET/DELETE /mcp # resolves environment from ?env= or EBAY_DEFAULT_ENVIRONMENT
450
- ```
451
-
452
- **Authentication behavior:**
453
- - `GET /mcp` (or scoped variant) without a valid Bearer token redirects the browser to the matching `oauth/start` URL
454
- - `POST /mcp` without a valid Bearer token returns a structured `401` JSON with an `authorization_url` field
455
- - All requests supply a session token via `Authorization: Bearer <session-token>`
456
-
457
- **Other utility endpoints:**
343
+ Each includes OAuth 2.1 discovery: `GET /sandbox/.well-known/oauth-authorization-server`
458
344
 
345
+ **Legacy auto-detect:**
459
346
  ```
460
- GET /health # Server health check (no auth required)
461
- GET /whoami # Session identity; requires Bearer session token
462
- GET /admin/session/:sessionToken # View session; requires X-Admin-API-Key
463
- POST /admin/session/:sessionToken/revoke # Revoke session
464
- DELETE /admin/session/:sessionToken # Delete session
465
- ```
466
-
467
- `/whoami` response:
468
- ```json
469
- {
470
- "userId": "...",
471
- "environment": "sandbox",
472
- "createdAt": "2026-03-23T08:00:00.000Z",
473
- "expiresAt": "2026-04-22T08:00:00.000Z",
474
- "lastUsedAt": "2026-03-23T09:30:00.000Z",
475
- "revokedAt": null
476
- }
347
+ POST/GET/DELETE /mcp # resolves from ?env= or EBAY_ENVIRONMENT/EBAY_DEFAULT_ENVIRONMENT
477
348
  ```
478
349
 
479
- `/whoami` is the quickest hosted-session debugging check when an MCP client appears authenticated but requests still fail. It confirms which stored user session is active, which environment it is bound to, and whether the session has expired or been revoked.
480
-
481
- ### Validation architecture
482
-
483
- The hosted backend now includes a deployment-oriented validation pipeline for non-MCP server-side execution. The route handlers live in [`src/server-http.ts`](src/server-http.ts), while the validation module lives under [`src/validation/`](src/validation).
484
-
485
- Current module layout:
486
-
487
- - [`src/validation/types.ts`](src/validation/types.ts) — request/response contracts for validation runs, decision payloads, debug payloads, and provider signal types
488
- - [`src/validation/effective-context.ts`](src/validation/effective-context.ts) — source-aware normalization layer that converts raw request payloads into a first-class effective validation context for item and event runs
489
- - [`src/validation/run-validation.ts`](src/validation/run-validation.ts) — orchestration entrypoint that validates input, queries providers, merges signals, and returns writes/decision/debug output
490
- - [`src/validation/recommendation.ts`](src/validation/recommendation.ts) — recommendation and automation decision logic
491
- - [`src/validation/providers/ebay.ts`](src/validation/providers/ebay.ts) — live eBay browse-market snapshot provider using the server's existing user-scoped eBay API client
492
- - [`src/validation/providers/ebay-sold.ts`](src/validation/providers/ebay-sold.ts) — temporary sold-data provider backed by an external API via `SOLD_ITEMS_API_URL` and `SOLD_ITEMS_API_KEY`
493
- - [`src/validation/providers/terapeak.ts`](src/validation/providers/terapeak.ts) — authenticated eBay Research provider orchestration for current-market and previous-POB metrics, including candidate scoring, fallback diagnostics, and sold-velocity bucketing
494
- - [`src/validation/providers/ebay-research.ts`](src/validation/providers/ebay-research.ts) — low-level authenticated eBay Research fetcher with session-cookie sourcing, response parsing, and auth-aware cache invalidation
495
- - [`src/validation/providers/query-utils.ts`](src/validation/providers/query-utils.ts) — shared multi-tier query candidate and fallback helpers used by browse and sold providers
496
- - [`src/validation/providers/social.ts`](src/validation/providers/social.ts) — phase-1 social provider for recent Twitter/X activity, YouTube view-rate proxy data, and Reddit recent-post counts with graceful degradation
497
- - [`src/validation/providers/chart.ts`](src/validation/providers/chart.ts) — chart-signal stub reserved for later implementation
498
- - [`src/validation/providers/research.ts`](src/validation/providers/research.ts) — stable previous-comeback research contract provider for orchestration-side historical inference; currently a placeholder contract with optional future `PERPLEXITY_API_KEY` support
499
-
500
- Current provider domains called by [`runValidation()`](src/validation/run-validation.ts:106):
501
-
502
- - **browse/current-market** via [`src/validation/providers/ebay.ts`](src/validation/providers/ebay.ts)
503
- - **sold enrichment** via [`src/validation/providers/ebay-sold.ts`](src/validation/providers/ebay-sold.ts)
504
- - **Terapeak / eBay research contract** via [`src/validation/providers/terapeak.ts`](src/validation/providers/terapeak.ts)
505
- - **social support signals** via [`src/validation/providers/social.ts`](src/validation/providers/social.ts)
506
- - **chart support signals** via [`src/validation/providers/chart.ts`](src/validation/providers/chart.ts)
507
- - **previous comeback research inference** via [`src/validation/providers/research.ts`](src/validation/providers/research.ts)
508
-
509
- Architecturally, the validation stack is split into two practical classes of providers:
510
-
511
- - **Server-side authenticated providers** — these run with the hosted backend's stored eBay user context and are the right place for authenticated marketplace retrieval. Today that means the live browse/current-market provider in [`src/validation/providers/ebay.ts`](src/validation/providers/ebay.ts), the sold enrichment layer in [`src/validation/providers/ebay-sold.ts`](src/validation/providers/ebay-sold.ts), and the Terapeak/eBay research contract in [`src/validation/providers/terapeak.ts`](src/validation/providers/terapeak.ts).
512
- - **Orchestration-side research providers** — these run as supporting inference layers inside orchestration rather than as part of the user-scoped eBay API client surface. Today that means previous comeback resolution and external historical-research inference in [`src/validation/providers/research.ts`](src/validation/providers/research.ts), plus non-authoritative support providers such as [`src/validation/providers/social.ts`](src/validation/providers/social.ts) and [`src/validation/providers/chart.ts`](src/validation/providers/chart.ts).
513
-
514
- Operationally, validation works like this:
515
-
516
- 1. An admin caller invokes an environment-scoped validation route.
517
- 2. The server resolves the environment (`sandbox` or `production`) from the mounted route tree.
518
- 3. The route looks up the configured validation runner user ID for that environment.
519
- 4. The server loads that user's stored refresh-token-backed credentials from the existing hosted auth store.
520
- 5. The validation orchestrator calls all six provider domains and gathers browse/current-market, sold enrichment, Terapeak/research contract data, social support signals, chart stub output, and previous-comeback research output.
521
- 6. Before provider execution, [`runValidation()`](src/validation/run-validation.ts) builds a normalized `effectiveContext` so downstream logic consumes a source-aware model (`item` or `event`) instead of relying on empty item placeholders.
522
- 7. [`runValidation()`](src/validation/run-validation.ts) deterministically merges the provider outputs into normalized field writes.
523
- 8. The response returns those writes, a conservative buy/track decision block, and provider debug metadata for downstream systems.
524
-
525
- #### Effective validation context
350
+ **Auth behavior:**
351
+ - `GET /mcp` without token → redirects to `oauth/start`
352
+ - `POST /mcp` without token → `401` JSON with `authorization_url`, `resource_metadata`, and a `WWW-Authenticate` Bearer challenge
353
+ - Normal user requests: `Authorization: Bearer <session-token>`
354
+ - Privileged admin bypass: `Authorization: Bearer <ADMIN_API_KEY>` when `ADMIN_API_KEY` is configured
526
355
 
527
- Validation runs now normalize incoming request data into an internal effective context before provider query planning and recommendation logic execute.
356
+ #### Admin key bypass
528
357
 
529
- - **Item-scope runs** normalize to an item-oriented context with the resolved artist, album/item phrase, location, and resolved search query.
530
- - **Event-scope runs** normalize to an event-oriented context with `searchArtist`, `searchEvent`, `searchItem`, `searchLocation`, timing metadata, and a derived `effectiveSearchQuery` when no direct resolved query is present.
531
- - Providers and recommendation logic consume that normalized context rather than reasoning about blank `item.recordId` or `item.name` fields.
532
- - Debug output now exposes `effectiveSourceType`, `effectiveContextMode`, `effectiveSearchQuery`, `hasItem`, and `hasEvent` so operators can confirm whether an event run was normalized correctly.
358
+ `ADMIN_API_KEY` is used in two distinct ways:
533
359
 
534
- The request schema also now accepts source-aware query-context fields for hosted validation runs:
360
+ | Use | Exact implementation | Scope |
361
+ |-----|----------------------|-------|
362
+ | Admin/validation HTTP routes | `X-Admin-API-Key: <ADMIN_API_KEY>` header | Required for `/admin/session/:sessionToken`, `/admin/session/:sessionToken/revoke`, `/admin/session/:sessionToken`, `/sandbox/validation/*`, `/production/validation/*`, and legacy `/validation/*`. |
363
+ | MCP authorization bypass | `Authorization: Bearer <ADMIN_API_KEY>` header | Lets privileged server-to-server/admin tooling call `/sandbox/mcp`, `/production/mcp`, or `/mcp` without a hosted user session lookup. The request runs with `userId` set to `admin` and the requested environment. |
535
364
 
536
- - `resolvedSearchArtist`
537
- - `resolvedSearchItem`
538
- - `resolvedSearchEvent`
539
- - `resolvedSearchLocation`
540
- - `resolvedSearchQuery`
365
+ The admin bypass does **not** use query parameters or request body fields. `ADMIN_API_KEY` must be configured on the server; if it is unset, admin HTTP routes return `500` and the MCP bypass is disabled.
541
366
 
542
- The validation contract is intentionally split between stable route orchestration and swappable providers. That is why the current sold-data source can be replaced later without changing downstream orchestration or the hosted route contract implemented in [`src/validation/run-validation.ts`](src/validation/run-validation.ts).
367
+ Security caveats:
368
+ - Treat `ADMIN_API_KEY` as a privileged root credential for this MCP server.
369
+ - Generate a long, random value and store it only in your deployment secret manager.
370
+ - Use it only for server-to-server automation, operational checks, or admin tooling. Do not ship it to browsers, desktop clients, or regular users.
371
+ - OAuth browser authorization and hosted session tokens remain the normal path for user MCP access.
543
372
 
544
- #### Deterministic merge precedence
373
+ **Utility endpoints:**
545
374
 
546
- The current merge order is fixed in [`runValidation()`](src/validation/run-validation.ts:106) so downstream systems can treat the writes as predictable rather than provider-order dependent:
547
-
548
- - **Watchers / preorder count / shipping / competition** prefer Terapeak contract output when available, then fall back to the browse/current-market provider.
549
- - **Market price** prefers Terapeak contract output, then the sold provider's median sold price, then the browse/current-market provider.
550
- - **Sold day buckets** (`day1Sold` through `day5Sold`, plus `daysTracked`) prefer the sold provider, then authenticated eBay Research sold-row bucketing, then the browse/current-market provider.
551
- - **Previous POB metrics** (`previousPobAvgPriceUsd`, `previousPobSellThroughPct`) are written from the Terapeak contract output when available.
552
- - **Previous comeback first-week sales** (`previousComebackFirstWeekSales`) is written from the orchestration-side research provider when available.
553
- - **Supportive social fields** are only written when a value is actually resolved, so the pipeline avoids blanking previously stored downstream data.
554
-
555
- The validation signal contracts in [`TerapeakValidationSignals`](src/validation/types.ts:142) and [`PreviousComebackResearchSignals`](src/validation/types.ts:164) also back the new write fields in [`ValidationWrites`](src/validation/types.ts:176): `previousPobAvgPriceUsd`, `previousPobSellThroughPct`, and `previousComebackFirstWeekSales`.
556
-
557
- ### Validation endpoints and auth model
375
+ ```
376
+ GET /health # health check (no auth)
377
+ GET /whoami # session identity (Bearer token)
378
+ GET /admin/session/:sessionToken # view session (admin key)
379
+ POST /admin/session/:sessionToken/revoke # revoke session
380
+ DELETE /admin/session/:sessionToken # delete session
381
+ ```
558
382
 
559
- Use the environment-scoped hosted routes for validation:
383
+ ### Validation endpoints
560
384
 
561
385
  ```
562
- POST /sandbox/validation/run
386
+ POST /sandbox/validation/run # run validation pipeline
563
387
  POST /production/validation/run
564
388
 
565
- GET /sandbox/validation/health
389
+ GET /sandbox/validation/health # check runner status
566
390
  GET /production/validation/health
567
391
  ```
568
392
 
569
- Both routes require the admin key:
570
-
571
- ```
572
- X-Admin-API-Key: YOUR_ADMIN_API_KEY
573
- ```
574
-
575
- Auth model summary:
576
-
577
- - Validation routes are **hosted HTTP backend routes**, not MCP tool endpoints.
578
- - They do **not** use MCP client auth for execution.
579
- - They reuse the existing stored refresh-token-backed hosted user architecture.
580
- - The caller authenticates with `X-Admin-API-Key`, and the server then impersonates the configured validation runner user for the target environment.
581
- - Validation runner identity comes from `VALIDATION_RUNNER_USER_ID`, `VALIDATION_RUNNER_USER_ID_SANDBOX`, or `VALIDATION_RUNNER_USER_ID_PRODUCTION`.
582
- - The validation runner must already have stored hosted tokens in the configured token store backend.
583
-
584
- #### `POST /validation/run`
585
-
586
- Runs the validation pipeline for the target environment.
393
+ Both require `X-Admin-API-Key: <ADMIN_API_KEY>`. The server impersonates the configured validation runner user from `VALIDATION_RUNNER_USER_ID` env vars.
587
394
 
588
- - Uses the configured validation runner user ID for that environment
589
- - Requires that stored refresh-token-backed eBay credentials already exist for that user
590
- - Returns either:
591
- - `status: "ok"` with `writes`, `decision`, and `debug`, or
592
- - `status: "error"` with `errorCode`, `message`, `retryable`, and `nextCheckAt`
395
+ See [Validation architecture](#validation-architecture) below for provider details.
593
396
 
594
- The request/response contract is defined in [`src/validation/types.ts`](src/validation/types.ts), and the orchestration behavior is implemented in [`src/validation/run-validation.ts`](src/validation/run-validation.ts).
595
-
596
- The `writes` payload is intentionally non-destructive for supportive and optional fields: if a social, authenticated eBay Research, or previous-comeback research provider cannot resolve data, the orchestration omits those optional writes instead of overwriting existing downstream values with empty placeholders.
597
-
598
- #### `GET /validation/health`
599
-
600
- Checks whether the validation runner is operational in the target environment.
601
-
602
- This endpoint is intended for deployment diagnostics and returns:
603
-
604
- - configured environment
605
- - configured validation runner user ID
606
- - whether stored tokens are present
607
- - whether token refresh/authentication succeeded
608
- - token status from the user-scoped eBay API client
609
- - `authDebug` diagnostics including token endpoint resolution and credential presence
610
- - provider availability summary
611
-
612
- The diagnostics are especially useful after the OAuth token endpoint fix in [`getOAuthTokenBaseUrl()`](src/config/environment.ts:373) and the debug additions in [`getAuthDebugInfo()`](src/auth/oauth.ts:282). If the validation runner cannot refresh tokens, `/validation/health` shows the resolved token endpoint and any captured upstream response status/body excerpt.
613
-
614
- ### Diagnostics and health endpoints
615
-
616
- Use these endpoints together when validating a hosted deployment:
397
+ ### Remote client configuration
617
398
 
399
+ **Cline (automatic OAuth):**
400
+ ```json
401
+ {
402
+ "mcpServers": {
403
+ "ebay-sandbox": { "url": "https://your-server.com/sandbox/mcp" },
404
+ "ebay-production": { "url": "https://your-server.com/production/mcp" }
405
+ }
406
+ }
618
407
  ```
619
- GET /health
620
- GET /whoami
621
- GET /sandbox/validation/health
622
- GET /production/validation/health
623
- POST /internal/ebay-research/check-session-expiry
624
- ```
625
-
626
- Recommended debugging flow:
627
-
628
- 1. Call `/health` to confirm the HTTP service is up.
629
- 2. Call `/whoami` with a Bearer hosted session token to confirm the active hosted user session, bound environment, expiry, and revocation status.
630
- 3. Call the matching env-scoped `/validation/health` route with `X-Admin-API-Key` to confirm the validation runner user is configured, stored tokens exist, and token refresh succeeds.
631
- 4. The internal `POST /internal/ebay-research/check-session-expiry` route is reserved for signed QStash callbacks and should not be used as an unauthenticated public endpoint.
632
-
633
- `/whoami` is especially useful when an operator wants to verify which hosted session is currently active before registering or troubleshooting the validation runner user. Validation routes themselves still authenticate with the admin key and a stored hosted runner identity, not with MCP auth.
634
-
635
- The validation health response is also the main place to verify the OAuth token-endpoint derivation fix from [`getOAuthTokenBaseUrl()`](src/config/environment.ts:373). If a refresh fails, the `authDebug` block exposes the resolved endpoint, credential-presence flags, and captured upstream response excerpts.
636
-
637
- ### Validation provider behavior and limitations
638
-
639
- Current backend status:
640
-
641
- - eBay live market snapshot support is implemented and wired into orchestration.
642
- - Sold-data enrichment is implemented through a **temporary external provider** abstraction.
643
- - Authenticated eBay Research is wired into orchestration for current-market and previous-POB retrieval, while previous-comeback research remains a separate placeholder contract.
644
- - Social support signals are implemented in phase 1.
645
- - Chart data remains a stub.
646
- - Validation is currently an **admin-operated hosted backend workflow**, not an MCP tool surface.
647
- - Event-scope validations are now handled as first-class normalized runs instead of as item-shaped requests with null item identity tolerated for compatibility.
648
-
649
- Provider behavior:
650
-
651
- - **Browse/eBay provider:** [`src/validation/providers/ebay.ts`](src/validation/providers/ebay.ts) uses the eBay Browse API plus shared query fallback logic from [`src/validation/providers/query-utils.ts`](src/validation/providers/query-utils.ts). It walks multiple query candidates, records the selected query and tier in debug output, and uses heuristic matching rather than a strict catalog identity join. Event-driven runs now build those fallback queries from normalized event context instead of raw item title assumptions.
652
- - **Browse debug semantics:** validation debug now keeps browse candidate generation, selected query/tier, browse-specific sample size, and per-candidate result counts separate from sold-provider result counts so operators can tell whether the browse layer contributed a field, fell back to a weaker query, or returned no usable match.
653
- - **Sold provider:** [`src/validation/providers/ebay-sold.ts`](src/validation/providers/ebay-sold.ts) uses a temporary external sold-data source configured by `SOLD_ITEMS_API_URL` and `SOLD_ITEMS_API_KEY`. It uses the same query-fallback strategy as the browse provider and returns sold-price ranges, sample sold items, and recent sold-velocity buckets when available.
654
- - **Terapeak / eBay research provider:** [`src/validation/providers/terapeak.ts`](src/validation/providers/terapeak.ts) now evaluates authenticated eBay Research candidates for both current-market and previous-POB contexts, scores them against title alignment and subtype coverage, preserves per-candidate diagnostics in debug output, and derives sold-day buckets from sold-row timestamps when available.
655
- - **Authenticated research session source:** [`src/validation/providers/ebay-research.ts`](src/validation/providers/ebay-research.ts) now prefers KV-backed Playwright storage state first, then environment-provided storage state / cookie fallbacks, then local storage-state/profile fallbacks for local development only. Parsed ACTIVE and SOLD tab responses are cached, automatically invalidated when the authenticated cookie fingerprint changes, and emit explicit auth-resolution debug fields including `sessionSource`, KV/env/filesystem attempt status, and fallback reasons.
656
- - **Social provider:** [`src/validation/providers/social.ts`](src/validation/providers/social.ts) supports phase-1 Twitter/X recent activity, YouTube average-daily-views proxy data exposed through the `youtubeViews24hMillions` field, and Reddit recent post counts. These signals degrade gracefully on provider/API failure and are used as supportive indicators rather than authoritative demand truth.
657
- - **Chart provider:** [`src/validation/providers/chart.ts`](src/validation/providers/chart.ts) is still a stub and does not currently contribute chart-based metrics.
658
- - **Previous comeback research provider:** [`src/validation/providers/research.ts`](src/validation/providers/research.ts) now performs Perplexity-backed historical research when `PERPLEXITY_API_KEY` is configured. It attempts to resolve the prior comeback, normalize previous first-week sales when support exists, assign a `perplexityHistoricalContextScore`, generate concise `historicalContextNotes`, and emit debug diagnostics covering the research query, citations/snippets, resolved prior release, confidence, and score reasoning.
408
+ Cline auto-discovers OAuth, opens browser login, exchanges auth code, and stores session token.
659
409
 
660
- Recommendation behavior:
661
-
662
- - [`src/validation/recommendation.ts`](src/validation/recommendation.ts) now accepts Terapeak and research inputs alongside browse, sold, social, and chart signals.
663
- - Recommendation generation also consumes the normalized effective context so event runs can carry source-aware monitoring notes and avoid item-only assumptions when no usable item identity exists.
664
- - Automatic tracking now pauses when the validation is still nominally in a watch state but the required source context or a usable derived query is missing.
665
- - The decisioning remains intentionally conservative: Terapeak and research data can improve monitoring notes and confidence context, but the system still avoids aggressive automatic buy-state changes from partial or proxy signals alone.
410
+ **Claude Desktop / Cursor (Bearer token):**
411
+ 1. Open `https://your-server.com/sandbox/oauth/start` → complete eBay login → copy session token
412
+ 2. Configure client:
413
+ ```json
414
+ {
415
+ "mcpServers": {
416
+ "ebay-sandbox": {
417
+ "url": "https://your-server.com/sandbox/mcp",
418
+ "headers": { "Authorization": "Bearer YOUR_SESSION_TOKEN" }
419
+ }
420
+ }
421
+ }
422
+ ```
666
423
 
667
- Known limitations in the current implementation:
424
+ **Make / Zapier / other platforms:**
425
+ 1. Complete OAuth via browser at `/oauth/start`
426
+ 2. Paste session token as API Key / Bearer token in connector settings
427
+ 3. Set MCP URL to `https://your-server.com/sandbox/mcp`
668
428
 
669
- - The sold-data provider depends on external configuration via `SOLD_ITEMS_API_URL` and `SOLD_ITEMS_API_KEY`.
670
- - If those sold-data variables are missing, validation still runs but sold enrichment degrades to an unavailable/error state rather than providing full historical-sales signals.
671
- - The sold-data provider is temporary and intended to be replaced by an internal implementation later.
672
- - Authenticated eBay Research requires a valid session source such as KV-backed Playwright storage state, `EBAY_RESEARCH_STORAGE_STATE_JSON`, `EBAY_RESEARCH_COOKIES_JSON`, a local Playwright storage-state file, or a local browser profile directory; without one, the provider degrades to diagnostic-only output with explicit structured auth-resolution debug.
429
+ ---
673
430
 
674
- #### eBay Research bootstrap and hosted runtime notes
431
+ ## Tool discovery for agents
675
432
 
676
- - Install Chromium for hosted runtimes with [`package.json`](package.json) script `playwright:install` (`pnpm run playwright:install`).
677
- - The Docker deployment path now provisions Chromium during image build in [`Dockerfile`](Dockerfile).
678
- - Canonical production session source of truth is KV-backed Playwright storage-state JSON stored under `ebay_research_storage_state_json` with companion metadata in `ebay_research_storage_state_meta`, including `updatedAt`, `expiresAt`, `ttlSeconds`, `marketplace`, `sessionStore`, and `sessionVersion`.
679
- - Bootstrap a signed-in eBay Research storage state into KV with [`src/scripts/bootstrap-ebay-research-session.ts`](src/scripts/bootstrap-ebay-research-session.ts) via the packaged/runtime-safe [`package.json`](package.json) script `research:bootstrap` (`pnpm run build && pnpm run research:bootstrap`).
680
- - Inspect canonical eBay Research session persistence and fresh-client readback diagnostics with [`src/scripts/inspect-ebay-research-session.ts`](src/scripts/inspect-ebay-research-session.ts) via the packaged/runtime-safe [`package.json`](package.json) script `research:inspect-session` (`pnpm run build && pnpm run research:inspect-session`).
681
- - Verify headless Chromium launchability with [`src/scripts/check-playwright.ts`](src/scripts/check-playwright.ts) via the packaged/runtime-safe [`package.json`](package.json) script `research:check-browser` (`pnpm run build && pnpm run research:check-browser`).
682
- - Runtime precedence is: KV storage state → `EBAY_RESEARCH_STORAGE_STATE_JSON` → `EBAY_RESEARCH_COOKIES_JSON` → local storage-state file → local Playwright profile → explicit auth-missing fallback.
683
- - Every candidate session source is validated against the first-party ACTIVE endpoint before the provider reports `authState = loaded`; failed validation is surfaced through debug fields including `kvStorageStateBytes`, `authValidationAttempted`, and `authValidationSucceeded`.
684
- - Once a validated session is loaded, ACTIVE and SOLD endpoint fetches automatically become the preferred first-party research source while legacy active/sold fallbacks remain intact when auth is missing or invalid.
685
- - Successful bootstrap also schedules signed QStash callbacks for 24 hours before expiry, 6 hours before expiry, and at expiry. Those callbacks target `POST /internal/ebay-research/check-session-expiry`, which verifies QStash signatures, suppresses stale reminders by `sessionVersion`, and sends Telegram alerts to `TELEGRAM_CHAT_ID`.
686
- - Alert scheduling is intentionally skipped when the callback URL resolves to localhost/loopback or when `EBAY_RESEARCH_SESSION_STORE` uses a backend without shared lock support, because those configurations cannot safely deliver or deduplicate hosted reminders.
687
- - Session refresh is manual by design for now: rerun `pnpm run build && pnpm run research:bootstrap` whenever eBay expires the stored session, then redeploy or restart the hosted service if your platform does not hot-reload env/KV-backed state.
688
- - The previous-comeback research provider depends on grounded external research and therefore degrades to low-confidence notes with a zero historical score when `PERPLEXITY_API_KEY` is missing, the response cannot be normalized, or reliable evidence is not found.
689
- - The browse provider still relies on heuristic query selection and fallback matching.
690
- - The YouTube-backed `youtubeViews24hMillions` field is currently an **average daily views proxy**, not a true trailing 24-hour delta.
691
- - Social signals are supportive/proxy data only and should not be presented as decisive automated buy logic.
692
- - eBay-derived metrics are intentionally practical rather than exhaustive, but authenticated ACTIVE research rows now populate watcher-derived metrics whenever watcher counts are present in the first-party response.
433
+ AI agents discover tools through the MCP protocol's `tools/list` call. The eBay MCP server exposes 325+ tools with a predictable naming and description structure optimized for agent searchability.
693
434
 
694
- ### Roadmap note: provider maturation
435
+ ### Tool naming convention
695
436
 
696
- - The current sold-data implementation is explicitly interim. It is isolated behind [`src/validation/providers/ebay-sold.ts`](src/validation/providers/ebay-sold.ts) so we can replace the external-provider-backed implementation with our own internal sales-data system later **without changing downstream validation orchestration or the hosted validation route contract**.
697
- - The Terapeak/eBay research layer is intentionally isolated behind [`src/validation/providers/terapeak.ts`](src/validation/providers/terapeak.ts) so a future authenticated research integration can drop in without changing the route contract or downstream writes.
698
- - The orchestration-side historical research layer is intentionally isolated behind [`src/validation/providers/research.ts`](src/validation/providers/research.ts) so future previous-comeback resolution or external inference providers can be added without rewriting the validation runner.
437
+ All tools follow the pattern `ebay_<action>_<resource>`:
699
438
 
700
- ### Remote client configuration
439
+ | Action | Meaning | Examples |
440
+ |--------|---------|----------|
441
+ | `get` | Read/fetch | `ebay_get_inventory_item`, `ebay_get_offers` |
442
+ | `create` | Create new | `ebay_create_offer`, `ebay_create_fulfillment_policy` |
443
+ | `update` | Modify existing | `ebay_update_offer`, `ebay_update_inventory_item` |
444
+ | `delete` | Remove | `ebay_delete_offer`, `ebay_delete_inventory_item` |
445
+ | `publish` | Activate | `ebay_publish_offer` |
446
+ | `withdraw` | Deactivate | `ebay_withdraw_offer` |
447
+ | `revise` | Modify listing | `ebay_revise_listing` |
448
+ | `bulk_` | Batch operations | `ebay_bulk_create_offer`, `ebay_bulk_update_price_quantity` |
701
449
 
702
- Replace `https://your-server.com` with your actual `PUBLIC_BASE_URL`.
450
+ ### Tool descriptions are self-documenting
703
451
 
704
- #### Cline (automatic OAuth — no manual token needed)
452
+ Each tool description includes:
453
+ - **What it does** — concise action description
454
+ - **OAuth scope required** — the minimum scope needed
455
+ - **Minimum scope URL** — for fine-grained permission setup
705
456
 
706
- Cline supports MCP OAuth 2.1 discovery natively. It fetches the discovery document, registers itself, opens the eBay browser login, exchanges the auth code for a session token, and stores it — all automatically.
457
+ Example:
458
+ ```
459
+ Get a specific inventory item by SKU.
707
460
 
708
- ```json
709
- {
710
- "mcpServers": {
711
- "ebay-sandbox": {
712
- "url": "https://your-server.com/sandbox/mcp"
713
- },
714
- "ebay-production": {
715
- "url": "https://your-server.com/production/mcp"
716
- }
717
- }
718
- }
461
+ Required OAuth Scope: sell.inventory.readonly or sell.inventory
462
+ Minimum Scope: https://api.ebay.com/oauth/api_scope/sell.inventory.readonly
719
463
  ```
720
464
 
721
- What Cline does automatically:
722
- 1. Fetches `/.well-known/oauth-authorization-server` for the scoped path
723
- 2. Registers at `POST /sandbox/register` (or `/production/register`)
724
- 3. Your browser opens `GET /sandbox/authorize`, which redirects to eBay login
725
- 4. After you grant access, eBay redirects to `/oauth/callback`, which issues an auth code
726
- 5. Cline exchanges the code at `POST /sandbox/token` for a session token and stores it
727
- 6. All subsequent `/sandbox/mcp` requests authenticate automatically
465
+ ### Tools organized by category
728
466
 
729
- #### Claude Desktop and Cursor (Bearer token)
467
+ Tools are grouped into 13 category files in `src/tools/definitions/`:
730
468
 
731
- Claude Desktop and most other remote MCP clients require a pre-obtained session token. Complete the browser OAuth flow first:
469
+ | Category file | Tool prefix | Key operations |
470
+ |---------------|-------------|----------------|
471
+ | `inventory.ts` | `ebay_*inventory*`, `ebay_*offer*` | CRUD inventory items, offers, locations, bulk ops |
472
+ | `fulfillment.ts` | `ebay_*fulfillment*`, `ebay_*shipment*` | Shipments, fulfillment orders, shipping labels |
473
+ | `account.ts` | `ebay_*policy*` | Payment, return, fulfillment policies |
474
+ | `marketing.ts` | `ebay_*promotion*`, `ebay_*markdown*` | Promotions, markdown listings |
475
+ | `analytics.ts` | `ebay_*analytics*`, `ebay_*report*` | Analytics, reporting |
476
+ | `communication.ts` | `ebay_*message*`, `ebay_*feedback*` | Messages, feedback, notifications |
477
+ | `metadata.ts` | `ebay_*metadata*`, `ebay_*policies*` | Category policies, listing metadata |
478
+ | `taxonomy.ts` | `ebay_*category*` | Category tree, suggestions, item specifics |
479
+ | `browse.ts` | `ebay_*browse*`, `ebay_*product*` | Browse/search products |
480
+ | `trading.ts` | `ebay_*listing*`, `ebay_*create_listing*` | Trading API (XML/SOAP) operations |
481
+ | `developer.ts` | `ebay_*signing*`, `ebay_*vero*` | Key management, VERO reports |
482
+ | `other.ts` | Mixed | Misc/legacy endpoints |
483
+ | `token-management.ts` | `ebay_*oauth*`, `ebay_*token*` | OAuth helpers, token management |
732
484
 
733
- 1. Open `https://your-server.com/sandbox/oauth/start` (or `/production/oauth/start`) in a browser
734
- 2. Log in with your eBay account
735
- 3. Copy the session token from the confirmation page
485
+ ### How agents find the right tool
736
486
 
737
- Then configure your client:
487
+ 1. **Search by name pattern** — `ebay_get_*` for reads, `ebay_create_*` for writes, `ebay_*offer*` for offers
488
+ 2. **Search by description** — descriptions contain action keywords and resource names
489
+ 3. **Search by OAuth scope** — `sell.inventory` for inventory, `sell.fulfillment` for shipping
490
+ 4. **Use `tools/list`** — agents receive the full tool list with names and descriptions; filter programmatically
738
491
 
739
- ```json
740
- {
741
- "mcpServers": {
742
- "ebay-sandbox": {
743
- "url": "https://your-server.com/sandbox/mcp",
744
- "headers": {
745
- "Authorization": "Bearer YOUR_SESSION_TOKEN"
746
- }
747
- }
748
- }
749
- }
750
- ```
492
+ ### Tool annotations
751
493
 
752
- #### Make / Zapier / TypingMind and similar platforms
494
+ Tools carry MCP annotations that help agents understand behavior:
753
495
 
754
- 1. Open `https://your-server.com/sandbox/oauth/start` in a browser and complete eBay login
755
- 2. Copy the session token from the confirmation page
756
- 3. Paste it as the **API Key / Bearer token** in the platform's MCP connector settings
757
- 4. Set the MCP endpoint URL to `https://your-server.com/sandbox/mcp`
496
+ | Annotation | Meaning |
497
+ |-----------|---------|
498
+ | `readOnlyHint: true` | Safe to call; no side effects |
499
+ | `destructiveHint: true` | Irreversible; use caution |
500
+ | `idempotentHint: true` | Safe to retry |
501
+ | `openWorldHint: true` | May interact with external services |
758
502
 
759
503
  ---
760
504
 
761
505
  ## Available tools
762
506
 
763
- 325+ tools across all eBay Sell API categories:
764
-
765
- - Account Management
766
- - Inventory Management
767
- - Order Fulfillment
768
- - Marketing & Promotions
769
- - Analytics & Reporting
770
- - Communication (messages, feedback, notifications, negotiation)
771
- - Metadata & Taxonomy
772
- - Developer Tools (key management, analytics)
773
- - Auth / Token helper tools
774
-
775
- Full tool source: [`src/tools/definitions/`](src/tools/definitions/)
507
+ 325+ tools across all eBay Sell API categories. Full source: [`src/tools/definitions/`](src/tools/definitions/)
776
508
 
777
509
  ---
778
510
 
779
511
  ## Development
780
512
 
781
- ### Commands reference
513
+ ### Commands
782
514
 
783
515
  | Command | Description |
784
516
  |---------|-------------|
785
- | `pnpm run build` | Compile TypeScript to JavaScript |
786
- | `pnpm start` | Run local STDIO MCP server |
787
- | `pnpm run start:http` | Run hosted HTTP MCP server |
788
- | `pnpm run dev` | Local STDIO server with hot reload |
789
- | `pnpm run dev:http` | Hosted HTTP server with hot reload |
790
- | `pnpm test` | Run test suite |
791
- | `pnpm run setup` | Interactive local setup wizard |
792
- | `pnpm run sync` | Download latest eBay OpenAPI specs and regenerate types |
793
- | `pnpm run diagnose` | Check configuration and connectivity |
794
- | `pnpm run typecheck` | Run TypeScript type checking |
795
- | `pnpm run check` | Typecheck + lint + format check |
796
- | `pnpm run fix` | Auto-fix lint and format issues |
517
+ | `pnpm run build` | Compile TypeScript |
518
+ | `pnpm start` | Run local STDIO server |
519
+ | `pnpm run start:http` | Run hosted HTTP server |
520
+ | `pnpm run dev` | STDIO with hot reload |
521
+ | `pnpm run dev:http` | HTTP with hot reload |
522
+ | `pnpm test` | Run tests |
523
+ | `pnpm run setup` | Interactive setup wizard |
524
+ | `pnpm run sync` | Download eBay OpenAPI specs, regenerate types |
525
+ | `pnpm run diagnose` | Check config and connectivity |
526
+ | `pnpm run typecheck` | TypeScript type checking |
527
+ | `pnpm run check` | Typecheck + lint + format |
528
+ | `pnpm run fix` | Auto-fix lint and format |
797
529
 
798
530
  ### `pnpm run sync`
799
531
 
800
- Downloads the latest eBay OpenAPI specs, regenerates TypeScript types, and reports implemented vs missing endpoints. Run this when you want to pick up new eBay API surface:
532
+ Download latest eBay OpenAPI specs and regenerate types:
801
533
 
802
534
  ```bash
803
- pnpm run sync
804
- pnpm run typecheck
805
- pnpm run build
535
+ pnpm run sync && pnpm run typecheck && pnpm run build
806
536
  ```
807
537
 
808
- Review the diff, commit the generated changes you want to keep, and deploy.
538
+ Review the diff, commit changes you want to keep, and deploy.
809
539
 
810
- ### Local env management
811
-
812
- For local development, standard runtime scripts load `.env` via dotenvx only when a real local [`.env`](.env) file is present. Hosted platforms should provide environment variables directly — the server skips dotenvx for hosted/runtime environments (including Nixpacks-style deployments, which set [`DISABLE_DOTENVX`](nixpacks.toml:26)) and whenever no local [`.env`](.env) file exists.
540
+ ### Env management
813
541
 
814
542
  ```bash
815
543
  pnpm run env:encrypt # encrypt .env for safe sharing
@@ -825,40 +553,74 @@ EBAY_ENABLE_FILE_LOGGING=true # write logs to files
825
553
 
826
554
  ---
827
555
 
556
+ ## Validation architecture
557
+
558
+ The hosted backend includes a validation pipeline for item evaluation. Routes live in [`src/server-http.ts`](src/server-http.ts), logic in [`src/validation/`](src/validation).
559
+
560
+ **Module layout:**
561
+
562
+ | File | Purpose |
563
+ |------|---------|
564
+ | `types.ts` | Request/response contracts |
565
+ | `effective-context.ts` | Source-aware normalization (item vs event runs) |
566
+ | `run-validation.ts` | Orchestration entrypoint |
567
+ | `recommendation.ts` | Buy/track decision logic |
568
+ | `providers/ebay.ts` | Live browse-market snapshot |
569
+ | `providers/ebay-sold.ts` | Sold-data enrichment (temporary external provider) |
570
+ | `providers/terapeak.ts` | Terapeak / eBay Research metrics |
571
+ | `providers/ebay-research.ts` | Authenticated eBay Research fetcher |
572
+ | `providers/social.ts` | Twitter/X, YouTube, Reddit signals |
573
+ | `providers/chart.ts` | Chart-signal stub |
574
+ | `providers/research.ts` | Historical comeback research (Perplexity) |
575
+
576
+ **Flow:** Admin calls `/validation/run` → server resolves runner user → orchestrator queries all providers → merges signals → returns writes + decision + debug metadata.
577
+
578
+ **Merge precedence:** Terapeak > sold provider > browse provider for overlapping fields. Optional providers (social, research) only write when data resolves — never blank existing values.
579
+
580
+ **Known limitations:**
581
+ - Sold-data provider is temporary (external API via `SOLD_ITEMS_API_URL`)
582
+ - Social signals are supportive only — not authoritative buy triggers
583
+ - Chart provider is a stub
584
+ - eBay Research requires valid Playwright session
585
+
586
+ **eBay Research session management:**
587
+ - Bootstrap: `pnpm run research:bootstrap`
588
+ - Inspect: `pnpm run research:inspect-session`
589
+ - Browser check: `pnpm run research:check-browser`
590
+ - Session source precedence: KV → env vars → local files → fallback
591
+ - QStash alerts: 24h before, 6h before, and at expiry → Telegram
592
+
593
+ ---
594
+
828
595
  ## Testing & validation
829
596
 
830
597
  ```bash
831
- # Build and type check
832
- pnpm run build
833
- pnpm run typecheck
598
+ # Build and test
599
+ pnpm run build && pnpm run typecheck && pnpm test
834
600
 
835
- # Run the test suite
836
- pnpm test
837
-
838
- # Check connectivity and token status
601
+ # Connectivity check
839
602
  pnpm run diagnose
840
603
 
841
- # Verify hosted server health
604
+ # Hosted health
842
605
  curl https://your-server.com/health
843
606
 
844
- # Verify a session token
845
- curl -H "Authorization: Bearer <session-token>" https://your-server.com/whoami
607
+ # Session check
608
+ curl -H "Authorization: Bearer <token>" https://your-server.com/whoami
846
609
 
847
- # Verify validation runner health for sandbox
610
+ # Validation runner health
848
611
  curl https://your-server.com/sandbox/validation/health \
849
612
  -H "X-Admin-API-Key: YOUR_ADMIN_API_KEY"
850
613
 
851
- # Run a hosted validation job
852
- curl -X POST https://your-server.com/sandbox/validation/run \
853
- -H "Content-Type: application/json" \
854
- -H "X-Admin-API-Key: YOUR_ADMIN_API_KEY" \
855
- -d '{"validationId":"example-123","runType":"manual","cadence":"Daily","timestamp":"2026-03-31T00:00:00.000Z","item":{"recordId":"1","name":"Example Item","variation":[],"itemType":[],"releaseType":[],"releaseDate":null,"releasePeriod":[],"availability":[],"wholesalePrice":null,"supplierNames":[],"canonicalArtists":[],"relatedAlbums":[]},"validation":{"validationType":"default","buyDecision":"Hold","automationStatus":"Manual","autoCheckEnabled":false,"dDay":null,"artistTier":"unknown","initialBudget":null,"reserveBudget":null,"currentMetrics":{"avgWatchersPerListing":null,"preOrderListingsCount":null,"twitterTrending":false,"youtubeViews24hMillions":null,"redditPostsCount7d":null,"marketPriceUsd":null,"avgShippingCostUsd":null,"competitionLevel":null,"marketPriceTrend":"Stable","day1Sold":null,"day2Sold":null,"day3Sold":null,"day4Sold":null,"day5Sold":null,"daysTracked":null}}}'
614
+ # Privileged MCP admin-key bypass check
615
+ curl https://your-server.com/sandbox/mcp \
616
+ -H "Authorization: Bearer YOUR_ADMIN_API_KEY" \
617
+ -H "Accept: application/json, text/event-stream"
856
618
 
857
- # Test MCP endpoint returns auth challenge when no token is provided
619
+ # MCP auth challenge test
858
620
  curl -X POST https://your-server.com/sandbox/mcp \
859
621
  -H "Content-Type: application/json" \
860
622
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
861
- # → should return 401 with authorization_url
623
+ # → 401 with authorization_url
862
624
  ```
863
625
 
864
626
  ---
@@ -867,59 +629,37 @@ curl -X POST https://your-server.com/sandbox/mcp \
867
629
 
868
630
  ### Hosted MCP returns 406
869
631
 
870
- Include the correct `Accept` header in your MCP client:
871
- ```
872
- Accept: application/json, text/event-stream
873
- ```
632
+ Include `Accept: application/json, text/event-stream` header.
874
633
 
875
634
  ### OAuth callback: "Invalid or expired OAuth state"
876
635
 
877
- OAuth state records expire in 15 minutes. If you see this error, restart the browser OAuth flow.
878
-
879
- ### Token verification fails on existing refresh token
636
+ OAuth state expires in 15 minutes. Restart the browser flow.
880
637
 
881
- Refresh tokens expire after ~18 months or can be revoked by eBay (password changes, etc.). Run the setup wizard again to obtain a new one:
882
- ```bash
883
- pnpm run setup
884
- ```
638
+ ### Token verification fails on refresh token
885
639
 
886
- In hosted mode, start a new browser OAuth flow at `/sandbox/oauth/start` or `/production/oauth/start`.
640
+ Refresh tokens expire after ~18 months or can be revoked. Run `pnpm run setup` or start new browser OAuth at `/oauth/start`.
887
641
 
888
- ### Session token no longer works in hosted mode
642
+ ### Session token no longer works
889
643
 
890
- Check whether the session was revoked or expired:
891
644
  ```bash
645
+ # Check status:
892
646
  curl -H "Authorization: Bearer <token>" https://your-server.com/whoami
893
- ```
894
647
 
895
- Revoke exposed session tokens via the admin endpoint:
896
- ```bash
648
+ # Revoke if exposed:
897
649
  curl -X POST https://your-server.com/admin/session/<token>/revoke \
898
650
  -H "X-Admin-API-Key: YOUR_ADMIN_API_KEY"
899
651
  ```
900
652
 
901
653
  ### Validation health is degraded
902
654
 
903
- Start with the environment-scoped health endpoint:
904
-
905
655
  ```bash
906
656
  curl https://your-server.com/sandbox/validation/health \
907
657
  -H "X-Admin-API-Key: YOUR_ADMIN_API_KEY"
908
658
  ```
909
659
 
910
- Common causes:
911
-
912
- - `VALIDATION_RUNNER_USER_ID` or the env-specific override is missing
913
- - the validation runner user has no stored refresh-token-backed credentials in the hosted token store
914
- - the refresh token is expired or revoked upstream
915
- - `SOLD_ITEMS_API_URL` or `SOLD_ITEMS_API_KEY` is missing, causing sold enrichment to degrade
916
- - one or more social-provider credentials are absent, which causes the related supportive signal to degrade gracefully instead of failing the entire run
660
+ Check: `VALIDATION_RUNNER_USER_ID` set, runner has stored tokens, tokens not expired, `SOLD_ITEMS_API_URL` configured.
917
661
 
918
- ### eBay Research debug shows `authState = missing` or `sessionStrategy = none`
919
-
920
- This means the first-party research provider could not load a validated authenticated session and validation is intentionally falling back to browse and/or the temporary sold provider.
921
-
922
- Run this checklist:
662
+ ### eBay Research shows `authState = missing`
923
663
 
924
664
  ```bash
925
665
  pnpm run playwright:install
@@ -928,28 +668,23 @@ pnpm run research:check-browser
928
668
  pnpm run research:bootstrap
929
669
  ```
930
670
 
931
- Expected post-bootstrap debug characteristics from [`src/validation/providers/ebay-research.ts`](src/validation/providers/ebay-research.ts):
671
+ Expected after bootstrap: `authState = loaded`, `sessionStrategy = storage_state`, `sessionSource = kv`, `authValidationSucceeded = true`.
932
672
 
933
- - `authState = loaded`
934
- - `sessionStrategy = storage_state`
935
- - `sessionSource = kv`
936
- - `kvLoadAttempted = true`
937
- - `kvLoadSucceeded = true`
938
- - `authValidationAttempted = true`
939
- - `authValidationSucceeded = true`
673
+ ### Stale MCP sessions after deployment
940
674
 
941
- If the provider still reports `missing`, verify that your hosted deployment can reach the configured KV backend, that Chromium is available in the runtime image, and that the stored eBay session has not expired. Refresh the session by rerunning `pnpm run research:bootstrap`.
675
+ After deploying new code, agents' MCP sessions may still point to old container code. **Agents must restart their MCP session** (`/reset` or reopen chat) to negotiate a fresh connection.
942
676
 
943
- If `authDebug.tokenEndpoint` or the captured upstream response looks wrong, verify the environment-specific OAuth configuration and token-base resolution.
677
+ ---
944
678
 
945
- ### Security checklist
679
+ ## Security checklist
946
680
 
947
- - Do not commit `.env` or session tokens to version control
948
- - Protect `/oauth/start` and `/admin/*` with `OAUTH_START_KEY` and `ADMIN_API_KEY`
949
- - Keep `/oauth/callback` publicly reachable (eBay redirects to it after login)
950
- - Keep `/health` reachable if Render uses it for health checks
951
- - For production-grade isolation, optionally place `/`, `/oauth/start`, and `/admin/*` behind Cloudflare Access
952
- - Rotate exposed eBay client secrets and update your secret file
681
+ - Do not commit `.env` or session tokens
682
+ - Protect `/oauth/start` with `OAUTH_START_KEY`, `/admin/*` and `/validation/*` with `ADMIN_API_KEY`
683
+ - Keep `ADMIN_API_KEY` long, random, server-side only, and separate from user session tokens; it also works as a privileged MCP Bearer-token bypass
684
+ - Keep `/oauth/callback` publicly reachable (eBay redirects here)
685
+ - Keep `/health` reachable for deployment health checks
686
+ - Rotate exposed eBay credentials and update secret file
687
+ - For production isolation, consider Cloudflare Access on `/`, `/oauth/start`, `/admin/*`
953
688
 
954
689
  ---
955
690