Skip to content

MCP: ה-SDK ממיר מחרוזות שנראות כמו JSON בפרמטרים מסוג str | None — ‏query="null" מחזיר את הקובץ המלא #3471

Description

@amirbiron

מה קורה

כשסוכן שולח לפרמטר מסוג str | None מחרוזת שנראית כמו JSON, ה-SDK של MCP ממיר אותה לפני שהכלי רואה אותה. המחרוזת "null" הופכת ל-None, כלומר ל"לא נשלח", ומערך או אובייקט JSON הופכים לרשימה או למילון ונדחים בוולידציה כ"לא מחרוזת".

נמדד על c11d428 (הראש של #3470), עם mcp 1.28.1 ו-pydantic 2.12.3, דרך mcp.call_tool מעל ProductionBackend ודמה של המסד:

קריאה מה חוזר
codekeeper_get_file(file_name=..., query="null") הקובץ המלא — אותה תשובה בדיוק כמו קריאה בלי query
codekeeper_get_file(file_name=..., section="null") הקובץ המלא
codekeeper_docs_get_section(..., section="null") עץ הכותרות (mode: toc), כאילו לא ביקשו סעיף
codekeeper_get_file(file_name="null") {"found": false} — קובץ ששמו null אינו נקרא לפי שם
codekeeper_get_file(file_name=..., query="[1, 2]") ToolError: validation error for get_fileArguments
codekeeper_get_file(file_name=..., query='{"a": 1}') ToolError: validation error for get_fileArguments
query="true" / query="123" עובדים כרגיל, חיפוש מילולי

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

למה

FuncMetadata.pre_parse_json ב-mcp/server/fastmcp/utilities/func_metadata.py (שורות 134–172 ב-1.28.1, נקרא מהחבילה המותקנת): לכל ארגומנט שה-annotation שלו אינו בדיוק str, ושערכו מחרוזת, הוא מריץ json.loads. אם התוצאה היא str, int או float — המחרוזת נשארת כמו שהיא (ולכן "true" ו-"123" לא נפגעים: bool הוא תת-מחלקה של int). אחרת — None, רשימה או מילון — התוצאה מחליפה את המחרוזת. ה-docstring שם מסביר שזה נועד ל-Claude Desktop, ששולח רשימות ומילונים כמחרוזות JSON. אצלנו זה פוגע בדיוק בפרמטרים שאמורים לקבל טקסט חופשי.

ההיקף

28 פרמטרים מסוג "מחרוזת או null" ב-13 כלים (נספר מ-arg_model של הכלים הרשומים):

codekeeper_add_to_collection.folder, .note · codekeeper_create_board_note.color, .mode, .title · codekeeper_create_note.anchor_text, .color · codekeeper_create_repo_note.color, .mode, .title · codekeeper_docs_get_section.ref, .repo, .section · codekeeper_get_collection_items.folder · codekeeper_get_file.file_id, .file_name, .query, .section · codekeeper_get_repo_file.ref, .symbol · codekeeper_list_repo_tree.path, .ref · codekeeper_save_file.language · codekeeper_search_code.language · codekeeper_search_repo.file_pattern · codekeeper_update_note.anchor_text, .color, .content

לא מדדתי את ההשפעה בכלי הכתיבה (למשל codekeeper_update_note(content="null")), כי זה דורש דמה של שכבת הפתקים. ההמרה עצמה קורית בכולם, לפני הכלי, ולכן בכל אחד מהם הערך שמגיע לכלי הוא None, רשימה או מילון.

כיוון לתיקון שורש (לא מומש)

ב-AdminAwareFastMCP (mcp_server/server.py), שכבר עוטפת את add_tool: לדלג על ה-pre-parse לפרמטרים שה-annotation שלהם כולל str, כך שמחרוזת תגיע לכלי כמחרוזת. זו אותה מחלקה של "המרה בגבול לפני הוולידציה" כמו StrictInt/StrictLines (#3316) ו-StrictBool (#3470). ולצד התיקון — טסט שעובר על כל הכלים הרשומים ושולח "null" ו-"[1]" לכל פרמטר כזה, כדי שפרמטר חדש לא יחזור על זה.

קישורים

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions