Laravel MCP 1.0 leans into protocol revision 2026-07-28: every request stands alone. The MCP-Session-Id header is gone. Request::sessionId(), setSessionId(), and SessionInitialized are gone. If your tools cached “conversation state” under a session id, that design does not survive the upgrade.
That sounds scary until you remember how you already scale Laravel: Octane workers, queues, and explicit keys. Stateless MCP is the same idea with stricter honesty.
As of the 1.0 upgrade guide and Laravel MCP docs, modern requests carry protocol version and client capabilities in params._meta. Older initialize clients still negotiate, but new work should assume no server-side MCP session.
What you lose when sessions disappear
You lose a free place to stash:
- “Last tool the user ran”
- Multi-step wizard progress keyed only by MCP session
- Implicit auth context that was set once at initialize time
You do not lose:
- Bearer / OAuth identity on each HTTP request
- Database rows, Redis keys, and queue jobs
- Idempotency keys you already should have had for mutations
If a feature needed a session, give it a first-class id the client sends (workspace id, run id, approval id) in tool arguments or _meta.

Scale with Octane and queues
HTTP MCP endpoints are ordinary Laravel routes (Mcp::web). Octane helps when you want warm workers and lower boot cost for chatty agents. Rules that already apply to Octane still apply:
- Do not leak mutable static state between requests
- Resolve tenant and auth per request
- Prefer short tool handlers; push heavy work to queues
Long-running work pattern:
- Tool validates auth + input.
- Dispatch a job; return a structured
job_id/ status URL immediately. - Store progress in Redis or the database.
- Optional follow-up tool
get_job_statusreads that store.
That is more reliable than holding an SSE stream open for ten minutes of report generation, and it survives worker restarts. Pair with Laravel Octane high traffic notes if you are tuning the runtime.
Streaming tools (generators + SSE on web servers) still work for progress notifications. Treat them as transport, not as durable session state.
Auth and idempotency become the session

Every mutation tool should already:
- Authenticate the actor (
auth:api/ Sanctum / your middleware). - Authorize the tenant.
- Accept an idempotency key for refunds, rotates, and creates.
- Return structured errors the model can recover from.
- Write an audit line.
Without MCP sessions, those five are not optional polish. They are how two Octane workers and a retried agent call stay coherent. See MCP idempotency for Laravel mutations.
For protocol 2026-07-28 HTTP clients, also send matching MCP-Protocol-Version, Mcp-Method, and (for calls) Mcp-Name headers. Mismatches return HTTP 400 with JSON-RPC -32020. Update feature tests that used bare postJson.
Correlation without MCP sessions
Pass your own identifiers:
{
"params": {
"name": "refund-order",
"arguments": {
"order_id": "gid://...",
"idempotency_key": "8f3c...",
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"app.correlation_id": "run_01J..."
}
}
}
}
Log app.correlation_id (or your chosen key) beside tenant and user ids. Agents can resume by asking for status with that id. Humans can grep it in observability.
Searchable catalogs and cache hints from Laravel MCP 1.0 (searchable tool catalogs) also assume list/read responses may be cached while tool calls stay uncacheable. Stateless fits that model: discovery can be hot; mutations stay cold and keyed.
Migration checklist from 0.9 session code
- Delete
sessionIdusage andSessionInitializedlisteners. - Move conversation memory to your app DB / Redis with explicit ids.
- Ensure every web MCP route still has auth middleware.
- Add idempotency on money and inventory tools if missing.
- Fix tests for
_meta+ MCP headers on2026-07-28clients. - Load-test Octane with concurrent tool calls for the same tenant.
- Document for agent authors: “no server session; send correlation + idempotency.”
What “stateless” does not mean
Stateless MCP does not mean “forget the user.” It means the MCP transport does not own a sticky session bag between JSON-RPC messages.
Your product still has:
- Logged-in users and OAuth tokens
- Tenant databases and feature flags
- Conversation threads in your tables if you build a chat product
- Approval records waiting for a human
Those live in application storage. The MCP server becomes a thin, horizontally scalable door into that storage. That is the same split you want for multi-tenant Laravel APIs: the request carries identity; the database carries history.
When an agent “resumes” work, it should call a tool with the ids you issued earlier (run_id, draft_id, approval_id), not hope the same load balancer owns an in-memory map from last Tuesday.
Takeaways
- Laravel MCP 1.0 processes each request independently; MCP session APIs are removed.
- Scale with Octane and queues; keep durable state in Redis/DB under your own ids.
- Auth, tenant checks, and idempotency replace “remembered” session context.
- Modern calls carry protocol + capabilities in
params._meta(and matching HTTP headers). - Streaming is fine for progress; it is not a substitute for durable job state.
If you deleted MCP-Session-Id tomorrow, which of your tools would still be safe under two parallel Octane workers retrying the same agent turn?