To build an MCP server in Next.js, you expose a single API route that speaks the Model Context Protocol over Streamable HTTP. It accepts the client's JSON-RPC requests, dispatches each one to a registered tool, and streams results back.
That is the whole shape. The interesting part is everything around it.
What you are actually building
An MCP server is a governed adapter. It gives an AI client scoped, audited access to your tools and data instead of pasting raw API keys into a prompt and hoping.
The route handler is the surface the AI client connects to. Behind it sit the tool implementations that actually touch your systems.
If that framing is new, start with what an MCP server is and come back.
Pick the transport: HTTP, not stdio
MCP defines two transports.
stdio is for a local subprocess the client spawns the server as a child process and talks over standard input and output. Useful for desktop tooling, wrong for a web app.
Streamable HTTP is for a remote server reached over the network. That is what a Next.js route 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. You do not have to invent a second operational stack for your AI surface.
A minimal route handler
The shape is a POST handler that:
- Authenticates the caller
- Parses the JSON-RPC request
- Dispatches to the right tool
- Returns the result
// app/api/mcp/route.ts
export async function POST(req: Request) {
const caller = await authenticate(req); // 1
if (!caller) return new Response("Unauthorized", { status: 401 });
const rpc = await req.json(); // 2
const tool = TOOLS[rpc.params?.name]; // 3
if (!tool) return jsonRpcError(rpc.id, -32601, "Method not found");
const args = tool.schema.parse(rpc.params.arguments);
const result = await tool.run(args, caller);
return Response.json({ // 4
jsonrpc: "2.0",
id: rpc.id,
result,
});
}
Note what happens before any tool runs: the caller is authenticated, and the arguments are parsed against a schema. Neither step is optional.
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.
A passthrough tool called callInternalApi(path, method, body) is not a tool. It is a remote shell with extra steps. The model can reach anything the service account can reach, and your audit log records "called the API" rather than "read customer 4821's billing status."
Three rules that hold up in production:
The server holds its own scoped credentials. The model never sees a key. Each tool runs with the narrowest permission that does its job.
No tool is "run arbitrary query." If a tool accepts SQL, Cypher, or a shell string, you have built an injection surface that no amount of prompting will close.
Every input is validated at the boundary. A model or a prompt injection riding inside retrieved data will eventually send something malformed or hostile. Parse with a schema, reject on failure, and log the rejection.
What changes in production
The demo works on your laptop. These are what break first when it does not.
| Concern | What to add |
|---|---|
| Authentication | Per-caller identity, not a shared secret |
| Authorization | Permission check inside each tool, not just at the route |
| Rate limiting | Per caller and per tool agents retry aggressively |
| Timeouts | Every downstream call needs one; agents will hang otherwise |
| Audit logging | Caller, tool, arguments, result, timestamp immutable |
| Error shape | Return structured JSON-RPC errors, never raw stack traces |
The last one matters more than it looks. A stack trace returned to a model becomes context the model reasons over, and leaks your internals into whatever the agent does next.
Where to go next
Once the route is live, the real work is tool design deciding what each tool exposes and, more importantly, what it refuses to.
That is a security question more than an engineering one. MCP server security covers the threat model and a hardening checklist to run before anything touches a system of record.
We build production MCP servers for regulated industries. If you are shipping one, talk to our team.
Top comments (0)