Securing MCP Server with JWT AuthenticationSecuring MCP Server with JWT AuthenticationSecuring MCP Server with JWT Authentication

2026::01::31
3 min
AUTHOR:Z.SHINCHVEN

To expose a Model Context Protocol (MCP) server securely using a token or JWT, you need to use the Server-Sent Events (SSE) transport instead of the default standard input/output (stdio). Because SSE operates over standard HTTP, you can leverage standard Node.js web frameworks like Express alongside standard JWT middleware to secure the endpoints.

Quick Start: Secure MCP Server with Express and JWT

Here is the complete implementation of a secure MCP server using Express and the jsonwebtoken library.

import express from 'express';
import jwt from 'jsonwebtoken';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';

const app = express();
app.use(express.json()); // Required to parse incoming JSON messages

// ---------------------------------------------------------
// 1. Authentication Middleware
// ---------------------------------------------------------
const SECRET_KEY = process.env.JWT_SECRET || 'your-super-secret-key';

const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  // Expecting header format: "Bearer <token>"
  const token = authHeader && authHeader.split(' ')[1];

  if (!token) {
    return res.status(401).json({ error: 'Authentication token required' });
  }

  jwt.verify(token, SECRET_KEY, (err, user) => {
    if (err) {
      return res.status(403).json({ error: 'Invalid or expired token' });
    }
    req.user = user;
    next();
  });
};

// ---------------------------------------------------------
// 2. Initialize MCP Server
// ---------------------------------------------------------
const mcpServer = new Server(
  {
    name: "secure-mcp-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {}, // Add your tools here
      resources: {} // Add your resources here
    },
  }
);

// Note: In a production environment with multiple clients,
// you need to manage transports per client connection.
let transport;

// ---------------------------------------------------------
// 3. Secure MCP Endpoints
// ---------------------------------------------------------

// Endpoint to establish the SSE connection
app.get('/sse', authenticateToken, async (req, res) => {
  // Create a new SSE transport instance.
  // The first argument is the endpoint where POST messages will be sent.
  transport = new SSEServerTransport('/message', res);

  // Connect the MCP server to this transport
  await mcpServer.connect(transport);

  console.log(`Client connected via SSE: ${req.user.id || 'Unknown User'}`);
});

// Endpoint to handle incoming messages from the client
app.post('/message', authenticateToken, async (req, res) => {
  if (!transport) {
    return res.status(400).send('SSE connection not initialized');
  }

  try {
    await transport.handlePostMessage(req, res);
  } catch (error) {
    console.error('Error handling message:', error);
    res.status(500).send('Internal Server Error');
  }
});

// ---------------------------------------------------------
// 4. Start the Server
// ---------------------------------------------------------
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Secure MCP Server listening on port ${PORT}`);
});

Prerequisites

You will need the MCP SDK, Express, and a JWT library.

npm install @modelcontextprotocol/sdk express jsonwebtoken

Client-Side Connection

When a client (like an LLM application) attempts to connect to your MCP server, it must construct an SSEClientTransport and pass the JWT in the headers.

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';

const token = "YOUR_GENERATED_JWT_TOKEN";

const transport = new SSEClientTransport(
  new URL("http://localhost:3000/sse"),
  {
    headers: {
      "Authorization": `Bearer ${token}`
    }
  }
);

const client = new Client(
  { name: "mcp-client", version: "1.0.0" },
  { capabilities: {} }
);

await client.connect(transport);
// Connection successful, authentication passed
RackNerd Billboard Banner
Share Node:

RELATED_DATA_STREAMS

SCANNING_DATABASE_FOR_CORRELATIONS...