מעבדה מקיפה ב MCP Server-Client

תוכן עניינים

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

 

ספריית FastMCP הופכת את בניית השרתים הללו לפשוטה ואינטואיטיבית, תוך שימוש במנגנוני decorators וסכמות נתונים מובנות. במהלך התרגול, נבנה את הקוד צעד אחר צעד, נבין כיצד פועלים Middlewares (תוכנות ביניים) לטיפול בבקשות, וכיצד להוסיף יכולות חדשות לשרת.

 

נבחן דרכים שונות לאינטראקציה עם השרת: שליחת בקשות ישירות (כמו עם curl), חיבור ככלי מקומי לכלים כמו GitHub Copilot ב-VS Code, ובניית Client ייעודי. התרגול הזה הוא קו זינוק מעשי להפיכת מודלי שפה לחלק אינטגרלי מהארכיטקטורה של האפליקציות שלכם.

 

שימו לב – תרגול זה מובנה מאוד והוא מהווה בסיס למחקר וללימוד דרך ״לכלוך הידיים״ בהמשך הדרך. חשוב להכיר את החלקים השונים בתרגול ובמערכת. עם זאת – בכל פרק וסעיף, הרגישו חופשי לחקור יותר לעומק ולסקור גם מושגים שונים ואפשרויות שונות שלא הוזכרו במפורש. נתקעתם? לא נורא – חזרו להוראות המקוריות, ותמיד אפשר להתחיל פרויקט חדש ונקי, ולהתנסות מראש הפרק הנוכחי.
 
בהצלחה!


1. התקנה והקמת שלד השרת (server.py)

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

 

הכנה

עליכם לוודא שמותקן אצלכם Python 3.10 ומעלה, ושאתם נמצאים בסביבה וירטואלית פעילה. צרו קובץ חדש בשם server.py.
 

  1. תרגיל 1 – התקנת FastMCP והגדרות בסיסיות
     

    • התקינו את FastMCP והספריות הנלוות הנדרשות לפרויקט:
      
      pip install fastmcp requests
                      
    •  

    • העתיקו והדביקו את קטעי הקוד הבאים לראש הקובץ server.py, אחד אחרי השני:
    •  

    • אימפורטים נדרשים:
      
      from fastmcp import FastMCP
      from fastmcp.server.middleware import Middleware, MiddlewareContext
      import logging
      import json
                      
    •  

    • הגדרת הלוגינג: הגדירו לוגינג בסיסי שיעזור לנו לעקוב אחר פעולת השרת:
      
      # Set logging level and disable FastMCP's default logging
      logging.basicConfig(level=logging.INFO, 
                          format='%(asctime)s - %(levelname)s - %(message)s') 
      log = logging.getLogger(__name__)
                      
    •  

    • יצירת מופע FastMCP: צרו את השרת הראשי.
      
      mcp = FastMCP("Demo 🚀", stateless_http=True)
                      

      הסבר: הארגומנט stateless_http=True חשוב. הוא מנחה את FastMCP להתייחס לכל בקשת HTTP כאל אירוע נפרד, ולוודא שה-Tool קורא ל-LLM לא ישמור מצב (State) בין שיחות שונות. זהו המצב הנפוץ ביותר בשרתי Tools.


2. יישום Middleware ללוגינג

Middleware מאפשר לכם להריץ לוגיקה לפני ואחרי הפעלת ה-Tool המרכזי. נשתמש בו כדי לראות את הבקשה המלאה בפורמט JSON-RPC.

 

  1. תרגיל 2 – יצירת HttpLoggingMiddleware
     

    • העתיקו את קוד ה-Middleware הבא והדביקו אותו מתחת לקטע הקוד הקודם:
      
      class HttpLoggingMiddleware(Middleware):
          async def on_message(self, context: MiddlewareContext, call_next):
              """Called for every MCP message (request)."""
              
              full_request_data = None
              
              if context.method is not None and context.message is not None: 
                  try:
                      # 1. Determine the serializable payload
                      serializable_body = None
                      if isinstance(context.message, dict):
                          serializable_body = context.message
                      elif hasattr(context.message, 'model_dump'):
                          serializable_body = context.message.model_dump()
                      elif hasattr(context.message, 'dict'):
                          serializable_body = context.message.dict()
                      else:
                          serializable_body = context.message
      
                      # 2. Reconstruct the full JSON-RPC envelope
                      full_request_data = {
                          "jsonrpc": "2.0"
                      }
      
                      if hasattr(context, 'id') and context.id is not None:
                          full_request_data["id"] = context.id
      
                      if isinstance(serializable_body, dict) and 'method' in serializable_body and ('params' in serializable_body or 'arguments' in serializable_body):
                          full_request_data.update(serializable_body)
                      else:
                          full_request_data["method"] = context.method
                          
                          if context.method is not None and context.method != "prompts/list" and context.method != "tools/list":
                              full_request_data["params"] = serializable_body
                          else:
                              full_request_data.update(serializable_body)
      
      
                      # 3. Pretty-print the full JSON body
                      if full_request_data is not None:
                          json_body = json.dumps(full_request_data, indent=4)
                          print("--- 📥 INCOMING FULL JSON-RPC REQUEST ---")
                          print(json_body)
                          print("--------------------------------------")
      
                  except Exception as e:
                      log.error(f"Failed to log full JSON body: {e.__class__.__name__}: {e}")
              
              # Continue processing the request
              result = await call_next(context)
              
              return result
                      
    •  

    • רישום Middleware: רשמו את ה-Middleware לשרת FastMCP שלכם:
      
      # Register the middleware
      mcp.add_middleware(HttpLoggingMiddleware())
                      

      הסבר: הפקודה mcp.add_middleware() מוסיפה את הלוגיקה החדשה שלנו לשרשרת הטיפול בבקשות.


3. הוספת כלים (Tools) והרצת השרת

Tools הם לב ליבו של שרת MCP. כל פונקציה עם Decorator מתאים הופכת לכלי זמין למודל.

 

  1. תרגיל 3 – יצירת ה-Tool הבסיסי (חישוב)
     

    • הוסיפו את ה-Tool הפשוט הבא שמטרתו היא להוסיף שני מספרים:
      
      @mcp.tool
      def add(a: int, b: int) -> int:
          """Add two numbers"""
          return a + b
                      
    •  

    • הוספת Tool מתקדם (מזג אוויר): הוסיפו את כלי מזג האוויר שמדמה קריאה לשירות חיצוני:
      
      @mcp.tool
      def get_current_weather(city: str) -> str:
          """
          Get the current weather for a specific city.
          
          Args:
              city: The name of the city (e.g., "Tel Aviv").
              
          Returns:
              A JSON string containing the current weather data.
          """
          log.info(f"Tool called: get_current_weather for {city}")
          
          # Dummy data response. In a real app, you would use requests/httpx here.
          weather_data = {
              "city": city,
              "temperature": 25,
              "unit": "Celsius",
              "condition": "Sunny with light wind"
          }
          
          return json.dumps(weather_data, indent=2)
                      
    •  

    • הרצת השרת: הוסיפו את בלוק ההרצה הראשי והריצו את הקובץ.
      
      if __name__ == "__main__":
          mcp.run(transport="http", 
                  host="127.0.0.1", 
                  port=3000, 
                  log_level="info"
              )
                      
    •  

    • הריצו את השרת מתוך סביבת הפיתוח את הטרמינל.
    • וודאו שהשרת מופעל ומאזין בכתובת http://127.0.0.1:3000.

4. אינטראקציה: בדיקה באמצעות cURL וחיבור ל-VS Code

נבחן את השרת באמצעות בקשות HTTP ישירות, ונחבר אותו ככלי ל-GitHub Copilot.

 

  1. תרגיל 4 – בדיקת הכלים באמצעות cURL
     

    • ודאו שהשרת רץ. פתחו טרמינל חדש ושלחו בקשה לכלי add (התחביר חייב להיות JSON-RPC):
       
      Linux / MacWindows PowerShellWindows CMD
      
      curl -X POST http://127.0.0.1:3000/mcp \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{
          "jsonrpc": "2.0",
          "id": 1,
          "method": "tools/call",
          "params": {
              "name": "add",
              "arguments": {
                  "a": 100,
                  "b": 25
              }
          }
      }'
      
      
      curl -Method POST "http://127.0.0.1:3000/mcp" `
        -Headers @{ "Content-Type" = "application/json" } `
        -Headers @{ "Accept" = "application/json, text/event-stream" } `
        -Body '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "add", "arguments": { "a": 100, "b": 25 } } }'
                      
      
      curl -X POST http://127.0.0.1:3000/mcp ^
        -H "Content-Type: application/json" ^
        -H "Accept: application/json, text/event-stream" ^
        -d "{ \"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"tools/call\", \"params\": { \"name\": \"add\", \"arguments\": { \"a\": 100, \"b\": 25 } } }"
      
    •  

    • בדקו את התוצאה (אמורה להיות 125). בדקו בטרמינל של השרת את הפלט המלא של ה-Middleware.
    •  

    • שלחו בקשה לכלי get_current_weather:
       
      Linux / MacWindows PowerShellWindows CMD
      
      curl -X POST http://127.0.0.1:3000/mcp \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{
          "jsonrpc": "2.0",
          "id": 2,
          "method": "tools/call",
          "params": {
              "name": "get_current_weather",
              "arguments": {
                  "city": "Berlin"
              }
          }
      }'
      
      
      curl -Method POST "http://127.0.0.1:3000/mcp" `
        -Headers @{ "Content-Type" = "application/json" } `
        -Headers @{ "Accept" = "application/json, text/event-stream" } `
        -Body '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "get_current_weather", "arguments": { "city": "Berlin" } } }'
       
      
      curl -X POST http://127.0.0.1:3000/mcp ^
        -H "Content-Type: application/json" ^
        -H "Accept: application/json, text/event-stream" ^
        -d "{ \"jsonrpc\": \"2.0\", \"id\": 2, \"method\": \"tools/call\", \"params\": { \"name\": \"get_current_weather\", \"arguments\": { \"city\": \"Berlin\" } } }"
       
  2.  

  3. תרגיל 5 – חיבור השרת ל-GitHub Copilot (דרך mcp.json)
     

    • התקינו את הרחבת GitHub Copilot Chat ב-VS Code.
    •  

    • צרו קובץ תצורה בשם mcp.json בנתיב המשתמש שלכם (לרוב ~/.mcp.json ב-Linux/Mac או בתיקיית המשתמש ב-Windows).
    •  

    • וודאו שהשרת עדיין מופעל ומאזין בכתובת http://127.0.0.1:3000.
    • הכניסו את התוכן הבא לקובץ.
       
      שימו לב: אם הקובץ mcp.json כבר קיים לכם במערכת ומופיע בו תוכן קודם – הטמיעו רק את התוכן בתוך אובייקט ה servers שכבר קיים ב JSON:

      
      {
          "servers": {
              "fastmcp-demo": {
                  "type": "http",
                  "url": "http://localhost:3000/mcp"
              }
          }
      }
      
    •  

    • פתחו את VS Code, ובצ'אט של Copilot Chat נסחו שאלה שתחייב אותו להשתמש ב-Tool. שימו לב לפקודה שתופיע בטרמינל השרת כאשר Copilot יחליט להשתמש בכלי.
      What is the sum of 5 and 37, and what is the current weather condition in London?

       


5. בניית FastMCP Client

FastMCP מספק גם Client פשוט וקל לשימוש, המאפשר קריאות Tool פרוגרמטיות מתוך קוד Python.

 

  1. תרגיל 6 – יצירת ה-Client
     

    • צרו קובץ חדש בשם client.py. וודאו ש-server.py עדיין רץ.
    •  

    • הכניסו את הקוד הבא:
      
      import asyncio
      from fastmcp import FastMCP
      
      async def main():
          # create an instance of the client
          client = FastMCP.from_client("http://127.0.0.1:3000/")
          
          try:
              # calling Add tool
              print("Calling 'add' tool...")
              result_add = await client._call_tool_mcp("add", {"a": 1, "b": 2})
              print(f"Result from 'add': {result_add}")
      
              # calling Weather tool
              print("\nCalling 'get_current_weather' tool...")
              result_weather = await client._call_tool_mcp("get_current_weather", {"city": 1, "b": 2})
              print(f"Result from 'get_current_weather': {result_weather}")
      
          except Exception as e:
              print(f"An error occurred: {e}")
          finally:
              # closing resources
              await client.close()
      
      if __name__ == "__main__":
          asyncio.run(main())
                      
    •  

    • הריצו את הלקוח:
      
      python client.py
                      
    •  

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

6. תרגול פתוח ואתגרים מתקדמים

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

 

  1. תרגיל 7 – חשיפת ה-localhost וחיבור ל-ChatGPT
     

    • התקנת cloudflared: ודאו שהכלי cloudflared מותקן במחשב שלכם.
    •  

    • יצירת מנהרה (Tunnel): ודאו ש-server.py רץ על פורט 3000, ופתחו טרמינל חדש כדי ליצור מנהרה ציבורית זמנית:
      
      cloudflared tunnel --url http://127.0.0.1:3000
                      
    •  

    • הכלי יספק לכם כתובת URL ציבורית זמנית (בסגנון https://random-id.trycloudflare.com).
    •  

    • חיבור ל-ChatGPT: היכנסו ל-OpenAI Platform ובחרו באפשרות ליצור Custom GPT (אם יש לכם גישת מפתחים) או Connector. עליכם לספק את ה-URL הציבורי שקיבלתם. (תרגיל זה מניח שה-LLM יודע ליצור את מפרט ה-OpenAPI הנדרש עבור ה-Tools).
    •  

    • בצעו ניסוי בצ'אט: שאלו שאלה באנגלית שתחייב את המודל להפעיל את get_current_weather tool דרך החיבור שיצרתם.
  2.  

  3. תרגיל 8 – הוספת כלי מתוחכם (API אמיתי)
     

    • הוסיפו כלי חדש המבצע קריאת HTTP אמיתית ל-API חיצוני חינמי (כגון API המציע בדיחות רנדומליות, ציטוטים, או מטבעות).
    •  

    • הגדירו Tool בשם get_quote שאינו מקבל פרמטרים ומחזיר מחרוזת של ציטוט רנדומלי (השתמשו בספריית requests הפעם).
    •  

    • הריצו מחדש את השרת ובדקו את ה-Tool באמצעות cURL או Copilot:
      Tell me a random programming quote.

       


סיימתם בהצלחה את התרגול המעשי לבניית שרת MCP באמצעות FastMCP. למדתם לבנות את הקוד בהדרגה, להטמיע לוגיקה באמצעות Middlewares, להוסיף כלים, ולהשתמש בשרת ממקורות שונים – החל מ-cURL, דרך Client ייעודי ועד לחיבור LLMs חיצוניים כמו Copilot ו-ChatGPT. ידע זה הוא הבסיס לבניית ארכיטקטורות AI מתקדמות.