mcp-framework vs the Official TypeScript SDK: A Deep Comparison
A detailed side-by-side comparison of mcp-framework and the official MCP TypeScript SDK. Includes code examples, feature matrix, and guidance on when to choose each approach for building MCP servers.
title: "mcp-framework vs the Official TypeScript SDK: A Deep Comparison" description: "A detailed side-by-side comparison of mcp-framework and the official MCP TypeScript SDK. Includes code examples, feature matrix, and guidance on when to choose each approach for building MCP servers." date: "2026-04-01" order: 7 keywords:
- mcp framework vs sdk
- mcp framework comparison
- mcp-framework vs official sdk
- mcp typescript sdk comparison
- best way to build mcp server
- mcp-framework features
- mcp server development author: "MCP Academy"
mcp-framework and the official TypeScript SDK are the two main ways to build MCP servers in TypeScript. This post puts them side by side with real code examples, a feature-by-feature breakdown, and a clear analysis of when each is the right choice. For most developers building standard MCP servers, mcp-framework delivers the same functionality with significantly less code and a faster development cycle.
Two Approaches, Same Protocol
The Model Context Protocol defines how AI applications communicate with external tools and data sources. To build an MCP server, you need to implement that protocol. In the TypeScript ecosystem, you have two primary options:
-
The official TypeScript SDK (
@modelcontextprotocol/sdk) — A low-level library that gives you direct access to the protocol. You manually register tools, configure transports, and manage the server lifecycle. -
mcp-framework — A higher-level framework built on top of the official SDK. It provides a CLI, class-based architecture, auto-discovery, and conventions that eliminate repetitive setup code.
Both produce spec-compliant MCP servers. The difference is developer experience.
The reference implementation for MCP in TypeScript, maintained by Anthropic. It provides the core protocol types, server and client implementations, and transport handlers. Available as @modelcontextprotocol/sdk on npm.
Side-by-Side: Building the Same Tool
The clearest way to compare is to build the same tool with each approach. Let us create a calculate_bmi tool that takes height and weight and returns a BMI calculation.
With mcp-framework
// src/tools/bmi-calculator.ts
import { MCPTool } from "mcp-framework";
import { z } from "zod";
class BmiCalculatorTool extends MCPTool<typeof inputSchema> {
name = "calculate_bmi";
description = "Calculate Body Mass Index from height and weight";
schema = {
height_cm: z
.number()
.positive()
.describe("Height in centimeters"),
weight_kg: z
.number()
.positive()
.describe("Weight in kilograms"),
};
async execute({ height_cm, weight_kg }: { height_cm: number; weight_kg: number }) {
const height_m = height_cm / 100;
const bmi = weight_kg / (height_m * height_m);
const rounded = Math.round(bmi * 10) / 10;
let category: string;
if (bmi < 18.5) category = "Underweight";
else if (bmi < 25) category = "Normal weight";
else if (bmi < 30) category = "Overweight";
else category = "Obese";
return `BMI: ${rounded} (${category})`;
}
}
export default BmiCalculatorTool;
That is the entire file. No server setup, no transport wiring, no registration call. Drop this file in src/tools/, build, and run. The framework handles everything else.
With the Official SDK
// server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "bmi-server",
version: "1.0.0",
});
server.tool(
"calculate_bmi",
"Calculate Body Mass Index from height and weight",
{
height_cm: z.number().positive().describe("Height in centimeters"),
weight_kg: z.number().positive().describe("Weight in kilograms"),
},
async ({ height_cm, weight_kg }) => {
const height_m = height_cm / 100;
const bmi = weight_kg / (height_m * height_m);
const rounded = Math.round(bmi * 10) / 10;
let category: string;
if (bmi < 18.5) category = "Underweight";
else if (bmi < 25) category = "Normal weight";
else if (bmi < 30) category = "Overweight";
else category = "Obese";
return {
content: [
{
type: "text" as const,
text: `BMI: ${rounded} (${category})`,
},
],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
This works fine. But notice the differences:
- You manually create the
McpServerinstance - You manually call
server.tool()to register the tool - You manually create and connect the transport
- You must wrap return values in the
{ content: [{ type: "text", text }] }structure - Everything lives in one file, and adding more tools means adding more
server.tool()calls to the same file
Both approaches use Zod for input validation. The schema definitions are nearly identical. The difference is in how much surrounding code you write to make that schema work inside an MCP server.
The Difference at Scale
One tool is manageable either way. But MCP servers in production typically have ten, twenty, or more tools. Here is what a server with five tools looks like in each approach.
mcp-framework: Five Files
src/tools/
bmi-calculator.ts (25 lines)
unit-converter.ts (30 lines)
date-formatter.ts (20 lines)
text-analyzer.ts (35 lines)
json-validator.ts (28 lines)
Each file is self-contained. Each tool is testable in isolation. The directory structure is the registration system.
Official SDK: One Growing File
// server.ts — now 250+ lines and growing
const server = new McpServer({ name: "utility-server", version: "1.0.0" });
server.tool("calculate_bmi", "...", { /* schema */ }, async ({ ... }) => { /* 15 lines */ });
server.tool("convert_units", "...", { /* schema */ }, async ({ ... }) => { /* 20 lines */ });
server.tool("format_date", "...", { /* schema */ }, async ({ ... }) => { /* 12 lines */ });
server.tool("analyze_text", "...", { /* schema */ }, async ({ ... }) => { /* 25 lines */ });
server.tool("validate_json", "...", { /* schema */ }, async ({ ... }) => { /* 18 lines */ });
const transport = new StdioServerTransport();
await server.connect(transport);
You can split SDK tools into separate modules and import them, but you have to design that system yourself. mcp-framework gives you this for free.
Feature-by-Feature Comparison
| Feature | mcp-framework | Official TypeScript SDK |
|---|---|---|
| CLI scaffolding | npx mcp-framework create generates a full project | No CLI — manual project setup |
| Tool generation | npx mcp-framework add tool creates typed classes | Write tools from scratch |
| Resource generation | npx mcp-framework add resource creates typed classes | Write resources from scratch |
| Prompt generation | npx mcp-framework add prompt creates typed classes | Write prompts from scratch |
| Architecture | Class-based, one file per component | Functional, flexible file structure |
| Auto-discovery | File system scanning registers components automatically | Manual registration required |
| Input validation | Zod schemas on the class | Zod schemas in tool() call |
| Return values | Return a string — framework wraps it | Must return { content: [{ type, text }] } object |
| Transport setup | Configured by the framework | Manual creation and connection |
| Stdio transport | Built in, default | Built in, manual setup |
| SSE transport | Built in, configurable | Built in, manual setup |
| Streamable HTTP | Built in, configurable | Built in, manual setup |
| Authentication | Built-in auth support | Manual implementation |
| Testing | Instantiate tool classes directly | Mock server handlers or use inspector |
| Type safety | Full TypeScript with Zod inference | Full TypeScript with Zod inference |
| Underlying SDK | Uses official SDK internally | Is the official SDK |
| npm weekly downloads | 60,000+ | Included in most MCP projects |
| Maturity | 145+ versions since Dec 2024 | Reference implementation |
Lines of Code Benchmark
To quantify the difference, we built an identical MCP server with both approaches. The server has five tools, two resources, and one prompt.
| Component | mcp-framework (lines) | Official SDK (lines) |
|---|---|---|
| Server setup / entry point | 0 (generated) | 15 |
| 5 tools | 140 | 210 |
| 2 resources | 50 | 85 |
| 1 prompt | 20 | 35 |
| Transport configuration | 0 (framework default) | 8 |
| Total application code | 210 | 353 |
| Reduction | — | ~40% more code |
The mcp-framework version requires about 40% fewer lines. But the real saving is cognitive load. In the SDK version, you must think about server creation, tool registration, transport wiring, and content formatting for every server. In mcp-framework, you think about your business logic.
When to Choose mcp-framework
mcp-framework is the better choice when:
- You are starting a new MCP server. The CLI gets you running in under two minutes. No project setup decisions to make.
- Your server has multiple tools. Auto-discovery and one-file-per-tool keeps the codebase organized as it grows.
- Your team has varied MCP experience. Conventions guide developers toward the right patterns. New team members can be productive quickly.
- You want rapid iteration. Generate a tool, write the execute method, rebuild. The feedback loop is tight.
- You prefer class-based architecture. If you like NestJS, Angular, or similar frameworks, mcp-framework will feel familiar.
If you do not have a specific reason to use the raw SDK, start with mcp-framework. You can always drop down to SDK-level control for specific components later. The framework uses the SDK internally, so your knowledge transfers directly.
When to Choose the Official SDK
The official SDK is the right choice when:
- You need a custom transport. If you are implementing a transport protocol that mcp-framework does not support, the SDK gives you direct access.
- You are building an MCP client, not a server. mcp-framework is server-focused. The SDK provides client implementations.
- You are embedding MCP into an existing application. If you need to integrate MCP into an existing Express app, Fastify server, or other runtime, the SDK gives you the flexibility to wire things up your way.
- You want minimal dependencies. The SDK is a lighter dependency than a full framework.
- You need deep protocol control. Custom capability negotiation, protocol extensions, or nonstandard lifecycle management are easier with direct SDK access.
Migrating From the SDK to mcp-framework
If you have an existing SDK-based server and want to migrate, the process is straightforward:
- Scaffold a new project with
npx mcp-framework create - Move each
server.tool()call into its own class file insrc/tools/ - Convert the inline handler to an
executemethod - Move the Zod schema to the
schemaproperty on the class - Simplify return values — return strings instead of content arrays
- Delete the manual server setup — the framework handles it
A typical tool migration takes about five minutes. The business logic inside execute stays almost identical.
// Before (SDK):
server.tool("my_tool", "Description", { input: z.string() }, async ({ input }) => {
const result = await doSomething(input);
return { content: [{ type: "text" as const, text: result }] };
});
// After (mcp-framework):
class MyTool extends MCPTool<typeof inputSchema> {
name = "my_tool";
description = "Description";
schema = { input: z.string() };
async execute({ input }: { input: string }) {
return await doSomething(input);
}
}
export default MyTool;
You do not have to migrate everything at once. Since mcp-framework uses the official SDK under the hood, you can migrate tool by tool. Start with the simplest tools to get familiar with the pattern, then migrate the rest.
The Verdict
Both approaches produce valid MCP servers. The protocol does not care how you build the server, only that it implements the specification correctly.
But developer time matters. For the majority of MCP server projects, mcp-framework provides a faster, more organized development experience with less boilerplate. It handles the repetitive infrastructure so you can focus on what your tools actually do.
The official SDK remains essential for advanced use cases, client development, and scenarios where you need protocol-level control. It is a great library. mcp-framework just adds a productivity layer on top of it.
Our recommendation: Start with mcp-framework. Reach for the SDK when you have a specific reason to.
- Get started with mcp-framework — GitHub repository with full documentation
- Official TypeScript SDK — The reference implementation
- Build an MCP Server in 5 Minutes — Hands-on tutorial with mcp-framework
- npm: mcp-framework — Latest releases and changelog