You can now grant access to specific Workers and choose from four roles to control the level of access you give teammates, agents, and CI/CD workflows.
Choose from four roles to control the level of access:
Metadata Read-Only: View settings, metrics, logs, and traces without access to Worker code or the ability to make changes.
Content Read-Only: Read Worker code, settings, and observability data without the ability to modify or deploy changes.
Editor: Update and deploy a Worker without the ability to delete it.
Admin: Everything in Editor, plus the ability to delete the Worker.
Worker-level access controls are available today for all customers. You can configure them in the Cloudflare dashboard, through the API, or with Terraform.
Roles designed for how teams build
Give Metadata Read-Only to a debugging agent so it can inspect settings and observability data without seeing Worker code. Give Content Read-Only to a code review agent so it can read code without changing it. Give Editor to a CI/CD workflow so it can deploy without deleting the Worker or accessing other Workers. Admin gives a teammate or agent full control over the Worker, including the ability to delete it.
Apply these roles across all Developer Platform products, across all Workers, or to an individual Worker.
Durable Objects
You can use granular permissions to control access to Durable Objects. Durable Objects do not have their own roles or scopes. Instead, they inherit the permissions assigned to the Worker that implements them.
In the Cloudflare dashboard, go to Manage Account > Members and select a member. Create a permission policy, set the scope to Individual Workers, select the Workers they need, and choose a role to grant the right level of access.
If several people on the same team or project need the same access, assign the permission policy to a User Group instead of each member individually. Everyone added to the group automatically inherits the policy.
Create a scoped API token
For an agent or CI/CD workflow, go to Manage Account > Account API Tokens and create an account-owned API token. Set the scope to Specified Workers, select the Workers the token can access, and choose a role to grant the right level of access.
Miniflare v5 prepares Cloudflare local development tooling for the upcoming cf CLI.
Miniflare powers local Workers development behind wrangler dev, the Cloudflare Vite plugin, and @cloudflare/vitest-plugin.
Most projects should use those tools instead of depending on Miniflare directly, and Miniflare v5 will not require any action.
The most significant change is a new configuration shape which aligns Miniflare with cloudflare.config.ts, the programmatic Cloudflare configuration format now available for testing.
Other breaking changes include:
Removed deprecated APIs and options, such as legacy alpha D1 bindings.
Removed now-unused, internal APIs like wrappedBindings
Removed Miniflare's built-in module discovery; higher-level tools like Wrangler and the Vite plugin should be providing the module graph.
Moved local-only /cdn-cgi routes under /cdn-cgi/local.
Replaced per-resource persistence options with shared persistence root options.
You can now manage Email Routing rules that route emails to Workers from your Wrangler configuration. Add literal addresses or a catch-all address to the top-level addresses field:
{ "$schema": "./node_modules/wrangler/config-schema.json", "name": "invoice-handler", "main": "src/index.ts", // Set this to today's date "compatibility_date": "2026-10-16", "addresses": [ "invoice@yourdomain.com" ]}
name = "invoice-handler"main = "src/index.ts"# Set this to today's datecompatibility_date = "2026-10-16"addresses = ["invoice@yourdomain.com"]
When you run wrangler deploy, Wrangler creates rules for new addresses, updates existing rules managed by the Worker, and removes managed rules that are no longer in the configuration. Wrangler shows the planned changes and asks for confirmation before applying potentially destructive changes.
Email Routing rules created by Wrangler also appear in the dashboard but with an icon that identifies them.
Enterprise customers can now configure a zone's CDN Maximum Upload Size up to 5 GB directly from the Network page in the Cloudflare dashboard. This removes the need to contact your account team or Cloudflare Support when applications need to accept request bodies larger than 500 MB and no greater than 5 GB.
The default maximum upload size remains 500 MB. Upload limits above 5 GB still require additional configuration through your account team or Cloudflare Support.
Very large uploads may reach connection or read timeouts before reaching the configured size limit. Make sure clients and origins allow enough time to complete the transfer when increasing this setting.
You can now deploy Workers with larger dependencies, heavier frameworks, and more code without hitting size limits.
When you deploy a Worker, Wrangler bundles your code and compresses it before uploading. Previously, Cloudflare checked that compressed size and rejected deploys over 3 MB (Free) or 10 MB (Paid). That limit has been removed. Cloudflare now only checks the uncompressed size of your bundle, which is 64 MiB across all plans.
To check your Worker's bundle size before deploying:
wrangler deploy --outdir bundled/ --dry-run
Total Upload: 259.61 KiB / gzip: 47.23 KiB
The Total Upload value is your uncompressed bundle size. This is what counts against the 64 MiB limit. The gzip value is shown for reference but is no longer a limit.
The wsgi.entrypoint is equivalent to creating a WorkerEntrypoint class and using the wsgi.fetch method. If you want more control over the WorkerEntrypoint class, you can do so:
Durable Objects can have up to ten distinct Dynamic Workers with in-flight requests, increased from four. This limit applies across all concurrent requests to the same Durable Object because they share an input/output (I/O) context. Other Workers can have up to four distinct Dynamic Workers with in-flight requests per request.
Multiple in-flight requests to the same Dynamic Worker count as one toward this limit.
Console methods now preserve exception details in your Worker's logs. When your Worker logs an exception, the corresponding log entry includes the exception name, message, and stack.
For example, your Worker can catch and log an exception:
try { throw new Error("Deliberately created exception");} catch (error) { console.error("caught exception:", error);}
try { throw new Error("Deliberately created exception");} catch (error) { console.error("caught exception:", error);}
If you use Workers Observability, your log is automatically enriched with structured error information. The following example shows how the enriched log appears in the Cloudflare dashboard:
The exception's stack trace appears directly in the log message.
If you send telemetry to a Tail Worker, the Tail Worker now receives a log entry with an errorInfo array:
{ "message": ["Request failed:", "RangeError: Value out of range"], "errorInfo": [ null, { "name": "RangeError", "message": "Value out of range", "stack": "RangeError: Value out of range\n at ..." } ], "level": "error", "timestamp": 1784851200000}
Each errorInfo item corresponds to the console argument at the same index in message. Arguments that are not exceptions have a null entry.
Wrangler and the Cloudflare API MCP server now use optional OAuth scopes. During authorization, you can choose which optional scopes to grant instead of approving every scope requested by each client.
The consent dialog now includes the option to edit the permissions you grant to Wrangler or the Cloudflare API MCP server:
You can then choose which specific permissions to grant:
Required scopes remain selected. Choosing fewer optional scopes limits each tool's access to the permissions needed for your workflow.
If a command or tool call needs a scope that you declined, reauthorize the client and grant that scope.
Durable Object namespaces now have a Deployments tab in the Cloudflare dashboard, showing the versions of the backing Worker that are currently live and the traffic split between them.
A Durable Object namespace is backed by a Worker script, so its deployments are the same as that Worker's deployments. Previously, checking on a gradual deployment in progress for a Durable Object meant navigating to the backing Worker. The new tab surfaces that information directly on the namespace, alongside the metrics that matter for it: requests, error rate, and wall time per version.
The tab is read-only — promoting, rolling back, or splitting traffic on a deployment is still managed from the backing Worker's Deployments tab.
Actual vs. configured traffic split
The Traffic % column, for both Workers and Durable Objects, now shows the actual, observed traffic share for each version next to the percentage you configured. Previously, this column only showed the configured percentage. If you moved a deployment from 50/50 to 100% on a new version, the configured number updated immediately, but requests take time to catch up, and there was no way to tell how far along that shift was without checking metrics elsewhere.
Actual traffic share is calculated from the same GraphQL Analytics API data that powers other Workers and Durable Objects metrics, so standard ingestion delay and sampling apply. Durable Objects analytics can lag Workers analytics by several minutes, so a version's actual share may take a little longer to catch up after a change.
To view this, go to Workers & Pages > Durable Objects, select a namespace, then select the Deployments tab. For more on how gradual deployments work, refer to Gradual deployments.
Version 1 of the Workers Vitest integration is published as @cloudflare/vitest-plugin ↗︎. The package was formerly named @cloudflare/vitest-pool-workers.
The Vitest configuration API is unchanged. Existing projects must update the dependency name, package imports, and TypeScript types entries.
Workers can now export a connect() handler without enabling any compatibility flag. Previously, using a connect() handler required the experimental compatibility flag, which cannot be enabled in production.
A connect() handler lets a Worker accept a TCP connection forwarded from another Worker over a service binding, and read and write raw bytes on it using a Socket object:
Protect an application across all its domains at once
Until now, if a Worker was reachable on a route, a Custom Domain, and a workers.dev URL, you had to manually add each one to an Access application and keep the list in sync whenever routes or domains changed.
Now, Access attaches the policy to the Worker itself, so every associated domain and preview URL stays protected even when its routes or domains change.
Protect all new and existing Workers by default
Make all Workers private by default, so every existing and newly created Worker requires sign-in before anyone can reach it.
If a specific Worker should remain publicly accessible, add a Worker-level bypass to exempt it.
Whether you protect a single application or all Workers at once, you can choose whether to protect preview deployments only or both previews and production, and control who can sign in by Cloudflare account membership, email address, or email domain.
For more advanced policy options, edit the policy in Zero Trust ↗︎.
View all of your Worker Access policies
You can view and manage all of your Access policies in the Access tab of the Workers & Pages section in the dashboard.
See who is accessing your Worker
When Access is enabled on your Worker, every authenticated request includes ctx.access. Call ctx.access.getIdentity() to get the user's email, name, and groups — no manual JWT validation required.
export default { async fetch(request, env, ctx) { if (!ctx.access) { return new Response("Access did not run", { status: 401 }); } const identity = await ctx.access.getIdentity(); return Response.json({ aud: ctx.access.aud, email: identity?.email }); },};
Test Access locally
You can now test Cloudflare Access locally with wrangler dev. Add a dev block to your wrangler.jsonc:
Your Worker will receive this identity through ctx.access and ctx.access.getIdentity(), letting you test authenticated and unauthenticated flows without deploying. Remove the dev block to simulate unauthenticated requests.
API and programmatic access
You can also set up these policies through the Workers API instead of the dashboard.
Cloudflare Agents ↗︎ now has a dedicated home in the Cloudflare dashboard that lists your traced agents and subagents.
Select an agent to inspect its traces in two ways:
Replay a session assembles the full conversation for a turn: system instructions, user messages, model reasoning, tool calls with arguments and results, and the final response.
View a trace shows the execution timeline, connecting agent operations to Workers infrastructure so you can see where time was spent.
wrangler dev and vite dev automatically capture structured OpenTelemetry traces and correlated console logs during local Worker invocations.
Debug with AI agents
When the tooling detects an AI agent session, it prints a terminal hint pointing to the Local Explorer API at /cdn-cgi/local/explorer/api. The API serves an OpenAPI schema and exposes a read-only observability query endpoint for discovering telemetry, querying traces and logs, and inspecting binding state.
The agent can identify the exact failing operation, fix the code, rerun the request, and verify the result. This debug loop requires no deployment or temporary logs.
Inspect traces in Local Explorer
Humans can inspect the same traces and correlated console logs in the Local Explorer browser UI. Each trace shows spans, timing, attributes, and errors.
Automatic spans cover handler calls, outbound fetch() calls, and binding calls. Custom spans appear alongside these automatic spans.
Workers now enable the nodejs_compat and nodejs_compat_v2 compatibility
flags by default for compatibility dates
of 2026-08-04 or later. These flags are not used for these compatibility
dates because the compatibility date enables the same behavior.
This means all Node.js built-in APIs supported
by the Workers runtime are available by default, including node:crypto,
node:buffer, node:stream, node:net, node:dns, node:fs, node:http,
and more. npm packages that depend on these APIs will work without additional
configuration.
Workers using an earlier compatibility date are not affected. They can still
opt in by adding nodejs_compat to compatibility_flags.
New projects do not need to add either flag. Existing projects can update their
compatibility date without removing them. Wrangler, Miniflare, the Cloudflare
Vite plugin, and Vitest Pool Workers ignore these redundant flags when starting
the runtime.
To turn off Node.js compatibility completely, remove any nodejs_compat and
nodejs_compat_v2 flags. Then add both of the following flags:
{ "$schema": "./node_modules/wrangler/config-schema.json", // Set this to today's date "compatibility_date": "2026-10-16", "compatibility_flags": [ "no_nodejs_compat", "no_nodejs_compat_v2" ]}
# Set this to today's datecompatibility_date = "2026-10-16"compatibility_flags = ["no_nodejs_compat", "no_nodejs_compat_v2"]
wrangler login now supports the OAuth 2.0 Device Authorization Grant ↗︎. Pass --device to authenticate without starting a temporary callback server on localhost:8976:
npx wrangler login --device
Wrangler prints a verification URL and a short user code, opens the URL in your default browser with the code already filled in, and polls Cloudflare for an access token while you approve the request:
⛅️ wrangler 4.119.0────────────────────Attempting to login via OAuth Device Authorization Grant...To authorize Wrangler, please visit: https://dash.cloudflare.com/oauth2/deviceand enter the code: jPqK6QvsYou have 5 minutes to approve this request.Opening a link in your default browser: https://dash.cloudflare.com/oauth2/device?user_code=jPqK6QvsSuccessfully logged in.
The default login flow needs your browser to reach localhost:8976, which is not always possible from containers, remote SSH sessions, or GitHub Codespaces. Previously these environments required forwarding ports or fetching the callback URL with curl from a second terminal session. Because --device has no callback server, those workarounds are no longer necessary.
Since the plain verification URL and user code are both printed to the terminal, you can also approve the request from a phone or another machine. Pass --browser=false to stop Wrangler from opening a browser at all.
Available in Wrangler version 4.119.0 or later. For more information, refer to wrangler login.
We're releasing an early preview of @cloudflare/computer ↗︎, an open-source agent runtime that gives every agent its own computer. The runtime dynamically orchestrates between fast, efficient isolates and full Linux containers, so the agent always runs on the right compute primitive for the task at hand.
@cloudflare/computer provides a virtual filesystem backed by SQLite, which you can populate from cloud storage, source control, or any files you choose. Agents can read, write, and edit files, run shell commands, and interact with Git repositories. All operations are gated, audited, and observed.
Install the package via npm:
npm install @cloudflare/computer
Instantiate a Workspace inside any Durable Object to give your agent a filesystem and execution runtime:
import { Workspace } from "@cloudflare/computer";export class Agent { workspace = new Workspace({ storage: this.ctx.storage, });}
Several execution backends are included or you can write your own:
Isolate runtime — fast, horizontally scalable execution via just-bash and Dynamic Workers, ideal for file manipulation and data processing.
Container runtime — full Linux environment via Cloudflare Containers, mounted through FUSE, for tasks that need native binaries, package managers, or a complete userland.
The AI SDK-compatible toolkit provides common agent tools (read, write, edit, ls, exec) and guides the model to choose the appropriate backend for each task.
You can now call methods between Python and JavaScript Workers using Workers RPC. This works through Service bindings without extra dependencies, schema definitions, or serialization code.
Cross-language RPC calls behave like ordinary function calls. Exceptions propagate to the call site. You can pass structured cloneable types ↗︎ as parameters or return values, and Pyodide Foreign Function Interface (FFI) automatically converts types between languages.
Call a TypeScript Worker from Python
Define a method in a TypeScript Worker:
index.jsjs
import { WorkerEntrypoint } from "cloudflare:workers";export class RpcService extends WorkerEntrypoint { async add(a, b) { return a + b; }}
index.tsts
import { WorkerEntrypoint } from "cloudflare:workers";export class RpcService extends WorkerEntrypoint { async add(a: number, b: number): Promise<number> { return a + b; }}
Call it from a Python Worker through a Service binding:
wrangler check startup now reports your Worker's raw and compressed bundle sizes. It also summarizes local CPU activity during startup directly in your terminal.
Large bundles and costly startup work can introduce cold-start latency, so use this command to find code and large dependencies that slow your Worker before it handles requests.
The summary includes sampled, active, garbage collection, and idle time. Wrangler continues to save a .cpuprofile file for detailed flamegraph analysis in Chrome DevTools or VS Code.
⛅️ wrangler 4.116.0───────────────────────────────────────────────├ Building your Worker│ Worker Built! 🎉│├ Analysing│ Startup phase analysed││ Bundle: 7171.25 KiB / gzip: 2197.00 KiB││ Local startup profile:│ Profile window: 70.3 ms│ Sampled time: 70.3 ms│ Active: 38.5 ms (including 3.7 ms garbage collection)│ Idle: 31.8 ms│ Samples: 36││ CPU Profile has been written to worker-startup.cpuprofile. Load it into the Chrome DevTools profiler (or directly in VSCode) to view a flamegraph.││ Note that the CPU Profile was measured on your Worker running locally on your machine, which has a different CPU than when your Worker runs on Cloudflare.││ As such, CPU Profile can be used to understand where time is spent at startup, but the overall startup time in the profile should not be expected to exactly match what your Worker's startup time will be when deploying to Cloudflare.
The profile runs locally, so its duration will differ from startup time on Cloudflare. For authoritative startup time, deploy your Worker or upload a version.
Available in Wrangler version 4.116.0 or later. For more information, refer to wrangler check startup.
Workers Builds now uses Node.js 24.18.0 by default. The build image preinstalls Node.js 22.23.2 and 24.18.0.
You can continue to override the default with the NODE_VERSION environment variable, an .nvmrc file, or a .node-version file. For more information, refer to Override default versions.
Cloudflare's product-specific MCP servers now support the new MCP 2026-07-28 Specification. Each request runs on a fresh stateless server without an MCP protocol session or protocol-specific Durable Object.
The /mcp endpoint also accepts stateless requests from 2025 Streamable HTTP clients. Most clients can reconnect without configuration changes.
Use /mcp for new connections. Historical /sse URLs continue to work as aliases for the same Streamable HTTP handler, but they no longer serve the deprecated HTTP+SSE transport. If a client forces SSE transport, change it to Streamable HTTP or automatic transport detection.
The Workers runtime now provides built-in tracing.startActiveSpan() and span.end() APIs, allowing you to write custom spans for operations that last beyond a single callback — for example, instrumenting a stream pipeline where the span should stay open until the stream is fully consumed.
This augments the existing API for writing custom spans, tracing.enterSpan(), which automatically ends a span when its callback is returned. With startActiveSpan(), the span remains open after the callback returns, and you call span.end() when the work is complete:
src/index.jsjs
import { tracing } from "cloudflare:workers";const encoder = new TextEncoder();export default { fetch() { return tracing.startActiveSpan("stream-response", (span) => { let timer; const body = new ReadableStream({ start(controller) { controller.enqueue(encoder.encode("Starting...\n")); timer = setTimeout(() => { controller.enqueue(encoder.encode("Complete.\n")); controller.close(); span.setAttribute("stream.status", "complete"); span.end(); }, 1000); }, cancel() { if (timer !== undefined) clearTimeout(timer); span.setAttribute("stream.status", "cancelled"); span.end(); }, }); return new Response(body, { headers: { "content-type": "text/plain" }, }); }); },};
Agents SDK v0.20.0 adds client and server support for the MCP 2026-07-28 release candidate ↗︎. Workers can serve tools, prompts, resources, and elicitation without an MCP transport session or Durable Object. Agents can connect to both MCP 2026-07-28 servers and existing legacy servers.
Client support
The MCP client manager now uses @modelcontextprotocol/client. For each connection, it probes for MCP 2026-07-28 support with server/discover. If the server does not support the stateless protocol, the client continues with the legacy initialize handshake on the same connection. Existing addMcpServer calls do not need a protocol-version setting or separate clients for each protocol generation.
For stateless requests, elicitation uses input_required through multi-round-trip requests (MRTR). The legacy path uses the same form and URL handlers for pushed requests. The SDK collects input, retries the original operation, and resolves the original callTool, getPrompt, or readResource promise with its final result.
OAuth callbacks now validate issuer metadata through the v2 SDK. Discovery state and issuer-bound credentials persist across browser redirects and Durable Object hibernation.
Run stateless servers
createMcpHandler now accepts a factory that returns a server from @modelcontextprotocol/server. The factory creates an isolated server for each request.
The isolated agents/mcp/server entry keeps McpAgent, WorkerTransport, MCP client transports, and SDK v1 modules out of stateless server bundles.
The Workers wrapper validates present browser Origins, supports explicit delegation to trusted Origin middleware, and exposes request handling plus typed change notifications.
Backward compatibility
The same createMcpHandler(createServer)(request, env, ctx) route serves MCP 2026-07-28 clients and legacy clients that use stateless requests. You do not need separate routes or tool definitions for ordinary tools, prompts, and resources.
McpAgent is deprecated and feature-frozen. Migrate existing McpAgent servers to the stateless handler at your earliest convenience. If a server depends on protocol sessions, RPC, pushed server-to-client requests, standalone streams, or replay, use the migration guide to design stateless equivalents and run both routes while clients transition.
Migrate existing SDK v1 servers
Upgrade the Agents SDK:
npm i agents@latest
yarn add agents@latest
pnpm add agents@latest
bun add agents@latest
Move ordinary SDK v1 server definitions into an SDK v2 factory and serve them with createMcpHandler. The handler's default legacy compatibility means most stateless deployments need only one route.
If an existing McpAgent server still needs sessionful features, add the stateless path beside it. Use isLegacyRequest() to send only legacy traffic to the existing route:
Migrate the remaining sessionful features, allow existing sessions to drain, then remove the legacy route. Refer to Migrate to MCP SDK v2 for package changes, compatibility limits, and rollout steps.
Deprecations in v0.20.0
This release deprecates the following Agents SDK APIs:
Deprecated API
Replacement
Status
McpAgent
Use an SDK v2 factory with createMcpHandler for stateless servers. Use the migration guide to replace stateful features before removing a legacy route.
Feature-frozen. No removal version is announced.
createMcpHandler(v1Server, options)
Move the server to an SDK v2 factory and call createMcpHandler(factory, options). Use createLegacyMcpHandler only as a temporary bridge for sessionful features.
Scheduled for removal in the next major version.
MCPClientManager.callTool(params, resultSchema, options) and the equivalent withX402Client overload
Use callTool(params, options) or callTool(confirm, params, options).
Compatibility overload. No removal version is announced.
The MCP 2026-07-28 draft separately deprecates Roots, Sampling, Logging, the old HTTP+SSE transport, and Dynamic Client Registration.