Claude Opus 5.5

agents-0-22-0-14-b

Claude Code's read-only tools, web search on, ludion sync off

Question

Where does MCPClientManager get its storage from in the Cloudflare Agents SDK?

Answer key

A correct answer says
`MCPClientManager` gets storage from the Lifecycle it is installed on; the `storage` option was removed, so standalone construction with explicit DurableObjectStorage is not supported.
The stale answer
Construct `MCPClientManager` with a `storage` option pointing at DurableObjectStorage.

Answer

In the installed `agents` package, `MCPClientManager` doesn't take storage as a constructor argument. Its options are only `env` and `createAuthProvider`. It extends `LifecycleCapability` and gets storage through `this.lifecycle.storage`. When the host Lifecycle (an Agent or Durable Object) installs the capability, it supplies a `LifecycleServices` object whose `storage` field is the host Durable Object's `DurableObjectStorage`, i.e. its `ctx.storage`.

`onStart()` uses that storage to check the MCP schema version and create the MCP server table if needed (`node_modules/agents/dist/client-1tEQ1Oqz.js:1304-1306`). It then restores the persisted HTTP and RPC connections from the same storage. If the manager isn't installed on a Lifecycle, as in isolated unit tests, `lifecycleServices` is undefined, so there's no storage to use.

Answering cost $0.17.

Grade

correct

The judge's reason: It says storage comes from the host Lifecycle via lifecycle services, that there is no storage constructor option, and that a standalone manager has no storage.

The judge's log

Session log

What the agent wrote, the tools it called, and what they returned, from docs/bench/2026-10-11-done3/runs/B-project/agents-0-22-0-14-b.jsonl. Thinking and the session's settings are left out. The raw log

  1. Tool call: Grep

    {
      "pattern": "class MCPClientManager",
      "output_mode": "content",
      "-A": 40
    }
  2. Tool output

    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts:2769:declare class MCPClientManager extends LifecycleCapability {
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2770-  private readonly _name;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2771-  private readonly _version;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2772-  mcpConnections: Record<string, MCPClientConnection>;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2773-  /** Cache only the current catalog so old schema graphs are not retained. */
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2774-  private readonly _aiToolSchemas;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2775-  private _didWarnAboutUnstableGetAITools;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2776-  private _oauthCallbackConfig?;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2777-  private _connectionDisposables;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2778-  private readonly _env;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2779-  private readonly _createAuthProviderFn;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2780-  private _isRestored;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2781-  private _pendingConnections;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2782-  private _elicitationHandlers?;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2783-  /** @internal Protected for testing purposes. */
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2784-  protected readonly _onObservabilityEvent: Emitter<MCPObservabilityEvent>;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2785-  readonly onObservabilityEvent: Event<MCPObservabilityEvent>;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2786-  private readonly _onServerStateChanged;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2787-  /**
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2788-   * Event that fires whenever any MCP server state changes (registered, connected, removed, etc.)
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2789-   * This is useful for broadcasting server state to clients.
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2790-   */
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2791-  readonly onServerStateChanged: Event<void>;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2792-  /**
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2793-   * Construct a reusable Durable Object MCP client capability.
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2794-   *
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2795-   * @param _name - MCP client implementation name sent during negotiation.
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2796-   * @param _version - MCP client implementation version sent during negotiation.
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2797-   * @param options - Optional runtime bindings and OAuth configuration.
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2798-   */
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2799-  constructor(
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2800-    _name: string,
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2801-    _version: string,
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2802-    options?: MCPClientManagerOptions
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2803-  );
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2804-  /** Restore persisted HTTP and RPC connections before the host handles work. */
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2805-  onStart(): Promise<void>;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2806-  /** Intercept a registered MCP OAuth callback request. */
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2807-  onRequest(context: CapabilityRequestContext): Promise<Response | undefined>;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2808-  private oauthCallbackResponse;
    node_modules\agents\dist\agent-routing-Bhdbvf7s.d.ts-2809-  /**
    --
    node_modules\agents\dist\client-1tEQ1Oqz.js.map:1:[Omitted long matching line]
  3. Tool call: Grep

    {
      "pattern": "type MCPClientManagerOptions|interface MCPClientManagerOptions|declare (abstract )?class LifecycleCapability|this\\.storage|ctx\\.storage|get storage",
      "path": "node_modules\\agents\\dist\\agent-routing-Bhdbvf7s.d.ts",
      "output_mode": "content",
      "-A": 25
    }
  4. Tool output

    1057: * A typical implementation reads/writes a single key on `this.ctx.storage`
    1058- * inside a Durable Object or Agent.
    1059- */
    1060-interface MCPStorageApi {
    1061-  get(): Promise<TransportState | undefined> | TransportState | undefined;
    1062-  set(state: TransportState): Promise<void> | void;
    1063-}
    1064-/** Shape of the persisted transport state. */
    1065-interface TransportState {
    1066-  sessionId?: string;
    1067-  initialized: boolean;
    1068-  initializeParams?: InitializeRequestParams;
    1069-}
    1070-interface WorkerTransportOptions extends WebStandardStreamableHTTPServerTransportOptions {
    1071-  /**
    1072-   * CORS options applied to every response and to OPTIONS preflight.
    1073-   * Defaults: `origin: *`, expose `mcp-session-id`, allow the standard MCP
    1074-   * methods/headers, max-age 86400.
    1075-   */
    1076-  corsOptions?: CORSOptions;
    1077-  /**
    1078-   * Optional storage adapter for persisting transport state across DO
    1079-   * hibernation / restart. Use this to keep an MCP session alive across
    1080-   * Durable Object wake-ups.
    1081-   */
    1082-  storage?: MCPStorageApi;
    --
    2743:type MCPClientManagerOptions = {
    2744-  /**
    2745-   * Runtime bindings used to restore persisted RPC MCP connections.
    2746-   * Required when the durable catalog contains `rpc://` servers.
    2747-   */
    2748-  readonly env?: Cloudflare.Env /** Construct the OAuth provider used for a persisted HTTP server. */;
    2749-  readonly createAuthProvider?: (callbackUrl: string) => AgentMcpOAuthProvider;
    2750-};
    2751-/**
    2752- * Filter options for scoping tools, prompts, resources, and resource templates
    2753- * to a subset of connected MCP servers. All specified criteria are AND'd together.
    2754- */
    2755-type MCPServerFilter = {
    2756-  /** Include only connections matching this server ID (or IDs). */ serverId?:
    2757-    | string
    2758-    | string[] /** Include only connections whose stored name matches (or is in) this value. */;
    2759-  serverName?:
    2760-    | string
    2761-    | string[] /** Include only connections currently in this state (or states). */;
    2762-  state?: MCPConnectionState | MCPConnectionState[];
    2763-};
    2764-/**
    2765- * A Durable Object capability that persists and aggregates MCP client
    2766- * connections. Installing it directly on a Lifecycle (outside `agent.mcp`)
    2767- * is experimental: that capability surface may change before stabilizing.
    2768- */
    --
    6701:   *       this.ctx.storage,
    6702-   *       this.name,
    6703-   *       callbackUrl
    6704-   *     );
    6705-   *   }
    6706-   * }
    6707-   *
    6708-   * @param callbackUrl The OAuth callback URL for the authorization flow
    6709-   * @returns An {@link AgentMcpOAuthProvider} instance used by {@link addMcpServer}
    6710-   */
    6711-  createMcpOAuthProvider(callbackUrl: string): AgentMcpOAuthProvider;
    6712-  private broadcastMcpServers;
    6713-}
    6714-/**
    6715- * Namespace for creating Agent instances
    6716- * @template Agentic Type of the Agent class
    6717- * @deprecated Use DurableObjectNamespace instead
    6718- */
    6719-type AgentNamespace<Agentic extends Agent<Cloudflare.Env>> =
    6720-  DurableObjectNamespace<Agentic>;
    6721-/**
    6722- * Agent's durable context
    6723- */
    6724-type AgentContext = DurableObjectState;
    6725-type EmailRoutingOptions<Env> = AgentOptions<Env> & {
    6726-  resolver: EmailResolver<Env>;
  5. Agent

    Let me check LifecycleCapability to see how storage is provided.
  6. Tool call: Grep

    {
      "pattern": "class LifecycleCapability",
      "path": "node_modules\\agents\\dist",
      "glob": "*.d.ts",
      "output_mode": "content",
      "-B": 15,
      "-A": 30
    }
  7. Tool output

    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-266-   * (Agent adds tracing span scope). Pass `scope` to make a live
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-267-   * connection or request ambient for the callback.
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-268-   */
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-269-  readonly runInHostContext: (
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-270-    fn: () => unknown,
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-271-    scope?: LifecycleHostContextScope
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-272-  ) => Promise<unknown>;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-273-  readonly events: LifecycleEvents;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-274-  readonly routes: LifecycleRoutes;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-275-};
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-276-/**
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-277- * Base class for capabilities that consume standard Lifecycle services.
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-278- *
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-279- * @experimental The API surface may change before stabilizing.
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-280- */
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts:281:declare abstract class LifecycleCapability<Props extends object = object> {
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-282-  readonly capabilityId: string;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-283-  /** How this capability claims traffic; see {@link DurableObjectCapability.claims}. */
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-284-  readonly claims: "selective" | "catch-all";
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-285-  protected constructor(capabilityId: string);
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-286-  /** Default startup hook; capabilities override when they own startup work. */
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-287-  onStart(_context: CapabilityStartContext<Props>): void;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-288-  /** Standard services when installed, or undefined in isolated unit tests. */
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-289-  protected get lifecycleServices(): LifecycleServices | undefined;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-290-  /** Standard services supplied when Lifecycle installs this capability. */
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-291-  protected get lifecycle(): LifecycleServices;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-292-}
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-293-//#endregion
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-294-//#region src/lifecycle/capability-runner.d.ts
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-295-type MaybePromise<T> = T | Promise<T>;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-296-/** One best-effort event published by a Lifecycle capability. */
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-297-type LifecycleEvent = {
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-298-  /** Stable capability or subsystem name. */ readonly source: string /** Stable event name within that source. */;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-299-  readonly type: string /** Event-specific data. */;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-300-  readonly payload: unknown;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-301-};
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-302-/** Context supplied when durable capabilities start. */
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-303-type CapabilityStartContext<Props extends object = object> = {
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-304-  /** Properties supplied while resolving the Durable Object. */ readonly props:
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-305-    | Props
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-306-    | undefined;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-307-};
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-308-/** Context supplied when durable capabilities inspect an HTTP request. */
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-309-type CapabilityRequestContext = {
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-310-  /** The request entering the Durable Object. */ readonly request: Request;
    node_modules\agents\dist\capability-runner-O5VbQzqg.d.ts-311-};
  8. Tool call: Grep

    {
      "pattern": "type LifecycleServices = \\{",
      "path": "node_modules\\agents\\dist\\capability-runner-O5VbQzqg.d.ts",
      "output_mode": "content",
      "-A": 25
    }
  9. Tool output

    225:type LifecycleServices = {
    226-  /** The host's Durable Object name; see `Lifecycle.name`. */ readonly name: string /** The host class name, as exported from the Worker. */;
    227-  readonly className: string;
    228-  readonly storage: DurableObjectStorage;
    229-  readonly sockets: LifecycleSockets;
    230-  /**
    231-   * Resolve once Lifecycle startup has finished, starting it if needed, and
    232-   * reject if that startup fails. Calls made from inside startup resolve
    233-   * immediately.
    234-   *
    235-   * Await it first in any operation that can run before startup and starts
    236-   * work `onStart` also awaits, such as lazily opening a store. Native RPC
    237-   * bypasses the automatic startup on fetch, alarm and WebSocket events, so
    238-   * such an operation can begin that work outside startup. `onStart` then
    239-   * awaits it while startup holds the input gate (`blockConcurrencyWhile`),
    240-   * which holds back the work's timers and I/O, and startup times out after
    241-   * 30 seconds and resets the object.
    242-   */
    243-  readonly ready: () => Promise<void>;
    244-  /**
    245-   * Startup state: `"zero"` before startup, `"starting"` while capability
    246-   * and host startup hooks run, `"started"` once `ready()` resolves without
    247-   * waiting.
    248-   */
    249-  readonly status: () => LifecycleStatus;
    250-  /**
  10. Tool call: Grep

    {
      "pattern": "lifecycle\\.(storage|sql|ctx)\\w*",
      "path": "node_modules\\agents\\dist",
      "glob": "*.js",
      "output_mode": "content",
      "-o": true,
      "head_limit": 20
    }
  11. Tool output

    node_modules\agents\dist\client-1tEQ1Oqz.js:1304:lifecycle.storage
    node_modules\agents\dist\client-1tEQ1Oqz.js:1305:lifecycle.storage
    node_modules\agents\dist\client-1tEQ1Oqz.js:1306:lifecycle.storage
    node_modules\agents\dist\client-1tEQ1Oqz.js:1353:lifecycle.storage
    node_modules\agents\dist\client-1tEQ1Oqz.js:1398:lifecycle.storage
    node_modules\agents\dist\client-1tEQ1Oqz.js:1407:lifecycle.storage
    node_modules\agents\dist\client-1tEQ1Oqz.js:1408:lifecycle.storage
    node_modules\agents\dist\client-1tEQ1Oqz.js:1585:lifecycle.storage
    node_modules\agents\dist\browser\index.js:239:lifecycle.storage
    node_modules\agents\dist\browser\index.js:241:lifecycle.storage
    node_modules\agents\dist\harness\ai-sdk\index.js:406:lifecycle.storage
    node_modules\agents\dist\harness\think\index.js:727:lifecycle.storage
    node_modules\agents\dist\harness\think\index.js:1690:lifecycle.storage
    node_modules\agents\dist\harness\think\index.js:1806:lifecycle.storage
    node_modules\agents\dist\harness\pi\index.js:530:lifecycle.storage
    node_modules\agents\dist\harness\container\index.js:1042:lifecycle.storage
    node_modules\agents\dist\harness\container\index.js:1186:lifecycle.storage
    node_modules\agents\dist\harness\container\index.js:1199:lifecycle.storage
    node_modules\agents\dist\harness\container\index.js:1202:lifecycle.storage
    node_modules\agents\dist\harness\container\index.js:1223:lifecycle.storage
    
    [Showing results with pagination = limit: 20]
  12. Tool call: Read

    {
      "file_path": "C:\\Users\\haya0\\AppData\\Local\\Temp\\ludion-bench-62A7Aw\\work\\node_modules\\agents\\dist\\client-1tEQ1Oqz.js",
      "offset": 1298,
      "limit": 12
    }
  13. Tool output

    1298			this.onServerStateChanged = this._onServerStateChanged.event;
    1299			this._env = options.env;
    1300			this._createAuthProviderFn = options.createAuthProvider;
    1301		}
    1302		/** Restore persisted HTTP and RPC connections before the host handles work. */
    1303		async onStart() {
    1304			if ((await this.lifecycle.storage.get(MCP_SCHEMA_VERSION_KEY) ?? 0) < CURRENT_MCP_SCHEMA_VERSION) {
    1305				ensureMcpServerTable(this.lifecycle.storage);
    1306				await this.lifecycle.storage.put(MCP_SCHEMA_VERSION_KEY, CURRENT_MCP_SCHEMA_VERSION);
    1307			}
    1308			await this.restoreConnectionsFromStorage(this._name);
    1309			await restoreRpcConnections(this, this._env, this.getRpcServersFromStorage());
  14. Agent

    In the installed `agents` package, `MCPClientManager` doesn't take storage as a constructor argument. Its options are only `env` and `createAuthProvider`. It extends `LifecycleCapability` and gets storage through `this.lifecycle.storage`. When the host Lifecycle (an Agent or Durable Object) installs the capability, it supplies a `LifecycleServices` object whose `storage` field is the host Durable Object's `DurableObjectStorage`, i.e. its `ctx.storage`.
    
    `onStart()` uses that storage to check the MCP schema version and create the MCP server table if needed (`node_modules/agents/dist/client-1tEQ1Oqz.js:1304-1306`). It then restores the persisted HTTP and RPC connections from the same storage. If the manager isn't installed on a Lifecycle, as in isolated unit tests, `lifecycleServices` is undefined, so there's no storage to use.