Tools
import McpUseInAgentPy from '/snippets/code-samples/mcp-use-in-agent-py.mdx'; import McpUseInAgentJs from '/snippets/code-samples/mcp-use-in-agent-js.mdx'; import McpToolErrorsPy from '/snippets/code-samples/mcp-tool-errors-py.mdx'; import McpToolErrorsJs from '/snippets/code-samples/mcp-tool-errors-js.mdx'; import McpToolMetadataPy from '/snippets/code-samples/mcp-tool-metadata-py.mdx'; import McpToolMetadataJs from '/snippets/code-samples/mcp-tool-metadata-js.mdx'; import McpMultimodalToolContentPy from '/snippets/code-samples/mcp-multimodal-tool-content-py.mdx'; import McpMultimodalToolContentJs from '/snippets/code-samples/mcp-multimodal-tool-content-v2-js.mdx'; import McpStructuredContentPy from '/snippets/code-samples/mcp-structured-content-py.mdx'; import McpStructuredContentJs from '/snippets/code-samples/mcp-structured-content-js.mdx'; import McpElicitationPy from '/snippets/code-samples/mcp-elicitation-py.mdx'; import McpElicitationJs from '/snippets/code-samples/mcp-elicitation-js.mdx'; import McpDestructiveGatePy from '/snippets/code-samples/mcp-destructive-gate-py.mdx'; import McpDestructiveGateJs from '/snippets/code-samples/mcp-destructive-gate-js.mdx';
The
langchain.mcpnamespace requireslangchain[mcp]>=1.4.0and is in beta. The API may change.
@[MCPAdapter] bridges MCP servers and LangChain agents: it discovers the tools a server advertises and adapts them into standard LangChain tools. Pass the tools from list_tools() to @[create_agent] as you would any other LangChain tool.
This page covers what is specific to that bridge: identifying MCP tools, controlling their execution, handling their outputs, and responding when a server needs input during a call. For the runnable discovery-and-agent example, see the MCP quickstart (opens in a new tab).
Use MCP tools in an agent
Discover the server's catalog with @[MCPAdapter.list_tools], then give the returned tools to @[create_agent]. From the agent's perspective, they behave like LangChain tools: the model chooses a tool, LangChain invokes it, and the resulting @[ToolMessage] returns to the model.
For general guidance on defining, binding, and using LangChain tools, see Tools (opens in a new tab). For several MCP servers and their namespaced tool catalogs, see Connections (opens in a new tab).
Handle tool outputs
MCP tool results become @[ToolMessage] objects with content the model can read, an artifact for application data, and a status indicating success or failure.
Multimodal content
The adapter converts model-visible MCP content into LangChain content blocks (opens in a new tab). For example, a tool that returns an MCP image block with a screenshot reaches the model as an image block.
Structured content
When a tool returns structured content, the adapter attaches it to the @[ToolMessage] as an artifact rather than folding it into the model-visible text. Run the agent, then read the artifact from the @[ToolMessage] instances in the result:
The artifact is an MCPToolArtifact, whose structured_content field holds the tool result's structuredContent. A tool that returns no structured content leaves artifact as None.
Errors
An MCP tool result carries an isError flag. When a server reports isError=True, the adapter converts it into a @[ToolMessage] with status="error" carrying the server's own message, so the agent can read it and correct itself:
A server-reported error reaches the model as a failed tool message, but a transport or session failure raises instead.
Tool metadata
Each adapted tool may carry its MCP provenance under an mcp namespace on the LangChain tool's metadata:
tool.metadata
# {
# "mcp": {
# "tool": {
# "annotations": {
# "destructive_hint": True,
# "read_only_hint": False,
# },
# "_meta": {"origin": "crm"},
# },
# "server": {
# "name": "crm",
# "version": "2.1.0",
# },
# },
# }Every nested field is optional: a server may provide tool annotations, _meta, server identity, any combination of those, or none. annotations contains MCP hints such as read_only_hint and destructive_hint; _meta is opaque metadata supplied by the server; and server identifies the MCP implementation that advertised the tool.
Read optional metadata defensively, so a missing field returns a default rather than failing:
Human-in-the-loop
Reading annotations lets you gate a tool based on what the server declares about it, rather than hardcoding tool names. The MCP annotation classifies the tool and LangChain's human-in-the-loop middleware enforces the approval policy.
Read the destructive hint from metadata once during tool discovery. Then give the human-in-the-loop configuration a when predicate that receives each pending tool call and returns whether the call needs approval:
When the agent calls a tool that the predicate gates, the run pauses. Approve the call to run it, or reject it to skip the tool and tell the model:
from langgraph.types import Command
# Approve the pending destructive call and resume.
resumed = await agent.ainvoke(
Command(resume={"decisions": [{"type": "approve"}]}), config
)The predicate can also inspect the call's arguments. This lets a tool run freely for safe inputs and pause only for risky ones, such as a delete_file call targeting a protected path.
Access the arguments through request.tool_call["args"].
Combine the metadata classification and argument check to gate a tool only when its type and inputs warrant approval. For the full approval workflow, see Human-in-the-loop (opens in a new tab).
Server requests during tool execution
Most tools finish without asking the client for anything mid-call. When a server needs input, @[MCPAdapter] surfaces elicitation (opens in a new tab) as a LangGraph @[interrupt].
Elicitation
Elicitation (opens in a new tab) lets an MCP server request input during a tool call. The adapter pauses the run with a LangGraph interrupt. Your application presents the request to the user and resumes the run with their answer.
Attach a checkpointer (opens in a new tab) to the agent and use the same thread_id when invoking and resuming it.
This example assumes the booking tool asks for one date and pauses once. Replace the sample date with the user's answer.
Elicitation is on by default. The adapter advertises the capability and drives the interrupt loop. A prebuilt client that already carries its own elicitation handler is honored instead of overridden.
Resume with Command(resume={"responses": {key: answer}}). The interrupt payload and answer types live in langchain.mcp.elicitation.
Resuming reruns the tool from the beginning. Any work performed before the server asks for input can repeat. Make that work safe to repeat without duplicating side effects.
Each answer uses one of these actions:
accept: Provide formcontentmatching the request's schema, or confirm completion of a URL interaction withoutcontent.decline: Refuse to provide the requested information.cancel: Indicate that the user canceled the interaction.
The adapter forwards the answer to the server, which decides how the tool call finishes.
Only elicitation is answered this way. A server that instead asks for sampling (opens in a new tab) or roots (opens in a new tab) raises an error, because the modern, sessionless protocol has no live back-channel for those requests. See Sampling and roots (opens in a new tab).
Interrupt-driven elicitation answers a server that returns its request as an
InputRequiredResult. A server that only pushes elicitation over a legacy handshake session cannot be answered this way.