Integrating with LangChain agentic framework

Complete the following steps to integrate the remote MCP server with LangChain and enable AI agents to invoke tools using custom metadata:

Note: All steps below are intended to be executed within a Python environment, specifically within a Jupyter notebook.
  1. Run the following code to install dependencies.

    !pip3 install langchain_core langchain_mcp_adapters langchain_ibm langchain fastmcp
    
  2. Run the following code to add environment variables.

    os.environ["WATSONX_API_KEY"]="<>"
    os.environ["WATSONX_PROJECT_ID"]="<>"
    os.environ["WATSONX_URL"]="<>"
    os.environ["MCP_ENDPOINT"]="<>"
    
    Note:
    • To obtain your WATSONX_API_KEY, refer API keys.
    • To find your WATSONX_PROJECT_ID, navigate to your project in the watsonx.ai web console, click the Manage tab, and locate the Project ID under the General Information section.
    • For WATSONX_URL, refer Introduction to IBM watsonx.ai Runtime and choose the correct instance URL based on your region.

  3. Run the following code to imprort dependencies.

    from fastmcp import Client
    import os
    from langchain.agents import create_agent
    
  4. Run the following code to load tools from the remote MCP server.

    authToken = ""
    config = {
        "mcpServers": {
            "server_name": {
                "transport": "streamable-http",
                "url": os.getenv("MCP_ENDPOINT"),
                "headers": {"Authorization": f"Bearer {authToken}"},
            },
        }
    }
    client = Client(config)
    
    async with client:
        # Initialize the connection
        await client.ping()
        # Get tools
        tools  = await client.list_tools()
    
        print(tools)
        print("\n",len(tools))
    
    Note:

    To generate authToken, refer Authentication.

  5. Run the following code to set up ChatWatsonx llm.

    from langchain_ibm import ChatWatsonx
    
    llm = ChatWatsonx(
    model_id="mistralai/mistral-medium-2505",
    url = os.getenv("WATSONX_URL"),
    apikey = os.getenv("WATSONX_API_KEY"),
    project_id = os.getenv("WATSONX_PROJECT_ID"),
    temperature=0,
    max_completion_tokens=15000
    )
    
  6. Run the following code to define progress handler callback.

    async def progress_handler(
        progress: float,
        total: float | None,
        message: str | None
    ) -> None:
        if total is not None:
            percentage = (progress / total) * 100
            print(f"Progress: {percentage:.1f}% - {message or ''}")
        else:
            print(f"Progress: {progress} - {message or ''}")
    
  7. Run the following code to patch tools with progress handler.

    from langchain_core.tools import StructuredTool
    from langchain_mcp_adapters.tools import _convert_call_tool_result
    
    def patch_tools_with_progress_handler(tools, session):
       wrapped = []
       for tool in tools:
    
          async def wrapper(_tool=tool,**kwargs):
                call_tool_result = None
                mcp_tool_name = _tool.name
    
                # Call the tool using the progress handler
                call_tool_result = await session.call_tool(
                    mcp_tool_name,
                    kwargs,
                    progress_callback=progress_handler
                 )
    
              if call_tool_result is None:
                 raise RuntimeError(
                    "Tool call failed: no result returned from the underlying MCP SDK. "
                    "This may indicate that an exception was handled or suppressed "
                    "by the MCP SDK (e.g., client disconnection, network issue, "
                    "or other execution error)."
                )
    
              # Use the original call_tool converter
              return _convert_call_tool_result(call_tool_result)
    
       # Create a NEW StructuredTool
          wrapped_tool = StructuredTool.from_function(
            name=tool.name,
            description=tool.description,
            args_schema=tool.args_schema,
            coroutine=wrapper,
            metadata=tool.metadata,
            response_format="content_and_artifact",
         )
    
          wrapped.append(wrapped_tool)
    
       return wrapped
    
  8. Run the following code to invoke the agent.

    from langchain_core.messages import AIMessage, SystemMessage, HumanMessage
    from langchain_mcp_adapters.tools import load_mcp_tools
    
    client = Client(config)
    async with client:
       # Initialize the connection
       await client.ping()
       tools = await client.list_tools()
       tools = await load_mcp_tools(session=client.session)
       wrapper_tools = patch_tools_with_progress_handler(tools,client.session)
       agent = create_agent(
             model=llm,
             tools=wrapper_tools,
        )
       guidance_prompt_with_query = await client.session.get_prompt(name="workflow_guidance")
       print("Guidance Prompt: ", guidance_prompt_with_query)
       prompt_text = guidance_prompt_with_query.messages[0].content.text
       messages = [SystemMessage(content=prompt_text)]
       print("\n--- Agent Ready ---\n")
    
       while True:
          user_input = input("Human: ")
    
          if user_input.strip().lower() in {"q", "quit", "exit"}:
             print("Exiting...")
             break
    
          messages.append(HumanMessage(content=user_input))
    
          result = await agent.ainvoke({"messages": messages})
          ai_msg = result["messages"][-1]
    
          print("AI:", ai_msg.content)
    
          messages.append(ai_msg)
    

Parent topic: IBM watsonx.data remote Model Context Protocol (MCP) server