Skip to main content
Hello and welcome back. This guide explains the shared wiring used when creating LangChain agents that call MCP tools. We use a single reusable Python module that handles discovery, LLM construction, agent creation, invocation, and cleanup. Below you’ll find the shell commands used to expose the cluster kubeconfig and run the MCP container locally, the full mcp_client.py module, and a step-by-step explanation of each section so you can see how the pieces fit together. Shell (context)
Full shared module: mcp_client.py
Key imports and what they do
  • Standard library: json, os, Path, and typing.Any for I/O, environment handling, and type hints.
  • dotenv.load_dotenv: loads environment variables from a .env file at the project root (via Path(__file__).parents[3]).
  • LangChain:
    • create_agent — build the agent with an LLM and available tools.
    • HumanMessage / BaseTool — message and tool abstractions used by LangChain agents.
  • LLM client: ChatOpenAI — a wrapper compatible with OpenAI-style LLM endpoints.
  • MCP tool conversion: convert_mcp_to_langchain_tools — performs the MCP handshake and returns LangChain-ready tool objects so you don’t have to manually define tool schemas.
Loading configuration from .env
  • The module expects a .env at your project root with values required to construct the LLM client. Typical variables:
    • OPENAI_MODEL_ID
    • OPENAI_BASE_URL
    • OPENAI_API_KEY
    • Optional tuning: OPENAI_TEMPERATURE, OPENAI_MAX_TOKENS
  • These are read by build_llm() to initialize ChatOpenAI.
Do not commit your real .env to source control. The .env contains secrets (API keys) and should remain local or be injected via secure CI/CD secret management.
Environment variables (at-a-glance) MCP URL and transport
  • MCP_URL defaults to http://localhost:8000/mcp to match the locally run MCP container. Override with K8S_MCP_URL as needed.
  • MCP_SERVERS sets up the server description used by convert_mcp_to_langchain_tools and specifies transport: "streamable_http" (the transport used by the MCP container in this setup).
Why convert MCP tools to LangChain tools?
  • convert_mcp_to_langchain_tools performs discovery/handshake with MCP and returns tool objects that the LangChain agent understands. This removes manual tool wiring and keeps your application focused on prompts and orchestration.
Helper: normalize tool output — decode_tool_result
  • MCP tool outputs vary: some return Python dictionaries, others return JSON strings. decode_tool_result normalizes either format into a predictable dict with a safe fallback {"items": []} for empty/invalid outputs. Use this when consuming raw tool results to simplify downstream logic.
Helper: look up a tool — tool_by_name
  • Simple utility that searches a list of LangChain tools by .name. It raises a clear RuntimeError if the requested tool isn’t available, making debugging easier when a tool is missing.
Getting MCP tools — get_mcp_tools
  • Calls convert_mcp_to_langchain_tools(MCP_SERVERS) and returns (tools, cleanup). The cleanup coroutine should be awaited when you’re finished with the tools to clean up any background connections.
Building the LLM — build_llm
  • Creates a configured ChatOpenAI instance from environment variables. You can override max_tokens when calling this function or run_agent.
Core agent loop — run_agent This function encapsulates the minimal, robust loop for an agent-based call:
  1. Discover MCP tools and obtain a cleanup coroutine.
  2. Build the LLM using build_llm.
  3. Create a LangChain agent with create_agent.
  4. Invoke the agent using agent.ainvoke(...) with a single HumanMessage containing your prompt.
  5. Return the agent’s final assistant message content.
  6. Ensure cleanup() is awaited in a finally block so resources are always released.
Usage pattern: where to write your prompt
  • Call run_agent(prompt, max_tokens=...) from your application. The shared client handles discovery, LLM construction, agent orchestration, invocation, and cleanup. Your application code only needs to compose the user-facing prompt.
Simple async example
Common troubleshooting tips
  • If tools are not discovered, verify K8S_MCP_URL and that the MCP container is reachable.
  • Ensure your .env variables match the expected names and values; missing or malformed keys will cause build_llm() to fail.
  • Use tool_by_name(tools, "tool-name") to validate that a specific tool exists before attempting to call it.
Next steps
  • The follow-up article will demonstrate a concise prompt that uses these helpers to call MCP tools and produce concrete answers. It will include examples of interpreting tool outputs and combining multiple tool calls in an agent workflow.
Links and references

Watch Video