El Protocolo de Contexto Modelo (MCP) proporciona una forma poderosa y estandarizada para que los LLM interactúen con herramientas externas. pero tan pronto como se pasa de una demostración local a una aplicación del mundo real, surge una pregunta crítica: How do you secure it? Exponer un servidor MCP sin seguridad es como dejar la puerta delantera de su casa abierta.Cualquiera podría entrar y usar sus herramientas, acceder a sus datos, o causar un desastre. Esta guía le guiará a través de la seguridad de un servidor MCP Node.js desde el principio usando Cubriremos la autenticación (¿quién eres tú?) y la autorización (¿qué te está permitido hacer?), con muestras prácticas de código basadas en este proyecto que se pueden encontrar en . JSON Web Tokens (JWT) Muestras de Azure/mcp-container-ts El objetivo: de desprotegido a totalmente asegurado Nuestro objetivo es tomar un servidor MCP básico y agregar una capa de seguridad robusta que: every request to ensure it comes from a known user. Authenticates the user, granting them specific permissions based on their role (e.g., vs. ). Authorizes admin readonly individual tools, so only authorized users can access them. Protects Por qué JWT es perfecto para la seguridad de MCP JWT es el estándar de la industria para la seguridad de las APIs, y es ideal para los servidores MCP por algunas razones clave: Each JWT contains all the information needed to verify a user. The server doesn't need to store session information, which makes it highly scalable—perfect for handling many concurrent requests from AI agents. Stateless: A JWT can carry user details, their role, and specific permissions directly within its payload. Self-Contained: JWTs are digitally signed. If a token is modified in any way, the signature becomes invalid, and the server will reject it. Tamper-Proof: A single JWT can be used to access multiple secured services, which is common in microservice architectures. Portable: Visualizar el flujo de seguridad Para los aprendices visuales, este diagrama de secuencia ilustra el flujo completo de autenticación y autorización: Una nota sobre el cumplimiento de la especificación de MCP! Es importante señalar que esta guía proporciona una implementación práctica y en el mundo real para asegurar un servidor MCP, pero sí La plena aplicación de la . not Especificaciones oficiales de autorización de MCP Esta implementación se centra en un patrón robusto, sin estado y ampliamente comprendido utilizando JWTs tradicionales y control de acceso basado en roles (RBAC), lo que es suficiente para muchos casos de uso. Sin embargo, para cumplir plenamente con la especificación de MCP, necesitaría implementar características adicionales. Recomendamos mirar el Mantenerte actualizado y recibir notificaciones sobre futuras mejoras. El repositorio de GitHub Paso 1: Definición de roles y permisos Antes de escribir cualquier código, debemos definir nuestras reglas de seguridad. ¿Qué roles existen? ¿Qué puede hacer cada rol? Esta es la base de nuestro sistema de autorización. En nuestro Archivo, Definición y Esto hace que nuestro código sea claro, legible y menos propenso a escribir. src/auth/authorization.ts UserRole Permission // src/auth/authorization.ts export enum UserRole { ADMIN = "admin", USER = "user", READONLY = "readonly", } export enum Permission { CREATE_TODOS = "create:todos", READ_TODOS = "read:todos", UPDATE_TODOS = "update:todos", DELETE_TODOS = "delete:todos", LIST_TOOLS = "list:tools", } // This interface defines the structure of our authenticated user export interface AuthenticatedUser { id: string; role: UserRole; permissions: Permission[]; } // A simple map to assign default permissions to each role const rolePermissions: Record<UserRole, Permission[]> = { [UserRole.ADMIN]: Object.values(Permission), // Admin gets all permissions [UserRole.USER]: [ Permission.CREATE_TODOS, Permission.READ_TODOS, Permission.UPDATE_TODOS, Permission.LIST_TOOLS, ], [UserRole.READONLY]: [Permission.READ_TODOS, Permission.LIST_TOOLS], }; Paso 2: Crear un servicio JWT A continuación, necesitamos un servicio centralizado para manejar toda la lógica relacionada con JWT: crear nuevos tokens para pruebas y, lo más importante, verificar los tokens entrantes. Aquí está el completo El archivo utiliza el Biblioteca para hacer el levantamiento pesado. src/auth/jwt.ts jsonwebtoken // src/auth/jwt.ts import * as jwt from "jsonwebtoken"; import { AuthenticatedUser, getPermissionsForRole, UserRole, } from "./authorization.js"; // These values should come from environment variables for security const JWT_SECRET = process.env.JWT_SECRET!; const JWT_AUDIENCE = process.env.JWT_AUDIENCE!; const JWT_ISSUER = process.env.JWT_ISSUER!; const JWT_EXPIRY = process.env.JWT_EXPIRY || "2h"; if (!JWT_SECRET || !JWT_AUDIENCE || !JWT_ISSUER) { throw new Error("JWT environment variables are not set!"); } /** * Generates a new JWT for a given user payload. * Useful for testing or generating tokens on demand. */ export function generateToken( user: Partial<AuthenticatedUser> & { id: string } ): string { const payload = { id: user.id, role: user.role || UserRole.USER, permissions: user.permissions || getPermissionsForRole(user.role || UserRole.USER), }; return jwt.sign(payload, JWT_SECRET, { algorithm: "HS256", expiresIn: JWT_EXPIRY, audience: JWT_AUDIENCE, issuer: JWT_ISSUER, }); } /** * Verifies an incoming JWT and returns the authenticated user payload if valid. */ export function verifyToken(token: string): AuthenticatedUser { try { const decoded = jwt.verify(token, JWT_SECRET, { algorithms: ["HS256"], audience: JWT_AUDIENCE, issuer: JWT_ISSUER, }) as jwt.JwtPayload; // Ensure the decoded token has the fields we expect if (typeof decoded.id !== "string" || typeof decoded.role !== "string") { throw new Error("Token payload is missing required fields."); } return { id: decoded.id, role: decoded.role as UserRole, permissions: decoded.permissions || [], }; } catch (error) { // Log the specific error for debugging, but return a generic message console.error("JWT verification failed:", error.message); if (error instanceof jwt.TokenExpiredError) { throw new Error("Token has expired."); } if (error instanceof jwt.JsonWebTokenError) { throw new Error("Invalid token."); } throw new Error("Could not verify token."); } } Paso 3: Construir el Middleware de Autenticación Un "middleware" es una función que es el lugar perfecto para poner nuestra verificación de seguridad. Este middleware inspeccionará cada solicitud de entrada, busque un JWT en el Header y comprobarlo. Antes Authorization Si el token es válido, ataja la información del usuario al objeto de la solicitud para su uso posterior. error y impide que la solicitud continúe. 401 Unauthorized Para hacer este tipo seguro, también ampliaremos la extensión de Interface para incluir nuestro El objeto. Request user // src/server-middlewares.ts import { Request, Response, NextFunction } from "express"; import { verifyToken, AuthenticatedUser } from "./auth/jwt.js"; // Extend the global Express Request interface to add our custom 'user' property declare global { namespace Express { interface Request { user?: AuthenticatedUser; } } } export function authenticateJWT( req: Request, res: Response, next: NextFunction ): void { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { res.status(401).json({ error: "Authentication required", message: "Authorization header with 'Bearer' scheme must be provided.", }); return; } const token = authHeader.substring(7); // Remove "Bearer " try { const userPayload = verifyToken(token); req.user = userPayload; // Attach user payload to the request next(); // Proceed to the next middleware or request handler } catch (error) { res.status(401).json({ error: "Invalid token", message: error.message, }); } } Paso 4: Proteger el servidor MCP Ahora tenemos todas las piezas. Vamos a ponerlas juntas para proteger nuestro servidor. En primer lugar, aplicamos nuestra entre el punto final principal de MCP y el Esto garantiza Solicitud de Debe tener un JWT válido. authenticateJWT src/index.ts every /mcp // src/index.ts // ... other imports import { authenticateJWT } from "./server-middlewares.js"; // ... const MCP_ENDPOINT = "/mcp"; const app = express(); // Apply security middleware ONLY to the MCP endpoint app.use(MCP_ENDPOINT, authenticateJWT); // ... rest of the file A continuación, vamos a hacer cumplir nuestros permisos de grano fino. Trabaja en Lo modificaremos para comprobar si el usuario autenticado tiene el permiso antes de devolver la lista de herramientas. ListTools src/server.ts Permission.LIST_TOOLS // src/server.ts // ... other imports import { hasPermission, Permission } from "./auth/authorization.js"; // ... inside the StreamableHTTPServer class private setupServerRequestHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async (request) => { // The user is attached to the request by our middleware const user = this.currentUser; // 1. Check for an authenticated user if (!user) { return this.createRPCErrorResponse("Authentication required."); } // 2. Check if the user has the specific permission to list tools if (!hasPermission(user, Permission.LIST_TOOLS)) { return this.createRPCErrorResponse( "Insufficient permissions to list tools." ); } // 3. If checks pass, filter tools based on user's permissions const allowedTools = TodoTools.filter((tool) => { const requiredPermissions = this.getToolRequiredPermissions(tool.name); // The user must have at least one of the permissions required for the tool return requiredPermissions.some((p) => hasPermission(user, p)); }); return { jsonrpc: "2.0", tools: allowedTools, }; }); // ... other request handlers } Con este cambio, un usuario con una el papel puede listar herramientas, pero un usuario sin el Se le negaría el acceso. readonly LIST_TOOLS Conclusión y próximos pasos ¡Felicitaciones!Has implementado con éxito una capa de autenticación y autorización robusta para tu servidor MCP. Al seguir estos pasos, tienes: Definir roles y permisos claros. Creó un servicio centralizado para el manejo de JWTs. Construye un middleware para proteger todas las solicitudes entrantes. Permisos granulares forzados al nivel de la herramienta. Su servidor MCP ya no es una puerta abierta, es un servicio seguro.Desde aquí, puede ampliar estos conceptos añadiendo más roles, más permisos y una lógica empresarial aún más compleja a su sistema de autorización. Nuestra estrella Mantenerte actualizado y recibir notificaciones sobre futuras mejoras. El repositorio de GitHub