# FrontMCP vs the official MCP TypeScript SDK

> How FrontMCP and the official MCP TypeScript SDK differ, what each is for, and when to use which.

Source: https://frontmcp.dev/compare/official-typescript-sdk

The official MCP TypeScript SDK is the Model Context Protocol project's own implementation of the protocol: a server library, a client library and adapters for a few web frameworks. FrontMCP is a framework for building servers, and it uses the SDK's protocol layer underneath. With the SDK you write the server around the protocol; with FrontMCP you declare tools, apps, providers and auth, and FrontMCP runs the protocol for you. This page explains what each one is, what FrontMCP uses from the SDK, when the SDK alone is the better choice, and what FrontMCP adds, with the same server written in both.

This site documents FrontMCP, so read it knowing who wrote it. Every SDK fact below comes from the SDK's own documentation, repository and npm entries for **v2.3.1**, checked on **2026-10-10**, and links to them. Every FrontMCP fact links to the page on this site that shows FrontMCP 1.9.4 doing it.

---

## What the official SDK is

The SDK lives in the [`modelcontextprotocol/typescript-sdk`](https://github.com/modelcontextprotocol/typescript-sdk) repository, under the MCP project's GitHub organization; its npm packages name Anthropic, PBC as the author. Version 2 is the current line. Its [README](https://github.com/modelcontextprotocol/typescript-sdk/blob/v2.3.1/README.md) calls it "the stable release line, implementing the 2026-07-28 MCP spec", and its [roadmap](https://github.com/modelcontextprotocol/typescript-sdk/blob/v2.3.1/ROADMAP.md) says v2.0.0 was released on 2026-07-27, alongside that revision of the spec. The MCP conformance suite runs against the 2025-11-25 and 2026-07-28 revisions on every push.

v2 is split into packages ([Packages](https://ts.sdk.modelcontextprotocol.io/v2/get-started/packages)), as published on 2026-10-10:

| Package | Version | What it's for |
| --- | --- | --- |
| [`@modelcontextprotocol/server`](https://www.npmjs.com/package/@modelcontextprotocol/server) | 2.3.1 | `McpServer`, tools, resources, prompts, `createMcpHandler()` for HTTP, `serveStdio()` |
| [`@modelcontextprotocol/client`](https://www.npmjs.com/package/@modelcontextprotocol/client) | 2.3.1 | `Client`, transports, OAuth for clients |
| `@modelcontextprotocol/core` | 2.3.1 | The protocol's wire schemas, shared by both |
| `@modelcontextprotocol/node`, `express`, `hono`, `fastify` | 2.0.1 to 2.1.1 | Thin adapters that mount a server in Node's `http` module or in one of those frameworks |
| `@modelcontextprotocol/server-legacy` | 2.3.1 | A frozen copy of v1's HTTP+SSE server transport and authorization-server helpers, deprecated |
| `@modelcontextprotocol/codemod` | 2.3.1 | `v1-to-v2`, which rewrites a v1 project for v2 |

v1, the single package [`@modelcontextprotocol/sdk`](https://www.npmjs.com/package/@modelcontextprotocol/sdk), is the maintenance line (1.32.1 on 2026-10-10). Its README says it implements the spec up to 2025-11-25, that "support for the 2026-07-28 spec is not planned for v1.x", and that it gets bug fixes and security updates for at least six months after v2's release ([v1.x README](https://github.com/modelcontextprotocol/typescript-sdk/blob/v1.x/README.md)).

The v2 packages have been Apache-2.0 since 2.3.0 and were MIT before; the repository's [LICENSE](https://github.com/modelcontextprotocol/typescript-sdk/blob/v2.3.1/LICENSE) explains the transition. v1 is MIT. The SDK needs Node 20 or later ([Build your first server](https://ts.sdk.modelcontextprotocol.io/v2/get-started/first-server)), and its README says it runs on Node, Bun and Deno.

It's used far more than anything else on this list. On 2026-10-10 the repository had 13,543 GitHub stars ([GitHub API](https://api.github.com/repos/modelcontextprotocol/typescript-sdk)), and in the week of 2026-10-02 to 2026-10-08 npm counted 65,440,256 downloads of v1's `@modelcontextprotocol/sdk` and 10,051,078 of `@modelcontextprotocol/server` ([npm API](https://api.npmjs.org/downloads/point/last-week/@modelcontextprotocol/server)). FrontMCP had 146 stars and 8,479 downloads of `@frontmcp/sdk` in the same week.

## What FrontMCP uses from it

FrontMCP 1.9.4 is built on **v1** of the SDK, not v2. Every import of the SDK goes through one internal package, `@frontmcp/protocol`, which pins `@modelcontextprotocol/sdk` to exactly 1.29.0 ([its `package.json` at v1.9.4](https://github.com/agentfront/frontmcp/blob/v1.9.4/libs/protocol/package.json)). From the SDK, FrontMCP takes ([`index.ts`](https://github.com/agentfront/frontmcp/blob/v1.9.4/libs/protocol/src/index.ts)):

- **The protocol's types and Zod schemas** for every request, result and notification, from `@modelcontextprotocol/sdk/types.js`.
- **The low-level `Server` class**, for clients that open a session with `initialize` (2025-11-25 and earlier). FrontMCP registers its own handler for each MCP method on it; it doesn't use `McpServer` or `registerTool()`.
- **The server transports**: Streamable HTTP, for Node and for web-standard runtimes, and stdio.
- **`InMemoryTransport`**, which links a client to a server in the same process, behind [`connect()`](https://frontmcp.dev/reference/sdk/connect).
- **The `Client`** and its Streamable HTTP, SSE and stdio transports. FrontMCP uses it to reach [remote servers](https://frontmcp.dev/reference/server/remote) and in `connect()`, and `@frontmcp/testing` re-exports it as `McpClient`.

FrontMCP writes the rest itself: everything from your decorators to those handlers, the older HTTP+SSE server transport, auth, and the whole of MCP 2026-07-28, because the v1 SDK doesn't have it. Its 2026-07-28 types say so in their header ([`types-20260728.ts`](https://github.com/agentfront/frontmcp/blob/v1.9.4/libs/protocol/src/types-20260728.ts)), and the stateless request handling, `server/discover` and a client for that revision are in [`transport/mcp-20260728`](https://github.com/agentfront/frontmcp/tree/v1.9.4/libs/sdk/src/transport/mcp-20260728). So both projects serve 2026-07-28, with separate implementations.

## The same server in both

One tool, `add`, that takes two numbers and returns their sum.

### With the official SDK

This is the shape of the SDK's [Build your first server](https://ts.sdk.modelcontextprotocol.io/v2/get-started/first-server) guide with the `add` tool in place of its weather tool, as we ran it with `@modelcontextprotocol/server` 2.3.1, `zod` 4.6.5 and `tsx`, after `npm pkg set type=module`:

```ts src/index.ts
import { McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';

function createServer(): McpServer {
    const server = new McpServer({ name: 'hello', version: '1.0.0' });

    server.registerTool(
        'add',
        {
            description: 'Add two numbers',
            inputSchema: z.object({ a: z.number(), b: z.number() })
        },
        async ({ a, b }) => ({
            content: [{ type: 'text', text: String(a + b) }]
        })
    );

    return server;
}

void serveStdio(createServer);
console.error('hello MCP server running on stdio');
```

`npx tsx src/index.ts` runs it over stdio. Over HTTP, the [HTTP guide](https://ts.sdk.modelcontextprotocol.io/v2/serving/http) builds a handler from the same kind of factory, here mounted on Node's `http` module with `@modelcontextprotocol/node` 2.1.1:

```ts src/http.ts
import { createServer } from 'node:http';

import { localhostHostValidation, localhostOriginValidation, toNodeHandler } from '@modelcontextprotocol/node';
import { createMcpHandler, McpServer } from '@modelcontextprotocol/server';
import * as z from 'zod/v4';

const handler = createMcpHandler(() => {
    const server = new McpServer({ name: 'hello', version: '1.0.0' });
    server.registerTool(
        'add',
        {
            description: 'Add two numbers',
            inputSchema: z.object({ a: z.number(), b: z.number() })
        },
        async ({ a, b }) => ({ content: [{ type: 'text', text: String(a + b) }] })
    );
    return server;
});

const nodeHandler = toNodeHandler(handler);
const validateHost = localhostHostValidation();
const validateOrigin = localhostOriginValidation();
createServer((req, res) => {
    if (!validateHost(req, res) || !validateOrigin(req, res)) return;
    void nodeHandler(req, res);
}).listen(3201, '127.0.0.1');

console.error('hello MCP server on http://127.0.0.1:3201/mcp');
```

A client from `@modelcontextprotocol/client` 2.3.1 called `add` with `{ "a": 2, "b": 3 }` twice: once with the default `initialize` handshake, and once with `versionNegotiation: { mode: "auto" }`, which asks for 2026-07-28 first:

```text
legacy 2025-11-25 {"content":[{"type":"text","text":"5"}]}
modern 2026-07-28 {"_meta":{"io.modelcontextprotocol/serverInfo":{"name":"hello","version":"1.0.0"}},"content":[{"type":"text","text":"5"}]}
```

2026-07-28 is opt-in on both sides. The SDK's guide to it says that "a hand-constructed `Client` / `Server` / `McpServer` keeps speaking the 2025-era protocol", and that serving 2026-07-28 "is always an explicit opt-in" ([2026-07-28 protocol support](https://ts.sdk.modelcontextprotocol.io/v2/migration/support-2026-07-28)). `createMcpHandler()` and `serveStdio()` are those entry points, and they serve 2025-era clients too: both servers above answered every revision we tried, 2024-11-05 to 2026-07-28. The README's shorter example, `server.connect(new StdioServerTransport())`, answered the four 2025-era revisions and not 2026-07-28.

### With FrontMCP

The same tool in a FrontMCP server, with an app that lists it. The Playground runs it and calls `add`, and the **Tests** tab runs the checks:

```ts main.ts active
import { App, FrontMcp, Tool, ToolContext, z } from "@frontmcp/sdk";

@Tool({
  name: "add",
  description: "Add two numbers",
  inputSchema: { a: z.number(), b: z.number() },
})
export class Add extends ToolContext {
  async execute({ a, b }: { a: number; b: number }) {
    return { sum: a + b };
  }
}

@App({ id: "calc", name: "Calculator", tools: [Add] })
export class CalcApp {}

@FrontMcp({ info: { name: "calc", version: "1.0.0" }, apps: [CalcApp] })
export default class Server {}
```

```ts add.test.ts
import { test, expect } from "@frontmcp/testing";

test("adds two numbers", async ({ mcp }) => {
  const result = await mcp.tools.call("add", { a: 2, b: 3 });
  expect(result).toBeSuccessful();
  expect(result.json()).toEqual({ sum: 5 });
});

test("rejects a string where a number belongs", async ({ mcp }) => {
  const result = await mcp.tools.call("add", { a: "2", b: 3 });
  expect(result).toBeError("INVALID_INPUT");
});
```

The differences in this small case:

- **The SDK builds the server in a function you write**; FrontMCP builds it from the classes you list. Neither needs you to touch JSON-RPC.
- **Results.** The SDK's tool returns MCP content blocks. FrontMCP's returns a plain object, which arrives as `structuredContent` with a text copy ([Your First Tool](https://frontmcp.dev/learn/your-first-tool#returning-results-and-errors)).
- **Running it.** The SDK's server is a script `tsx` runs, with no build step. FrontMCP needs Node 24 or later and TypeScript's decorator settings ([Installation](https://frontmcp.dev/learn/installation)); [`tool()`](https://frontmcp.dev/reference/sdk/tool#writing-a-tool-as-a-function) and [`app()`](https://frontmcp.dev/reference/sdk/app#building-an-app-without-a-decorator) are forms without decorators. In Node, importing the `@FrontMcp` class starts an HTTP server ([`FrontMcpInstance`](https://frontmcp.dev/reference/sdk/frontmcp-instance)), [`runStdio()`](https://frontmcp.dev/reference/sdk/frontmcp-instance#serving-over-stdio) serves it over stdio, and [`frontmcp dev`](https://frontmcp.dev/reference/cli#frontmcp-dev) runs it while you work.
- **Revisions.** Like the SDK's `createMcpHandler()`, FrontMCP serves 2024-11-05 to 2026-07-28 on one endpoint ([Error codes](https://frontmcp.dev/reference/errors)).
- **Size.** On 2026-10-10, `npm install @modelcontextprotocol/server` installed 3 packages (the server, `core` and `zod`) in 16 MB; `npm install @frontmcp/sdk` installed 126 packages in 61 MB.

## When the SDK alone is the better choice

- **You're building a client, a host or a gateway.** `@modelcontextprotocol/client` connects to any server, negotiates the protocol revision, and handles OAuth for users and machines ([OAuth](https://ts.sdk.modelcontextprotocol.io/v2/clients/oauth)). FrontMCP's clients on this site are for calling your own server's tools in-process ([`connect()`](https://frontmcp.dev/reference/sdk/connect)) and for serving a remote server's tools from a FrontMCP server ([Remote servers](https://frontmcp.dev/reference/server/remote)).
- **The server is small and you want few dependencies.** A handful of tools in one file, three packages, plain functions, no decorators or `reflect-metadata`, and Node 20 rather than 24.
- **You want to follow the specification closely.** v2.0.0 shipped with the 2026-07-28 revision, the conformance suite runs on every push, and the other libraries compared here build on the SDK. FrontMCP builds on v1 and implements 2026-07-28 itself.
- **MCP is one route in an app you already have.** The adapters mount a server in Express, Hono, Fastify or Node's `http` module, and `createMcpHandler()` returns a web-standard `fetch` handler for Workers, Deno and Bun ([Web-standard runtimes](https://ts.sdk.modelcontextprotocol.io/v2/serving/web-standard)).
- **You want a schema library other than Zod.** Schemas are [Standard Schema](https://ts.sdk.modelcontextprotocol.io/v2/advanced/schema-libraries): Zod 4, ArkType, Valibot, or plain JSON Schema through `fromJsonSchema()`.
- **Your identity provider issues the tokens.** The SDK makes your server an OAuth resource server: `requireBearerAuth()` with a verifier you write, protected resource metadata and scope step-up per tool ([Authorization](https://ts.sdk.modelcontextprotocol.io/v2/serving/authorization)).

## What FrontMCP adds

Each row links to the lesson that teaches it and the reference page that covers it.

| You need | The SDK v2 | FrontMCP 1.9.4 |
| --- | --- | --- |
| Tools organised as the server grows | `registerTool()` calls in a factory ([Tools](https://ts.sdk.modelcontextprotocol.io/v2/servers/tools)) | `@Tool` classes grouped in `@App`s, several apps on one server: [Grouping Capabilities into Apps](https://frontmcp.dev/learn/grouping-capabilities-into-apps), [`@App`](https://frontmcp.dev/reference/sdk/app), [Apps, discovery and splitting](https://frontmcp.dev/reference/server/apps) |
| Shared services, like a database client | Module-scope objects the factory closes over ([HTTP](https://ts.sdk.modelcontextprotocol.io/v2/serving/http)) | Providers with scopes, read with `this.get()`: [Sharing State with Providers](https://frontmcp.dev/learn/sharing-state-with-providers), [`@Provider`](https://frontmcp.dev/reference/sdk/provider) |
| Code that runs around every call | Not documented on the server | Hooks and plugins: [Hooking into Calls](https://frontmcp.dev/learn/hooking-into-calls), [Extending with Plugins](https://frontmcp.dev/learn/extending-with-plugins), [Hooks](https://frontmcp.dev/reference/sdk/hooks) |
| Rate limits, concurrency caps, timeouts | Not documented | Per tool or server: [Limiting and Timing Out Calls](https://frontmcp.dev/learn/limiting-calls), [Guard options](https://frontmcp.dev/reference/sdk/guard) |
| Sign-in | A resource-server gate; the authorization-server helpers are frozen in `server-legacy`, and the docs say to "use a dedicated identity provider" ([Authorization](https://ts.sdk.modelcontextprotocol.io/v2/serving/authorization)) | Five modes, from static keys and JWT checks to FrontMCP as the OAuth server, with its own sign-in page or an upstream provider: [Authenticating Clients](https://frontmcp.dev/learn/authenticating-clients), [Auth modes](https://frontmcp.dev/reference/auth/modes) |
| Who may call what | Scope checks per tool with `scopeChallenge` | Rules on tools, resources and prompts: [Deciding Who Can Call What](https://frontmcp.dev/learn/authorizing-calls), [Authorities](https://frontmcp.dev/reference/auth/authorities) |
| Tests | An in-process client against the handler's `fetch` ([Testing](https://ts.sdk.modelcontextprotocol.io/v2/testing)) | `@frontmcp/testing` with MCP matchers and fixtures, run by `frontmcp test`: [Testing Your Server](https://frontmcp.dev/learn/testing-your-server), [Testing](https://frontmcp.dev/reference/testing) |
| Agents, jobs, workflows, skills | Not documented | [Your First Agent](https://frontmcp.dev/learn/your-first-agent), [Your First Job](https://frontmcp.dev/learn/your-first-job), [Chaining Jobs into Workflows](https://frontmcp.dev/learn/chaining-jobs-into-workflows), [Teaching the Model Skills](https://frontmcp.dev/learn/teaching-the-model-skills); [`@Agent`](https://frontmcp.dev/reference/sdk/agent), [`@Job`](https://frontmcp.dev/reference/sdk/job), [`@Workflow`](https://frontmcp.dev/reference/sdk/workflow), [`@Skill`](https://frontmcp.dev/reference/sdk/skill) |
| Widgets for MCP Apps hosts | A separate official package, [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) | A tool's `ui` option, which also serves the OpenAI Apps SDK: [Your First Widget](https://frontmcp.dev/learn/your-first-widget), [Tool UI](https://frontmcp.dev/reference/ui), [Hosts and platforms](https://frontmcp.dev/reference/ui/hosts) |
| Caching tool results on the server, memory, approvals, feature flags | Not documented. The SDK's caching is the freshness hint a server puts on a result, which the client's response cache honours ([Caching](https://ts.sdk.modelcontextprotocol.io/v2/clients/caching)) | Official plugins: [Built-in Plugins and Adapters](https://frontmcp.dev/learn/built-in-plugins-and-adapters), [Plugins](https://frontmcp.dev/reference/plugins) |
| Tools from an OpenAPI spec | Not documented | [Wrapping an OpenAPI Service](https://frontmcp.dev/learn/wrapping-an-openapi-service), [OpenAPI adapter](https://frontmcp.dev/reference/adapters/openapi) |
| A project to start from, and a build per platform | A manual setup; the CLI is the codemod | `frontmcp create`, `dev`, `build` for Node, Vercel, AWS Lambda, Cloudflare Workers and more, `test`: [Installation](https://frontmcp.dev/learn/installation), [Deploying Your Server](https://frontmcp.dev/learn/deploying-your-server), [CLI](https://frontmcp.dev/reference/cli) |
| Errors the model can read, without leaking internals | Typed protocol errors ([Errors](https://ts.sdk.modelcontextprotocol.io/v2/servers/errors)) | `PublicMcpError`; other errors hidden in production: [Your First Tool](https://frontmcp.dev/learn/your-first-tool#returning-results-and-errors), [Error classes](https://frontmcp.dev/reference/sdk/error-classes) |

The SDK documents things this site doesn't show for FrontMCP, too: completion of prompt arguments with `completable()` ([Completion](https://ts.sdk.modelcontextprotocol.io/v2/servers/completion); FrontMCP's pages show it for [resource template parameters](https://frontmcp.dev/reference/sdk/resource-template#completing-parameters)), a choice of schema library, mounting in Express, Fastify or Hono with an adapter, and Bun and Deno, which run in its CI.

## Side by side

| | Official SDK 2.3.1 | FrontMCP 1.9.4 |
| --- | --- | --- |
| MCP revisions served | 2024-11-05 to 2026-07-28 through `createMcpHandler()` and `serveStdio()`; 2025-era only through a hand-wired transport ([Protocol versions](https://ts.sdk.modelcontextprotocol.io/v2/protocol-versions)) | 2024-11-05 to 2026-07-28 ([Error codes](https://frontmcp.dev/reference/errors)) |
| Declaring a tool | `server.registerTool(name, config, handler)` | A [`@Tool`](https://frontmcp.dev/reference/sdk/tool) class, or the `tool()` function |
| Schemas | Standard Schema: Zod 4, ArkType, Valibot, JSON Schema; `outputSchema` | Zod 4; `outputSchema` ([Schemas Are Contracts](https://frontmcp.dev/learn/schemas-are-contracts)) |
| Resources, prompts, completions, elicitation | All four ([Resources](https://ts.sdk.modelcontextprotocol.io/v2/servers/resources), [Prompts](https://ts.sdk.modelcontextprotocol.io/v2/servers/prompts), [Completion](https://ts.sdk.modelcontextprotocol.io/v2/servers/completion), [Elicitation](https://ts.sdk.modelcontextprotocol.io/v2/servers/elicitation)) | All four ([`@Resource`](https://frontmcp.dev/reference/sdk/resource), [`@Prompt`](https://frontmcp.dev/reference/sdk/prompt), [completers](https://frontmcp.dev/reference/sdk/resource-template#completing-parameters), [`this.elicit`](https://frontmcp.dev/reference/sdk/elicit)) |
| Transports | stdio, Streamable HTTP (stateless, or with sessions for 2025-era clients); the old HTTP+SSE server only in the deprecated `server-legacy` ([Legacy clients](https://ts.sdk.modelcontextprotocol.io/v2/serving/legacy-clients)) | Streamable HTTP with or without sessions, the older HTTP+SSE, stdio, a Unix socket, in-memory ([`FrontMcpInstance`](https://frontmcp.dev/reference/sdk/frontmcp-instance)) |
| Runtimes | Node 20+, Bun, Deno, Cloudflare Workers; adapters for Express, Hono, Fastify | Node 24+; builds for Vercel, AWS Lambda, Cloudflare Workers, a browser module ([Production build](https://frontmcp.dev/reference/deployment/production-build)); [`createFetchHandler()`](https://frontmcp.dev/reference/sdk/create-fetch-handler) for other runtimes |
| License | Apache-2.0 (v2 since 2.3.0); v1 MIT | Apache-2.0 |

The [overview](https://frontmcp.dev/compare) compares every library on the full list of criteria.

## How this was checked

On 2026-10-10 we installed `@modelcontextprotocol/server` 2.3.1 with `@modelcontextprotocol/node` 2.1.1, ran both servers above, and called `add` with `@modelcontextprotocol/client` 2.3.1. We then sent each server an `initialize` request for every revision from 2024-11-05 to 2025-11-25, and the 2026-07-28 `server/discover` and `tools/call` requests. The `createMcpHandler()` and `serveStdio()` servers answered all five; a server wired by hand to `NodeStreamableHTTPServerTransport`, and the README's `StdioServerTransport` example, answered the four 2025-era revisions only. FrontMCP's side is checked by this page's Playground and its tests. The [overview](https://frontmcp.dev/compare#how-this-was-checked) describes the method for every library.
