Back to blog

MCP's Sessionless Release Candidate: What It Means for Security

The MCP 2026-07-28 release candidate removes protocol-level sessions. That is a transport change, not a claim that applications no longer need state. A remote server can sit behind an ordinary load balancer, and a client no longer has to preserve an Mcp-Session-Id between calls.

The state does not disappear. The reference to that state moves into the agent's working context.

A server that once kept a cart, browser, task, draft, or approval in session state now returns an identifier and expects the agent to send it back later. The proposed pattern is deliberately simple: create_basket returns a basket_id; the agent or its harness includes that value when it calls add_item or checkout. The MCP project calls these explicit state handles. They are ordinary strings in tool results and later tool arguments, not a new protocol object with built-in access control. SEP-2567

That distinction creates a runtime-security problem worth taking seriously. A handle can identify a resource without granting the right to use it. If a backend treats possession as permission, a copied value from an agent transcript can become an access path.

The useful change that changes the boundary

The 2026-07-28 release candidate makes MCP stateless at the protocol layer. It removes the initialization handshake and session header. A request carries the protocol version, client information, and capabilities in its own metadata, so any server instance can process it. Requests no longer need to return to the same server instance, and servers no longer need to share a protocol session store. They may still need application state that any instance can resolve from a handle. MCP release candidate

Stateful applications still need stateful objects. A customer-support agent may open a case. A coding agent may start a browser. A finance agent may assemble a payment batch. Those objects must survive more than one tool call, and sometimes more than one model turn.

Before this change, application state could be scoped to a connection, even though MCP did not define a reliable session lifetime. After it, the object name travels through the model. The model may retain it in a conversation summary, hand it to a subagent, include it in a tool call, or repeat it in a message. That is not a defect in the design. It is the point of an addressable handle. It is also why the MCP proposal says handles will appear in chat logs, subagent prompts, copy-paste buffers, and potentially other users' screens. SEP-2567

The security review must therefore change its question. Do not ask whether the handle is secret. Ask whether the service will do anything dangerous merely because the handle is present.

A reference is not authority

Consider a support agent that opens a refund case and receives case_7f4. The identifier is useful. The agent needs it to add evidence, request an approval, and later close the case. It should not be sufficient to issue a refund.

That sounds obvious until the agent has several systems behind it. One tool returns a case identifier. Another tool accepts a case identifier and a dollar amount. A third tool sends the result to a payment provider. A support note may contain an indirect prompt that tells the agent to paste the identifier into a different workflow. If the payment service accepts case_7f4 as proof of authority, the bug is not prompt injection. The bug is object-level authorization.

This is where agent systems differ from a conventional API client. A developer normally decides where an ID goes in code. An agent decides at runtime, after reading data that may be untrusted. The same state reference can move between tools, agents, and conversations without a developer adding a new route.

The practical rule is short:

A handle answers what object the agent means. It must never answer whether the agent may act on that object.

MCP's own guidance takes the same position. For authenticated servers, it recommends validating the pair of handle and authentication context on every call. For unauthenticated servers, a handle is necessarily a bearer token and needs at least 128 bits of cryptographic randomness and a bounded lifetime. SEP-2567

The missing half is an execution grant

Authentication alone does not settle every action decision. A valid user token can prove who called the server. It does not by itself establish whether this agent may change this object now, after seeing this input, for this purpose.

MCP does not define this extra control. We recommend a server-side execution grant for sensitive actions. The model can carry a reference, while the runtime retains the authority record that makes an action possible. That record states who is asking, which agent is acting, which object and action are in scope, and whether the approval is still valid and unrevoked.

When the model asks to act, the runtime evaluates both pieces:

  • The reference identifies case_7f4.
  • The grant says whether the current principal and agent may refund that case, up to a stated amount, before a stated time.

In this pattern, the model does not need to see a provider token or a reusable capability. It only needs enough information to continue the workflow. A gateway can map the model-visible reference to the provider's object after it approves the exact action. This is the useful split: visible state for the agent, invisible authority for the runtime.

It also gives developers a clean contract. A tool handler should not accept case_id and trust it. It should receive an authenticated principal and an approved action context from the runtime, then verify ownership, tenant, lifecycle, and operation before touching the provider. The MCP authorization specification requires authorization on every protected HTTP request and requires a server to validate that each token was issued for that server. Those transport-level checks do not replace resource-level checks inside the application. MCP authorization specification

Why chat retention is now part of access design

A handle that can act like a credential should be treated like one wherever it persists: source control, ticket exports, terminal history, logs. Explicit state handles add agent transcripts to that list.

Not every handle is sensitive. A public issue number is designed to be copied. A private browser context, a payment draft, an approval request, or an export job may not be. The classification should follow the consequence of replay, not the field name.

This produces a useful engineering test. Take a handle from a persisted conversation and replay it under another user, another tenant, another agent, and after the original approval expires. Every attempt should fail with a logged reason. If any replay succeeds because the identifier still resolves, the handle is operating as an ungoverned capability.

Long-running work deserves the same treatment. The release candidate moves Tasks to an extension and removes tasks/list because it cannot be scoped safely without sessions. That is a precise warning: listing or operating on durable agent work requires an explicit ownership model, not a vague idea of which conversation started it. MCP release candidate

Five tests before adopting sessionless MCP

Developers should add these cases to integration tests, not a policy document:

  • Cross-user replay: A second authenticated user submits a captured handle. The server denies it.
  • Cross-tenant replay: The same user identity in a different tenant submits the handle. The server denies it.
  • Agent substitution: A different agent, even one acting for the same user, tries to use the handle outside the original workflow. The runtime evaluates it as a new request.
  • Lifecycle replay: The handle is used after its task is cancelled, its approval expires, or its underlying authorization is revoked. The server denies it.
  • Data-path replay: An agent receives a handle after reading untrusted content, then attempts a sensitive action. The runtime records both the source data and the decision that allowed or denied execution.

The final test matters because prompt injection can influence which handle an agent chooses and where it sends it. That is not a reason to make the model responsible for authorization. It is a reason to preserve provenance and enforce policy below the model. Our earlier work on taint analysis for agent tool calls covers the related problem of tracing untrusted and private data across tools. Here, the focus is narrower: an object reference must not become an authority channel while it moves through that flow.

A better way to think about statelessness

Stateless MCP is not inherently less secure than session-based MCP. It changes the exposure surface by making handles visible to the agent, while leaving the underlying state on the server. That can be a security improvement when the server binds each handle to the authenticated request and the runtime mediates sensitive actions.

The danger is assuming that a new string in an agent transcript is harmless because it is not called a credential. In an agentic system, a reference can become powerful when the next tool is willing to treat it as authority.

The test is concrete: can a copied handle change anything by itself? If the answer is yes, the handle is carrying authority it was never supposed to have.

Where CodeIntegrity fits

CodeIntegrity is designed to treat every tool call as a runtime decision. It binds execution to identity, policy, provenance, and an audit trail, so a model-visible reference cannot quietly become permission to act. If you are moving agents to remote MCP, book a demo to see that control plane in a real workflow.

Other blog

View all