@kya-os/mcp-i-cloudflare 1.7.61 → 1.7.62

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/agent.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * Users only need to extend this class and register their tools.
7
7
  */
8
8
  import { McpAgent } from "agents/mcp";
9
- import type { DurableObjectState } from "@cloudflare/workers-types";
9
+ import type { DurableObjectState, DurableObjectNamespace, KVNamespace } from "@cloudflare/workers-types";
10
10
  import type { CloudflareEnv } from "./types";
11
11
  import type { CloudflareRuntimeConfig } from "./config";
12
12
  import { CloudflareRuntime } from "./runtime";
@@ -16,8 +16,8 @@ import { type IProviderRegistry } from "@kya-os/provider-registry";
16
16
  /**
17
17
  * Extended CloudflareEnv with prefixed KV bindings for multi-agent deployments
18
18
  */
19
- export interface PrefixedCloudflareEnv extends Omit<CloudflareEnv, "NONCE_CACHE" | "PROOF_ARCHIVE" | "IDENTITY_STORAGE" | "DELEGATION_STORAGE" | "TOOL_PROTECTION_KV"> {
20
- [key: string]: KVNamespace | string | DurableObjectState | undefined;
19
+ export interface PrefixedCloudflareEnv extends Omit<CloudflareEnv, "NONCE_CACHE" | "PROOF_ARCHIVE" | "IDENTITY_STORAGE" | "DELEGATION_STORAGE" | "TOOL_PROTECTION_KV" | "MCP_OBJECT"> {
20
+ [key: string]: KVNamespace | string | DurableObjectState | DurableObjectNamespace | undefined;
21
21
  DO_ROUTING_STRATEGY?: string;
22
22
  DO_SHARD_COUNT?: string;
23
23
  NONCE_CACHE?: KVNamespace;
@@ -287,6 +287,41 @@ export declare abstract class MCPICloudflareAgent extends McpAgent {
287
287
  consentConfigCleared?: boolean;
288
288
  error?: string;
289
289
  }>;
290
+ /**
291
+ * Store delegation data to DO internal storage
292
+ *
293
+ * Uses multiple storage keys for efficient lookup:
294
+ * - `delegation:session:${sessionId}` - Primary key for session-based lookup
295
+ * - `delegation:user:${userDid}:agent:${agentDid}` - Secondary key for user-based lookup
296
+ *
297
+ * @param data - Delegation data to store
298
+ */
299
+ protected storeDelegationToStorage(data: {
300
+ sessionId: string;
301
+ delegationToken: string;
302
+ delegationId: string;
303
+ userDid?: string;
304
+ agentDid: string;
305
+ }): Promise<void>;
306
+ /**
307
+ * Retrieve delegation from DO internal storage
308
+ *
309
+ * Looks up in priority order:
310
+ * 1. Session key (primary)
311
+ * 2. User+agent key (if userDid available)
312
+ *
313
+ * @param sessionId - Session ID
314
+ * @param userDid - Optional user DID for secondary lookup
315
+ * @param agentDid - Agent DID for secondary lookup
316
+ * @returns Delegation data or undefined if not found
317
+ */
318
+ protected getDelegationFromStorage(sessionId: string, userDid?: string, agentDid?: string): Promise<{
319
+ delegationToken: string;
320
+ delegationId: string;
321
+ userDid?: string;
322
+ agentDid: string;
323
+ storedAt: number;
324
+ } | undefined>;
290
325
  /**
291
326
  * Handle MCP-I handshake JSON-RPC request
292
327
  *
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
package/dist/agent.js CHANGED
@@ -738,137 +738,69 @@ export class MCPICloudflareAgent extends McpAgent {
738
738
  serverUrl: serverUrl?.slice(0, 50) || "undefined",
739
739
  });
740
740
  }
741
- // ✅ Look up delegation token from KV storage (3-priority system)
741
+ // ✅ Look up delegation token from DO storage (strongly consistent)
742
+ // Using DO internal storage eliminates KV eventual consistency issues.
743
+ // No retry mechanism needed - storage and retrieval happen on the same DO instance.
742
744
  let delegationToken;
743
- let delegationId; // Extract delegationId for proof submission
744
- let userDid; // Extract userDid for IDP token resolution
745
- let clientDid; // Extract clientDid from handshake for proof binding
746
- const delegationStorage = mappedEnv.DELEGATION_STORAGE;
747
- if (delegationStorage) {
748
- try {
749
- const agentDid = (await this.mcpiRuntime.getIdentity()).did;
750
- logger.debug("[MCPICloudflareAgent] 🔍 Starting delegation token lookup:", {
751
- sessionId: sessionId.slice(0, 20) + "...",
752
- agentDid: agentDid.slice(0, 20) + "...",
753
- hasDelegationStorage: !!delegationStorage,
754
- });
755
- // PRIORITY 0: Get userDid from BOTH identity key and session key
756
- // Due to KV key separation (see initializeSessionStorage), userDid may be in either:
757
- // - session:identity:${sessionId} → NEW location for identity data
758
- // - session:${sessionId} → OLD location (backward compat) + delegation data
745
+ let delegationId;
746
+ let userDid;
747
+ let clientDid;
748
+ try {
749
+ const agentDid = (await this.mcpiRuntime.getIdentity()).did;
750
+ logger.debug("[MCPICloudflareAgent] 🔍 Starting delegation lookup (DO storage):", {
751
+ sessionId: sessionId.slice(0, 20) + "...",
752
+ agentDid: agentDid.slice(0, 20) + "...",
753
+ });
754
+ // Get userDid and clientDid from KV session data (still needed for identity context)
755
+ const delegationStorage = mappedEnv.DELEGATION_STORAGE;
756
+ if (delegationStorage) {
759
757
  const identityKey = STORAGE_KEYS.sessionIdentity(sessionId);
760
758
  const sessionKey = STORAGE_KEYS.session(sessionId);
761
- // Check identity key first (new pattern)
762
- const identityData = (await delegationStorage.get(identityKey, "json"));
763
- // Then check session key (backward compat + delegation data)
764
- const sessionData = (await delegationStorage.get(sessionKey, "json"));
765
- // Prefer identity key, fallback to session key
759
+ const identityData = await delegationStorage.get(identityKey, "json");
760
+ const sessionData = await delegationStorage.get(sessionKey, "json");
766
761
  userDid = identityData?.userDid || sessionData?.userDid;
767
- // Extract clientDid from handshake for proof binding
768
- if (sessionData?.clientDid) {
769
- clientDid = sessionData.clientDid;
770
- }
771
- // Extract delegationId for proof submission attribution
772
- if (sessionData?.delegationId) {
773
- delegationId = sessionData.delegationId;
762
+ clientDid = sessionData?.clientDid;
763
+ }
764
+ // Look up delegation from DO storage (strongly consistent - no retries needed)
765
+ const doData = await this.getDelegationFromStorage(sessionId, userDid, agentDid);
766
+ if (doData) {
767
+ delegationToken = doData.delegationToken;
768
+ delegationId = doData.delegationId;
769
+ // Prefer userDid from DO storage if available
770
+ if (doData.userDid) {
771
+ userDid = doData.userDid;
774
772
  }
775
- // Track which source the token was found in for accurate logging
776
- let tokenSource;
777
- // ✅ KV EVENTUAL CONSISTENCY FIX: Retry delegation lookup with exponential backoff
778
- // Cloudflare KV can take up to 60 seconds to replicate globally.
779
- // When consent completes at edge A and tool execution happens at edge B,
780
- // the delegation might not be visible yet. Retry with exponential backoff.
781
- const MAX_KV_RETRIES = 5;
782
- const KV_RETRY_DELAYS_MS = [0, 2000, 4000, 6000, 8000]; // 0, 2s, 4s, 6s, 8s = 20s total max
783
- for (let kvRetry = 0; kvRetry < MAX_KV_RETRIES && !delegationToken; kvRetry++) {
784
- const delayMs = KV_RETRY_DELAYS_MS[kvRetry] || 8000;
785
- if (kvRetry > 0) {
786
- logger.debug(`[MCPICloudflareAgent] 🔄 KV retry ${kvRetry}/${MAX_KV_RETRIES - 1} after ${delayMs}ms delay (eventual consistency)`);
787
- await new Promise((resolve) => setTimeout(resolve, delayMs));
788
- }
789
- if (userDid) {
790
- const userAgentKey = STORAGE_KEYS.delegation(userDid, agentDid);
791
- if (kvRetry === 0) {
792
- logger.debug("[MCPICloudflareAgent] 🔍 PRIORITY 1: Checking user+agent scoped key:", {
793
- key: userAgentKey,
794
- userDid: userDid.slice(0, 20) + "...",
795
- agentDid: agentDid.slice(0, 20) + "...",
796
- });
797
- }
798
- const userAgentToken = await delegationStorage.get(userAgentKey, "text");
799
- if (kvRetry === 0 || userAgentToken) {
800
- logger.debug("[MCPICloudflareAgent] 🔍 PRIORITY 1: User+agent scoped lookup result:", {
801
- found: !!userAgentToken,
802
- tokenLength: userAgentToken?.length || 0,
803
- ...(kvRetry > 0 && { kvRetry }),
804
- });
805
- }
806
- if (userAgentToken) {
807
- delegationToken = userAgentToken;
808
- tokenSource = "user+agent-key";
809
- if (kvRetry > 0) {
810
- logger.info(`[MCPICloudflareAgent] ✅ Delegation found after ${kvRetry} KV retry(s) - eventual consistency resolved`);
811
- }
812
- break;
813
- }
814
- }
815
- // PRIORITY 2: Session-scoped token (if session ID persists)
816
- if (!delegationToken) {
817
- if (kvRetry === 0) {
818
- logger.debug("[MCPICloudflareAgent] 🔍 PRIORITY 2: Checking session-scoped key:", {
819
- key: sessionKey,
820
- sessionId: sessionId.slice(0, 20) + "...",
821
- });
822
- }
823
- const sessionTokenData = (await delegationStorage.get(sessionKey, "json"));
824
- if (kvRetry === 0 || sessionTokenData?.delegationToken) {
825
- logger.debug("[MCPICloudflareAgent] 🔍 PRIORITY 2: Session-scoped lookup result:", {
826
- found: !!sessionTokenData,
827
- hasDelegationToken: !!sessionTokenData?.delegationToken,
828
- hasDelegationId: !!sessionTokenData?.delegationId,
829
- tokenLength: sessionTokenData?.delegationToken?.length || 0,
830
- ...(kvRetry > 0 && { kvRetry }),
831
- });
832
- }
833
- if (sessionTokenData?.delegationToken) {
834
- delegationToken = sessionTokenData.delegationToken;
835
- tokenSource = "session-key";
836
- // Extract delegationId if not already set
837
- if (!delegationId && sessionTokenData.delegationId) {
838
- delegationId = sessionTokenData.delegationId;
839
- }
840
- if (kvRetry > 0) {
841
- logger.info(`[MCPICloudflareAgent] ✅ Delegation found after ${kvRetry} KV retry(s) - eventual consistency resolved`);
842
- }
843
- break;
844
- }
773
+ logger.debug("[MCPICloudflareAgent] Delegation found in DO storage:", {
774
+ sessionId: sessionId.slice(0, 20) + "...",
775
+ delegationId,
776
+ hasUserDid: !!userDid,
777
+ });
778
+ }
779
+ else {
780
+ // Fallback: Check KV for backward compatibility with existing delegations
781
+ // This handles delegations created before the DO storage migration
782
+ if (delegationStorage && userDid) {
783
+ const userAgentKey = STORAGE_KEYS.delegation(userDid, agentDid);
784
+ const kvToken = await delegationStorage.get(userAgentKey, "text");
785
+ if (kvToken) {
786
+ delegationToken = kvToken;
787
+ logger.debug("[MCPICloudflareAgent] ✅ Delegation found in KV (legacy):", {
788
+ sessionId: sessionId.slice(0, 20) + "...",
789
+ source: "kv-fallback",
790
+ });
845
791
  }
846
792
  }
847
- // ❌ REMOVED: Legacy agent-scoped fallback (PRIORITY 3)
848
- // This was a security vulnerability that allowed cross-user delegation sharing.
849
- // Delegations are now strictly session-scoped or user+agent scoped.
850
- // See: Security fix for cross-user delegation leakage
851
- if (delegationToken) {
852
- logger.debug("[MCPICloudflareAgent] ✅ Delegation token retrieved:", {
853
- sessionId: sessionId.slice(0, 20) + "...",
854
- tokenLength: delegationToken.length,
855
- source: tokenSource,
856
- });
857
- }
858
- else {
859
- // NOTE: This is informational - delegation is only required for protected tools
860
- // If the tool doesn't require delegation, this message can be ignored
861
- // The proper fix for KV consistency is passing userDid through redirect params
862
- logger.debug("[MCPICloudflareAgent] ℹ️ No delegation token found (normal if tool doesn't require delegation):", {
793
+ if (!delegationToken) {
794
+ logger.debug("[MCPICloudflareAgent] ℹ️ No delegation found:", {
863
795
  sessionId: sessionId.slice(0, 20) + "...",
864
796
  hasUserDid: !!userDid,
865
- note: "Delegation only required for tools configured with 'requiresDelegation: true'",
797
+ note: "Delegation only required for tools with 'requiresDelegation: true'",
866
798
  });
867
799
  }
868
800
  }
869
- catch (error) {
870
- logger.warn("[MCPICloudflareAgent] Failed to retrieve delegation token:", error);
871
- }
801
+ }
802
+ catch (error) {
803
+ logger.warn("[MCPICloudflareAgent] Failed to retrieve delegation:", error);
872
804
  }
873
805
  // ✅ FIX: Extract userDid from delegation token if not available from session storage
874
806
  // This handles KV eventual consistency issues where session cache is stale but delegation exists
@@ -1798,6 +1730,33 @@ export class MCPICloudflareAgent extends McpAgent {
1798
1730
  }), { status: 500, headers: { "Content-Type": "application/json" } });
1799
1731
  }
1800
1732
  }
1733
+ // Handle internal delegation storage request
1734
+ // This is called by consent.service.ts to store delegation tokens directly in DO storage.
1735
+ // Using DO storage instead of KV eliminates eventual consistency issues since
1736
+ // both storage and retrieval happen on the same DO instance (strongly consistent).
1737
+ if (url.pathname === "/_internal/delegation/store" && request.method === "POST") {
1738
+ logger.info("[MCPICloudflareAgent] Handling internal delegation store request");
1739
+ try {
1740
+ const body = await request.json();
1741
+ if (!body.sessionId || !body.delegationToken || !body.delegationId || !body.agentDid) {
1742
+ return new Response(JSON.stringify({ success: false, error: "Missing required fields" }), { status: 400, headers: { "Content-Type": "application/json" } });
1743
+ }
1744
+ await this.storeDelegationToStorage(body);
1745
+ logger.info("[MCPICloudflareAgent] ✅ Delegation stored to DO storage:", {
1746
+ sessionId: body.sessionId.slice(0, 20) + "...",
1747
+ delegationId: body.delegationId,
1748
+ hasUserDid: !!body.userDid,
1749
+ });
1750
+ return new Response(JSON.stringify({ success: true }), { status: 200, headers: { "Content-Type": "application/json" } });
1751
+ }
1752
+ catch (error) {
1753
+ logger.error("[MCPICloudflareAgent] Delegation store request failed:", error);
1754
+ return new Response(JSON.stringify({
1755
+ success: false,
1756
+ error: error instanceof Error ? error.message : String(error),
1757
+ }), { status: 500, headers: { "Content-Type": "application/json" } });
1758
+ }
1759
+ }
1801
1760
  // Handle internal cache-clear request
1802
1761
  if (url.pathname === "/_do/cache-clear" && request.method === "POST") {
1803
1762
  logger.info("[MCPICloudflareAgent] Handling internal cache-clear request");
@@ -1936,6 +1895,93 @@ export class MCPICloudflareAgent extends McpAgent {
1936
1895
  };
1937
1896
  }
1938
1897
  }
1898
+ // ============================================================================
1899
+ // DO-BASED DELEGATION STORAGE
1900
+ // ============================================================================
1901
+ // These methods provide strongly consistent delegation storage using the
1902
+ // Durable Object's internal storage (this.ctx.storage). This eliminates the
1903
+ // eventual consistency issues with KV that require retry mechanisms.
1904
+ //
1905
+ // Flow:
1906
+ // 1. Consent service (Worker) calls /_internal/delegation/store via DO stub
1907
+ // 2. DO stores delegation in this.ctx.storage keyed by sessionId
1908
+ // 3. Tool execution reads from this.ctx.storage (same DO = strongly consistent)
1909
+ // ============================================================================
1910
+ /**
1911
+ * Store delegation data to DO internal storage
1912
+ *
1913
+ * Uses multiple storage keys for efficient lookup:
1914
+ * - `delegation:session:${sessionId}` - Primary key for session-based lookup
1915
+ * - `delegation:user:${userDid}:agent:${agentDid}` - Secondary key for user-based lookup
1916
+ *
1917
+ * @param data - Delegation data to store
1918
+ */
1919
+ async storeDelegationToStorage(data) {
1920
+ const { sessionId, delegationToken, delegationId, userDid, agentDid } = data;
1921
+ const storageData = {
1922
+ delegationToken,
1923
+ delegationId,
1924
+ userDid,
1925
+ agentDid,
1926
+ storedAt: Date.now(),
1927
+ };
1928
+ // Store with session key (primary for tool execution)
1929
+ const sessionKey = `delegation:session:${sessionId}`;
1930
+ await this.ctx.storage.put(sessionKey, storageData);
1931
+ // Store with user+agent key (for persistence across sessions)
1932
+ if (userDid) {
1933
+ const userAgentKey = `delegation:user:${userDid}:agent:${agentDid}`;
1934
+ await this.ctx.storage.put(userAgentKey, storageData);
1935
+ }
1936
+ logger.debug("[MCPICloudflareAgent] Delegation stored to DO storage:", {
1937
+ sessionKey,
1938
+ hasUserAgentKey: !!userDid,
1939
+ delegationId,
1940
+ });
1941
+ }
1942
+ /**
1943
+ * Retrieve delegation from DO internal storage
1944
+ *
1945
+ * Looks up in priority order:
1946
+ * 1. Session key (primary)
1947
+ * 2. User+agent key (if userDid available)
1948
+ *
1949
+ * @param sessionId - Session ID
1950
+ * @param userDid - Optional user DID for secondary lookup
1951
+ * @param agentDid - Agent DID for secondary lookup
1952
+ * @returns Delegation data or undefined if not found
1953
+ */
1954
+ async getDelegationFromStorage(sessionId, userDid, agentDid) {
1955
+ // Try session key first
1956
+ const sessionKey = `delegation:session:${sessionId}`;
1957
+ const sessionData = await this.ctx.storage.get(sessionKey);
1958
+ if (sessionData) {
1959
+ logger.debug("[MCPICloudflareAgent] Delegation found via session key:", {
1960
+ sessionId: sessionId.slice(0, 20) + "...",
1961
+ delegationId: sessionData.delegationId,
1962
+ });
1963
+ return sessionData;
1964
+ }
1965
+ // Try user+agent key if we have userDid
1966
+ if (userDid && agentDid) {
1967
+ const userAgentKey = `delegation:user:${userDid}:agent:${agentDid}`;
1968
+ const userAgentData = await this.ctx.storage.get(userAgentKey);
1969
+ if (userAgentData) {
1970
+ logger.debug("[MCPICloudflareAgent] Delegation found via user+agent key:", {
1971
+ userDid: userDid.slice(0, 20) + "...",
1972
+ agentDid: agentDid.slice(0, 20) + "...",
1973
+ delegationId: userAgentData.delegationId,
1974
+ });
1975
+ return userAgentData;
1976
+ }
1977
+ }
1978
+ logger.debug("[MCPICloudflareAgent] No delegation found in DO storage:", {
1979
+ sessionId: sessionId.slice(0, 20) + "...",
1980
+ hasUserDid: !!userDid,
1981
+ hasAgentDid: !!agentDid,
1982
+ });
1983
+ return undefined;
1984
+ }
1939
1985
  /**
1940
1986
  * Handle MCP-I handshake JSON-RPC request
1941
1987
  *