2025, Oct 31 18:01

python-a2a का to_langchain_tool FastMCP पर 404 क्यों देता है और क्या करें

python-a2a v0.5.9 LangChain एडेप्टर FastMCP पर GET /mcp/tools भेजकर 404/406 देता है, जबकि MCP POST /mcp चाहता है. कारण, async वर्कअराउंड और सही HTTP सेटअप जानें.

python-a2a के साथ किसी लोकल MCP Server को LangChain टूल में बदलना ऊपर से सीधा लगता है, लेकिन MCPToolConversionError और 404 के साथ असफल हो सकता है। अड़चन आपके FastMCP सर्वर में नहीं है; समस्या यह है कि python-a2a MCP एंडपॉइंट से टूल्स लाने की कोशिश कैसे करता है।

समस्या को दोहराने वाला न्यूनतम सेटअप

LangChain एडेप्टर कॉल सरल है। यह एक MCP एंडपॉइंट को लक्ष्य बनाती है और किसी खास टूल का अनुरोध करती है।

from python_a2a.langchain import to_langchain_tool
lc_tool_obj = to_langchain_tool("http://localhost:8080/mcp", "add")
print(lc_tool_obj)

FastMCP के साथ एक बुनियादी MCP Server कुछ ऐसा दिख सकता है।

from fastmcp import FastMCP
svc = FastMCP("Demo", stateless_http=True)
@svc.tool
def add(a: int, b: int) -> int:
    total = a + b
    print(total)
    return total
if __name__ == "__main__":
    svc.run()

fastmcp.client के जरिए सर्वर को कॉल करना सफल रहता है। यह टूल्स की सूची देता है और add टूल को अपेक्षित रूप से चलाता है, लेकिन कोड को एक async फ़ंक्शन में निष्पादित करना होता है और asyncio.run से शुरू करना पड़ता है।

import asyncio
from fastmcp.client import Client
async def main():
    http_client = Client("http://localhost:8080/mcp")
    async with http_client:
        tool_list = await http_client.list_tools()
        print(tool_list)
        outcome = await http_client.call_tool("add", arguments={"a": 10, "b": 20})
        print(outcome.content)
if __name__ == "__main__":
    asyncio.run(main())

इसके बावजूद, python-a2a एडेप्टर 404 के साथ विफल हो जाता है।

असल में गड़बड़ी कहाँ है

FastMCP द्वारा एक्सपोज़ किया गया MCP एंडपॉइंट /mcp के तहत POST अनुरोधों का उत्तर देता है। जब fastmcp क्लाइंट टूल सूची मांगता है, तो सर्वर लॉग POST ट्रैफ़िक और उसके बाद 200 OK के साथ एक रीडायरेक्ट दिखाते हैं।

INFO:     127.0.0.1:43026 - "POST /mcp HTTP/1.1" 307 Temporary Redirect
INFO:     127.0.0.1:43026 - "POST /mcp/ HTTP/1.1" 200 OK

इसके उलट, python-a2a v0.5.9 अलग URL कॉल करता है और गलत HTTP मेथड इस्तेमाल करता है। इसका to_langchain_tool() ऐसा GET एंडपॉइंट हिट करता है जिसे FastMCP टूल डिस्कवरी के लिए सर्व नहीं करता, और सर्वर के रिस्पॉन्स पाथ पर निर्भर करते हुए 406 या 404 मिल जाता है।

INFO:     127.0.0.1:47460 - "GET /mcp/tools HTTP/1.1" 406 Not Acceptable

यह व्यवहार सीधे एडेप्टर के सोर्स से आता है, जो /tools पाथ के खिलाफ एक GET बनाता है।

tools_response = requests.get(f"{mcp_url}/tools")

क्योंकि सर्वर /mcp पर POST की अपेक्षा करता है, न कि /mcp/tools पर GET की, एडेप्टर MCPToolConversionError के साथ फेल होता है और 404 बताता है।

तुरंत लागू किए जा सकने वाले सुधार

आपकी ओर से दो समायोजन ज़रूरी हैं, हालांकि वे अकेले एडेप्टर की समस्या पूरी तरह नहीं सुलझाएंगे। पहला, सही ट्रांसपोर्ट चुनकर सुनिश्चित करें कि आपका FastMCP सर्वर stdio की बजाय HTTP पर उपलब्ध हो। दूसरा, क्लाइंट को asyncio.run के साथ एक async एंट्री पॉइंट से चलाएँ; सही निष्पादन के लिए यह अनिवार्य है।

यहाँ HTTP ट्रांसपोर्ट का इस्तेमाल करने वाला सर्वर कॉन्फ़िगरेशन है।

from fastmcp import FastMCP
svc = FastMCP("Demo", transport="streamable-http")
@svc.tool
def add(a: int, b: int) -> int:
    total = a + b
    print(total)
    return total
if __name__ == "__main__":
    svc.run()

और यहाँ async क्लाइंट इनवोकेशन है, जो FastMCP के साथ पहले से सही काम करता है।

import asyncio
from fastmcp.client import Client
async def main():
    http_client = Client("http://localhost:8080/mcp")
    async with http_client:
        tool_list = await http_client.list_tools()
        print(tool_list)
        outcome = await http_client.call_tool("add", arguments={"a": 10, "b": 20})
        print(outcome.content)
if __name__ == "__main__":
    asyncio.run(main())

मुख्य बात वही रहती है: उचित HTTP ट्रांसपोर्ट और सही async क्लाइंट होने पर भी, python-a2a का to_langchain_tool() अब भी GET /mcp/tools भेजता है और फेल हो जाता है। यह असंगति एडेप्टर में है और इसे अपस्ट्रीम ठीक करने की ज़रूरत है। समस्या python-a2a के मेंटेनर्स को रिपोर्ट की जा चुकी है: https://github.com/themanojdesai/python-a2a/issues/74.

आपके स्टैक के लिए यह क्यों मायने रखता है

MCP सर्वरों को LangChain टूल्स में बदलना एक उपयोगी इंटीग्रेशन रास्ता है, लेकिन छोटे-छोटे प्रोटोकॉल मिसमैच इसे पटरी से उतार सकते हैं। सर्वर को किस तरह के अनुरोध चाहिए (POST /mcp पर) और एडेप्टर वास्तव में क्या भेज रहा है (GET /mcp/tools) — यह समझना आपको गलत कॉन्फ़िगरेशन और एडेप्टर-स्तरीय दोषों में जल्दी फर्क करने में मदद करता है। इससे 404 और 406 त्रुटियों के पीछे स्टैक के गलत पक्ष पर समय बर्बाद होने से भी बचाव होता है।

निष्कर्ष और व्यावहारिक सलाह

यदि तुरंत काम चलाने की ज़रूरत है, तो fastmcp.client और एक async एंट्री पॉइंट के साथ अपने MCP Server से इंटरैक्ट करें। सर्वर को streamable-http जैसे HTTP ट्रांसपोर्ट के साथ चलाएँ ताकि वह HTTP पर पहुँचा जा सके। LangChain टूल कन्वर्ज़न के लिए, python-a2a के to_langchain_tool() में सुधार का इंतज़ार करें, क्योंकि मौजूदा इम्प्लिमेंटेशन /tools पाथ पर GET करता है जिसे FastMCP डिस्कवरी के लिए सर्व नहीं करता। अपस्ट्रीम इश्यू पर नज़र रखें और तब तक प्रोडक्शन में to_langchain_tool() पर निर्भर न रहें जब तक एडेप्टर MCP की HTTP फ्लो के अनुरूप न हो जाए।

यह लेख StackOverflow के एक प्रश्न (लेखक: JayantSeth) और furas के उत्तर पर आधारित है।