All posts
mcpexpress

MCP With Express: A Practical Guide for Full-Stack Developers

A practical guide to integrating Model Context Protocol with an Express application, exposing existing routes and logic as MCP tools.

SR

Suhail Roushan

August 6, 2026

·
5 min read
·
0 views

Express applications already have the business logic an MCP server needs — the work isn't building new functionality, it's exposing existing route handlers and services through a new, standardized interface that AI clients can discover and invoke correctly.

Integrating MCP with Express means mounting an MCP server as middleware or a dedicated route within your existing Express app, exposing specific tools backed by the same services and data layer your REST endpoints already use. This lets AI clients interact with your application's capabilities without a separate service or duplicated business logic.

Why MCP With Express Matters (and When to Skip It)

For teams with an existing Express backend, adding an MCP server as another route in the same application is a low-friction way to expose AI-assistant access to application capabilities — you reuse existing middleware (authentication, logging), existing services, and existing deployment infrastructure, rather than standing up a separate system.

Skip it if there's no actual use case for AI clients interacting with your application — adding an MCP endpoint speculatively, without a concrete workflow it serves, is added surface area (and security consideration) without corresponding value.

Getting Started with MCP and Express

Mounting an MCP server within an existing Express app:

import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod";

const app = express();
app.use(express.json());

const mcpServer = new McpServer({ name: "my-app", version: "1.0.0" });

mcpServer.tool(
  "get_order",
  { orderId: z.string() },
  async ({ orderId }) => {
    const order = await orderService.findById(orderId); // existing service
    return { content: [{ type: "text", text: JSON.stringify(order) }] };
  }
);

app.post("/mcp", async (req, res) => {
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
  await mcpServer.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

app.listen(3000);

Core MCP With Express Concepts Every Developer Should Know

Reuse existing Express middleware for authentication before the MCP handler runs. Your existing auth middleware (verifying a session or API token) should run on the /mcp route the same way it runs on your REST routes — the MCP endpoint isn't exempt from the authentication your application already requires elsewhere.

app.post("/mcp", authenticateRequest, async (req, res) => {
  // req.user is now available for tool handlers to use for authorization
});

Tool handlers should call existing service-layer functions, not duplicate logic inline. If orderService.findById() already enforces access control and business rules, the MCP tool handler should call it directly, keeping a single source of truth rather than reimplementing order-lookup logic separately for the MCP interface.

The transport layer (Streamable HTTP, in current MCP SDK versions) handles the protocol-level request/response mechanics, so your Express integration work is mostly about defining tools and wiring authentication/authorization correctly, not implementing MCP's wire protocol yourself.

Per-request session/user context needs to flow into tool handlers correctly. Since Express middleware runs per-request, the authenticated user context available in req.user needs to be threaded through to tool handlers (via closures or a request-scoped context) so each tool call is authorized against the actual calling user, not a shared or default identity.

Common Mistakes Integrating MCP With Express and How to Fix Them

Mistake 1: skipping authentication middleware on the MCP route, treating it as a lower-trust or internal-only endpoint. Fix: apply the same authentication requirements to the MCP route as any other sensitive API route.

Mistake 2: writing tool handlers that duplicate existing service logic instead of calling into it, creating two divergent implementations of the same business rules. Fix: call existing service functions from tool handlers rather than reimplementing their logic.

Mistake 3: not threading per-request user context into tool handlers, resulting in tools that can't correctly scope their actions to the calling user's actual permissions. Fix: pass authenticated user context explicitly into each tool handler's execution scope.

When Should You Mount MCP Within Express Instead of a Separate Service?

Mount MCP within your existing Express app when it needs the same data, services, and authentication as your existing API, and a separate service would just duplicate that infrastructure. Use a separate dedicated MCP service when the tools it exposes are meaningfully independent from your main application (a different data domain, different scaling/deployment needs) and coupling them to your main app's deployment lifecycle isn't the right fit.

MCP With Express in Production

Apply the same authentication and rate limiting to the MCP route that you'd apply to any sensitive API endpoint, and route tool handlers through existing, tested service-layer functions rather than new inline logic. Also log MCP tool invocations with the same observability rigor as your REST API, since debugging an AI client's unexpected tool usage needs the same visibility a human-triggered API call would.

If you have an Express backend and a concrete use case for AI-assistant access to it, mounting MCP as another authenticated route is the most direct path — no new service or infrastructure required.

Related posts

Written by Suhail Roushan — Full-stack developer. More posts on AI, Next.js, and building products at suhailroushan.com/blog.

Get in touch