Guide · September 24, 2026 · 10 min read
How to build an MCP server in Next.js.
The transport to pick, a minimal route handler, how to expose tools without handing out the keys, and what changes when it goes to production.
The short answer
To build an MCP server in Next.js, implement a route handler (e.g. app/mcp/route.ts) that speaks the Streamable HTTP transport: it accepts the client's JSON-RPC requests, dispatches each to a registered tool, and streams results back. Because it's a normal Next.js route, it inherits your auth, logging, and hosting — which is exactly why the security work is the real work, not the wiring.
What you're actually building
An MCP server is a governed adapter that gives an AI client scoped, audited access to your tools and data — instead of pasting raw API keys into a prompt. If that framing is new, start with what is an MCP server, then come back. In Next.js, "the server" is one route that the AI client connects to; behind it sit the tool implementations that actually touch your systems.
Pick the transport: HTTP, not stdio
MCP defines two transports. stdio is for a server launched as a local subprocess by a desktop client — great for a CLI tool on your laptop, wrong for a web app. Streamable HTTP is for a remote server reached over the network, which is what a Next.js deployment is. Choose HTTP: it lets many clients connect, and it sits behind the same authentication, rate limiting, and observability you already run in front of your app.
A minimal route handler
The shape is a POST handler that authenticates the caller, parses the JSON-RPC request, dispatches it, and returns the result. Illustrative, not copy-paste — the exact SDK helpers evolve, but the control flow is stable:
// app/mcp/route.ts
export async function POST(req: Request) {
const caller = await authenticate(req); // 1. who is this?
if (!caller) return new Response("Unauthorized", { status: 401 });
const rpc = await req.json(); // 2. JSON-RPC request
const tool = TOOLS[rpc.params?.name]; // 3. resolve the tool
if (!tool) return jsonRpcError(rpc.id, "Unknown tool");
const input = tool.schema.parse(rpc.params.arguments); // 4. validate
const result = await tool.run(input, caller); // 5. run, scoped to caller
return jsonRpcResult(rpc.id, result); // 6. stream back
}Every interesting decision lives in those six lines: authenticate before anything, resolve the tool from a registry (not from arbitrary strings), validate input against a schema, and run scoped to the caller so a tool can only touch what this caller may touch.
Exposing tools without handing out the keys
A tool is a named, typed function the model may call. The temptation is to expose a thin passthrough to an internal API; the discipline is to expose the capability, not the API. Three rules that keep a Next.js MCP server safe:
- Least privilege per tool. The server holds its own scoped credentials; each tool exposes the narrowest operation that does the job. No tool returns raw keys, and no tool is "run arbitrary query."
- Validate every input. Parse arguments against a schema before use. A model — or a prompt injection riding inside retrieved data — will eventually send something malformed or hostile.
- Human approval on writes. Read tools can run freely; anything that changes state should return a proposed action for a human to confirm, and every call should land in an immutable audit log.
This is the same posture we cover in depth on MCP security — the failure modes are real, not theoretical.
What changes in production
A demo that works on localhost is maybe a fifth of the job. Going to production adds: real authentication and per-caller authorization, input validation on every tool, rate limiting and timeouts so one client can't exhaust the backend, structured audit logging, versioning so you can change a tool without breaking connected clients, and an evaluation suite that runs the tools as both an authorized and an unauthorized caller to prove nothing leaks across the boundary.
When the MCP server is going to touch systems of record, that hardening is the project. We deliver it end to end on MCP server development.
Frequently asked questions
Which MCP transport should a Next.js server use?
The HTTP-based (Streamable HTTP) transport. stdio is for a local subprocess launched by a desktop client; a Next.js app is a remote HTTP service, so HTTP is the natural match and lets normal web auth sit in front of it.
How do you secure an MCP server in Next.js?
Authenticate every request before dispatch, scope each tool to least privilege, validate all inputs, and require human approval on state-changing actions. The server holds its own scoped credentials and enforces per-caller authorization — it never hands raw keys to the model.
Keep reading
Explainer
What is an MCP server? →
The definition and architecture, before you build one.
Service
MCP security →
The failure modes and the controls that close them.
Comparison
MCP vs function calling →
When a protocol beats a bag of function definitions.
Service
MCP server development →
Production delivery for enterprise MCP servers.