איך בכל זאת התחברתי לשרת ה MCP של ספוטיפיי
יש שני סוגים של שרתי MCP, שרתי MCP בתקשורת stdio שרצים אצלכם על המחשב ושרתי MCP שרצים על מכונה מרוחקת. אנטרופיק החליטו לבלבל את התעשייה עם סטנדרט חדש ושמות חדשים אבל האמת היא שמאחורי השמות מסתתרים אותם רכיבים שאנחנו מכירים:
שרת MCP מסוג stdio הוא בסך הכל כלי משורת הפקודה או סקריפט.
שרת MCP מסוג http הוא בסך הכל API.
במילים אחרות, אם הורדתם שרת MCP מסוג stdio וחיברתם אותו לקלוד או ל VS Code אתם ממש לא חייבים לעבוד איתו רק דרך קלוד או VS Code ואפשר לעבוד איתו רגיל משורת הפקודה עם כלי תרגום כמו mcp2cli. זה באמת בסך הכל סקריפט שמקבל פרמטרים בצורה קצת יותר מסורבלת.
מה שקצת מבלבל אנשים הוא ההזדהות - לקוחות MCP מנהלים בשבילנו את ההזדהות ולכן כשקלוד או VS Code מתחברים לשרת MCP הם שולחים אותנו לאינטרנט כדי להזדהות מול השירות שמתחברים אליו, לוקחים אליהם את הטוקן שחזר ושומרים אותו כדי להפעיל את הכלים. הפעלת כלים בשרת MCP בענן היא בסך הכל קריאת API.
לפעמים שרתי MCP מנסים להרחיק אותנו וליצור מגבלות מלאכותיות. לדוגמה שרת ה MCP של ספוטיפיי עובד מצוין מתוך קלוד אבל כשניסיתי להתחבר אליו מקוד או מ mcp2cli הוא זרק אותי ואמר שהוא מוכן להתחבר רק ללקוחות מורשים. מעבר לקונקטור בקלוד או ChatGPT גם לא מצאתי אזכור רשמי לאותו שרת MCP של ספוטיפיי. מה עושים? קצת מחקר:
שאלתי את קלוד מה הכתובת של שרת ה MCP של ספוטיפיי אליה הוא מחובר. את זה לא היתה לו בעיה לספר לי.
שמתי לב שכשאני מתחבר לשרת ה MCP של ספוטיפיי הדפדפן מראה כתובת ארוכה שכוללת משתנים כמו redirect url ו client id. רשמתי אותם בצד.
שמתי לב שאחרי אישור הגישה לספוטיפיי הדפדפן שלי עובר בבקשת POST לקלוד שלוקח את הקוד שהתקבל ומשתמש בו כדי לקבל אסימון גישה, כל זה מאחורי הקלעים. אז חסמתי את הגישה ל claude באמצעות קובץ
/etc/hostsכדי שלא יקבל את האסימון ולקחתי את הכתובת הזאת אליי, גם אותה רשמתי בצד.
עכשיו כבר היה לי את כל מה שהייתי צריך כדי להתחזות לקלוד ולייצר אסימון גישה ל MCP של ספוטיפיי. ביקשתי מקלוד לכתוב סקריפט פייתון שמייצר פרמטרים למסך ההרשאות של ספוטיפיי ומחכה שאני אדביק את כתובת החזרה כדי לקחת משם את הפרמטרים של התשובה, מחבר את הכל לאסימון ואז מחפש קצת מוזיקה בספרדית. זאת התוצאה:
import asyncio
import os
from urllib.parse import parse_qs, parse_qsl, urlencode, urlparse, urlunparse
from dotenv import load_dotenv
from fastmcp.client.auth import OAuth
from pydantic import AnyHttpUrl
from pydantic_ai.mcp import MCPToolset
MCP_URL = "https://mcp-gateway-external-pilot.spotify.net/mcp"
REDIRECT_URI = "https://claude.ai/api/mcp/auth_callback"
class ConsoleOAuth(OAuth):
def __init__(self, client_id: str) -> None:
super().__init__(
mcp_url=MCP_URL,
client_id=client_id,
callback_port=1,
callback_timeout=600,
)
redirect_uris = [AnyHttpUrl(REDIRECT_URI)]
self.context.client_metadata.redirect_uris = redirect_uris
if self._static_client_info is not None:
self._static_client_info.redirect_uris = redirect_uris
async def redirect_handler(self, authorization_url: str) -> None:
parsed = urlparse(authorization_url)
query = dict(parse_qsl(parsed.query))
query["show_dialog"] = "true"
print(urlunparse(parsed._replace(query=urlencode(query))))
async def callback_handler(self) -> tuple[str, str | None]:
callback_url = await asyncio.to_thread(input, "Paste callback URL: ")
query = parse_qs(urlparse(callback_url).query)
code = query.get("code", [None])[0]
state = query.get("state", [None])[0]
if not code:
raise RuntimeError("Callback URL does not contain an OAuth code")
return code, state
async def main() -> None:
load_dotenv()
client_id = "a0894f3786c34e64b0fa0e77f8ea83db"
toolset = MCPToolset(
MCP_URL,
auth=ConsoleOAuth(client_id),
init_timeout=600,
)
async with toolset:
tools = await toolset.list_tools()
search = next(tool for tool in tools if tool.name == "search")
field = "query" if "query" in search.inputSchema["properties"] else "prompt"
result = await toolset.client.call_tool(
"search",
{field: "nice Spanish music", "language": "en"},
)
print(result.model_dump_json(indent=2, exclude_none=True))
if __name__ == "__main__":
asyncio.run(main())
שרת MCP הוא בסך הכל API. הוא כבר נותן לכם את כל מה שצריך בשביל לבנות אוטומציות וחיבורים למערכת מתוך קוד. ה AI הוא רק בונוס.