A logo showing the text blog.marcnuri.com
Español
Home»Artificial Intelligence»LangChain.js MCP Adapters: Connect Agents to MCP Servers

Recent Posts

  • Fabric8 Kubernetes Client 8.0 is now available!
  • Fabric8 Kubernetes Client 7.9 is now available!
  • Eclipse JKube 1.20 is now available!
  • Project, Agent, Person: The Missing Axis of AI Coding Tools
  • Fabric8 Kubernetes Client 7.8 is now available!

Categories

  • Artificial Intelligence
  • Backend Development
  • Cloud Native
  • Engineering Insights
  • Frontend Development
  • JavaScript
  • Legacy
  • Operations
  • Personal
  • Pet projects
  • Quality Engineering
  • Tools

Archives

  • September 2026
  • July 2026
  • June 2026
  • May 2026
  • April 2026
  • March 2026
  • February 2026
  • January 2026
  • December 2025
  • October 2025
  • September 2025
  • July 2025
  • May 2025
  • April 2025
  • March 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • August 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • July 2022
  • June 2022
  • May 2022
  • March 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • August 2020
  • July 2020
  • June 2020
  • May 2020
  • March 2020
  • February 2020
  • January 2020
  • December 2019
  • November 2019
  • October 2019
  • September 2019
  • July 2019
  • March 2019
  • November 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
  • February 2018
  • December 2017
  • October 2017
  • August 2017
  • July 2017
  • January 2017
  • May 2016
  • December 2015
  • November 2015
  • December 2014
  • November 2014
  • October 2014
  • March 2014
  • February 2011
  • November 2008
  • June 2008
  • May 2008
  • April 2008
  • January 2008
  • November 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • April 2007
  • March 2007

LangChain.js MCP Adapters: Connect Agents to MCP Servers

2025-04-01 in Artificial Intelligence / JavaScript tagged JavaScript / LLM / AI Agent / Model Context Protocol (MCP) / LangChain.js by Marc Nuri | Last updated: 2026-10-14
Versión en Español

Introduction

⁠

To connect a LangChain.js agent to a Model Context Protocol (MCP) server from JavaScript or TypeScript, you use the @langchain/mcp-adapters package. Its MCPAdapter class starts or connects to each MCP server, and its listTools() method turns the server's tools into LangChain tools that you pass to createAgent. The agent's Large Language Model (LLM) can then call those tools to read real-time data and take actions in response to the user's requests.

In this post, I'll show you how to connect a JavaScript application to an MCP server using LangChain.js, over both the stdio and Streamable HTTP transports, and how to build an agent that interacts with a Kubernetes cluster using a local model. I tested every step in October 2026 with langchain 1.5.16, @langchain/mcp-adapters 2.0.1, @langchain/ollama 1.3.0, the Kubernetes MCP Server v0.0.67, and the qwen3:4b-instruct model running on Ollama, so you can run every example without an API key. If your code uses MultiServerMCPClient from version 1.x, see what changed in 2.0.

What you need

⁠

To follow along, you need:

  • Node.js 22 or later (@langchain/mcp-adapters 2.x itself needs 20.10 or later, but Node.js 20 is no longer supported).
  • Ollama with a model that supports tool calling. I used qwen3:4b-instruct (about 2.5 GB), which you can download with ollama pull qwen3:4b-instruct. Make sure the Ollama server is running (the desktop app starts it; otherwise run ollama serve).
  • Access to a Kubernetes cluster through your kubeconfig file. Any local cluster, like the ones kind or minikube create, works.

Create a new project and install the packages:

npm init -y
npm install langchain @langchain/core @langchain/langgraph @langchain/mcp-adapters @langchain/ollama

@langchain/core and @langchain/langgraph are peer dependencies of the adapters, and @langchain/ollama provides the chat model for Ollama.

Configuring the MCP servers

⁠

Before you can use MCP tools with LangChain.js, you need MCP clients that can communicate with MCP servers. With @langchain/mcp-adapters 2.x, you don't create those clients yourself: you describe each server in an MCPAdapter, and the adapter creates and manages the clients for you. It picks the transport from each server's configuration: a command means stdio, and a url means Streamable HTTP.

Connecting to a local server over stdio

⁠

The stdio transport is ideal for command-line tools and direct integrations. It allows communication with an MCP server running as a subprocess of your application. This approach is particularly useful for local execution, which is convenient for agents that perform tasks on the same machine where the server is running.

The following code snippet configures an MCPAdapter with a single stdio server:

stdio-adapter.mjs
import {MCPAdapter} from '@langchain/mcp-adapters';

const adapter = new MCPAdapter({
  servers: {
    kubernetes: {
      command: 'npx',
      args: ['-y', 'kubernetes-mcp-server@latest']
    }
  }
});

In this example, I'm creating an adapter with one server named kubernetes that runs the Kubernetes MCP Server package using npx. The adapter spawns the command as a child process and communicates with it through its standard input and output streams.

These are the key components of the stdio server configuration:

  • The key in the servers map (kubernetes) is the server name. The adapter uses it to identify the server and, by default, as a prefix for its tool names.
  • command: The command to execute the MCP server.
  • args: The array of arguments to pass to the command.

The adapter doesn't connect when you create it; it connects the first time you ask it for tools.

Note

The stdio transport doesn't pass your whole environment to the server process. On Linux and macOS it only passes HOME, LOGNAME, PATH, SHELL, TERM and USER (Windows has its own list). If you select your cluster with the KUBECONFIG environment variable, add env: {KUBECONFIG: process.env.KUBECONFIG} to the server's configuration, or pass the server's --kubeconfig flag in args.

Connecting to a remote server over Streamable HTTP

⁠

The Streamable HTTP transport is suitable for MCP servers running in a remote environment. It establishes a connection with the MCP server over HTTP using a single endpoint for bidirectional messaging.

Note

Prior to MCP protocol version 2025-03-26, the HTTP+SSE (Server-Sent Events) transport was used for remote connections. It has been deprecated since then in favor of Streamable HTTP. The adapter still connects to servers that only support SSE: with a plain url it falls back to SSE when the server answers 404 or 405, and transport: 'sse' skips the Streamable HTTP attempt.

To try it locally, start the Kubernetes MCP Server in HTTP mode in a separate terminal:

npx -y kubernetes-mcp-server@latest --port 8080 --bind-address 127.0.0.1

The server then listens for Streamable HTTP requests at http://localhost:8080/mcp. By default, it binds to all network interfaces (0.0.0.0), which would expose your cluster to anyone who can reach your machine; --bind-address 127.0.0.1 keeps it local. The following code snippet configures an MCPAdapter that connects to it:

http-adapter.mjs
import {MCPAdapter} from '@langchain/mcp-adapters';

const adapter = new MCPAdapter({
  servers: {
    kubernetes: {
      url: 'http://localhost:8080/mcp'
    }
  }
});

The only difference from the stdio configuration is the server entry: a url instead of a command. Streamable HTTP is the default for URL-based servers, so you don't need to set transport: 'http'. For a server that requires authentication, the same entry takes headers or an authProvider for OAuth.

A single adapter can also combine both kinds of servers: add one entry per server to the servers map, and the agent gets the tools of all of them.

Loading MCP tools as LangChain.js tools

⁠

Now that the adapter knows about your MCP server, you can load its tools and use them with LangChain.js. Continuing from the previous snippet, the listTools() method connects to every configured server, discovers its tools, and wraps each one as a LangChain tool:

const tools = await adapter.listTools();
console.log(tools.map(tool => tool.name));
// [ 'kubernetes_configuration_view', 'kubernetes_events_list', ... ]

Behind the scenes, the adapter negotiates the protocol version with each server, lists its tools, and creates a LangChain tool with the same name, description and input schema for each one. With the default configuration, the Kubernetes MCP Server v0.0.67 exposed 20 tools.

Notice the kubernetes_ prefix: since version 2.0, the adapter prefixes every tool name with its server name and an underscore, even when there is only one server. This avoids collisions when two servers expose a tool with the same name. You can set prefixToolNameWithServerName: false in the adapter's configuration to keep the original names.

The adapter keeps the connections open while your agent uses the tools. When you're done, call await adapter.close() to close them and stop any server processes it started.

Using your own MCP client with loadMcpTools

⁠

If your application already manages its own MCP client, the standalone loadMcpTools function adapts that client's tools without handing the connection over to an MCPAdapter. Since @langchain/mcp-adapters 2.0, it expects a client from version 2 of the MCP TypeScript SDK, which you install with npm install @modelcontextprotocol/client:

load-mcp-tools.mjs
import {Client} from '@modelcontextprotocol/client';
import {StdioClientTransport} from '@modelcontextprotocol/client/stdio';
import {loadMcpTools} from '@langchain/mcp-adapters';

const client = new Client(
  {name: 'blog.marcnuri.com', version: '1.0.0'},
  {versionNegotiation: {mode: 'auto'}}
);
await client.connect(new StdioClientTransport({
  command: 'npx',
  args: ['-y', 'kubernetes-mcp-server@latest']
}));
const tools = await loadMcpTools('kubernetes', client);

For a remote server, import StreamableHTTPClientTransport from @modelcontextprotocol/client and pass it new URL('http://localhost:8080/mcp') instead.

These are the key components of the example:

  • Client: The MCP client, with a name and version that identify it to the server. versionNegotiation: {mode: 'auto'} lets it use the latest protocol revision (2026-07-28) when the server supports it, and fall back to the previous one when it doesn't; without it, the SDK only speaks the older revisions. In my tests, the client negotiated 2026-07-28 with the Kubernetes MCP Server over stdio, and 2025-11-25 over Streamable HTTP.
  • StdioClientTransport: The stdio transport, which spawns the server as a child process.
  • loadMcpTools: Lists the client's tools and returns them as LangChain tools. The first argument is the server name; unlike MCPAdapter, loadMcpTools doesn't prefix the tool names unless you ask it to.

You're responsible for this client: close it with await client.close() when you're done.

Building a LangChain.js agent with createAgent and LangGraph.js

⁠

You now have all the pieces together to create an agent that can use the MCP tools. Since LangChain.js 1.0 (October 2025), createAgent from the langchain package is the standard way to build one. It runs on LangGraph.js and replaces LangGraph's prebuilt createReactAgent, which is now deprecated.

Here's a complete example that creates an agent that can interact with a Kubernetes cluster using the Kubernetes MCP Server and a local model running on Ollama:

kubernetes-agent.mjs
import {createAgent} from 'langchain';
import {ChatOllama} from '@langchain/ollama';
import {MCPAdapter} from '@langchain/mcp-adapters';

const adapter = new MCPAdapter({
  servers: {
    kubernetes: {
      command: 'npx',
      args: ['-y', 'kubernetes-mcp-server@latest']
    }
  }
});

try {
  const tools = await adapter.listTools();
  const agent = createAgent({
    model: new ChatOllama({model: 'qwen3:4b-instruct'}),
    tools
  });
  const listPods = await agent.invoke({
    messages: [{
      role: 'user',
      content: 'List all pods in my cluster and output their name and namespace as a Markdown table'
    }]
  });
  console.log(listPods.messages.at(-1).text);
} finally {
  await adapter.close();
}

You can run it with node kubernetes-agent.mjs. In this example, I create a very simple assistant that connects the Kubernetes MCP Server tools with the qwen3:4b-instruct model running on your machine.

These are the key components of the agent example:

  • ChatOllama:
    A LangChain chat model that talks to the local Ollama server (http://127.0.0.1:11434 by default).
    • model: The name of the Ollama model to use.
  • adapter.listTools():
    Loads the MCP tools from the Kubernetes MCP Server as described in the previous section.
  • createAgent:
    Creates a LangChain agent using the model and the tools you loaded.
  • invoke:
    Invokes the agent with a user request to list all pods in the Kubernetes cluster. The agent will use the tools to perform the action and return the result.
  • listPods.messages:
    The agent's state after the run. The example prints the text of the last message, which contains the model's final answer.
  • adapter.close():
    Closes the connection and stops the server process, even if the agent fails.

Tip

Any LangChain chat model that supports tool calling works in place of ChatOllama. For a hosted model like Google's Gemini, install @langchain/google-genai, add import {ChatGoogleGenerativeAI} from '@langchain/google-genai', and use new ChatGoogleGenerativeAI({model: 'gemini-2.5-flash'}) with your GOOGLE_API_KEY.

The agent follows a ReAct (Reasoning and Acting) pattern, alternating between reasoning about what to do next and taking action using the available tools. In this case, the agent can use the tools provided by the Kubernetes MCP Server to interact with your Kubernetes cluster.

I ran the example against a fake local test cluster whose only Pods were an nginx deployment of two replicas, and the output was (on a kind or minikube cluster, the list also includes the kube-system Pods):

| NAME                                  | NAMESPACE |
|--------------------------------------|-----------|
| nginx-7f8fbb96d-kjc5p               | default   |
| nginx-7f8fbb96d-l5svd              | default   |

The listPods.messages array contains the complete list of messages exchanged during the run. In this case, there were four: the user's request, the model's call to the kubernetes_pods_list tool, the tool's result, and the model's final answer.

The beauty of this approach is that the agent decides which tools to use based on the user's request. For example, if the user asks to list pods, the agent identifies that it needs the Kubernetes tool that fetches pod information, and then formats the result as a Markdown table.

Small models need some help, though. qwen3:4b-instruct called the right tool in every one of my runs, but it usually added a status.phase=Running filter nobody asked for, which would hide pods in any other state, and the table's columns changed from run to run. Other small models I tried sent empty or null values for optional arguments, which the adapter rejects because they don't match the tool's input schema. The agent gets the error back as the tool's result, so the model can retry, but the small models rarely recovered: granite4 usually repeated the same call and then answered with a placeholder table, and llama3.2:3b gave up. I covered how to get the most out of small models in Giving Superpowers to Small Language Models with MCP.

The Kubernetes MCP Server also exposes tools that delete or modify resources in your cluster. To experiment safely, add --read-only to its args, so it only exposes the tools that don't change anything. Read-only prevents changes, not reads: tools like resources_get can still return Secrets, which then reach the model (and its provider, if you use a hosted one); the server's denied_resources setting can keep them out.

Upgrading from @langchain/mcp-adapters 1.x

⁠

Anthropic introduced MCP in November 2024, and it quickly became the standard way to expose tools to artificial intelligence (AI) applications. In March 2025, the LangChain community released the MCP adapters for LangChain.js, a JavaScript library for building applications powered by LLMs; the adapters started as a separate project and now live in the main LangChain.js repository. @langchain/mcp-adapters 2.0 (October 2026) moved to version 2 of the MCP TypeScript SDK, which adds the 2026-07-28 revision of the MCP specification while still talking to older servers, with MCPAdapter as its new entry point. I first wrote this post in April 2025, with hand-built MCP clients, the SSE transport, LangGraph's createReactAgent and OpenAI's GPT-4o mini through GitHub Models. In January 2026, I moved it to Streamable HTTP, createAgent and Google's Gemini.

If you followed an earlier version of this post, or any tutorial that uses MultiServerMCPClient, this is what changed in @langchain/mcp-adapters 2.0:

1.x2.x
MultiServerMCPClientMCPAdapter
mcpServers: {...}servers: {...}
getTools()listTools()
Unprefixed tool names (pods_list)Prefixed tool names (kubernetes_pods_list)
@modelcontextprotocol/sdk client@modelcontextprotocol/client client

The old class and method names still work but are deprecated. Passing a client from the old @modelcontextprotocol/sdk package to loadMcpTools doesn't: it fails with TypeError: client.getProtocolEra is not a function. Switch the imports to @modelcontextprotocol/client (and @modelcontextprotocol/client/stdio for the stdio transport), as shown above. The prefixed tool names also affect anything that refers to a tool by name, like prompts or human-in-the-loop approval rules. The official migration guide covers the rest of the changes.

Conclusion

⁠

In this post, I've shown you how to connect your JavaScript applications to an MCP server using LangChain.js. I've covered how to configure stdio and Streamable HTTP servers with MCPAdapter, how to load their tools (or adapt your own MCP client's tools with loadMcpTools), and how to bind everything together with createAgent and a local model. With the MCP adapters, any of the many available MCP servers becomes a set of tools your LangChain.js agents can use with a few lines of configuration.

You can find the source code for this post on GitHub.

You Might Also Like

⁠
  • AI SDK MCP Client: Connect JavaScript to an MCP Server
  • LangChain4j MCP Client: Connect Java to an MCP Server
  • Introduction to the Model Context Protocol (MCP): The Future of AI Integration
  • Giving Superpowers to Small Language Models with Model Context Protocol (MCP)
  • MCP Tool Annotations: Every Hint and Its Default Value
Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Post navigation
The Future of Developer Tools: Adapting to Machine-Based DevelopersAI SDK MCP Client: Connect JavaScript to an MCP Server
© 2007 - 2026 Marc Nuri