Debug langchain-mcp-adapters ToolException errors fast. Causes, code fixes, and a checklist for connecting LangChain agents to MCP servers.

TL;DR: A ToolException from langchain-mcp-adapters means the MCP tool was found and called, but the underlying MCP server returned an error result (isError: true) or the transport failed mid-call. The fix is almost always one of four things: a crashed or unreachable server process, an argument schema mismatch, a session that closed before the tool ran, or an unhandled exception inside the server's tool handler.
langchain-mcp-adapters is the official bridge between Model Context Protocol servers and the LangChain/LangGraph ecosystem. It does one job well: it connects to one or more MCP servers, calls tools/list to discover their tools, and wraps each remote tool as a native LangChain StructuredTool that an agent can invoke like any local Python function.
That wrapping is where ToolException enters the picture. When your LangGraph agent decides to call an MCP tool, the adapter issues a JSON-RPC tools/call request over the configured transport (stdio or streamable HTTP). If the server responds with a normal result, the adapter returns the text content to the model. If the server responds with isError: true, or the transport breaks before a response arrives, the adapter raises langchain_core.tools.ToolException.
A minimal, working setup looks like this:
If everything is wired correctly, the agent calls the multiply tool exposed by math_server.py and answers 84. If anything is wrong, you get a ToolException — and the message is often unhelpfully generic. The rest of this guide is about turning that generic message into a fix.
ToolException is not an MCP concept. It is a LangChain-level exception (langchain_core.tools.ToolException) that the adapter raises to signal "the tool ran but failed." This distinction matters because it tells you where to look.
There are three categories of failure, and only one of them is a true ToolException:
The key insight: by the time you see ToolException, the tool was found and called. The problem is downstream of discovery. So stop checking whether the tool exists — it does — and start checking what happens when it runs.
This is the number-one cause of ToolException with stdio transport, and it is deceptively easy to miss because the error appears at call time, not at startup.
With stdio, langchain-mcp-adapters spawns your server as a child process using the command and args you provide. If that command is wrong, the process exits immediately. Discovery may even appear to succeed if the adapter caches an empty toolset, but the first real tools/call fails because there is nothing on the other end of the pipe.
Reproduce the failure mode deliberately:
The fixes, in order of reliability:
A missing env var that the server reads at import time will crash it before it can answer a single call — and the only symptom you ever see is ToolException at the agent layer.
MultiServerMCPClient has two ways to get tools, and mixing them up produces errors that look like server failures but are actually lifecycle bugs.
await client.get_tools() (no arguments) opens a fresh, short-lived session per tool call. This is convenient and stateless, but it means each invocation re-spawns or re-connects the server. For stdio servers that are slow to boot, or HTTP servers behind a cold-start proxy, the repeated connect/disconnect cycle can race and surface as a ToolException with a closed-stream error underneath.
When you need a persistent session — for stateful servers, connection pooling, or performance — open one explicitly:
The classic mistake is loading tools inside the async with block, then storing them and invoking the agent after the block exits. The session is closed, the transport is gone, and the next tools/call raises ToolException. If your error stack mentions ClosedResourceError, anyio, or a closed memory stream, this is your bug.
Rule of thumb: use client.get_tools() for simple, stateless tools, and the explicit client.session(...) context manager only when you genuinely need persistence — and never let session-bound tools escape their async with scope.
LangChain does not deeply validate arguments against the MCP server's input schema before sending them — it trusts the model to produce arguments matching the tool's JSON schema. When the model gets it slightly wrong, the server rejects the call and you get ToolException.
Common variants:
Diagnose by inspecting the actual schema the adapter discovered:
If the schema looks correct but calls still fail, the fix usually lives on the server side: relax overly strict typing, add sensible coercion, or improve the tool's description so the model populates arguments correctly. A precise, example-rich tool description is one of the highest-leverage changes you can make — the model's argument quality is downstream of how well the tool describes itself.
If your MCP server's tool function raises an unhandled exception, a well-behaved server converts it into an MCP error result (isError: true) — which the adapter faithfully re-raises as ToolException. The LangChain side is working correctly; the bug is in your tool code.
The fastest way to see the real error is to stop LangChain from swallowing it. By default, create_react_agent and tool wrappers may convert tool errors into a message fed back to the model. Disable that to surface the raw payload:
Then, on the server, make sure you are actually returning the error text rather than a stack-trace-free generic. With the FastMCP-style API:
When b == 0, the client receives isError: true with the message "Cannot divide by zero...", and langchain-mcp-adapters raises ToolException("Cannot divide by zero..."). Now the error tells you exactly what to fix.
Work this list top to bottom — it is ordered by how often each cause appears in practice:
A large share of langchain-mcp-adapters confusion comes from version drift. The library evolved quickly, and copy-pasted snippets from blog posts often target an API that no longer matches your installed version.
If you are following a guide that uses "transport": "sse" and it fails, switch the server and client to streamable_http, which is the supported HTTP transport in current releases. Mismatched transport names are a quiet but frequent source of connection-time ToolException.
For a deeper understanding of how MCP clients and servers negotiate capabilities — which underpins all of this — see our complete MCP protocol guide and our analysis of Cline's MCP specification adherence, which dissects the same tools/call lifecycle from the client's perspective.
A ToolException is a langchain_core.tools.ToolException raised when an MCP tool was successfully discovered and called, but the server returned an error result (isError: true) or the transport failed during the call. It signals a runtime failure during invocation, not a missing tool — a tool that does not exist fails during discovery instead.
The most common reason is a session lifecycle bug: tools loaded inside a client.session(...) context manager are bound to that session and become invalid once the block exits. If you store the tools and invoke the agent afterward, the transport is already closed and the call raises ToolException. Keep all tool invocations inside the session's async with block, or use client.get_tools() for stateless, self-managed sessions.
Set handle_tool_errors = False on the wrapped tools so the adapter re-raises the underlying MCP error instead of returning a sanitized message to the model. Combine this with running the MCP server standalone (python server.py) to surface any startup traceback, and use LangSmith traces to inspect the exact arguments the model sent.
No. The adapter trusts the model to produce arguments matching the tool's JSON input schema and forwards them to the server. If the model sends a wrong type, omits a required field, or adds unexpected fields, the server rejects the call and the adapter raises ToolException. Improving the tool's description and input schema is the most effective way to reduce these failures.
Aaron is an engineering leader, software architect, and founder with 18 years building distributed systems and cloud infrastructure. Now focused on LLM-powered platforms, agent orchestration, and production AI. He shares hands-on technical guides and framework comparisons at fp8.co.
Master Model Context Protocol from architecture to implementation. Build MCP servers, understand the spec, and integrate with Claude Code and Cursor.
AI Development Tools, Model Context ProtocolCompare AgentCore and LangChain for AI agents. Architecture, pricing, and deployment trade-offs explained with code.
AI Engineering, Agent FrameworksSee exactly how Cline implements the Model Context Protocol — client architecture, tool discovery, and JSON-RPC, with real source code walkthroughs.
Agentic AI, MCP, Cline