Same topic, related formats. Practice these next.
Same topic, related formats. Practice these next.
Explain what a stateful MCP session enables that a stateless per-call model cannot. Describe at least one concrete multi-step scenario and the scaling tradeoff this model introduces.
A stateful MCP session remembers context across calls (enabling transactions and server-pushed notifications) but the cost is sticky routing that breaks easy horizontal scaling.
Think of a stateless call like a fast-food counter where a new cashier serves you each time. You must repeat your whole order every visit, and nobody can start cooking now to finish later. A stateful MCP session is a sit-down restaurant with one waiter who remembers your table all night. They open a tab, take several courses, and close it at the end. That memory is powerful: the waiter can also walk over and tell you news without being asked. The catch is you are tied to that one waiter. If they go on break, nobody else knows your tab. The restaurant cannot just throw any free waiter at your table the way the fast-food counter could.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example.
Everything important, quickly.
5 min: define session state, give a transaction example, list server-initiated features, explain sticky routing and cleanup, then close on when Streamable HTTP stateless mode is the right call.
// Stateful session: the server holds a DB transaction across calls.
const sessions = new Map<string, { tx: Transaction; lastSeen: number }>();
server.setRequestHandler("tools/call", async (req, sessionId) => {
const { name, arguments: args } = req.params;
if (name === "begin_transaction") {
const tx = await db.beginTransaction();
sessions.set(sessionId, { tx, lastSeen: Date.now() });
return ok("transaction opened");
}
const session = sessions.get(sessionId);
if (!session) throw new Error("no active session"); // sticky routing failed
session.lastSeen = Date.now();
if (name === "execute_query") return ok(await session.tx.query(args.sql));
if (name === "commit") {
await session.tx.commit();
sessions.delete(sessionId); // cleanup on graceful close
return ok("committed");
}
});
// Idle sweeper prevents leaks from ungraceful disconnects.
setInterval(() => {
const cutoff = Date.now() - 30 * 60 * 1000;
for (const [id, s] of sessions) {
if (s.lastSeen < cutoff) { s.tx.rollback(); sessions.delete(id); }
}
}, 60 * 1000);| Concern | Stateful session | Stateless per-call |
|---|---|---|
| Multi-step transactions | Yes, handle persists across calls | No, each call is isolated |
| Server-initiated push | Notifications, subscriptions, sampling | Not supported |
| Capability negotiation | Once at initialize | Per request, if at all |
| Load balancing | Needs sticky routing | Plain round-robin works |
| Replica failure | Live sessions lost | Any replica can serve |
Real products, models, and research that use this idea.
What an interviewer would ask next. Try answering before peeking at the approach.
Red flags and common mistakes that signal junior thinking. Click to expand.
Naming only the benefit and forgetting the cost. Senior answers must connect in-memory session state to sticky routing and the loss of stateless load balancing.
The night-before-the-interview bullets. Scan these on the way to the call.
Primary sources. Skim if you want the original framing.