Why FrontMCP: when the MCP SDK alone isn't enough
FrontMCP is a TypeScript framework for MCP servers that are becoming applications. On top of the protocol, it adds typed Zod tools, dependency injection, sign-in, tests, plugins, apps, jobs, workflows and agents, with builds for Node.js, Vercel, AWS Lambda and Cloudflare Workers. Use it when a server is growing past a few tools. For a few tools in one file, the official MCP SDK alone is the simpler choice.
This site documents FrontMCP, so read it knowing who wrote it. The facts about other libraries come from the comparisons this page links to, each checked against that library's own documentation.
When the official SDK alone is enough
The official MCP TypeScript SDK is the protocol's reference implementation, and every library here builds on it. It's the better choice when:
- The server is small. A handful of tools in one file, plain functions, three packages, and no build step.
- You're building a client, a host or a gateway. The SDK's client connects to any server. FrontMCP's clients call your own server's tools in-process, or serve a remote server's tools from a FrontMCP server.
- MCP is one route in an app you already have, in Express, Hono or Fastify, or on Bun or Deno.
- You want a schema library other than Zod. The SDK takes any Standard Schema library.
Each of these has its sources in When the SDK alone is the better choice.
What a growing server needs
A server usually starts as a few tools, and then needs more: a database client the tools share, sign-in, rules about who may call what, limits, tests, work that runs in the background, and a second team's tools beside the first. With the SDK alone, each of these is code you write around it. In FrontMCP, each is a declaration:
| A server needs | FrontMCP | Learn it |
|---|---|---|
| Tools grouped as they multiply | @Tool classes in @Apps, several apps on one server | Grouping Capabilities into Apps |
| A service the tools share, like a database client | Providers, read with this.get() | Sharing State with Providers |
| Sign-in | Five modes, from static keys to FrontMCP as the OAuth server | Authenticating Clients |
| Rules about who may call what | Rules on tools, resources and prompts | Deciding Who Can Call What |
| Rate limits, concurrency caps and timeouts | Options on a tool or the server | Limiting and Timing Out Calls |
| Code around every call | Hooks, and plugins that package them | Hooking into Calls |
| Tests | @frontmcp/testing, run by frontmcp test | Testing Your Server |
| Work in the background | Jobs, and workflows that chain them | Your First Job |
| Agents | @Agent, with tools of its own | Your First Agent |
| Tools from a REST API you already have | The OpenAPI adapter | Wrapping an OpenAPI Service |
| A build for where it runs | frontmcp build for Node.js, Vercel, AWS Lambda and Cloudflare Workers | Deploying Your Server |
| Clients on older and newer protocol revisions | 2024-11-05 to 2026-07-28 on one endpoint | Protocol versions |
What FrontMCP adds has the same list next to what the SDK documents for each item.
What it looks like
A help desk's search tool, with a ticket store that its tools share, a rate limit, an error the model can read, and tests. The Playground runs it and calls search_tickets, and the Tests tab runs the checks:
import { PublicMcpError, Tool, ToolContext, z } from "@frontmcp/sdk";
import { TicketStore } from "./ticket-store";
@Tool({
name: "search_tickets",
description: "Search support tickets by a word in their title. Returns each match's id, title and status.",
inputSchema: {
query: z.string().min(2).describe("A word to look for in ticket titles, like login"),
status: z.enum(["open", "closed"]).optional().describe("Only tickets with this status"),
},
annotations: { readOnlyHint: true },
rateLimit: { maxRequests: 30, windowMs: 60_000 },
})
export class SearchTickets extends ToolContext {
async execute({ query, status }: { query: string; status?: "open" | "closed" }) {
const tickets = this.get(TicketStore).search(query, status);
if (tickets.length === 0) this.fail(new PublicMcpError(`No ticket title mentions "${query}". Try a shorter word.`));
return { tickets };
}
}Starting FrontMCP in your browser…
FrontMCP starts when this example comes into view.
With the SDK alone, the store would be an object the server's factory closes over, the rate limit would be yours to write, and the tests would run a client against the server's handler (the SDK's testing guide).
When FrontMCP is the wrong choice
- You can't run Node 24. FrontMCP needs it; the SDK runs on Node 20.
- You want a schema library other than Zod.
- You want the smallest dependency tree. On 2026-10-10,
npm install @frontmcp/sdkinstalled 126 packages, against 3 for the SDK's server. - You need a general-purpose MCP client or agent. The SDK and mcp-use provide one.
Other libraries fit other cases, like one-file servers, file-based routing in a web app, or MCP Apps with React views: see Which one fits.
Where to start
- Quick Start: build a server in your browser, one concept at a time.
- FrontMCP and the official SDK: the same server written in both, and what each one adds.
- How the TypeScript MCP frameworks compare: every library, the criteria, and popularity.