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

מאז השקת MCP בנובמבר 2024, האימוץ היה מהיר: הקהילה בנתה אלפי שרתי MCP, קיימים SDK-ים לכל שפות התכנות העיקריות, והתעשייה אימצה את MCP כתקן דה-פקטו לחיבור סוכנים לכלים ונתונים.

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

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

צריכת טוקנים מופרזת מכלים פוגעת ביעילות הסוכנים

ככל ששימוש ב-MCP עובר סקיילינג, ישנן שתי תבניות נפוצות שיכולות להגדיל את העלות והשהייה של הסוכנים:

  1. הגדרות כלים מעמיסות על חלון ההקשר;
  2. תוצאות ביניים של כלים צורכות טוקנים נוספים.

1. הגדרות כלים מעמיסות על חלון ההקשר

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

gdrive.getDocument
     Description: Retrieves a document from Google Drive
     Parameters:
                documentId (required, string): The ID of the document to retrieve
                fields (optional, string): Specific fields to return
     Returns: Document object with title, body content, metadata, permissions, etc.
salesforce.updateRecord
    Description: Updates a record in Salesforce
    Parameters:
               objectType (required, string): Type of Salesforce object (Lead, Contact,      Account, etc.)
               recordId (required, string): The ID of the record to update
               data (required, object): Fields to update with their new values
     Returns: Updated record object with confirmation

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

2. תוצאות ביניים של כלים צורכות טוקנים נוספים

רוב לקוחות ה-MCP מאפשרים למודלים לקרוא ישירות לכלי MCP. לדוגמה, תוכלו לבקש מהסוכן שלכם: "הורד את תמלול הפגישה שלי מ-Google Drive וצרף אותו לליד ב-Salesforce."

המודל יבצע קריאות כמו:

TOOL CALL: gdrive.getDocument(documentId: "abc123")
        → returns "Discussed Q4 goals...\n[full transcript text]"
           (loaded into model context)

TOOL CALL: salesforce.updateRecord(
			objectType: "SalesMeeting",
			recordId: "00Q5f000001abcXYZ",
  			data: { "Notes": "Discussed Q4 goals...\n[full transcript text written out]" }
		)
		(model needs to write entire transcript into context again)

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

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

ביצוע קוד עם MCP משפר את יעילות חלון ההקשר

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

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

servers
├── google-drive
│   ├── getDocument.ts
│   ├── ... (other tools)
│   └── index.ts
├── salesforce
│   ├── updateRecord.ts
│   ├── ... (other tools)
│   └── index.ts
└── ... (other servers)

אז כל כלי מתאים לקובץ, בערך כך:

// ./servers/google-drive/getDocument.ts
import { callMCPTool } from "../../../client.js";

interface GetDocumentInput {
  documentId: string;
}

interface GetDocumentResponse {
  content: string;
}

/* Read a document from Google Drive */
export async function getDocument(input: GetDocumentInput): Promise<GetDocumentResponse> {
  return callMCPTool<GetDocumentResponse>('google_drive__get_document', input);
}

הדוגמה שלנו של Google Drive ל-Salesforce לעיל הופכת לקוד הבא:

// Read transcript from Google Docs and add to Salesforce prospect
import * as gdrive from './servers/google-drive';
import * as salesforce from './servers/salesforce';

const transcript = (await gdrive.getDocument({ documentId: 'abc123' })).content;
await salesforce.updateRecord({
  objectType: 'SalesMeeting',
  recordId: '00Q5f000001abcXYZ',
  data: { Notes: transcript }
});

הסוכן מגלה כלים על ידי חקירת מערכת הקבצים: הוא מפרט את התיקייה ./servers/ כדי למצוא שרתים זמינים (כמו google-drive ו-salesforce), ולאחר מכן קורא את קובצי הכלים הספציפיים שהוא זקוק להם (כמו getDocument.ts ו-updateRecord.ts) כדי להבין את הממשק של כל כלי. זה מאפשר לסוכן לטעון רק את ההגדרות הנחוצות לו למשימה הנוכחית. גישה זו מפחיתה את צריכת הטוקנים מ-150,000 טוקנים ל-2,000 טוקנים – חיסכון של 98.7% בזמן ובעלות.

Cloudflare פרסמה ממצאים דומים, והתייחסה לביצוע קוד עם MCP כ-"Code Mode". התובנה המרכזית זהה: מודלי שפה גדולים (LLMs) מיומנים בכתיבת קוד, ומפתחים צריכים לנצל חוזקה זו כדי לבנות סוכנים המתקשרים עם שרתי MCP בצורה יעילה יותר.

יתרונות ביצוע קוד עם MCP

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

חשיפה פרוגרסיבית

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

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

תוצאות כלים יעילות בהקשר

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

// Without code execution - all rows flow through context
TOOL CALL: gdrive.getSheet(sheetId: 'abc123')
        → returns 10,000 rows in context to filter manually

// With code execution - filter in the execution environment
const allRows = await gdrive.getSheet({ sheetId: 'abc123' });
const pendingOrders = allRows.filter(row => 
  row["Status"] === 'pending'
);
console.log(`Found ${pendingOrders.length} pending orders`);
console.log(pendingOrders.slice(0, 5)); // Only log first 5 for review

הסוכן רואה חמש שורות במקום 10,000. תבניות דומות עובדות עבור צבירה (aggregations), צירופים בין מקורות נתונים מרובים, או חילוץ שדות ספציפיים – הכל מבלי לנפח את חלון ההקשר.

זרימת בקרה חזקה ויעילה יותר בהקשר

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

let found = false;
while (!found) {
  const messages = await slack.getChannelHistory({ channel: 'C123456' });
  found = messages.some(m => m.text.includes('deployment complete'));
  if (!found) await new Promise(r => setTimeout(r, 5000));
}
console.log('Deployment notification received');

גישה זו יעילה יותר מאשר מעבר לסירוגין בין קריאות לכלי MCP ופקודות השהייה (sleep) בלולאת הסוכן.

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

פעולות שמורות פרטיות

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

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

const sheet = await gdrive.getSheet({ sheetId: 'abc123' });
for (const row of sheet.rows) {
  await salesforce.updateRecord({
    objectType: 'Lead',
    recordId: row.salesforceId,
    data: { 
      Email: row.email,
      Phone: row.phone,
      Name: row.name
    }
  });
}
console.log(`Updated ${sheet.rows.length} leads`);

לקוח ה-MCP מיירט את הנתונים ומבצע טוקניזציה למידע מזהה אישי (PII) לפני שהוא מגיע למודל:

// What the agent would see, if it logged the sheet.rows:
[
  { salesforceId: '00Q...', email: '[EMAIL_1]', phone: '[PHONE_1]', name: '[NAME_1]' },
  { salesforceId: '00Q...', email: '[EMAIL_2]', phone: '[PHONE_2]', name: '[NAME_2]' },
  ...
]

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

שימור מצב ויכולות (Skills)

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

const leads = await salesforce.query({
  query: 'SELECT Id, Email FROM Lead LIMIT 1000'
});
const csvData = leads.map(l => `${l.Id},${l.Email}`).join('\n');
await fs.writeFile('./workspace/leads.csv', csvData);

// Later execution picks up where it left off
const saved = await fs.readFile('./workspace/leads.csv', 'utf-8');

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

// In ./skills/save-sheet-as-csv.ts
import * as gdrive from './servers/google-drive';
export async function saveSheetAsCsv(sheetId: string) {
  const data = await gdrive.getSheet({ sheetId });
  const csv = data.map(row => row.join(',')).join('\n');
  await fs.writeFile(`./workspace/sheet-${sheetId}.csv`, csv);
  return `./workspace/sheet-${sheetId}.csv`;
}

// Later, in any agent execution:
import { saveSheetAsCsv } from './skills/save-sheet-as-csv';
const csvPath = await saveSheetAsCsv('abc123');

זה מתקשר קרוב לרעיון ה-Skills (יכולות), שהם תיקיות של הוראות, סקריפטים ומשאבים לשימוש חוזר עבור מודלים לשיפור הביצועים במשימות מיוחדות. הוספת קובץ SKILL.md לפונקציות השמורות הללו יוצרת יכולת מובנית שמודלים יכולים להפנות אליה ולהשתמש בה. עם הזמן, זה מאפשר לסוכן שלכם לבנות ארגז כלים של יכולות ברמה גבוהה יותר, ולפתח את הפיגומים (scaffolding) שהוא צריך כדי לעבוד בצורה היעילה ביותר.

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

סיכום

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

למרות שרבות מהבעיות כאן מרגישות חדשות – ניהול הקשר, הרכבת כלים (tool composition), שימור מצב – יש להן פתרונות ידועים מהנדסת תוכנה. ביצוע קוד מיישם את התבניות המבוססות הללו על סוכנים, ומאפשר להם להשתמש במבני תכנות מוכרים כדי לתקשר עם שרתי MCP בצורה יעילה יותר. אם אתם מיישמים גישה זו, אנו מעודדים אתכם לשתף את הממצאים שלכם עם קהילת MCP.

תודות

מאמר זה נכתב על ידי אדם ג'ונס (Adam Jones) וקונור קלי (Conor Kelly). תודה לג'רמי פוקס (Jeremy Fox), ג'רום סוונאק (Jerome Swannack), סטיוארט ריצ'י (Stuart Ritchie), מולי וורוורק (Molly Vorwerck), מאט סמואלס (Matt Samuels) ומאגי וו (Maggie Vo) על המשוב לטיוטות הפוסט.