Модель контекстного протоколу (MCP) забезпечує потужний, стандартизований спосіб для LLM взаємодіяти з зовнішніми інструментами. How do you secure it? Викриття MCP-сервера без безпеки - це те ж саме, що залишити двері вашого будинку широко відкритими.Кожен може зайти і використовувати ваші інструменти, отримати доступ до ваших даних або викликати хаос. Цей посібник пройде вас через забезпечення Node.js MCP-сервера з нуля за допомогою Ми будемо охоплювати аутентифікацію (хто ви?) і авторизацію (що вам дозволено робити?), з практичними зразками коду на основі цього проекту, які можна знайти на . JSON Web Tokens (JWT) Налаштування Azure-Samples/mcp-container-ts Мета: від незахищеного до повністю забезпеченого Наша мета полягає в тому, щоб взяти базовий сервер MCP і додати надійний шар безпеки, який: 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 Чому JWT ідеально підходить для MCP Security JWT є галузевим стандартом для забезпечення API, і він ідеально підходить для серверів MCP з декількох ключових причин: 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: Візуалізація потоку безпеки Для візуальних учнів ця схема послідовності ілюструє повний потік аутентифікації та авторизації: Зверніть увагу на відповідність специфікації MCP! Важливо відзначити, що це керівництво надає практичну реалізацію для забезпечення MCP-сервера, але це не так. Повністю реалізовується . not Офіційна специфікація авторизації MCP Ця реалізація фокусується на міцному, бездержавному і широко зрозумілому шаблоні з використанням традиційних JWT і управління доступом на основі ролей (RBAC), що достатньо для багатьох випадків використання. Однак для повного дотримання специфікації MCP вам потрібно буде впровадити додаткові функції. Рекомендуємо подивитися на Щоб залишатися в курсі і отримувати повідомлення про майбутні поліпшення. Репозиторій GitHub Крок 1: Визначення ролей та дозволів Перш ніж писати будь-який код, ми повинні визначити наші правила безпеки. Які ролі існують? Що може робити кожна роль? Це основа нашої системи авторизації. В нашому Файли, ми визначаємо та Це робить наш код чітким, читаним і менш схильним до типу. 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], }; Крок 2: Створення сервісу JWT Далі нам потрібна централізована служба для обробки всієї логіки, пов'язаної з JWT: створення нових токенів для тестування і, найголовніше, перевірка вхідних токенів. Ось і повний Файл використовується для Бібліотека для важкого підйому. 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."); } } Крок 3: Створення Middleware аутентифікації «Middleware» - це функція, яка працює Це ідеальне місце для того, щоб поставити нашу безпечну перевірку.Цей middleware буде перевіряти кожен вхідний запит, шукати JWT в Header, і перевірте це. Перед Authorization Якщо токен дійсний, він прикріплює інформацію користувача до об'єкта запиту для подальшого використання. Помилка і зупиняє запит від подальшого просування. 401 Unauthorized Щоб зробити цей тип безпечним, ми також розширимо експрес Інтерфейс для включення нашого Об’єкт 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, }); } } Крок 4: Захист MCP-сервера Тепер у нас є всі фрагменти. давайте поєднаємо їх, щоб захистити наш сервер. По-перше, ми використовуємо нашу перехід до основного кінцевого пункту MCP в Це гарантує Запрошення на Ви повинні мати дійсний JWT. 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 Далі ми будемо застосовувати наші дозволи з дрібними зернами. Займається в Ми будемо змінювати його, щоб перевірити, чи має автентифікований користувач дозволу перед поверненням списку інструментів. 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 } З цією зміною, користувач з Роль може перераховувати інструменти, але користувач без Дозвіл на доступ буде відмовлено. readonly LIST_TOOLS Висновки та наступні кроки Вітаємо! Ви успішно впровадили надійний рівень аутентифікації та авторизації для вашого сервера MCP. Виконуючи ці кроки, ви отримаєте: Визначені чіткі ролі та дозволи. Створено централізований сервіс для обробки JWT. Побудуйте middleware для захисту всіх вхідних запитів. Примусові гранулярні дозволи на рівні інструменту. Ваш MCP-сервер більше не є відкритими дверима — це безпечна послуга. Звідси ви можете розширити ці концепції, додавши більше ролей, більше дозволів та ще більш складну бізнес-логіку до вашої системи авторизації. Зірка наша Щоб залишатися в курсі і отримувати повідомлення про майбутні поліпшення. Репозиторій GitHub