A logo showing the text blog.marcnuri.com
Español
Home»Artificial Intelligence»AI SDK MCP Client: Connect JavaScript to an MCP Server

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
  • August 2012
  • 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

AI SDK MCP Client: Connect JavaScript to an MCP Server

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

Introduction

⁠

To connect a JavaScript application to a Model Context Protocol (MCP) server with the Vercel AI SDK, you create an MCP client with createMCPClient from the @ai-sdk/mcp package (experimental_createMCPClient in older versions), pass the tools it returns to generateText or streamText, and close the client when you're done. The model then decides which of the server's tools to call, and the AI SDK runs them and feeds the results back to the model.

MCP provides a standardized way for artificial intelligence (AI) models to interact with external tools and systems. Check my introduction to MCP if you're new to the protocol. Vercel AI SDK is a TypeScript toolkit with a growing list of supported model providers, which makes it an excellent alternative to the LangChain.js library.

In this post, I'll show you how to connect your JavaScript applications to an MCP server using the AI SDK. I'll describe how to set up the MCP client over stdio and Streamable HTTP, configure a model provider, and prompt the model using the generateText function. I tested every step in October 2026 with AI SDK 7.0.140, @ai-sdk/mcp 2.0.74, the Kubernetes MCP Server v0.0.67, and a small local model (Qwen3 4B) running in Ollama, so you can follow along without an API key.

Which import do you need: experimental_createMCPClient or createMCPClient?

⁠

Vercel added an experimental MCP client in AI SDK 4.2 (March 2025), and I first wrote this post a few weeks later, in April 2025. The client moved to its own package, @ai-sdk/mcp, in October 2025, and AI SDK 6 (December 2025) made it stable and dropped the experimental_ prefix, so the import in older tutorials depends on the AI SDK version they target:

AI SDK versionMCP client import
4.2 to 5.0.78experimental_createMCPClient from ai
5.0.79 to 5.xexperimental_createMCPClient from @ai-sdk/mcp (0.x)
6 and 7createMCPClient from @ai-sdk/mcp (1.x and 2.x)

As of October 2026, @ai-sdk/mcp still exports the experimental_createMCPClient name as an alias, but new code should use createMCPClient. AI SDK 7 (June 2026) made every package ESM-only and renamed some of the functions this post uses. If you're upgrading code from AI SDK 6, these are the changes from its migration guide that affect this post:

  • stepCountIs is now isStepCount (the old name still works as an alias).
  • The client option name is deprecated in favor of clientName, which late AI SDK 6 releases already accept (name still works).
  • The HTTP transport rejects redirects by default; set redirect: 'follow' if your server relies on them.
  • All packages are ESM-only and need Node.js 22 or later.

Setting up the project

⁠

The AI SDK 7 packages are ESM-only and need Node.js 22 or later (check with node --version). Create a project and switch it to ES modules:

npm init -y
npm pkg set type=module

Install the AI SDK, its MCP client, the client package of the official MCP TypeScript SDK (for the stdio transport), and the Ollama provider:

npm install ai @ai-sdk/mcp @modelcontextprotocol/client ollama-ai-provider-v2

The examples use a local model through Ollama. Install Ollama and pull a small model that supports tool calling (about 2.5 GB):

ollama pull qwen3:4b-instruct

The Kubernetes MCP Server needs a cluster to talk to. It uses your default kubeconfig file, so any cluster works; I used a local kind cluster, which kind create cluster creates and adds to that file.

Creating the AI SDK MCP clients

⁠

To interact with an MCP server, we first need to create an MCP client. AI SDK provides a function called createMCPClient in the @ai-sdk/mcp package that allows us to establish this connection.

The MCP specification defines two standard transports: stdio and Streamable HTTP. Let's learn how to create a client for each of them.

Setting up an STDIO transport client

⁠

The STDIO transport method allows us to spawn a local process and communicate with it through standard input and output streams. This is useful for running local MCP servers or processes that expose the local system's capabilities.

The following code snippet demonstrates how to set up an STDIO transport client for the Kubernetes MCP server:

stdio-client.js
import {createMCPClient} from '@ai-sdk/mcp';
import {StdioClientTransport} from '@modelcontextprotocol/client/stdio';

const initStdioClient = async () => {
  const transport = new StdioClientTransport({
    command: 'npx',
    args: ['-y', 'kubernetes-mcp-server@latest']
  });
  return createMCPClient({clientName: 'blog.marcnuri.com', transport});
};

In this code, we're creating an MCP client instance that runs the Kubernetes MCP server using the npx command.

These are the key steps to set up the STDIO transport client:

  1. Import the necessary modules from the @ai-sdk/mcp and @modelcontextprotocol/client packages.
  2. Create a new instance of StdioClientTransport, passing the command and arguments to run the MCP server.
  3. Call createMCPClient with the transport instance to create the MCP client and establish the connection. clientName is optional; it's the name the client reports to the server.

Older tutorials, and the AI SDK docs as of October 2026, import the same transport from @modelcontextprotocol/sdk/client/stdio.js, version 1 of the MCP TypeScript SDK, which still works.

Note

The transport doesn't pass your whole environment to the server process: only a few variables, such as HOME, PATH and USER (on Windows, their equivalents). If the server needs another one, like KUBECONFIG or a proxy setting for npx, add it with the transport's env option, which is merged with those defaults, for example env: {KUBECONFIG: process.env.KUBECONFIG}.

The AI SDK also ships its own stdio transport, Experimental_StdioMCPTransport in @ai-sdk/mcp/mcp-stdio. Before the classic handshake, it probes the server for the new stateless protocol revision (2026-07-28) and gives up on that probe after one second. In my tests with v2.0.74, npx -y kubernetes-mcp-server@latest took longer than that to start the server, and the connection failed with a duplicate "initialize" received error. It connected once I set protocolVersionDiscovery: false in createMCPClient, or when the server started quickly from a local install. The MCP SDK transport above uses the classic handshake from the start, so I stick with it.

Setting up a Streamable HTTP transport client

⁠

The Streamable HTTP transport method allows us to connect to a remote MCP server over HTTP. This is useful when you have an MCP server already running as a service that you want to connect to.

Note

Prior to MCP protocol version 2025-03-26, the SSE (Server-Sent Events) transport was used for remote connections. SSE is now deprecated in favor of Streamable HTTP.

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

Without --bind-address, the server listens on all network interfaces (0.0.0.0) and doesn't authenticate its clients by default, so anyone who can reach the port can use your cluster with your kubeconfig credentials.

The following code snippet demonstrates how to set up a Streamable HTTP transport client:

http-client.js
import {createMCPClient} from '@ai-sdk/mcp';

const initHttpClient = async () => {
  return createMCPClient({
    clientName: 'blog.marcnuri.com',
    transport: {
      type: 'http',
      url: 'http://localhost:8080/mcp'
    }
  });
};

In this fragment, we're creating an MCP client that connects to a local server running on port 8080 using the Streamable HTTP protocol. This approach is more suitable for production environments or when you have a dedicated MCP server running.

Note

In production environments, your URL should point to the actual address of your MCP server (which should also be HTTPS-enabled). The transport also accepts headers (for example, an Authorization header) and an OAuth authProvider. Since AI SDK 7, it rejects HTTP redirects by default; set redirect: 'follow' if your server relies on them.

These are the key steps to set up the Streamable HTTP transport client:

  1. Import the necessary module from the @ai-sdk/mcp package.
  2. Call createMCPClient with the transport configuration, specifying the type as http and providing the URL of the MCP server.
  3. The function returns an MCP client instance that can be used to interact with the remote server.

Notice how the HTTP-based transport is much simpler to set up than the STDIO-based transport: the AI SDK includes it, so you don't need the MCP SDK.

Setting up the AI SDK provider and model

⁠

Once we have the MCP client set up, we need to configure the AI SDK provider and model that will use the MCP server's tools. The AI SDK has a long list of supported providers; in this case, we will use the community Ollama provider to run a model locally.

The following code snippet demonstrates how to set up the Ollama provider with the Qwen3 4B model:

ollama.js
import {ollama} from 'ollama-ai-provider-v2';

const model = ollama('qwen3:4b-instruct');

In this code, we're using the default instance of the Ollama provider and specifying the model we want to use:

  • The default ollama instance connects to a local Ollama server at http://127.0.0.1:11434/api. If yours runs elsewhere, create your own instance with createOllama({baseURL: '...'}).
  • The ollama function is then called with the model name to create a model instance that can be used for generating text.

The model must support tool calling, and small models vary a lot in how well they do it. I wrote about this in Giving Superpowers to Small Language Models with MCP.

Tip

Switching to a hosted model only changes these two lines. For example, with the Google provider (@ai-sdk/google), you'd import google and create the model with google('<model-id>'), reading the API key from the GOOGLE_GENERATIVE_AI_API_KEY environment variable. In AI SDK 7, the Google provider's createGoogleGenerativeAI function is now createGoogle.

Prompting the model with generateText

⁠

Now that we've set up all the necessary components, we can put the pieces together to prompt the AI model and let it interact with the MCP server's tools.

In this example, we are going to generate text by asking the model to list all Kubernetes pods in a Markdown table format.

The following code snippet shows the relevant parts of the pipeline:

assistant.js
import {generateText, isStepCount} from 'ai';
import {ollama} from 'ollama-ai-provider-v2';

const assistant = async () => {
  const mcpClient = await initStdioClient();
  try {
    const tools = await mcpClient.tools();
    const model = ollama('qwen3:4b-instruct');
    const result = await generateText({
      model,
      tools,
      stopWhen: isStepCount(10),
      messages: [{
        role: 'user',
        content: 'List all pods in my cluster and output their name and namespace as a Markdown table'
      }]
    });
    console.log(result.text);
  } finally {
    await mcpClient.close();
  }
};

assistant().catch(err => {
  console.error('Error:', err);
});

In this code, we create an assistant that connects the Qwen3 4B model to the Kubernetes MCP server tools and perform a simple prompt.

These are the key components of this example:

  • We first initialize the STDIO client and retrieve the available tools by calling mcpClient.tools(). This lists every tool the server offers (20 with the Kubernetes MCP Server's default toolsets) and converts them to AI SDK tools.
  • We then call the generateText function with the following parameters:
    • model: The model instance we created earlier.
    • tools: The tools available from the MCP server client.
    • stopWhen: A condition that determines when to stop the generation loop. We use isStepCount(10) to limit the model to a maximum of 10 steps.
      The default for generateText is a single step: the AI SDK would run the tool the model asks for, but the model wouldn't get another step to turn the result into an answer, so result.text would be empty. Setting the value to more than 1 ensures the model can perform the initial prompt and subsequent prompts with the tools execution results.
    • messages: An array of messages that represent the conversation history. In this case, we provide a single user message asking the model to list all pods in the cluster and format the output as a Markdown table.
  • Finally, we log the generated text to the console and close the client in a finally block, so the server process stops even if the generation fails.

The output of this invocation will be similar to:

| NAMESPACE | NAME |
|----------|------|
| default | nginx-6f5fc479d9-j25wp |
| default | nginx-6f5fc479d9-vq4r9 |
| kube-system | coredns-668d6bf9bc-2q9pp |
| kube-system | coredns-668d6bf9bc-r2jg2 |
| kube-system | etcd-ai-sdk-refresh-control-plane |
| kube-system | kindnet-zncdz |
| kube-system | kube-apiserver-ai-sdk-refresh-control-plane |
| kube-system | kube-controller-manager-ai-sdk-refresh-control-plane |
| kube-system | kube-proxy-rpsdh |
| kube-system | kube-scheduler-ai-sdk-refresh-control-plane |
| local-path-storage | local-path-provisioner-7dc846544d-8xlh6 |

result.text only holds the model's final answer. result.steps contains every step of the loop, including the tool calls and their results, which is where you can check what the model actually did.

That check matters with a small model. In my runs, Qwen3 4B always called the pods_list tool, but whenever I asked for specific columns, it added a status.phase=Running field selector I hadn't asked for, which would have hidden any pending or failing pod. When I asked for every column instead, it called the tool without filters, but copied a pod IP wrong while rewriting the 13-column table. On my laptop, each run took between 25 and 40 seconds.

Limiting the tools the model can use

⁠

mcpClient.tools() hands the model every tool the server exposes, including destructive ones like pods_delete and resources_delete. The simplest way to keep a model away from them is on the server side: the Kubernetes MCP Server's --read-only flag exposes only the tools annotated as read-only. 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.

args: ['-y', 'kubernetes-mcp-server@latest', '--read-only']

With this flag, the client gets 14 tools instead of 20. On the client side, the AI SDK exposes each tool's MCP tool annotations in its metadata, and its toolApproval option lets you require user approval for the tools that aren't marked read-only. Annotations are hints from the server, though: only rely on them for servers you trust, since a server can mark a destructive tool as read-only and skip the approval.

Conclusion

⁠

In this post, I showed you how to connect to an MCP server from JavaScript using the Vercel AI SDK. We learned how to set up both STDIO and Streamable HTTP transport clients, configure a local model through the Ollama provider, and prompt the model using the generateText function to generate useful output.

This modular architecture illustrates the flexibility of the SDK: swapping the transport or the model provider only changes a few lines, and the rest of the code stays the same. By following these steps, you can integrate your JavaScript applications with an MCP server and let LLMs interact with external tools and systems.

As the MCP ecosystem matures, the same MCP servers work from the AI SDK, from LangChain.js, or from any other MCP client.

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

You Might Also Like

⁠
  • Introduction to the Model Context Protocol (MCP): The Future of AI Integration
  • LangChain.js MCP Adapters: Connect Agents to MCP Servers
  • LangChain4j MCP Client: Connect Java to an MCP Server
  • 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
LangChain.js MCP Adapters: Connect Agents to MCP ServersFabric8 Kubernetes Client 7.2 is now available!
© 2007 - 2026 Marc Nuri