Pair AI Agents with MCP to Access the Outside World

Part of the series AI Agents and MCP Server: Teaming Up for the Agentic Web

Our MCP server is ready, but the agent still does not know it exists. The next step is to give the agent an MCP client that can discover the server's tools and make them available to the model.

When we tested the server manually, the MCP Inspector used List Tools to retrieve its available tools. The AI SDK can do the same programmatically: it connects to the server, lists the tools, and adapts them to the interface used by streamText.

Before doing so, we need to add a Nitro runtime config to indicate the URL of the MCP.

ts
import { defineNitroConfig } from 'nitropack/config'

export default defineNitroConfig({
  runtimeConfig: {
    openAiApiKey: '',
    mcpEndpoint: '',
  },
  // ...
})

In your .env file, set the MCP_ENDPOINT variable to the MCP URL:

ini
MCP_ENDPOINT=http://localhost:3000/mcp

Then, we can connect our agent to the MCP server. The client is created for the request, retrieves the available tools, and remains open until the streamed response finishes.

ts
import { createOpenAI } from '@ai-sdk/openai'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
import { convertToModelMessages, experimental_createMCPClient, stepCountIs, streamText } from 'ai'
import { defineEventHandler, defineLazyEventHandler, readBody } from 'h3'
import { useRuntimeConfig } from 'nitropack/runtime'

export default defineLazyEventHandler(() => {
  const runtimeConfig = useRuntimeConfig()

  const model = createOpenAI({
    apiKey: runtimeConfig.openAiApiKey,
  })

  return defineEventHandler(async (event) => {
    const { messages } = await readBody(event)

    const httpTransport = new StreamableHTTPClientTransport(
      new URL(runtimeConfig.mcpEndpoint)
    )
    const httpClient = await experimental_createMCPClient({
      transport: httpTransport
    })
    const tools = await httpClient.tools()

    const result = streamText({
      model: model('gpt-5-nano'),
      system: `You are a helpful assistant. You can use the tool to add two numbers together.`,
      stopWhen: stepCountIs(2),
      tools,
      messages: convertToModelMessages(messages),
      onFinish: () => httpClient.close(),
      onAbort: () => httpClient.close(),
    })

    return result.toUIMessageStreamResponse()
  })
})

The most important line is const tools = await httpClient.tools(). Like the Inspector's List Tools action, it retrieves the tools exposed by the MCP server. Passing them to streamText makes those tools available to the model. When the model requests one, experimental_createMCPClient uses the HTTP transport to call the MCP server on the agent's behalf.

Note

The MCP client uses the Streamable HTTP transport to exchange protocol messages with the MCP server. Closing the client in onFinish or onAbort keeps it available while the response is streaming and releases its resources afterward.

From the user's perspective, the agent behaves just as it did with a direct tool. But from the architectural perspective, the agent now consumes a standardized capability provided by a separate server.

The AI Agent using a tool and generating a response.

The agent can now use a tool without knowing how it is implemented. We can evolve the MCP server independently, let compatible clients use it, or connect the agent to another MCP server when it needs additional capabilities. Next, we will add a chat interface so people can use the agent directly.

PP

Thanks for reading! My name is Estéban, and I love to write about web development and the human journey around it.

I've been coding for several years now, and I'm still learning new things every day. I enjoy sharing my knowledge with others, as I would have appreciated having access to such clear and complete resources when I first started learning programming.

If you have any questions or want to chat, feel free to comment below or reach out to me on Bluesky, X, and LinkedIn.

I hope you enjoyed this article and learned something new. Please consider sharing it with your friends or on social media, and feel free to leave a comment or a reaction below, it would mean a lot to me! If you'd like to support my work, you can sponsor me on GitHub!

Continue readingA Powerful AI Application Made with Nitro and Nuxt UI

Reactions

Discussions

Add a Comment

You need to be logged in to access this feature.