ebay-mcp-remote-edition 2.0.12 → 2.0.14

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.
@@ -6,25 +6,28 @@ import axios from 'axios';
6
6
  * All data is lost on process restart.
7
7
  */
8
8
  export class InMemoryKVStore {
9
+ backendName = 'InMemoryKVStore';
9
10
  store = new Map();
10
- async get(key) {
11
+ get(key) {
11
12
  const entry = this.store.get(key);
12
13
  if (!entry)
13
- return null;
14
+ return Promise.resolve(null);
14
15
  if (entry.expiresAt !== undefined && Date.now() > entry.expiresAt) {
15
16
  this.store.delete(key);
16
- return null;
17
+ return Promise.resolve(null);
17
18
  }
18
- return entry.value;
19
+ return Promise.resolve(entry.value);
19
20
  }
20
- async put(key, value, expirationTtl) {
21
+ put(key, value, expirationTtl) {
21
22
  this.store.set(key, {
22
23
  value,
23
24
  expiresAt: expirationTtl !== undefined ? Date.now() + expirationTtl * 1_000 : undefined,
24
25
  });
26
+ return Promise.resolve();
25
27
  }
26
- async delete(key) {
28
+ delete(key) {
27
29
  this.store.delete(key);
30
+ return Promise.resolve();
28
31
  }
29
32
  }
30
33
  /**
@@ -32,6 +35,7 @@ export class InMemoryKVStore {
32
35
  * Used when EBAY_TOKEN_STORE_BACKEND=cloudflare-kv (the default for hosted deployments).
33
36
  */
34
37
  export class CloudflareKVStore {
38
+ backendName = 'CloudflareKVStore';
35
39
  client;
36
40
  accountId;
37
41
  namespaceId;
@@ -100,10 +104,25 @@ export class CloudflareKVStore {
100
104
  this.cache.clear();
101
105
  }
102
106
  }
103
- // ── Factory ───────────────────────────────────────────────────────────────────
107
+ // ── Singleton factory ─────────────────────────────────────────────────────────
108
+ /**
109
+ * Process-level singleton KV store.
110
+ *
111
+ * All callers within the same Node.js process share one backend instance.
112
+ * This is the root cause guard: it makes it impossible for two different
113
+ * MultiUserAuthStore instances to accidentally resolve to different backends
114
+ * (e.g. one reading EBAY_TOKEN_STORE_BACKEND before dotenv runs and one after).
115
+ *
116
+ * The singleton is lazy: it is created on the first call to `createKVStore()`
117
+ * and reused for every subsequent call.
118
+ *
119
+ * For automated tests only, call `resetKVStoreSingleton()` between test cases
120
+ * that need to swap the backend.
121
+ */
122
+ let _kvStoreSingleton = null;
104
123
  /**
105
- * Returns the appropriate KV store backend based on the EBAY_TOKEN_STORE_BACKEND
106
- * environment variable:
124
+ * Returns (or lazily creates) the process-wide KV store singleton based on the
125
+ * EBAY_TOKEN_STORE_BACKEND environment variable:
107
126
  *
108
127
  * memory → InMemoryKVStore (no external dependencies, data lost on restart)
109
128
  * cloudflare-kv → CloudflareKVStore (default; requires CLOUDFLARE_* env vars)
@@ -112,14 +131,39 @@ export class CloudflareKVStore {
112
131
  * existing hosted deployments continue to work without any config change.
113
132
  */
114
133
  export function createKVStore() {
115
- const backend = (process.env.EBAY_TOKEN_STORE_BACKEND ?? 'cloudflare-kv').toLowerCase().trim();
134
+ if (_kvStoreSingleton) {
135
+ return _kvStoreSingleton;
136
+ }
137
+ const rawEnv = process.env.EBAY_TOKEN_STORE_BACKEND;
138
+ const backend = (rawEnv ?? 'cloudflare-kv').toLowerCase().trim();
139
+ // Log exactly once, at the point the singleton is first constructed.
116
140
  switch (backend) {
117
141
  case 'memory':
118
- case 'in-memory':
119
- return new InMemoryKVStore();
142
+ case 'in-memory': {
143
+ console.log(`[kv-store] EBAY_TOKEN_STORE_BACKEND="${rawEnv ?? ''}" → using InMemoryKVStore (no external KV calls)`);
144
+ _kvStoreSingleton = new InMemoryKVStore();
145
+ break;
146
+ }
120
147
  case 'cloudflare-kv':
121
148
  case 'cloudflare':
122
- default:
123
- return new CloudflareKVStore();
149
+ default: {
150
+ console.log(`[kv-store] EBAY_TOKEN_STORE_BACKEND="${rawEnv ?? '(unset)'}" → using CloudflareKVStore (accountId="${process.env.CLOUDFLARE_ACCOUNT_ID ?? '(unset)'}") — set EBAY_TOKEN_STORE_BACKEND=memory to disable`);
151
+ _kvStoreSingleton = new CloudflareKVStore();
152
+ break;
153
+ }
124
154
  }
155
+ return _kvStoreSingleton;
156
+ }
157
+ /**
158
+ * **TEST USE ONLY** – resets (or replaces) the process-wide KV store singleton.
159
+ *
160
+ * Call this in `afterEach`/`beforeEach` when a test needs to control which
161
+ * backend is in use without mutating `process.env.EBAY_TOKEN_STORE_BACKEND`.
162
+ *
163
+ * @param replacement Optional KV store to install as the new singleton.
164
+ * Pass `null` (default) to clear the singleton so that the
165
+ * next `createKVStore()` call rebuilds it from env.
166
+ */
167
+ export function resetKVStoreSingleton(replacement = null) {
168
+ _kvStoreSingleton = replacement;
125
169
  }
@@ -1,7 +1,19 @@
1
1
  import { randomUUID } from 'crypto';
2
2
  import { createKVStore } from '../auth/kv-store.js';
3
3
  export class MultiUserAuthStore {
4
- kv = createKVStore();
4
+ kv;
5
+ /**
6
+ * @param kv Optional KV store override. If omitted the process-wide singleton
7
+ * returned by `createKVStore()` is used (the normal production path).
8
+ * Pass an explicit store in unit tests to avoid touching env vars.
9
+ */
10
+ constructor(kv) {
11
+ this.kv = kv ?? createKVStore();
12
+ }
13
+ /** Returns the backend name of the underlying KV store (e.g. "InMemoryKVStore"). */
14
+ get backendName() {
15
+ return this.kv.backendName;
16
+ }
5
17
  /**
6
18
  * In-memory map of sessionToken → timestamp of last KV write for touchSession.
7
19
  * Prevents a KV PUT on every single authenticated request — we only persist
@@ -21,6 +21,10 @@ const CONFIG = {
21
21
  oauthStartKey: process.env.OAUTH_START_KEY ?? '',
22
22
  };
23
23
  const authStore = new MultiUserAuthStore();
24
+ // Emit the concrete backend class so logs can never claim "memory" while
25
+ // actually using Cloudflare (or vice-versa). This fires once on startup,
26
+ // after dotenv has already been loaded by the config/environment import.
27
+ console.log(`[auth-store] Active KV backend: ${authStore.backendName}`);
24
28
  function getServerBaseUrl() {
25
29
  return CONFIG.publicBaseUrl || `http://localhost:${CONFIG.port}`;
26
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ebay-mcp-remote-edition",
3
- "version": "2.0.12",
3
+ "version": "2.0.14",
4
4
  "description": "Remote + Local MCP server for eBay APIs - provides access to eBay developer functionality through MCP (Model Context Protocol)",
5
5
  "type": "module",
6
6
  "main": "build/index.js",