@kya-os/mcp-i-cloudflare 1.7.74 → 1.7.76

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/dist/adapter.d.ts CHANGED
@@ -13,6 +13,8 @@ import type { MCPIRuntimeBase } from "@kya-os/mcp-i-core";
13
13
  import { type ClientMessagesConfig } from "@kya-os/mcp-i-core";
14
14
  import { KVProofArchive } from "./storage/kv-proof-archive";
15
15
  import type { KVNamespace } from "@cloudflare/workers-types";
16
+ import type { DurableObjectNamespace } from "@cloudflare/workers-types";
17
+ import { type DORoutingStrategy } from "./utils/do-routing";
16
18
  export interface ToolDefinition {
17
19
  name: string;
18
20
  description: string;
@@ -95,6 +97,9 @@ declare class CloudflareMCPServer {
95
97
  private proofArchive?;
96
98
  private requestOrigin?;
97
99
  private delegationStorage?;
100
+ private doNamespace?;
101
+ private doRoutingStrategy;
102
+ private doShardCount;
98
103
  private initializeContexts;
99
104
  private sessionClientIds;
100
105
  private environment;
@@ -102,12 +107,31 @@ declare class CloudflareMCPServer {
102
107
  constructor(runtime: MCPIRuntimeBase, serverInfo: {
103
108
  name: string;
104
109
  version: string;
105
- }, tools?: ToolDefinition[], proofArchive?: KVProofArchive, delegationStorage?: KVNamespace, environment?: string, clientMessagesConfig?: ClientMessagesConfig);
110
+ }, tools?: ToolDefinition[], proofArchive?: KVProofArchive, delegationStorage?: KVNamespace, environment?: string, clientMessagesConfig?: ClientMessagesConfig, doNamespace?: DurableObjectNamespace, doRoutingStrategy?: DORoutingStrategy, doShardCount?: number);
106
111
  /**
107
112
  * Set request origin for consent URL building
108
113
  * Called from fetch handler to auto-detect server URL
109
114
  */
110
115
  setRequestOrigin(origin: string): void;
116
+ /**
117
+ * Calculate DO instance ID based on routing strategy.
118
+ * Delegates to shared utility so adapter, agent, and consent.service
119
+ * are guaranteed to route identically.
120
+ */
121
+ private calculateDOInstanceId;
122
+ /**
123
+ * Fetch delegation from Durable Object (strongly consistent storage)
124
+ *
125
+ * This provides parity with the Agent pattern by using the same DO-based
126
+ * storage for delegations. The consent service stores to DO first, and
127
+ * now the adapter can read from DO first as well.
128
+ *
129
+ * @param sessionId - Session ID to lookup
130
+ * @param userDid - Optional user DID for user+agent scoped lookup
131
+ * @param agentDid - Agent DID for the lookup
132
+ * @returns Delegation token if found, undefined otherwise
133
+ */
134
+ private getDelegationFromDO;
111
135
  /**
112
136
  * Handle JSON-RPC request
113
137
  */
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
package/dist/adapter.js CHANGED
@@ -15,6 +15,7 @@ import { KVProofArchive } from "./storage/kv-proof-archive";
15
15
  import { STORAGE_KEYS } from "./constants/storage-keys";
16
16
  import { DEFAULT_SESSION_CACHE_TTL, DEFAULT_SESSION_TTL } from "./constants";
17
17
  import { normalizeCloudflareEnv } from "./helpers/env-mapper";
18
+ import { calculateDOInstanceId, parseDORoutingStrategy, parseDOShardCount, } from "./utils/do-routing";
18
19
  import { AdminService } from "./services/admin.service";
19
20
  const INITIALIZE_CONTEXT_TTL_MS = 60_000;
20
21
  /**
@@ -27,16 +28,22 @@ class CloudflareMCPServer {
27
28
  proofArchive;
28
29
  requestOrigin; // Store request origin for consent URL building
29
30
  delegationStorage; // KV storage for delegation tokens
31
+ doNamespace; // DO namespace for strongly consistent storage
32
+ doRoutingStrategy;
33
+ doShardCount;
30
34
  initializeContexts;
31
35
  sessionClientIds; // Persists client IDs for error formatting
32
36
  environment; // Runtime environment (development, production)
33
37
  clientMessagesConfig; // Client-specific delegation messages
34
- constructor(runtime, serverInfo, tools = [], proofArchive, delegationStorage, environment = "production", clientMessagesConfig) {
38
+ constructor(runtime, serverInfo, tools = [], proofArchive, delegationStorage, environment = "production", clientMessagesConfig, doNamespace, doRoutingStrategy = "singleton", doShardCount = 10) {
35
39
  this.runtime = runtime;
36
40
  this.serverInfo = serverInfo;
37
41
  this.tools = new Map(tools.map((t) => [t.name, t]));
38
42
  this.proofArchive = proofArchive;
39
43
  this.delegationStorage = delegationStorage;
44
+ this.doNamespace = doNamespace;
45
+ this.doRoutingStrategy = doRoutingStrategy;
46
+ this.doShardCount = doShardCount;
40
47
  this.initializeContexts = new Map();
41
48
  this.sessionClientIds = new Map(); // Persists client IDs beyond context consumption
42
49
  this.environment = environment;
@@ -49,6 +56,67 @@ class CloudflareMCPServer {
49
56
  setRequestOrigin(origin) {
50
57
  this.requestOrigin = origin;
51
58
  }
59
+ /**
60
+ * Calculate DO instance ID based on routing strategy.
61
+ * Delegates to shared utility so adapter, agent, and consent.service
62
+ * are guaranteed to route identically.
63
+ */
64
+ calculateDOInstanceId(sessionId) {
65
+ return calculateDOInstanceId(sessionId, this.doRoutingStrategy, this.doShardCount);
66
+ }
67
+ /**
68
+ * Fetch delegation from Durable Object (strongly consistent storage)
69
+ *
70
+ * This provides parity with the Agent pattern by using the same DO-based
71
+ * storage for delegations. The consent service stores to DO first, and
72
+ * now the adapter can read from DO first as well.
73
+ *
74
+ * @param sessionId - Session ID to lookup
75
+ * @param userDid - Optional user DID for user+agent scoped lookup
76
+ * @param agentDid - Agent DID for the lookup
77
+ * @returns Delegation token if found, undefined otherwise
78
+ */
79
+ async getDelegationFromDO(sessionId, userDid, agentDid) {
80
+ if (!this.doNamespace) {
81
+ return undefined;
82
+ }
83
+ try {
84
+ // Use the same routing strategy as consent service
85
+ const doInstanceId = this.calculateDOInstanceId(sessionId);
86
+ const doId = this.doNamespace.idFromName(doInstanceId);
87
+ const doStub = this.doNamespace.get(doId);
88
+ // Build request URL - use the origin or a placeholder
89
+ const origin = this.requestOrigin || "https://localhost";
90
+ const url = new URL("/_internal/delegation/get", origin);
91
+ const response = await doStub.fetch(url.toString(), {
92
+ method: "POST",
93
+ headers: { "Content-Type": "application/json" },
94
+ body: JSON.stringify({
95
+ sessionId,
96
+ userDid,
97
+ agentDid,
98
+ }),
99
+ });
100
+ if (!response.ok) {
101
+ console.error("[CloudflareMCPServer] DO delegation lookup failed:", { status: response.status, sessionId: sessionId.slice(0, 20) + "..." });
102
+ return undefined;
103
+ }
104
+ const data = (await response.json());
105
+ if (data.success && data.data?.delegationToken) {
106
+ console.error("[CloudflareMCPServer] ✅ Delegation token retrieved from DO:", {
107
+ sessionId: sessionId.slice(0, 20) + "...",
108
+ userDid: userDid ? userDid.slice(0, 20) + "..." : "none",
109
+ tokenLength: data.data.delegationToken.length,
110
+ });
111
+ return data.data.delegationToken;
112
+ }
113
+ return undefined;
114
+ }
115
+ catch (error) {
116
+ console.error("[CloudflareMCPServer] DO delegation lookup error:", error);
117
+ return undefined;
118
+ }
119
+ }
52
120
  /**
53
121
  * Handle JSON-RPC request
54
122
  */
@@ -104,7 +172,7 @@ class CloudflareMCPServer {
104
172
  session = await this.runtime.getCurrentSession();
105
173
  console.error("[CloudflareMCPServer] Session check:", {
106
174
  hasSession: !!session,
107
- sessionId: session?.id?.slice(0, 20) + "..." || "none",
175
+ sessionId: session?.id ? session.id.slice(0, 20) + "..." : "none",
108
176
  hasDelegationToken: !!session?.delegationToken,
109
177
  hasDelegationStorage: !!this.delegationStorage,
110
178
  method: "tools/call",
@@ -169,7 +237,7 @@ class CloudflareMCPServer {
169
237
  }
170
238
  // Check KV storage for stored delegation token if not provided in params
171
239
  let delegationToken = params.delegationToken;
172
- if (!delegationToken && this.delegationStorage) {
240
+ if (!delegationToken && (this.delegationStorage || this.doNamespace)) {
173
241
  try {
174
242
  // Get userDID from params first
175
243
  let userDid = params.clientDid || params.userDid;
@@ -192,7 +260,7 @@ class CloudflareMCPServer {
192
260
  }
193
261
  }
194
262
  // ✅ PRIORITY 2: If userDid not in params or OAuth mapping, try to retrieve from session cache
195
- if (!userDid && sessionId) {
263
+ if (!userDid && sessionId && this.delegationStorage) {
196
264
  try {
197
265
  const sessionKey = STORAGE_KEYS.session(sessionId);
198
266
  const sessionData = (await this.delegationStorage.get(sessionKey, "json"));
@@ -209,15 +277,26 @@ class CloudflareMCPServer {
209
277
  // Non-fatal - continue without userDid
210
278
  }
211
279
  }
212
- console.error("[CloudflareMCPServer] 🔍 Starting delegation token lookup from KV:", {
280
+ console.error("[CloudflareMCPServer] 🔍 Starting delegation token lookup:", {
213
281
  agentDid: agentDid.slice(0, 20) + "...",
214
- userDid: userDid?.slice(0, 20) + "..." || "none",
215
- sessionId: sessionId?.slice(0, 20) + "..." || "none",
282
+ userDid: userDid ? userDid.slice(0, 20) + "..." : "none",
283
+ sessionId: sessionId ? sessionId.slice(0, 20) + "..." : "none",
216
284
  hasDelegationStorage: !!this.delegationStorage,
285
+ hasDONamespace: !!this.doNamespace,
217
286
  });
287
+ // ✅ PRIORITY 0: Try Durable Object (strongly consistent, parity with Agent pattern)
288
+ // This is the preferred approach for consistent delegation storage across all deployment types
289
+ if (this.doNamespace && sessionId) {
290
+ const doToken = await this.getDelegationFromDO(sessionId, userDid, agentDid);
291
+ if (doToken) {
292
+ delegationToken = doToken;
293
+ console.error("[CloudflareMCPServer] ✅ PRIORITY 0: Delegation token from DO (strongly consistent)");
294
+ }
295
+ }
218
296
  // PRIORITY 1: Try user+agent scoped token (user-specific, most secure)
219
297
  // This is the preferred approach for multi-user scenarios with proper user isolation
220
- if (userDid) {
298
+ // Skip if we already have a token from DO (PRIORITY 0)
299
+ if (!delegationToken && userDid && this.delegationStorage) {
221
300
  const userAgentKey = STORAGE_KEYS.delegation(userDid, agentDid);
222
301
  console.error("[CloudflareMCPServer] 🔍 PRIORITY 1: Checking user+agent scoped key:", {
223
302
  key: userAgentKey,
@@ -262,7 +341,7 @@ class CloudflareMCPServer {
262
341
  // PRIORITY 2: Try session-scoped token (if session ID was persisted)
263
342
  // Note: Sessions are ephemeral in Cloudflare Workers, so this is mainly
264
343
  // useful for caching tokens within a single request chain
265
- if (!delegationToken && sessionId) {
344
+ if (!delegationToken && sessionId && this.delegationStorage) {
266
345
  const sessionKey = STORAGE_KEYS.session(sessionId);
267
346
  console.error("[CloudflareMCPServer] 🔍 PRIORITY 2: Checking session-scoped key:", {
268
347
  key: sessionKey,
@@ -301,8 +380,8 @@ class CloudflareMCPServer {
301
380
  }
302
381
  console.error("[CloudflareMCPServer] ⚠️ No delegation token found - user must complete consent flow:", {
303
382
  agentDid: agentDid.slice(0, 20) + "...",
304
- userDid: userDid?.slice(0, 20) + "..." || "none (consent-only)",
305
- sessionId: sessionId?.slice(0, 20) + "..." || "none",
383
+ userDid: userDid ? userDid.slice(0, 20) + "..." : "none (consent-only)",
384
+ sessionId: sessionId ? sessionId.slice(0, 20) + "..." : "none",
306
385
  checkedKeys,
307
386
  hint: "Delegation tokens are session-scoped or user+agent scoped. Complete consent flow for this session.",
308
387
  });
@@ -347,10 +426,10 @@ class CloudflareMCPServer {
347
426
  hasSession: !!session,
348
427
  hasDelegationToken: !!session?.delegationToken,
349
428
  hasDelegationStorage: !!this.delegationStorage,
350
- sessionId: session?.id?.slice(0, 20) + "...",
429
+ sessionId: session?.id ? session.id.slice(0, 20) + "..." : "none",
351
430
  sessionKeys: Object.keys(session || {}),
352
431
  });
353
- if (!session.delegationToken && this.delegationStorage) {
432
+ if (!session.delegationToken && (this.delegationStorage || this.doNamespace)) {
354
433
  console.error("[CloudflareMCPServer] ⚠️ Session has no delegationToken, attempting KV lookup...");
355
434
  try {
356
435
  const agentDid = (await this.runtime.getIdentity()).did;
@@ -381,16 +460,27 @@ class CloudflareMCPServer {
381
460
  }
382
461
  }
383
462
  console.error("[CloudflareMCPServer] 🔍 Looking up delegation token for existing session:", {
384
- sessionId: sessionId?.slice(0, 20) + "...",
385
- userDid: userDid?.slice(0, 20) + "...",
386
- agentDid: agentDid?.slice(0, 20) + "...",
463
+ sessionId: sessionId ? sessionId.slice(0, 20) + "..." : "none",
464
+ userDid: userDid ? userDid.slice(0, 20) + "..." : "none",
465
+ agentDid: agentDid ? agentDid.slice(0, 20) + "..." : "none",
387
466
  });
467
+ let delegationToken = undefined;
468
+ // PRIORITY 0: Try Durable Object (strongly consistent, parity with Agent pattern)
469
+ if (this.doNamespace && sessionId) {
470
+ const doToken = await this.getDelegationFromDO(sessionId, userDid, agentDid);
471
+ if (doToken) {
472
+ delegationToken = doToken;
473
+ console.error("[CloudflareMCPServer] ✅ PRIORITY 0: Delegation token from DO (existing session)");
474
+ }
475
+ }
388
476
  // PRIORITY 1: Try user+agent scoped token (user-specific, most secure)
389
477
  // This is the preferred approach for multi-user scenarios with proper user isolation
390
- let delegationToken = undefined;
391
478
  // Track which source the token was found in for accurate logging
392
479
  let tokenSource;
393
- if (userDid) {
480
+ if (delegationToken) {
481
+ tokenSource = "do";
482
+ }
483
+ else if (userDid && this.delegationStorage) {
394
484
  const userAgentKey = STORAGE_KEYS.delegation(userDid, agentDid);
395
485
  console.error("[CloudflareMCPServer] 🔍 PRIORITY 1: Checking user+agent scoped key (existing session):", {
396
486
  key: userAgentKey,
@@ -409,7 +499,7 @@ class CloudflareMCPServer {
409
499
  }
410
500
  }
411
501
  // PRIORITY 2: Try session-scoped token (if session ID was persisted)
412
- if (!delegationToken && sessionId) {
502
+ if (!delegationToken && sessionId && this.delegationStorage) {
413
503
  const sessionKey = STORAGE_KEYS.session(sessionId);
414
504
  console.error("[CloudflareMCPServer] 🔍 PRIORITY 2: Checking session-scoped key (existing session):", {
415
505
  key: sessionKey,
@@ -436,7 +526,7 @@ class CloudflareMCPServer {
436
526
  delegationToken,
437
527
  };
438
528
  console.error("[CloudflareMCPServer] ✅ Delegation token set on existing session:", {
439
- sessionId: sessionId?.slice(0, 20) + "..." || "none",
529
+ sessionId: sessionId ? sessionId.slice(0, 20) + "..." : "none",
440
530
  tokenLength: delegationToken.length,
441
531
  sessionHasToken: !!session.delegationToken,
442
532
  source: tokenSource,
@@ -453,7 +543,7 @@ class CloudflareMCPServer {
453
543
  checkedKeys.push(STORAGE_KEYS.session(sessionId));
454
544
  }
455
545
  console.error("[CloudflareMCPServer] ⚠️ No delegation token found - user must complete consent flow:", {
456
- sessionId: sessionId?.slice(0, 20) + "..." || "none",
546
+ sessionId: sessionId ? sessionId.slice(0, 20) + "..." : "none",
457
547
  checkedKeys,
458
548
  hint: "Complete consent flow for this session to get a delegation token.",
459
549
  });
@@ -497,49 +587,52 @@ class CloudflareMCPServer {
497
587
  session.toolParams = params.arguments || {};
498
588
  session.scopeId = scopeId; // ✅ ADDED: Pass scopeId for tool auto-discovery
499
589
  // ✅ CRITICAL: Verify delegation token is set on session before processToolCall
500
- // Also do a final KV lookup if token is still missing (defensive check)
501
- // Use same 3-priority lookup: user+agent scoped, then session-scoped, then agent-scoped (last resort)
502
- if (!session?.delegationToken && this.delegationStorage) {
590
+ // Final defensive lookup: DO first, then KV fallback
591
+ if (!session?.delegationToken && (this.doNamespace || this.delegationStorage)) {
503
592
  try {
504
593
  const agentDid = (await this.runtime.getIdentity()).did;
505
594
  const userDid = session.userDid || params.clientDid || params.userDid;
506
595
  let finalToken = undefined;
596
+ // PRIORITY 0: Durable Object (strongly consistent)
597
+ if (this.doNamespace && session.id) {
598
+ const doToken = await this.getDelegationFromDO(session.id, userDid, agentDid);
599
+ if (doToken) {
600
+ finalToken = doToken;
601
+ }
602
+ }
507
603
  // PRIORITY 1: User+agent scoped token
508
- if (userDid) {
604
+ if (!finalToken && userDid && this.delegationStorage) {
509
605
  const userAgentKey = STORAGE_KEYS.delegation(userDid, agentDid);
510
606
  const token = await this.delegationStorage.get(userAgentKey, "text");
511
607
  finalToken = token || undefined; // Convert null to undefined
512
608
  }
513
609
  // PRIORITY 2: Session-scoped token
514
- if (!finalToken && session.id) {
610
+ if (!finalToken && session.id && this.delegationStorage) {
515
611
  const sessionKey = STORAGE_KEYS.session(session.id);
516
612
  const sessionData = (await this.delegationStorage.get(sessionKey, "json"));
517
613
  finalToken = sessionData?.delegationToken;
518
614
  }
519
- // ❌ REMOVED: Legacy agent-scoped fallback (PRIORITY 3)
520
- // This was a security vulnerability that allowed cross-user delegation sharing.
521
615
  if (finalToken) {
522
616
  session.delegationToken = finalToken;
523
- console.error("[CloudflareMCPServer] ✅ Final KV lookup succeeded, token set on session:", {
617
+ console.error("[CloudflareMCPServer] ✅ Final lookup succeeded, token set on session:", {
524
618
  toolName,
525
619
  agentDid: agentDid.slice(0, 20) + "...",
526
- userDid: userDid?.slice(0, 20) + "..." || "none (consent-only)",
620
+ userDid: userDid ? userDid.slice(0, 20) + "..." : "none",
527
621
  tokenLength: finalToken.length,
528
- source: userDid ? "user+agent-scoped" : "session-scoped",
529
622
  });
530
623
  }
531
624
  }
532
625
  catch (error) {
533
- console.error("[CloudflareMCPServer] Final KV lookup failed:", error);
626
+ console.error("[CloudflareMCPServer] Final delegation lookup failed:", error);
534
627
  }
535
628
  }
536
629
  console.error("[CloudflareMCPServer] ✅ About to call processToolCall with session:", {
537
630
  toolName,
538
- sessionId: session?.id?.slice(0, 20) + "..." || "none",
631
+ sessionId: session?.id ? session.id.slice(0, 20) + "..." : "none",
539
632
  hasDelegationToken: !!session?.delegationToken,
540
633
  delegationTokenLength: session?.delegationToken?.length || 0,
541
634
  hasConsentProof: !!session?.consentProof,
542
- agentDid: session?.agentDid?.slice(0, 20) + "..." || "none",
635
+ agentDid: session?.agentDid ? session.agentDid.slice(0, 20) + "..." : "none",
543
636
  });
544
637
  // ✅ Use processToolCall which handles delegation checks AND proof generation
545
638
  // This ensures delegation is checked BEFORE tool execution
@@ -1166,14 +1259,19 @@ export function createMCPICloudflareAdapter(config) {
1166
1259
  : undefined;
1167
1260
  // Get delegation storage from normalized env if available
1168
1261
  const delegationStorage = mappedEnv.DELEGATION_STORAGE;
1262
+ // Get DO namespace for strongly consistent delegation storage (parity with Agent pattern)
1263
+ const doNamespace = mappedEnv.MCP_OBJECT;
1169
1264
  // Initialize AdminService (if API key is present)
1170
1265
  // This enables cache clearing and other admin operations
1171
1266
  let adminService;
1172
1267
  if (mappedEnv.AGENTSHIELD_API_KEY) {
1173
1268
  adminService = new AdminService(mappedEnv);
1174
1269
  }
1270
+ // Get DO routing configuration from environment (shared utility ensures parity)
1271
+ const doRoutingStrategy = parseDORoutingStrategy(mappedEnv.DO_ROUTING_STRATEGY);
1272
+ const doShardCount = parseDOShardCount(mappedEnv.DO_SHARD_COUNT);
1175
1273
  // Create lightweight MCP server
1176
- const server = new CloudflareMCPServer(runtime, serverInfo, config.tools || [], proofArchive, delegationStorage, config.environment || "production", config.clientMessagesConfig);
1274
+ const server = new CloudflareMCPServer(runtime, serverInfo, config.tools || [], proofArchive, delegationStorage, config.environment || "production", config.clientMessagesConfig, doNamespace, doRoutingStrategy, doShardCount);
1177
1275
  // Return fetch handler
1178
1276
  // Note: mappedEnv is captured in closure for admin endpoints
1179
1277
  return {