The problem with dynamic MCP tools

Model Context Protocol (MCP) is becoming a common way to federate tool calls between agents, with enterprises increasingly treating it as a microservice architecture for reusing tools across AI applications. But running MCP tools in production agents carries real risks. Tool names, descriptions, and argument schemas end up as part of your agent's prompt, and they can change unexpectedly—even when the upstream server isn't compromised or malicious.

Those changes translate into concrete problems. A compromised or drifted server can inject prompts through tool descriptions that are preloaded into context. Servers that were read-only can introduce destructive tools. Maintainers can push schema changes or entirely new capabilities without versioning. And token costs add up: GitHub's MCP server, for instance, consumes roughly 50,000 tokens just for tool definitions, most of which a given agent will never call. Generic descriptions written for a broad audience also make it harder for your model to decide when to use a tool or how to format arguments.

Vendoring as a solution

The team at Vercel looked at how shadcn/ui solved a similar problem in component libraries. Instead of forcing a trade-off between flexibility and simplicity, it offered a third path: copy the code into your project. You own the code while still getting the benefits of a curated library via the generating CLI.

The same idea now applies to AI tools. mcp-to-ai-sdk is a CLI that connects to any MCP server, downloads the tool definitions, and generates AI SDK–compatible tools that live in your codebase. At runtime, those tools still call the original MCP server, but their schemas and descriptions are versioned in your repository and only change when you explicitly update them through code review.

How the CLI works

Point the CLI at any MCP server and it generates local tool stubs for the AI SDK:

npx mcp-to-ai-sdk https://mcp.grep.app

The output is local files with standard AI SDK tool definitions:

import { tool } from "ai";

import { type Client } from "@modelcontextprotocol/sdk/client/index.js";

import { z } from "zod";

// Auto-generated wrapper for MCP tool: searchGitHub

// Source: https://mcp.grep.app

export const searchGitHubToolWithClient = (

getClient: () => Promise<Client> | Client,

) =>

tool({

description: "Find real-world code examples from GitHub repositories",

inputSchema: z.object({

query: z.string().describe("Code pattern to search for"),

language: z

.array(z.string())

.optional()

.describe("Programming languages"),

}),

execute: async (args): Promise<string> => {

const client = await getClient();

const result = await client.callTool({

name: "searchGitHub",

arguments: args,

});

// Handle different content types from MCP

if (Array.isArray(result.content)) {

return result.content

.map((item: unknown) =>

typeof item === "string" ? item : JSON.stringify(item),

)

.join("\n");

} else if (typeof result.content === "string") {

return result.content;

} else {

return JSON.stringify(result.content);

}

},

});

These integrate directly with your existing AI SDK setup—import the ones you need and pass them to your agent configuration. You control exactly which tools exist and how they're described, while the generated tools still call the original MCP server.

What vendored tools give you

  • Security through source control: Tool definitions are checked into your repo and change only through code review, preventing prompt injection via tool descriptions.
  • Performance through selective loading: You decide which tools enter your agent's context, avoiding token waste on definitions you don't need.
  • Reliability through version control: Schemas and descriptions stay stable. Upstream drift and surprise tool additions remain under your control.
  • Customization through local editing: Tune descriptions for your model, restrict argument ranges, or add application-specific logic like authentication.

Bringing it into your project

After generating tools from an MCP server, import them into your AI SDK project:

import { generateText } from "ai";

import { openai } from "@ai-sdk/openai";

import { mcpGrepTools } from "./mcps/mcp.grep.app"; // Domain-based export name

const result = await generateText({

model: openai("gpt-5"),

tools: mcpGrepTools, // Use all tools from the MCP server

prompt: "Find examples of React hooks usage",

});

From there, you can modify them, combine them with other tools, or use them as starting points for more specialized implementations.

The CLI works with any MCP server, including those requiring authentication, custom headers, or different transport protocols. Configuration options and sample outputs are available in the repository.

Striking the right balance

MCP remains excellent for discovery and prototyping. The trouble starts when dynamic definitions flow directly into production agents, where stability and reviewability matter more than flexibility. mcp-to-ai-sdk offers a middle ground: MCP's discovery benefits during development, with vendored tools' security benefits in production. Treating server responses as untrusted input is still essential, since your agent is calling the server at runtime. But vendoring tool definitions is one way to keep the boundary between your application and upstream servers clearly drawn as AI applications move from prototypes to production systems.