@kya-os/mcp-i-cloudflare 1.5.8-canary.3 → 1.5.8-canary.31
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/__tests__/e2e/test-config.d.ts +37 -0
- package/dist/__tests__/e2e/test-config.d.ts.map +1 -0
- package/dist/__tests__/e2e/test-config.js +62 -0
- package/dist/__tests__/e2e/test-config.js.map +1 -0
- package/dist/adapter.d.ts +31 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +416 -58
- package/dist/adapter.js.map +1 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +88 -3
- package/dist/agent.js.map +1 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +19 -3
- package/dist/app.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +33 -4
- package/dist/config.js.map +1 -1
- package/dist/helpers/env-mapper.d.ts +60 -1
- package/dist/helpers/env-mapper.d.ts.map +1 -1
- package/dist/helpers/env-mapper.js +136 -6
- package/dist/helpers/env-mapper.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/runtime/audit-logger.d.ts +96 -0
- package/dist/runtime/audit-logger.d.ts.map +1 -0
- package/dist/runtime/audit-logger.js +276 -0
- package/dist/runtime/audit-logger.js.map +1 -0
- package/dist/runtime.d.ts +12 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +30 -2
- package/dist/runtime.js.map +1 -1
- package/dist/services/admin.service.d.ts.map +1 -1
- package/dist/services/admin.service.js +15 -1
- package/dist/services/admin.service.js.map +1 -1
- package/dist/services/consent-audit.service.d.ts +91 -0
- package/dist/services/consent-audit.service.d.ts.map +1 -0
- package/dist/services/consent-audit.service.js +243 -0
- package/dist/services/consent-audit.service.js.map +1 -0
- package/dist/services/consent.service.d.ts +43 -0
- package/dist/services/consent.service.d.ts.map +1 -1
- package/dist/services/consent.service.js +1420 -20
- package/dist/services/consent.service.js.map +1 -1
- package/dist/services/proof.service.d.ts +5 -3
- package/dist/services/proof.service.d.ts.map +1 -1
- package/dist/services/proof.service.js +19 -6
- package/dist/services/proof.service.js.map +1 -1
- package/dist/types.d.ts +28 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +13 -9
package/dist/adapter.js
CHANGED
|
@@ -13,6 +13,7 @@ import { WELL_KNOWN_CORS_HEADERS, MCP_CORS_HEADERS, PREFLIGHT_CORS_HEADERS, } fr
|
|
|
13
13
|
import { KVProofArchive } from "./storage/kv-proof-archive";
|
|
14
14
|
import { STORAGE_KEYS } from "./constants/storage-keys";
|
|
15
15
|
import { DEFAULT_SESSION_CACHE_TTL } from "./constants";
|
|
16
|
+
import { normalizeCloudflareEnv } from "./helpers/env-mapper";
|
|
16
17
|
const INITIALIZE_CONTEXT_TTL_MS = 60_000;
|
|
17
18
|
/**
|
|
18
19
|
* Lightweight MCP protocol implementation for Cloudflare Workers
|
|
@@ -77,81 +78,204 @@ class CloudflareMCPServer {
|
|
|
77
78
|
}
|
|
78
79
|
// Call tool with identity/proof wrapping
|
|
79
80
|
if (method === "tools/call") {
|
|
81
|
+
// Entry point logging for debugging
|
|
82
|
+
console.log("🔵 [CloudflareMCPServer] handleRequest: tools/call", {
|
|
83
|
+
toolName: params.name,
|
|
84
|
+
hasParams: !!params,
|
|
85
|
+
paramsKeys: params ? Object.keys(params) : [],
|
|
86
|
+
});
|
|
80
87
|
const toolName = params.name;
|
|
81
88
|
const tool = this.tools.get(toolName);
|
|
82
89
|
if (!tool) {
|
|
83
90
|
throw new Error(`Tool not found: ${toolName}`);
|
|
84
91
|
}
|
|
85
92
|
// Get current session if available (stateful environments like Node.js)
|
|
86
|
-
|
|
93
|
+
// Wrap in try-catch to catch any exceptions
|
|
94
|
+
let session = null;
|
|
95
|
+
try {
|
|
96
|
+
session = await this.runtime.getCurrentSession();
|
|
97
|
+
console.log("[CloudflareMCPServer] Session check:", {
|
|
98
|
+
hasSession: !!session,
|
|
99
|
+
sessionId: session?.id?.slice(0, 20) + "..." || "none",
|
|
100
|
+
hasDelegationToken: !!session?.delegationToken,
|
|
101
|
+
hasDelegationStorage: !!this.delegationStorage,
|
|
102
|
+
method: "tools/call",
|
|
103
|
+
toolName,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.error("[CloudflareMCPServer] Error in getCurrentSession:", error);
|
|
108
|
+
// Continue with null session (will create ephemeral)
|
|
109
|
+
}
|
|
87
110
|
// For stateless environments (Cloudflare Workers), create ephemeral session
|
|
88
111
|
if (!session) {
|
|
112
|
+
// Extract session_id from params if available (from consent flow)
|
|
113
|
+
// This allows us to retrieve delegation tokens stored with the original session_id
|
|
114
|
+
const providedSessionId = params.session_id || params.sessionId;
|
|
89
115
|
const timestamp = Date.now();
|
|
90
116
|
const randomSuffix = Math.random().toString(36).substring(2, 10);
|
|
91
117
|
const agentDid = (await this.runtime.getIdentity()).did;
|
|
92
|
-
|
|
118
|
+
// Use provided session_id if available, otherwise create ephemeral
|
|
119
|
+
const sessionId = providedSessionId || `ephemeral-${timestamp}-${randomSuffix}`;
|
|
120
|
+
console.log("[CloudflareMCPServer] Creating session:", {
|
|
121
|
+
sessionId: sessionId.slice(0, 20) + "...",
|
|
122
|
+
isProvided: !!providedSessionId,
|
|
123
|
+
isEphemeral: !providedSessionId,
|
|
124
|
+
});
|
|
93
125
|
// Check KV storage for stored delegation token if not provided in params
|
|
94
126
|
let delegationToken = params.delegationToken;
|
|
95
127
|
if (!delegationToken && this.delegationStorage) {
|
|
96
128
|
try {
|
|
97
|
-
// Get userDID from params
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (sessionToken) {
|
|
116
|
-
delegationToken = sessionToken;
|
|
129
|
+
// Get userDID from params first
|
|
130
|
+
let userDid = params.clientDid || params.userDid;
|
|
131
|
+
// ✅ FIX: If userDid not in params, try to retrieve from session cache
|
|
132
|
+
if (!userDid && sessionId) {
|
|
133
|
+
try {
|
|
134
|
+
const sessionKey = STORAGE_KEYS.session(sessionId);
|
|
135
|
+
const sessionData = (await this.delegationStorage.get(sessionKey, "json"));
|
|
136
|
+
if (sessionData?.userDid) {
|
|
137
|
+
userDid = sessionData.userDid;
|
|
138
|
+
console.log("[CloudflareMCPServer] ✅ Retrieved userDid from session cache:", {
|
|
139
|
+
userDid: userDid.slice(0, 20) + "...",
|
|
140
|
+
sessionId: sessionId.slice(0, 20) + "...",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
console.warn("[CloudflareMCPServer] Failed to get userDid from session:", error);
|
|
146
|
+
// Non-fatal - continue without userDid
|
|
117
147
|
}
|
|
118
148
|
}
|
|
119
|
-
|
|
149
|
+
console.log("[CloudflareMCPServer] 🔍 Starting delegation token lookup from KV:", {
|
|
150
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
151
|
+
userDid: userDid?.slice(0, 20) + "..." || "none",
|
|
152
|
+
sessionId: sessionId?.slice(0, 20) + "..." || "none",
|
|
153
|
+
hasDelegationStorage: !!this.delegationStorage,
|
|
154
|
+
});
|
|
155
|
+
// PRIORITY 1: Try agent-scoped token FIRST (stable across sessions)
|
|
156
|
+
// This is the most reliable lookup since sessions are ephemeral in Cloudflare Workers
|
|
157
|
+
// The consent service stores tokens with this key format
|
|
158
|
+
const agentKey = STORAGE_KEYS.legacyDelegation(agentDid);
|
|
159
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 1: Checking agent-scoped key:", {
|
|
160
|
+
key: agentKey,
|
|
161
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
162
|
+
});
|
|
163
|
+
const agentToken = await this.delegationStorage.get(agentKey, "text");
|
|
164
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 1: Agent-scoped lookup result:", {
|
|
165
|
+
key: agentKey,
|
|
166
|
+
found: !!agentToken,
|
|
167
|
+
tokenLength: agentToken?.length || 0,
|
|
168
|
+
tokenPreview: agentToken ? agentToken.substring(0, 20) + "..." : null,
|
|
169
|
+
});
|
|
170
|
+
if (agentToken) {
|
|
171
|
+
delegationToken = agentToken;
|
|
172
|
+
console.log("[CloudflareMCPServer] ✅ Delegation token retrieved from agent-scoped key:", {
|
|
173
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
174
|
+
key: agentKey,
|
|
175
|
+
tokenLength: agentToken.length,
|
|
176
|
+
});
|
|
177
|
+
// Cache it for this session for faster future lookups
|
|
178
|
+
// Store full session data object to match consent service format
|
|
179
|
+
if (sessionId) {
|
|
180
|
+
const sessionKey = STORAGE_KEYS.session(sessionId);
|
|
181
|
+
// Read existing session data to preserve userDid and other fields
|
|
182
|
+
const existingSession = (await this.delegationStorage.get(sessionKey, "json"));
|
|
183
|
+
await this.delegationStorage.put(sessionKey, JSON.stringify({
|
|
184
|
+
...existingSession,
|
|
185
|
+
userDid,
|
|
186
|
+
agentDid,
|
|
187
|
+
delegationToken: agentToken,
|
|
188
|
+
cachedAt: Date.now(),
|
|
189
|
+
}), {
|
|
190
|
+
expirationTtl: 1800, // 30 minutes
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// PRIORITY 2: Try user+agent scoped token (if userDID is available)
|
|
120
195
|
// This is the preferred approach for multi-user scenarios
|
|
121
196
|
if (!delegationToken && userDid) {
|
|
122
|
-
const userAgentKey =
|
|
123
|
-
|
|
197
|
+
const userAgentKey = STORAGE_KEYS.delegation(userDid, agentDid);
|
|
198
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 2: Checking user+agent scoped key:", {
|
|
199
|
+
key: userAgentKey,
|
|
200
|
+
userDid: userDid.slice(0, 20) + "...",
|
|
201
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
202
|
+
});
|
|
203
|
+
const userAgentToken = await this.delegationStorage.get(userAgentKey, "text");
|
|
204
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 2: User+agent scoped lookup result:", {
|
|
205
|
+
key: userAgentKey,
|
|
206
|
+
found: !!userAgentToken,
|
|
207
|
+
tokenLength: userAgentToken?.length || 0,
|
|
208
|
+
tokenPreview: userAgentToken ? userAgentToken.substring(0, 20) + "..." : null,
|
|
209
|
+
});
|
|
124
210
|
if (userAgentToken) {
|
|
125
211
|
delegationToken = userAgentToken;
|
|
212
|
+
console.log("[CloudflareMCPServer] ✅ Delegation token retrieved from user+agent scoped key:", {
|
|
213
|
+
userDid: userDid.slice(0, 20) + "...",
|
|
214
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
215
|
+
key: userAgentKey,
|
|
216
|
+
tokenLength: userAgentToken.length,
|
|
217
|
+
});
|
|
126
218
|
// Cache it for this session for faster future lookups
|
|
219
|
+
// Store full session data object to match consent service format
|
|
127
220
|
if (sessionId) {
|
|
128
|
-
|
|
221
|
+
const sessionKey = STORAGE_KEYS.session(sessionId);
|
|
222
|
+
// Read existing session data to preserve userDid and other fields
|
|
223
|
+
const existingSession = (await this.delegationStorage.get(sessionKey, "json"));
|
|
224
|
+
await this.delegationStorage.put(sessionKey, JSON.stringify({
|
|
225
|
+
...existingSession,
|
|
226
|
+
userDid,
|
|
227
|
+
agentDid,
|
|
228
|
+
delegationToken: userAgentToken,
|
|
229
|
+
cachedAt: Date.now(),
|
|
230
|
+
}), {
|
|
129
231
|
expirationTtl: 1800, // 30 minutes
|
|
130
232
|
});
|
|
131
233
|
}
|
|
132
234
|
}
|
|
133
235
|
}
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
236
|
+
// PRIORITY 3: Try session-scoped token (if session ID was persisted)
|
|
237
|
+
// Note: Sessions are ephemeral in Cloudflare Workers, so this is mainly
|
|
238
|
+
// useful for caching tokens within a single request chain
|
|
239
|
+
if (!delegationToken && sessionId) {
|
|
240
|
+
const sessionKey = STORAGE_KEYS.session(sessionId);
|
|
241
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 3: Checking session-scoped key:", {
|
|
242
|
+
key: sessionKey,
|
|
243
|
+
sessionId: sessionId.slice(0, 20) + "...",
|
|
244
|
+
});
|
|
245
|
+
const sessionData = (await this.delegationStorage.get(sessionKey, "json"));
|
|
246
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 3: Session-scoped lookup result:", {
|
|
247
|
+
key: sessionKey,
|
|
248
|
+
found: !!sessionData,
|
|
249
|
+
hasDelegationToken: !!sessionData?.delegationToken,
|
|
250
|
+
tokenLength: sessionData?.delegationToken?.length || 0,
|
|
251
|
+
tokenPreview: sessionData?.delegationToken ? sessionData.delegationToken.substring(0, 20) + "..." : null,
|
|
252
|
+
});
|
|
253
|
+
if (sessionData?.delegationToken) {
|
|
254
|
+
delegationToken = sessionData.delegationToken;
|
|
255
|
+
console.log("[CloudflareMCPServer] ✅ Delegation token retrieved from session cache:", {
|
|
256
|
+
sessionId: sessionId.slice(0, 20) + "...",
|
|
257
|
+
tokenLength: delegationToken.length,
|
|
258
|
+
});
|
|
149
259
|
}
|
|
150
260
|
}
|
|
261
|
+
if (!delegationToken) {
|
|
262
|
+
console.log("[CloudflareMCPServer] ⚠️ No delegation token found in KV storage:", {
|
|
263
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
264
|
+
userDid: userDid?.slice(0, 20) + "..." || "none",
|
|
265
|
+
sessionId: sessionId?.slice(0, 20) + "..." || "none",
|
|
266
|
+
checkedKeys: [
|
|
267
|
+
agentKey,
|
|
268
|
+
userDid
|
|
269
|
+
? STORAGE_KEYS.delegation(userDid, agentDid)
|
|
270
|
+
: null,
|
|
271
|
+
sessionId ? STORAGE_KEYS.session(sessionId) : null,
|
|
272
|
+
].filter(Boolean),
|
|
273
|
+
});
|
|
274
|
+
}
|
|
151
275
|
}
|
|
152
276
|
catch (error) {
|
|
153
277
|
// Log but don't fail - delegation lookup is best-effort
|
|
154
|
-
console.
|
|
278
|
+
console.error("[CloudflareMCPServer] Failed to retrieve delegation token from KV:", error);
|
|
155
279
|
}
|
|
156
280
|
}
|
|
157
281
|
session = {
|
|
@@ -168,9 +292,207 @@ class CloudflareMCPServer {
|
|
|
168
292
|
serverOrigin: this.requestOrigin,
|
|
169
293
|
// These fields are for ephemeral sessions - won't persist
|
|
170
294
|
};
|
|
295
|
+
// ✅ CRITICAL: Log delegation token status before processToolCall
|
|
296
|
+
console.log("[CloudflareMCPServer] ✅ Session created with delegation token status:", {
|
|
297
|
+
sessionId: sessionId.slice(0, 20) + "...",
|
|
298
|
+
hasDelegationToken: !!delegationToken,
|
|
299
|
+
delegationTokenLength: delegationToken?.length || 0,
|
|
300
|
+
delegationTokenSource: params.delegationToken ? "params" : (delegationToken ? "kv-storage" : "none"),
|
|
301
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
302
|
+
});
|
|
171
303
|
}
|
|
172
304
|
else {
|
|
173
|
-
// For existing sessions,
|
|
305
|
+
// For existing sessions, check KV storage for delegation token if not already set
|
|
306
|
+
// This ensures newly created delegations are found even for existing sessions
|
|
307
|
+
console.log("[CloudflareMCPServer] Existing session detected, checking KV storage:", {
|
|
308
|
+
hasSession: !!session,
|
|
309
|
+
hasDelegationToken: !!session?.delegationToken,
|
|
310
|
+
hasDelegationStorage: !!this.delegationStorage,
|
|
311
|
+
sessionId: session?.id?.slice(0, 20) + "...",
|
|
312
|
+
sessionKeys: Object.keys(session || {}),
|
|
313
|
+
});
|
|
314
|
+
if (!session.delegationToken && this.delegationStorage) {
|
|
315
|
+
console.log("[CloudflareMCPServer] ⚠️ Session has no delegationToken, attempting KV lookup...");
|
|
316
|
+
try {
|
|
317
|
+
const agentDid = (await this.runtime.getIdentity()).did;
|
|
318
|
+
const userDid = params.clientDid || params.userDid || session.clientDid;
|
|
319
|
+
const sessionId = session.id;
|
|
320
|
+
console.log("[CloudflareMCPServer] 🔍 Looking up delegation token for existing session:", {
|
|
321
|
+
sessionId: sessionId?.slice(0, 20) + "...",
|
|
322
|
+
userDid: userDid?.slice(0, 20) + "...",
|
|
323
|
+
agentDid: agentDid?.slice(0, 20) + "...",
|
|
324
|
+
});
|
|
325
|
+
// PRIORITY 1: Try agent-scoped token FIRST (stable across sessions)
|
|
326
|
+
const agentKey = STORAGE_KEYS.legacyDelegation(agentDid);
|
|
327
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 1: Checking agent-scoped key:", {
|
|
328
|
+
key: agentKey,
|
|
329
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
330
|
+
});
|
|
331
|
+
let delegationToken = await this.delegationStorage.get(agentKey, "text");
|
|
332
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 1: Agent-scoped lookup result:", {
|
|
333
|
+
key: agentKey,
|
|
334
|
+
found: !!delegationToken,
|
|
335
|
+
tokenLength: delegationToken?.length || 0,
|
|
336
|
+
});
|
|
337
|
+
// PRIORITY 2: Try user+agent scoped token (if userDID is available)
|
|
338
|
+
if (!delegationToken && userDid) {
|
|
339
|
+
const userAgentKey = STORAGE_KEYS.delegation(userDid, agentDid);
|
|
340
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 2: Checking user+agent scoped key:", {
|
|
341
|
+
key: userAgentKey,
|
|
342
|
+
userDid: userDid.slice(0, 20) + "...",
|
|
343
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
344
|
+
});
|
|
345
|
+
const userAgentToken = await this.delegationStorage.get(userAgentKey, "text");
|
|
346
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 2: User+agent scoped lookup result:", {
|
|
347
|
+
key: userAgentKey,
|
|
348
|
+
found: !!userAgentToken,
|
|
349
|
+
tokenLength: userAgentToken?.length || 0,
|
|
350
|
+
});
|
|
351
|
+
if (userAgentToken) {
|
|
352
|
+
delegationToken = userAgentToken;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// PRIORITY 3: Try session-scoped token (if session ID was persisted)
|
|
356
|
+
if (!delegationToken && sessionId) {
|
|
357
|
+
const sessionKey = STORAGE_KEYS.session(sessionId);
|
|
358
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 3: Looking up session key:", {
|
|
359
|
+
sessionKey,
|
|
360
|
+
sessionId: sessionId.slice(0, 20) + "...",
|
|
361
|
+
});
|
|
362
|
+
try {
|
|
363
|
+
const sessionData = (await this.delegationStorage.get(sessionKey, "json"));
|
|
364
|
+
console.log("[CloudflareMCPServer] 🔍 PRIORITY 3: Session-scoped lookup result:", {
|
|
365
|
+
key: sessionKey,
|
|
366
|
+
found: !!sessionData,
|
|
367
|
+
hasDelegationToken: !!sessionData?.delegationToken,
|
|
368
|
+
tokenLength: sessionData?.delegationToken?.length || 0,
|
|
369
|
+
});
|
|
370
|
+
if (sessionData?.delegationToken) {
|
|
371
|
+
delegationToken = sessionData.delegationToken;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
console.error("[CloudflareMCPServer] Error retrieving session data:", error);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// ✅ CRITICAL: Set delegation token on session if found
|
|
379
|
+
if (delegationToken) {
|
|
380
|
+
session = {
|
|
381
|
+
...session,
|
|
382
|
+
delegationToken,
|
|
383
|
+
};
|
|
384
|
+
console.log("[CloudflareMCPServer] ✅ Delegation token set on existing session:", {
|
|
385
|
+
sessionId: sessionId?.slice(0, 20) + "..." || "none",
|
|
386
|
+
tokenLength: delegationToken.length,
|
|
387
|
+
sessionHasToken: !!session.delegationToken,
|
|
388
|
+
source: "kv-storage",
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
console.log("[CloudflareMCPServer] ⚠️ No delegation token found in KV storage for existing session:", {
|
|
393
|
+
sessionId: sessionId?.slice(0, 20) + "..." || "none",
|
|
394
|
+
checkedKeys: [
|
|
395
|
+
agentKey,
|
|
396
|
+
userDid ? STORAGE_KEYS.delegation(userDid, agentDid) : null,
|
|
397
|
+
sessionId ? STORAGE_KEYS.session(sessionId) : null,
|
|
398
|
+
].filter(Boolean),
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
// PRIORITY 2: Try user+agent scoped token (if userDID is available)
|
|
402
|
+
if (!session.delegationToken && userDid) {
|
|
403
|
+
const userAgentKey = STORAGE_KEYS.delegation(userDid, agentDid);
|
|
404
|
+
console.log("[CloudflareMCPServer] PRIORITY 2: Looking up user+agent key:", {
|
|
405
|
+
userAgentKey,
|
|
406
|
+
userDid: userDid.slice(0, 20) + "...",
|
|
407
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
408
|
+
});
|
|
409
|
+
try {
|
|
410
|
+
const userAgentToken = await this.delegationStorage.get(userAgentKey);
|
|
411
|
+
if (userAgentToken) {
|
|
412
|
+
session = {
|
|
413
|
+
...session,
|
|
414
|
+
delegationToken: userAgentToken,
|
|
415
|
+
};
|
|
416
|
+
console.log("[CloudflareMCPServer] ✅ Delegation token retrieved from user+agent scoped key (existing session):", {
|
|
417
|
+
userDid: userDid.slice(0, 20) + "...",
|
|
418
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
419
|
+
key: userAgentKey,
|
|
420
|
+
tokenLength: userAgentToken.length,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
console.log("[CloudflareMCPServer] ⚠️ No token found at user+agent key");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
console.error("[CloudflareMCPServer] Error retrieving user+agent token:", error);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
console.log("[CloudflareMCPServer] ⚠️ Skipping PRIORITY 2:", {
|
|
433
|
+
hasToken: !!session.delegationToken,
|
|
434
|
+
hasUserDid: !!userDid,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
// PRIORITY 3: Try agent-scoped token (fallback)
|
|
438
|
+
if (!session.delegationToken) {
|
|
439
|
+
const agentKey = STORAGE_KEYS.legacyDelegation(agentDid);
|
|
440
|
+
console.log("[CloudflareMCPServer] PRIORITY 3: Looking up agent key:", {
|
|
441
|
+
agentKey,
|
|
442
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
443
|
+
});
|
|
444
|
+
try {
|
|
445
|
+
const agentToken = await this.delegationStorage.get(agentKey);
|
|
446
|
+
if (agentToken) {
|
|
447
|
+
session = {
|
|
448
|
+
...session,
|
|
449
|
+
delegationToken: agentToken,
|
|
450
|
+
};
|
|
451
|
+
console.log("[CloudflareMCPServer] ✅ Delegation token retrieved from agent-scoped key (existing session):", {
|
|
452
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
453
|
+
key: agentKey,
|
|
454
|
+
tokenLength: agentToken.length,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
console.log("[CloudflareMCPServer] ⚠️ No token found at agent key");
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
console.error("[CloudflareMCPServer] Error retrieving agent token:", error);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
console.log("[CloudflareMCPServer] ✅ Already have token, skipping PRIORITY 3");
|
|
467
|
+
}
|
|
468
|
+
// Final check - log if we still don't have a token
|
|
469
|
+
if (!session.delegationToken) {
|
|
470
|
+
console.log("[CloudflareMCPServer] ❌ FINAL CHECK: Still no delegation token after all KV lookups:", {
|
|
471
|
+
sessionId: session?.id?.slice(0, 20) + "..." || "none",
|
|
472
|
+
userDid: userDid?.slice(0, 20) + "..." || "none",
|
|
473
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
474
|
+
checkedKeys: [
|
|
475
|
+
sessionId ? STORAGE_KEYS.session(sessionId) : null,
|
|
476
|
+
userDid
|
|
477
|
+
? STORAGE_KEYS.delegation(userDid, agentDid)
|
|
478
|
+
: null,
|
|
479
|
+
STORAGE_KEYS.legacyDelegation(agentDid),
|
|
480
|
+
].filter(Boolean),
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
console.log("[CloudflareMCPServer] ✅ FINAL CHECK: Delegation token found:", {
|
|
485
|
+
tokenLength: session.delegationToken.length,
|
|
486
|
+
sessionHasToken: !!session.delegationToken,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
catch (error) {
|
|
491
|
+
// Log but don't fail - delegation lookup is best-effort
|
|
492
|
+
console.error("[CloudflareMCPServer] Failed to retrieve delegation token from KV (existing session):", error);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// Update delegation fields if provided in params (takes precedence)
|
|
174
496
|
if (params.delegationToken) {
|
|
175
497
|
session.delegationToken = params.delegationToken;
|
|
176
498
|
}
|
|
@@ -202,6 +524,34 @@ class CloudflareMCPServer {
|
|
|
202
524
|
session.toolName = toolName;
|
|
203
525
|
session.toolParams = params.arguments || {};
|
|
204
526
|
session.scopeId = scopeId; // ✅ ADDED: Pass scopeId for tool auto-discovery
|
|
527
|
+
// ✅ CRITICAL: Verify delegation token is set on session before processToolCall
|
|
528
|
+
// Also do a final KV lookup if token is still missing (defensive check)
|
|
529
|
+
if (!session?.delegationToken && this.delegationStorage) {
|
|
530
|
+
try {
|
|
531
|
+
const agentDid = (await this.runtime.getIdentity()).did;
|
|
532
|
+
const agentKey = STORAGE_KEYS.legacyDelegation(agentDid);
|
|
533
|
+
const finalToken = await this.delegationStorage.get(agentKey, "text");
|
|
534
|
+
if (finalToken) {
|
|
535
|
+
session.delegationToken = finalToken;
|
|
536
|
+
console.log("[CloudflareMCPServer] ✅ Final KV lookup succeeded, token set on session:", {
|
|
537
|
+
toolName,
|
|
538
|
+
agentDid: agentDid.slice(0, 20) + "...",
|
|
539
|
+
tokenLength: finalToken.length,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
console.error("[CloudflareMCPServer] Final KV lookup failed:", error);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
console.log("[CloudflareMCPServer] ✅ About to call processToolCall with session:", {
|
|
548
|
+
toolName,
|
|
549
|
+
sessionId: session?.id?.slice(0, 20) + "..." || "none",
|
|
550
|
+
hasDelegationToken: !!session?.delegationToken,
|
|
551
|
+
delegationTokenLength: session?.delegationToken?.length || 0,
|
|
552
|
+
hasConsentProof: !!session?.consentProof,
|
|
553
|
+
agentDid: session?.agentDid?.slice(0, 20) + "..." || "none",
|
|
554
|
+
});
|
|
205
555
|
// ✅ Use processToolCall which handles delegation checks AND proof generation
|
|
206
556
|
// This ensures delegation is checked BEFORE tool execution
|
|
207
557
|
// If delegation is required but not provided, this will throw DelegationRequiredError
|
|
@@ -441,8 +791,7 @@ class CloudflareMCPServer {
|
|
|
441
791
|
const name = typeof nameValue === "string" && nameValue.trim().length > 0
|
|
442
792
|
? nameValue.trim()
|
|
443
793
|
: "unknown";
|
|
444
|
-
const clientId = handshakeClientId ||
|
|
445
|
-
crypto.randomUUID();
|
|
794
|
+
const clientId = handshakeClientId || crypto.randomUUID();
|
|
446
795
|
const capabilities = this.isRecord(request.clientCapabilities)
|
|
447
796
|
? request.clientCapabilities
|
|
448
797
|
: initializeContext?.capabilities;
|
|
@@ -557,25 +906,33 @@ function buildRequestMeta(request) {
|
|
|
557
906
|
*
|
|
558
907
|
* Supports SSE (Server-Sent Events) and HTTP JSON-RPC transports for compatibility
|
|
559
908
|
* with Claude Desktop, Cursor, MCP Inspector, and other MCP clients.
|
|
909
|
+
*
|
|
910
|
+
* Automatically handles prefixed KV bindings via `envPrefix` parameter or auto-detection.
|
|
560
911
|
*/
|
|
561
912
|
export function createMCPICloudflareAdapter(config) {
|
|
562
|
-
//
|
|
563
|
-
|
|
913
|
+
// Normalize environment to handle prefixed KV bindings
|
|
914
|
+
// This ensures consistent KV access regardless of prefix usage
|
|
915
|
+
const mappedEnv = normalizeCloudflareEnv(config.env, config.envPrefix);
|
|
916
|
+
// Create the runtime with normalized environment
|
|
917
|
+
const runtime = createCloudflareRuntime({
|
|
918
|
+
...config,
|
|
919
|
+
env: mappedEnv,
|
|
920
|
+
});
|
|
564
921
|
// Server info
|
|
565
922
|
const serverInfo = config.serverInfo || {
|
|
566
923
|
name: "MCP-I Cloudflare Server",
|
|
567
924
|
version: "1.0.0",
|
|
568
925
|
};
|
|
569
926
|
// Initialize proof archive if PROOF_ARCHIVE KV is available
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
? new KVProofArchive(env.PROOF_ARCHIVE)
|
|
927
|
+
const proofArchive = mappedEnv.PROOF_ARCHIVE
|
|
928
|
+
? new KVProofArchive(mappedEnv.PROOF_ARCHIVE)
|
|
573
929
|
: undefined;
|
|
574
|
-
// Get delegation storage from env if available
|
|
575
|
-
const delegationStorage =
|
|
930
|
+
// Get delegation storage from normalized env if available
|
|
931
|
+
const delegationStorage = mappedEnv.DELEGATION_STORAGE;
|
|
576
932
|
// Create lightweight MCP server
|
|
577
933
|
const server = new CloudflareMCPServer(runtime, serverInfo, config.tools || [], proofArchive, delegationStorage);
|
|
578
934
|
// Return fetch handler
|
|
935
|
+
// Note: mappedEnv is captured in closure for admin endpoints
|
|
579
936
|
return {
|
|
580
937
|
server,
|
|
581
938
|
runtime,
|
|
@@ -766,15 +1123,16 @@ export function createMCPICloudflareAdapter(config) {
|
|
|
766
1123
|
headers: { "Content-Type": "application/json" },
|
|
767
1124
|
});
|
|
768
1125
|
}
|
|
769
|
-
|
|
1126
|
+
// Use normalized environment (handles prefixed KV bindings)
|
|
1127
|
+
// mappedEnv is already normalized in createMCPICloudflareAdapter
|
|
770
1128
|
// GET /admin/nonces - List active nonces
|
|
771
1129
|
if (url.pathname === "/admin/nonces") {
|
|
772
1130
|
try {
|
|
773
1131
|
// Use KV list to get nonce keys
|
|
774
|
-
const noncesList = await
|
|
1132
|
+
const noncesList = await mappedEnv.NONCE_CACHE.list({ prefix: "nonce:" });
|
|
775
1133
|
const nonces = [];
|
|
776
1134
|
for (const key of noncesList.keys) {
|
|
777
|
-
const value = await
|
|
1135
|
+
const value = await mappedEnv.NONCE_CACHE.get(key.name);
|
|
778
1136
|
if (value) {
|
|
779
1137
|
nonces.push({
|
|
780
1138
|
nonce: key.name.replace("nonce:", ""),
|
|
@@ -800,8 +1158,8 @@ export function createMCPICloudflareAdapter(config) {
|
|
|
800
1158
|
}
|
|
801
1159
|
}
|
|
802
1160
|
// Initialize proof archive if available
|
|
803
|
-
const proofArchive =
|
|
804
|
-
? new KVProofArchive(
|
|
1161
|
+
const proofArchive = mappedEnv.PROOF_ARCHIVE
|
|
1162
|
+
? new KVProofArchive(mappedEnv.PROOF_ARCHIVE)
|
|
805
1163
|
: null;
|
|
806
1164
|
if (!proofArchive) {
|
|
807
1165
|
return new Response(JSON.stringify({
|
|
@@ -874,7 +1232,7 @@ export function createMCPICloudflareAdapter(config) {
|
|
|
874
1232
|
if (url.pathname === "/admin/stats") {
|
|
875
1233
|
try {
|
|
876
1234
|
const [noncesList, proofStats] = await Promise.all([
|
|
877
|
-
|
|
1235
|
+
mappedEnv.NONCE_CACHE.list({ prefix: "nonce:" }),
|
|
878
1236
|
proofArchive.getStats(),
|
|
879
1237
|
]);
|
|
880
1238
|
return new Response(JSON.stringify({
|