המערכת של YUV.AI

הדרכות

MCP בלי זיכרון: המדריך המעשי לספק החסר-מצב, להגירה ול-Code Mode

גרסה 2026-07-28 של MCP מחקה את הסשנים ואת לחיצת היד initialize, וכל בקשה מביאה איתה את כל ההקשר. בונים שרת מאפס על SDK v2, ממירים שרת ישן, ומבינים למה Pi 1.0 ו-Code Mode נותנים לסוכן לכתוב קוד במקום ללחוץ על כלים.

אהבתם? שתפו
MCP בלי זיכרון: המדריך המעשי לספק החסר-מצב, להגירה ול-Code Mode
מבוסס על מקור · לכתבה המקורית

אמ;לק

5 הדברים שצריך לדעת

בגרסה 2026-07-28 הוסרו לחיצת היד initialize והכותרת Mcp-Session-Id. כל בקשה נושאת ב-_meta את הגרסה ואת יכולות הלקוח.

כל בקשה יכולה לנחות על כל עותק של השרת, מאחורי מאזן עומסים פשוט וגם ב-Serverless, בלי סשנים דביקים ובלי מאגר משותף.

ה-SDK הרשמי של TypeScript התפצל ל-@modelcontextprotocol/server ו-client, ו-createMcpHandler בונה שרת טרי לכל בקשה. בבדיקה שלנו הוא ענה גם ללקוח ישן.

Roots, Sampling, Logging ו-HTTP+SSE סומנו Deprecated. הם ממשיכים לעבוד לפחות 12 חודשים, אבל לא מאמצים אותם בקוד חדש.

הסוכן כותב קוד שקורא לכלי MCP בתוך ארגז חול, במקום לקרוא להם אחד אחד. Pi 1.0 הכניס את זה כ-Codemode, אחרי שבעבר אמר לא ל-MCP.

הכתבה ב־5 שניות · נבנה עם HyperFrames

הפרוטוקול MCP, שמחבר מודלי AI לכלים ולמידע שלנו, עבר בגרסה 2026-07-28 את השינוי הכי גדול שלו מאז שנולד: הוא הפך לחסר מצב (Stateless). אין יותר "לחיצת יד" בתחילת השיחה, אין יותר מזהה סשן, וכל בקשה מגיעה לשרת עם כל מה שהוא צריך לדעת עליה. בבלוג הרשמי של הפרוטוקול קוראים לזה "הגרסה הגדולה ביותר מאז ההשקה", ובצדק.

במקביל, באותם שבועות, Earendil הוציאה את Pi 1.0 עם MCP מובנה, אחרי שבמשך חודשים אמרה שהיא לא תתמוך בו. הדרך שבה הם עשו את זה נקראת Codemode, ובעיניי שני הסיפורים האלה הם בעצם סיפור אחד: הפרוטוקול מתבגר, והופך מצעצוע שרץ על המחשב שלנו לתשתית שאפשר להעמיד בפרודקשן.

במדריך הזה נבנה יחד, מאפס, שרת MCP קטן על הספק החדש, נמיר אליו שרת ישן, ונבין למה כל זה חשוב. נלך עם דוגמה אחת לאורך כל הדרך: שרת בשם ticket-desk, שנותן לסוכן AI לחפש קריאות שירות במערכת התמיכה שלנו.

בואו נתחיל מאפס: מה זה בכלל MCP

פרוטוקול MCP (Model Context Protocol) הוא שפה פתוחה שמגדירה תקן אחד משותף שבו אפליקציית AI מדברת עם כלים ומקורות מידע חיצוניים, עבור כל מי שרוצה לחבר מודל למערכות שלו בלי לכתוב אינטגרציה נפרדת לכל אחד. את הפרוטוקול הציגה Anthropic בסוף 2024, ומאז הוא הפך לסטנדרט שכמעט כל כלי AI רציני מדבר.

התיעוד הרשמי משתמש באנלוגיה שקשה לשפר: הפרוטוקול הוא כמו חיבור USB-C לאפליקציות AI. פעם היה לכל מכשיר כבל משלו, והמגירה הייתה מלאה בכבלים. היום יש שקע אחד, וכל מכשיר שמדבר USB-C מתחבר לכל מחשב שמדבר USB-C. אותו דבר כאן: שרת MCP אחד שכתבנו יעבוד עם Claude, עם Cursor, עם Pi ועם כל לקוח (Client, האפליקציה שמפעילה את המודל) שמדבר את הפרוטוקול.

בואו נפרק את המונחים הבסיסיים, כי נשתמש בהם כל הזמן:

  • שרת MCP הוא תוכנה קטנה שחושפת יכולות. אצלנו זה ticket-desk.
  • כלי (Tool) הוא פעולה שהמודל יכול לבקש להפעיל, כמו lookup-ticket שמקבל מזהה קריאה ומחזיר את הסטטוס שלה.
  • טרנספורט (Transport) הוא "הכביש" שעליו ההודעות נוסעות: או stdio, כלומר תקשורת ישירה עם תוכנה שרצה על אותו מחשב, או HTTP, כשהשרת יושב בענן.

כמה גדול זה? לפי הבלוג הרשמי, לפני הגרסה הזו ה-SDKs הרשמיים כבר הגיעו ל"כמעט חצי מיליארד הורדות בחודש", וגם ה-SDK של TypeScript וגם של Python חצו מיליארד הורדות בסך הכל.

המצב והמכשול: השרת שזוכר כל מי שדיבר איתו

עד הגרסה הקודמת (2025-11-25), כל שיחה עם שרת MCP התחילה בטקס קבוע. הלקוח שלח הודעת initialize ("שלום, אני Claude, אני מדבר בגרסה כזו ותומך ביכולות כאלה"), השרת ענה, והלקוח אישר בהודעת initialized. בשרתים מרוחקים, השרת גם הנפיק מזהה סשן בכותרת בשם Mcp-Session-Id, והלקוח היה חייב לצרף אותו לכל בקשה מאותו רגע.

תחשבו על זה כמו על מלצר במסעדה. בעולם הישן, אנחנו מתיישבים, מספרים למלצר שאנחנו צמחונים ואלרגיים לאגוזים, ומאותו רגע רק הוא יכול לשרת אותנו, כי רק הוא זוכר. אם הוא הולך הביתה באמצע הארוחה, אנחנו מתחילים מהתחלה.

ככה בדיוק נראה ticket-desk בגרסה הישנה. בקוד הרשמי מה-README של SDK v1, השרת מחזיק מפה (Map) של כל הסשנים בזיכרון, ובכל בקשה בודק: יש מזהה סשן? יש לי אותו? אם לא, וזו לא בקשת initialize, הבקשה נדחית:

לפני: ticket-desk על SDK v1, עם סשנים
// BEFORE - SDK v1, protocol 2025-11-25 (stateful)
// based on the official v1 README: "Streamable HTTP > With Session Management"
import express from "express";
import { randomUUID } from "node:crypto";
import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";

const app = express();
app.use(express.json());

// every session lives in THIS process's memory
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};

app.post("/mcp", async (req, res) => {
  const sessionId = req.headers["mcp-session-id"] as string | undefined;
  let transport: StreamableHTTPServerTransport;

  if (sessionId && transports[sessionId]) {
    transport = transports[sessionId];               // known session
  } else if (!sessionId && isInitializeRequest(req.body)) {
    transport = new StreamableHTTPServerTransport({  // handshake
      sessionIdGenerator: () => randomUUID(),
      onsessioninitialized: (id) => { transports[id] = transport; }
    });
    transport.onclose = () => {
      if (transport.sessionId) delete transports[transport.sessionId];
    };
    const server = new McpServer({ name: "ticket-desk", version: "1.0.0" });
    server.tool("lookup-ticket", "Find a support ticket by its id",
      { id: z.string() },
      async ({ id }) => ({
        content: [{ type: "text", text: `Ticket ${id}: open, waiting for customer` }]
      }));
    await server.connect(transport);
  } else {
    res.status(400).json({ jsonrpc: "2.0",
      error: { code: -32000, message: "Bad Request: No valid session ID provided" },
      id: null });
    return;
  }
  await transport.handleRequest(req, res, req.body);
});

app.listen(3000);

למה סשנים הורגים סקייל. כל עוד השרת רץ על המחשב שלנו, זה לא מפריע. הבעיה מתחילה כשרוצים להעמיד את ticket-desk בפרודקשן, לאלפי משתמשים. בשביל זה מריצים כמה עותקים של השרת, ושמים מולם מאזן עומסים (Load Balancer), רכיב שמחלק את הבקשות בין העותקים, כמו מארחת שמושיבה אורחים בין כמה מלצרים.

ועכשיו תראו מה קורה: הבקשה הראשונה הגיעה לעותק A, והוא שמר את הסשן אצלו בזיכרון. הבקשה השנייה הגיעה לעותק B, והוא לא מכיר את מזהה הסשן ודוחה אותה. מסתבר שהיו רק שני פתרונות, ושניהם כואבים:

  • סשנים דביקים (Sticky Sessions): מלמדים את מאזן העומסים לשלוח את אותו לקוח תמיד לאותו עותק. עובד, אבל אם העותק נופל, כל הסשנים שלו הולכים איתו.
  • מאגר סשנים משותף: שמים Redis או מסד נתונים באמצע, וכל עותק בודק שם. עובד, אבל עכשיו יש לנו עוד רכיב לתחזק, ועוד נקודה שיכולה ליפול.

וזה עוד לפני שרתים בסגנון Serverless, כמו Vercel או Cloudflare Workers, שבהם כל בקשה יכולה לרוץ על מכונה אחרת ואין "זיכרון" בין בקשות בכלל. להוגנות צריך להגיד שהגישה הישנה לא הייתה טיפשית: פרוטוקול דו-כיווני עם סשן נתן לשרת לשלוח הודעות ללקוח מתי שרצה, וזה נוח מאוד. פשוט המחיר שלה בתשתית התברר כגבוה מדי.

המענה: כל בקשה מביאה איתה את כל ההקשר

הספק החדש פותר את זה בצורה פשוטה להפליא. לפי ה-changelog הרשמי, הוסרו גם הסשנים ברמת הפרוטוקול וגם הכותרת Mcp-Session-Id, והוסרה לחיצת היד initialize. במקום לספר לשרת פעם אחת מי אנחנו ולקוות שהוא יזכור, כל בקשה נושאת בתוכה את גרסת הפרוטוקול ואת היכולות של הלקוח, בשדה בשם _meta.

בחזרה למסעדה: עכשיו כל הזמנה מגיעה עם פתק מצורף, "צמחוני, בלי אגוזים". כל מלצר יכול לקחת אותה, וכל מלצר יכול ללכת הביתה בלי שאף אחד ירגיש. זה בדיוק מה שהבלוג הרשמי כותב: כל בקשה יכולה לנחות על כל עותק של השרת, מאחורי מאזן עומסים פשוט שמחלק בתורות (Round Robin), בלי אחסון משותף.

עוד כמה שינויים חשובים שנלווים לזה:

  • הקריאה server/discover: קריאה חדשה שכל שרת חייב לממש, שבה הוא מפרסם אילו גרסאות הוא תומך, מה הוא יודע לעשות ומה שמו. הלקוח יכול לשאול אותה מראש, במקום טקס הפתיחה.
  • מצב שצריך לשרוד בין קריאות עובר למקום גלוי: השרת מנפיק "ידית" (Handle), מזהה משלו, והמודל מעביר אותה כפרמטר רגיל לכלי הבא. כמו מספר הזמנה שאנחנו מקריאים בטלפון, במקום לצפות שהמוקדנית תזכור אותנו.
  • ההודעות ping ו-logging/setLevel נעלמו, ורמת הלוגים נשלחת עכשיו בכל בקשה בנפרד.
  • בקשות מרובות סבבים (MRTR): אם כלי צריך מהמשתמש עוד מידע באמצע, השרת מחזיר תוצאת ביניים, והלקוח שולח שוב את הבקשה המקורית עם התשובה.

השוואה

MCP לפני ואחרי: 2025-11-25 מול 2026-07-28

בידיים: בונים את ticket-desk ומעבירים אליו שרת ישן

עכשיו לחלק הכיפי. ה-SDK הרשמי של TypeScript יצא בגרסה 2 יחד עם הספק, ב-28 ביולי 2026 (ב-npm, הגרסה העדכנית ביום כתיבת השורות היא 2.3.1). החבילה האחת @modelcontextprotocol/sdk התפצלה לחבילות נפרדות לשרת וללקוח, והמתאם ל-Node יושב בחבילה משלו. צריך Node 20 ומעלה ו-Zod בגרסה 4.2 ומעלה.

הלב של השרת החדש הוא פונקציה בשם createMcpHandler. היא מקבלת "מפעל" (Factory), פונקציה שבונה שרת, ומריצה אותו מחדש לכל בקשת HTTP. לפי התיעוד, היא לא מחזיקה שום דבר בין בקשות, ולכן שרת v2 הוא חסר מצב ומתרחב לרוחב כברירת מחדל. הנה ticket-desk המלא, שהרצנו בפועל:

אחרי: ticket-desk על SDK v2, חסר מצב
// AFTER - SDK v2, protocol 2026-07-28 (stateless)
// npm install @modelcontextprotocol/server @modelcontextprotocol/node zod
import { createServer } from 'node:http';
import { createMcpHandler, McpServer } from '@modelcontextprotocol/server';
import { toNodeHandler, localhostHostValidation, localhostOriginValidation } from '@modelcontextprotocol/node';
import * as z from 'zod/v4';

// the factory runs fresh for EVERY request - nothing is kept between requests
const handler = createMcpHandler(() => {
  const server = new McpServer({ name: 'ticket-desk', version: '1.0.0' });
  server.registerTool(
    'lookup-ticket',
    {
      description: 'Find a support ticket by its id',
      inputSchema: z.object({ id: z.string() })
    },
    async ({ id }) => ({
      content: [{ type: 'text', text: `Ticket ${id}: open, waiting for customer` }]
    })
  );
  return server;
});

const nodeHandler = toNodeHandler(handler);
const validateHost = localhostHostValidation();     // DNS-rebinding protection
const validateOrigin = localhostOriginValidation();
createServer((req, res) => {
  if (!validateHost(req, res) || !validateOrigin(req, res)) return;
  void nodeHandler(req, res);
}).listen(3000, '127.0.0.1');

שימו לב מה חסר בקוד הזה, וזה בדיוק הקטע המדליק: אין מפה של סשנים, אין randomUUID, אין בדיקת initialize ואין ניקוי סשנים כשהחיבור נסגר. שתי השורות של validateHost ו-validateOrigin הן לא קישוט: התיעוד דורש אותן כדי להגן מפני מתקפה בשם DNS Rebinding, שבה אתר זדוני מנסה לדבר עם שרת שרץ אצלנו על המחשב.

כשהרצנו את השרת ושלחנו קריאה לכלי, התשובה חזרה בלי שום כותרת Mcp-Session-Id, עם resultType: "complete" (שדה שכל תוצאה חייבת לשאת עכשיו) ועם שם השרת בתוך _meta. ובונוס שבדקנו בעצמנו: אותו שרת בדיוק ענה גם לבקשת initialize ישנה בגרסה 2025-11-25, כך שלקוחות שעוד לא התעדכנו ממשיכים לעבוד. לפי עמוד גרסאות הפרוטוקול של ה-SDK, זה מכוון: createMcpHandler משרת את שני הדורות מאותה נקודת קצה.

עוד פרט שכדאי להכיר: בגרסה החדשה בקשת HTTP חייבת לשאת גם כותרת MCP-Protocol-Version וגם את הגרסה בתוך הגוף. כששלחנו בקשה בלי הכותרת, השרת החזיר שגיאה מפורשת שהכותרות והגוף לא מסכימים. ה-SDK בצד הלקוח עושה את זה לבד, אבל מי שכותב לקוח ידני ייתקל בזה מהר.

ההגירה: איך מעבירים שרת ישן. לשרת שכבר קיים יש מסלול מסודר. מדריך ההגירה הרשמי של ה-SDK מתחיל בכלי אוטומטי (Codemod, סקריפט שמשכתב קוד לפי כללים), ואחריו נשארת עבודה ידנית קטנה. ככה זה נראה אצל ticket-desk:

צעד אחר צעד

הגירה של שרת קיים ל-SDK v2, צעד אחרי צעד

1

1. מריצים את ה-codemod הרשמי

בתיקיית הפרויקט: npx @modelcontextprotocol/codemod@latest v1-to-v2 . - הוא משכתב imports, שמות ופונקציות. אחר כך מחפשים מה הוא לא הצליח: grep -rn '@mcp-codemod-error' .

1 / 6

ובמילים: מריצים את ה-codemod הרשמי, מחליפים את החבילה הישנה בחבילות החדשות ומעדכנים את Zod לגרסה 4, הופכים כל server.tool ל-registerTool עם אובייקט הגדרות, ומוחקים את כל ניהול הסשנים לטובת createMcpHandler. אם הקוד שלנו נשען על מזהה הסשן כדי לזכור משהו, זה הזמן להפוך את המידע הזה לידית מפורשת שעוברת כפרמטר. בסוף מריצים tsc --noEmit ואת הבדיקות.

ולגבי לוח הזמנים: הספק החדש אימץ לראשונה מדיניות רשמית של מחזור חיים לפיצ'רים, עם חלון מינימלי של 12 חודשים בין הרגע שפיצ'ר מסומן כ-Deprecated (מיועד להסרה) לבין הרגע שמותר להסיר אותו. בגרסה הזו סומנו כך Roots, Sampling, Logging והטרנספורט הישן HTTP+SSE. לפי הבלוג הרשמי הם "ימשיכו לעבוד לפחות 12 חודשים", אבל מימושים חדשים לא אמורים לאמץ אותם. כלומר, אין פאניקה, אבל יש שעון.

הטרנד השני, Code Mode: כשהסוכן כותב קוד במקום ללחוץ על כפתורים

עכשיו לטרנד השני. נניח שהסוכן שלנו צריך לעבור על 200 קריאות ב-ticket-desk ולמצוא את אלה שבהן הלקוח כועס. בדרך הרגילה, הוא קורא לכלי, מקבל את כל הטקסט לתוך חלון ההקשר (הזיכרון הזמני של המודל בשיחה), קורא לכלי שוב, וכן הלאה. כל קריאה עולה טוקנים, וכל תוצאת ביניים עוברת דרך המודל.

הגישה של Code Mode היא גישה אחרת: במקום לקרוא לכלים אחד אחד, המודל כותב קטע קוד קצר שקורא לכלים, מסנן ומחבר את התוצאות, ורץ בתוך ארגז חול (Sandbox), סביבה סגורה שבה הקוד לא יכול לגעת במה שאסור לו. רק התוצאה הסופית חוזרת למודל. תחשבו על זה כמו ההבדל בין להכתיב למזכירה כל צעד, לבין לתת לה רשימת משימות אחת.

ב-Cloudflare קראו לזה Code Mode, וב-Anthropic פרסמו בנובמבר 2025 מאמר הנדסי על "הרצת קוד עם MCP", עם דוגמה שבה צריכת הטוקנים ירדה מ-150,000 ל-2,000, חיסכון של 98.7%. חשוב לדייק: זו דוגמה אחת שלהם, לא ממוצע. ובאותו מאמר הם גם מזהירים שהרצת קוד דורשת סביבה מבודדת, מגבלות משאבים וניטור.

ו-Pi? ב-29 בספטמבר 2026 Earendil פרסמו פוסט בשם "You Said No MCP!", שצבר 687 נקודות ב-Hacker News. הם מודים שבעבר פסלו את MCP בפודקאסטים ובפוסטים, ומסבירים שהעולם השתנה ו-MCP השתפר. הפתרון שלהם הוא Codemode: ארגז חול של JavaScript שרץ בצד ה-Harness (המעטפת שמפעילה את המודל), ונותן לסוכן לשלב קריאות לכלים בקוד ולבחור באיזה סדר להריץ אותן. ב-1 באוקטובר יצא Pi 1.0 עם Codemode ותמיכה מובנית ב-MCP.

בעיניי: מה זה אומר עלינו, ומה עדיין לא פתור

בעיניי, שני המהלכים האלה משלימים זה את זה. הספק החסר-מצב הופך את שרת ה-MCP לשירות HTTP רגיל, כזה שכל צוות DevOps יודע להעמיד, להרחיב ולנטר. ו-Code Mode מטפל בצד השני של אותה בעיה: איך הסוכן משתמש בעשרות כלים בלי לחנוק את חלון ההקשר.

שורה תחתונה למי שבונה היום: שרת חדש, כותבים ישר על SDK v2. שרת קיים, מתכננים הגירה ברבעון הקרוב, במיוחד אם הוא רץ מאחורי מאזן עומסים. וכדאי לבדוק אם הלקוחות שלנו כבר תומכים: Anthropic הודיעה שהתמיכה בגרסה החדשה "מתגלגלת" למוצרים של Claude, בלי תאריך מדויק לכל מוצר.

וחשוב להגיד גם את המגבלות. המצב לא נעלם, הוא רק עבר מהפרוטוקול אל האפליקציה: עכשיו אנחנו אחראים לידיות, לבקשות שאפשר להריץ פעמיים בלי נזק (Idempotency) ולניסיונות חוזרים. גם חידוש זרם שנקטע הוסר מהטרנספורט, ובקשה שנקטעה באמצע פשוט נשלחת מחדש. ומי שבנה על Sampling או על Roots צריך לתכנן מעבר, גם אם יש לו שנה.

בדקו את עצמכם

למה ticket-desk החדש יכול לרוץ בעשרה עותקים מאחורי מאזן עומסים פשוט, בלי סשנים דביקים?

אז איזה שרת MCP שלנו נעביר ראשון לספק החדש, ומה נגלה כשנעשה את זה?

רוצים ללכת עם זה רחוק יותר?

את כל העולם של MCP, Skills וחיבור Claude לכלים שלנו בלי לכתוב שורת קוד, אני מפרק לעומק בקורס Practical AI with Claude, בעברית. ולמי שרוצה לבנות סוכנים ושרתים בעצמו מהטרמינל, יש את Claude Code Mastery באנגלית.

את הדמואים, כולל שרתי MCP שאני בונה בשידור, אני מעלה ל-YouTube ול-LinkedIn. אפשר לעקוב גם ב-X, ב-Instagram וב-TikTok, ואת הקוד אני משתף ב-GitHub.

אז איזה שרת MCP אצלנו היה מרוויח הכי הרבה מזה שהוא כבר לא צריך לזכור אף אחד?

אהבתם את הכתבה? ספרו לי

רוצים ללמוד את זה לעומק?

אוטומציות ו-AI

הופכים תהליכים לאוטומטיים ומשלבים בהם AI - בלי קוד. טכני אבל לא מפחיד, שלב אחר שלב.

לכל 7 הקורסים · מתחילים מאפס? הקורס למתחילים בחינם

למידע והרשמה

פניות תקשורת

לראיונות, שיתופי פעולה והרצאות, נשמח לדבר.

info@yuv.ai