@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 +25 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +133 -35
- package/dist/adapter.js.map +1 -1
- package/dist/agent.d.ts +4 -7
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +42 -59
- package/dist/agent.js.map +1 -1
- package/dist/helpers/env-mapper.d.ts.map +1 -1
- package/dist/helpers/env-mapper.js +8 -0
- package/dist/helpers/env-mapper.js.map +1 -1
- package/dist/services/consent.service.d.ts.map +1 -1
- package/dist/services/consent.service.js +3 -20
- package/dist/services/consent.service.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/do-routing.d.ts +37 -0
- package/dist/utils/do-routing.d.ts.map +1 -0
- package/dist/utils/do-routing.js +80 -0
- package/dist/utils/do-routing.js.map +1 -0
- package/package.json +24 -23
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
|
*/
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAiB,MAAM,SAAS,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAKL,KAAK,oBAAoB,EAC1B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;
|
|
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
|
|
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
|
|
280
|
+
console.error("[CloudflareMCPServer] 🔍 Starting delegation token lookup:", {
|
|
213
281
|
agentDid: agentDid.slice(0, 20) + "...",
|
|
214
|
-
userDid: userDid
|
|
215
|
-
sessionId: sessionId
|
|
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 (
|
|
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
|
|
305
|
-
sessionId: sessionId
|
|
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
|
|
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
|
|
385
|
-
userDid: userDid
|
|
386
|
-
agentDid: agentDid
|
|
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 (
|
|
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
|
|
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
|
|
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
|
-
//
|
|
501
|
-
|
|
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
|
|
617
|
+
console.error("[CloudflareMCPServer] ✅ Final lookup succeeded, token set on session:", {
|
|
524
618
|
toolName,
|
|
525
619
|
agentDid: agentDid.slice(0, 20) + "...",
|
|
526
|
-
userDid: userDid
|
|
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
|
|
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
|
|
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
|
|
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 {
|