Your Agent Toolkit: MCP Server, SDK, or Both?
MCP promises standardization but framework support varies wildly. SDKs give control but mean vendor lock-in. The hybrid approach fills gaps that pure options leave open.
On this page
Every developer building AI agents faces the same question: how should my agent consume tools?
The obvious answer used to be “just use function calling.” Define schemas inline, let the model pick tools, handle the results. Simple enough for a demo. Then you need approval workflows, tool filtering, output transformations, and suddenly you’re rebuilding infrastructure that should already exist.
MCP promised to standardize this. One protocol, any client. Connect to any MCP server and discover tools dynamically. A year later, 10,000+ MCP servers exist and adoption includes Claude, ChatGPT, Cursor, VS Code, and Copilot.
But the promise doesn’t match reality for everyone. Framework support varies dramatically. Vercel AI SDK’s MCP integration can’t deploy stdio transport to production. Missing features include session management, resumable streams, and approval workflows. Security and multi-tenant isolation? You’re on your own.
So how should you actually consume tools in your agent?
Three Approaches
There are three ways to give your agent access to tools. Each optimizes for different constraints.
Pure MCP Client: Connect to MCP servers, discover tools dynamically. The protocol handles discovery, you handle everything else.
Pure SDK (Function Calling): Import tools as functions directly in your codebase. Define schemas with JSON Schema or Zod. No external dependencies.
Hybrid Toolset SDK: Libraries like StackOne that pull tool definitions dynamically but wrap them with utilities the protocol doesn’t provide (filtering, approvals, framework adapters, output transformations).
The right choice depends on how many integrations you need, whether you need runtime flexibility, and what utilities you can’t build yourself.
Pure MCP Client: Dynamic Discovery, Variable Support
Connecting to MCP servers gives you access to thousands of pre-built integrations. Any MCP-compatible server exposes tools your agent can discover at runtime.
// Connect to a remote MCP server (Sentry's hosted endpoint)
import { experimental_createMCPClient } from "ai";
const mcpClient = await experimental_createMCPClient({
transport: {
type: "sse",
url: "https://mcp.sentry.dev/sse",
},
});
// Discover available tools dynamically
const tools = await mcpClient.tools();
// Returns: [{ name: "get_issue", ... }, { name: "search_errors", ... }]
Remote MCP servers like Sentry’s handle OAuth, scaling, and state management. Local stdio servers work for development but can’t deploy to production in most frameworks.
Pure MCP Client
Connect to existing MCP servers without writing integration code
Tools discovered at runtime, not hardcoded
Same MCP server works with Claude, GPT, Gemini, local models
Major player adoption, Linux Foundation governance
Vercel AI SDK's stdio transport 'cannot be deployed to production'
Protocol doesn't define human-in-the-loop patterns
OAuth/PKCE/token refresh left to implementer
Every tool call goes through the server
The framework variance issue is real. I’ve seen teams spend days working around the gap between what MCP specifies and what their framework actually implements. Session management, notifications, and resumable streams exist in the spec but not consistently in SDKs.
What Developers Actually Say
The frustration isn’t theoretical. Developers report:
“Yeah MCP is the worst documented technology I have ever encountered… I have read so much about MCP and have zero fucking clue except vague marketing speak.” — epistasis, Hacker News
“This is a stunningly bad specification, perhaps the worst I have seen in my career.” — jes5199, Hacker News
Heavy MCP setups can consume 55-100k+ tokens before you type a word. That’s 27-50% of a 200k context window gone on tool definitions alone. — Mario Giancini, “The Hidden Cost of MCP Servers”
And on client-side implementation issues specifically:
MCP servers don’t reload when code changes. Must fully restart Claude Code CLI, losing entire conversation context every time. This has been broken for 6+ months. — GitHub Issue #7174
Server support is fine. Client implementations lag behind the spec. Custom headers, OAuth flows, and session management work differently (or not at all) across frameworks.
If your tools are for broad ecosystem consumption (open source, community contribution, cross-platform) and you can live with lowest-common-denominator features, pure MCP works.
Pure SDK: Full Control, Limited Reach
When you control both the agent and the tools, SDK-based function calling gives you type safety, embedded utilities, and no network overhead.
// SDK function calling with Zod
import { z } from "zod";
import { tool } from "ai";
const searchDocs = tool({
description: "Search documentation",
parameters: z.object({
query: z.string().describe("Search query"),
}),
execute: async ({ query }) => {
const results = await searchIndex(query);
return results.slice(0, 5);
},
});
const result = await generateText({
model: anthropic("claude-sonnet-4-20250514"),
tools: { searchDocs },
prompt: "Find docs about authentication",
});
Pure SDK (Function Calling)
Tool behavior is your code, not protocol-mediated
Zod schemas, TypeScript inference, compile-time checks
Tools are functions, no infrastructure to deploy
Add caching, approvals, transformations inline
OpenAI, Anthropic, Vercel each have different patterns
New integration = new tool code
Hardcoded tool imports, no runtime querying
Tool changes require code changes
Pure SDK makes sense for single-framework projects with custom tools. When you’re building internal tools for your specific agent, the overhead of running an MCP server doesn’t pay off.
Hybrid: SDK Utilities with MCP Discovery
The hybrid approach recognizes that MCP’s value is in discovery and standardization, but the protocol has real gaps that SDKs fill better.
// Hybrid: dynamic discovery + SDK utilities
const toolset = new StackOneToolSet();
// Fetch tools dynamically, filter by provider and action pattern
const tools = await toolset.fetchTools({
providers: ["hibob", "slack"],
actions: ["*_list_*", "*_create_*"],
});
// Convert for your framework
const openaiTools = tools.toOpenAI();
const anthropicTools = tools.toAnthropic();
const vercelTools = await tools.toAISDK();
The hybrid approach pulls tool definitions from an MCP-style endpoint but wraps them with utilities the protocol doesn’t provide.
Hybrid Approach
Tools fetched at runtime, always current
Single source, converts to OpenAI/Anthropic/Vercel/etc.
Glob patterns, provider scopes, action filters
Approvals, transformations, file handling built-in
Relies on the hybrid provider's implementation
One more layer between agent and tools
May not expose all raw MCP capabilities
Tool Filtering in Action
Try filtering tools by pattern and provider. Watch how token costs drop:
Filter tools dynamically
Reduce token overhead by selecting only the tools you need
tools.toOpenAI()
// Returns:
[
{ type: "function", function: { name: "hibob_list_employees", ... } },
{ type: "function", function: { name: "hibob_list_departments", ... } },
// ... 1 more
]What Your Framework Doesn’t Give You
You’ve seen the three approaches. But whichever you choose, your framework probably doesn’t handle everything you need. SDKs vary wildly in what utilities they provide.
The Variance Is Real
The same feature works differently (or not at all) across frameworks. Try switching between MCP support, approval workflows, and execution hooks to see what each framework actually provides:
Framework Feature Support
Same feature, different implementations. See what each framework actually provides.
Connect to MCP servers and discover tools dynamically
import { Agent, MCPServerStdio } from "@openai/agents";
const agent = new Agent({
name: "assistant",
tools: [
new MCPServerStdio({
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem"],
}),
],
});
// Tools discovered automatically from MCP serverThe interactive grid above lets you explore each feature in detail. Here’s the same data as a quick-reference matrix:
Framework Feature Matrix
Click a framework name to see code examples. Hover cells for details.
| Feature | |||||||
|---|---|---|---|---|---|---|---|
MCP Support Model Context Protocol integration | ● | ◐ | ◐ | β | ● | ● | ● |
Custom Headers Auth headers for HTTP transport | ● | ◐ | ● | ◐ | ● | ● | ● |
Tool Filtering Limit available tools via patterns/permissions | ◐ | ◐ | ◐ | ● | ◐ | ● | ◐ |
Tool Search Semantic discovery from large tool sets | ○ | ○ | ◐ | ● | ○ | ○ | ○ |
Human-in-Loop Approval workflows and interrupts | ● | ● | ● | ◐ | ◐ | ● | ◐ |
Code Execution Sandboxed code interpreter | ● | ◐ | ● | ○ | ● | ○ | ● |
Execution Hooks Step callbacks and lifecycle events | ● | ● | ● | ◐ | ◐ | ◐ | ● |
Streaming Real-time response streaming | ● | ● | ● | ● | ◐ | ● | ● |
The gaps below are what you’ll need to build yourself, or find a toolset SDK that provides them.
1. Approval Workflows
MCP has no concept of human-in-the-loop. The protocol says “here’s a tool, here’s how to call it.” What it doesn’t say: should this call require approval? From whom? Under what conditions?
Human-in-the-Loop Approvals
SDKs provide approval workflows that MCP doesn't specify
I'll execute bamboohr_delete_employee
Delete an employee record from BambooHR
{
"employeeId": "12345",
"reason": "Termination"
}View SDK code pattern
const tools = await toolset.fetchTools({
needsApproval: (tool) => {
// Auto-approve reads, require approval for writes
return tool.name.includes("create")
|| tool.name.includes("delete")
|| tool.name.includes("update");
},
});// SDK-level approval pattern
const tools = await toolset.fetchTools({
needsApproval: true, // All tools require approval
});
// Or conditional approval
const tools = await toolset.fetchTools({
needsApproval: (tool) => {
// Auto-approve reads, require approval for writes
return tool.name.includes("create") || tool.name.includes("delete");
},
});
2. Output Transformations
Tools return data. Sometimes that data is massive (file contents, search results, API responses). Raw MCP sends everything to the model, burning tokens on data the agent doesn’t need to see in full.
Output Transformation: What the model actually needs
Raw API responses waste tokens. SDK transforms extract what matters.
{
"query": "employee onboarding policy",
"total_results": 47,
"took_ms": 234,
"results": [
{
"id": "doc-001",
"title": "Employee Onboarding Checklist 2024",
"content": "This comprehensive guide covers all aspects of onboarding new employees including: Day 1 orientation schedule, IT equipment setup procedures, HR paperwork requirements, benefits enrollment deadlines, manager introduction protocols, team lunch scheduling, building access card procedures, parking permit applications, corporate credit card requests, expense reporting training, time tracking system setup, Slack workspace invitations, email signature configuration, calendar sharing permissions, project management tool access, code repository permissions, VPN configuration, security training requirements, compliance acknowledgments, emergency contact forms, direct deposit setup, tax withholding elections, 401k enrollment options, health insurance selection, dental and vision plans, life insurance beneficiary forms, HSA/FSA elections, commuter benefits, professional development budget allocation, conference attendance policies, remote work agreements, equipment home office stipend...",
"score": 0.94,
"metadata": {
"author": "HR Team",
"last_updated": "2024-01-15",
"department": "Human Resources",
"document_type": "policy",
"word_count": 3847,
"version": "2.3.1"
},
"highlights": [
"...new <em>employee onboarding</em> process should begin...",
"...<em>onboarding</em> checklist must be completed within..."
]
},
{
"id": "doc-002",
"title": "Remote Onboarding Guidelines",
"content": "For employees joining remotely, additional considerations apply. Equipment shipping typically takes 3-5 business days. Virtual orientation sessions are conducted via Zoom every Monday at 9 AM PST. Remote employees should set up their home office according to ergonomic guidelines provided in Section 4.2. Internet reimbursement of up to $50/month is available after completing the Remote Work Agreement form HR-107...",
"score": 0.89,
"metadata": {
"author": "Remote Work Committee",
"last_updated": "2024-02-20",
"department": "Human Resources",
"document_type": "guidelines",
"word_count": 2156,
"version": "1.4.0"
},
"highlights": [
"...remote <em>onboarding</em> requires additional setup...",
"...<em>employee</em> equipment shipping procedures..."
]
},
{
"id": "doc-003",
"title": "IT Systems Access Request Procedures",
"content": "All new employees require access to core systems within their first week. Submit access requests through ServiceNow ticket category 'New Employee Setup'. Required approvals vary by system sensitivity level. Level 1 systems (email, Slack, calendar) are auto-provisioned. Level 2 systems (project tools, code repos) require manager approval. Level 3 systems (production databases, financial systems) require VP approval and security review...",
"score": 0.85,
"metadata": {
"author": "IT Security",
"last_updated": "2024-03-01",
"department": "Information Technology",
"document_type": "procedure",
"word_count": 4521,
"version": "3.1.0"
},
"highlights": []
}
]
}Full document content, metadata, and all 47 results would be sent to the model. Most of this is never used.
The SDK can split results: full data for your app’s UI, summarized data for the model. Your user sees the complete search results while the agent only consumes tokens for what it needs to reason about.
3. File Handling
MCP doesn’t specify how to handle file downloads, binary data, or content that needs encoding. A tool that returns a PDF gets you a URL. But the model can’t read PDFs from URLs.
File Handling: What MCP returns vs. what you need
MCP gives you URLs. SDKs give you usable data.
{
"content_uri": "https://storage.example.com/files/abc123",
"mime_type": "application/pdf",
"size_bytes": 2516582
}⚠️Model can't read PDFs. Returns a URL the agent can't use.
SDKs handle the complexity: fetching URLs, detecting formats, extracting text from PDFs, encoding images for vision models, parsing spreadsheets into structured data. Without this, you’re writing format-specific handlers for every file type your tools might return.
4. Execution Hooks
Think Express middleware or DOM event handlers. Before any tool executes, your code runs first. You can log, check permissions, detect prompt injection, sanitize inputs, or block execution entirely.
Execution Hooks: Middleware for Agent Tool Calls
Like Express middleware or DOM event handlers. Return false to block, modify input, or let it continue.
Track every tool call for compliance and debugging
hooks.beforeToolCall(async (tool, input) => {
await auditLog.write({
tool: tool.name,
input: JSON.stringify(input),
user: session.userId,
timestamp: Date.now(),
});
return { continue: true }; // Allow execution
});{ continue: true } to proceed,{ block: true } to stop, or { continue: true, input: modified } to transform. MCP has no equivalent—this is SDK-level control.This is critical for security. The agent might call file_write with a path from user input that contains ../../../etc/passwd. Or search_docs with a query that says “ignore previous instructions.” Pattern matching catches obvious attacks. Semantic similarity (embeddings compared against known attack vectors) catches the subtle ones. Without hooks, you’re trusting third-party tools completely. With hooks, you control every call.
MCP has no equivalent. It’s a protocol for tool discovery, not execution control. Hooks are SDK-level middleware that frameworks provide but the protocol doesn’t specify.
Which Should You Choose?
Question 1 of 6
Are you building tools for others to consume?
Where This Is Going
MCP isn’t going away. Linux Foundation governance means it’ll evolve slowly but stay stable. The spec will eventually address multi-tenancy, better auth, and the other gaps.
My prediction: two or three agent frameworks will consolidate to cover 70% of the market with solid MCP support. The winners will be Claude’s Agent SDK and Pydantic AI, not OpenAI’s Agents SDK. Anthropic has the model quality lead and is building the tooling to match. Pydantic AI has the Pythonic simplicity that LangChain never achieved. Vercel AI SDK will own the TypeScript/Next.js niche. OpenAI’s SDK feels like the safe choice that loses to teams with stronger opinions about developer experience.
But SDK toolsets like StackOne will continue to provide capabilities that harnesses don’t prioritize. Things like tool filtering with glob patterns, conditional approval workflows, output transformations that reduce token costs. These are features specific to multi-tenant SaaS that general-purpose agent frameworks won’t optimize for.
Multi-tenant isolation is the blind spot. Every framework in the comparison table assumes single-user contexts. Account-scoped tool visibility, per-tenant rate limits, audit logs per customer: you’re building that yourself.
Pure MCP servers will be for ecosystem contribution. Pure SDKs will be for internal tools. Hybrid approaches will power production SaaS features where you need both dynamic discovery and the utilities that make tools safe for real users.
There’s a fourth approach emerging that I haven’t covered here: Code Mode (or “code execution” in some frameworks). Instead of pre-defined tools, the agent writes and executes code to accomplish tasks. Cloudflare’s workers-ai, OpenAI’s code interpreter, and several frameworks are experimenting with this pattern. It deserves its own post.
This is part of a series on AI infrastructure. See also: MCP: Where It’s Been, Where It’s Going.