Розгортання сервера MCP на EC2 після змін у специфікації безстанових сесій
Переміщення сервера MCP з localhost на EC2: streamable HTTP, nginx та автентифікація через токени, а також що означають безстанові сесії та OAuth 2.1 для реального розгортання.
Джерела: MCP Servers Explained: A Complete Guide to What I Learned Deploying One to AWS EC2
Від localhost до справжнього сервера
Навчальні матеріали про MCP зазвичай залишаються абстрактними. У них показаний клієнт поруч із сервером, протокол порівнюється з портом USB-C, або наводиться п’ятирядковий приклад, який працює лише на localhost та ніколи не виходить за межі ноутбука розробника. Справжня робота починається після цього: інструмент, написаний локально, має функціонувати на справжньому сервері, за справжнім зворотним проксі, де справжній клієнт зможе звертатися до нього через публічний Інтернет. Це окрема проблема, яка майже не розглядається.
Розгортання, описане тут, починається з локального сервера MCP, потім його переміщують на інстанцію EC2 та надають через протокол HTTP з можливістю потокової передачі даних, причому nginx вимагає токен автентифікації заздалегідь. Усе працювало без проблем. Невдовзі після цього протокол було перенесено під його керування. 28 липня 2026 року найбільша на сьогодні версія специфікації MCP перейшла від статусу кандидата на випуск до остаточної версії, що зробило сам протокол повністю безстановим. Команди часто відмічають „підтримку MCP“ як єдиний пункт, але насправді це включає три аспекти: спосіб передачі даних, автентифікацію та, з цим оновленням, масштабування. Неправильний вибір у будь-якому з цих аспектів визначає різницю між інструментом демонстраційного рівня та тим, якому можна довіряти у продакшені.
У статті розглядається кілька питань: які аспекти проблеми визначає MCP, а які залишає нерозв’язаними, чому транспорт на основі HTTP з підтримкою стрімінгу замінив транспорт на основі SSE, як nginx та токен-носій дозволяють відкрити локальний сервер на EC2, які зміни відбуваються під час безстанової переробки, чому статичний токен вважається засобом автентифікації, проте не відповідає вимогам специфікації, та які частини процесу розгортання довелося змінити, щоб йти в ногу з цими змінами.
Що стандартизує, а що не стандартизує MCP
На папері Протокол контексту моделі визначає лише одну річ: спосіб, яким програма ШІ знаходить та викликає інструменти, а також читає ресурси, які надає окремий процес сервера, через повідомлення JSON-RPC, що передаються через визначений канал зв’язку. Логіка інструментів, проектування та розгортання агентів знаходяться поза межами цього протоколу. Це стандарт для підключення, а не фреймворк. Раніше кожній програмі ШІ, яка хотіла отримати доступ до чогось на кшталт внутрішнього API, потрібна була індивідуальна інтеграція. За допомогою MCP один сервер обслуговує всіх клієнтів, які відповідають вимогам.
Якщо дивитися з цієї точки зору, стає зрозуміло, чому розгортання створює труднощі. Протокол описує структуру повідомлень, але не говорить про те, чи можна досягти сервера лише з одного ноутбука чи з будь-якого місця, чи є щось, що захищає його, та чи він може обробляти кількох клієнтів одночасно. Відповіді на всі ці запитання залежать від оператора.
Чому публічне розгортання відрізняється
- Локальні сервери майже нічого не відкривають, тоді як публічні — багато чого. За наявності публічної URL сервер стає автентифікованим API, як і будь-який інший, тому обробка токенів та перевірка вхідних даних мають таку саму важливість.
- Раніше правила роботи сеансів та масштабування були нечіткими. Неправильні припущення призводили або до того, що сервер тихо зламувався під одночасним навантаженням, або до того, що він опинявся під тягарем зайвих сеансів, які йому зовсім не були потрібні.
- Специфікація постійно розвивається. Розгортання, побудоване на припущеннях, старих на шість місяців, може вийти за межі відповідності без жодної помилки, оскільки клієнти просто домовляються про використання старішої версії протоколу.
Архітектура виглядає ось так.
AI Client ── HTTPS POST ──▶ nginx (TLS termination, auth check, reverse proxy)
│
▼
MCP Server Process
(Streamable HTTP transport)
│
┌────────────┴────────────┐
Tools Resources
Один принцип керує всім цим. Після того, як сервер MCP отримує публічну URL, до нього потрібно ставитися як до будь-якого автентифікованого API, що працює через EC2, адже структурно вони є однаковими. MCP — це лише формат повідомлень, тоді як питання безпеки та масштабування залишаються такими ж, як у будь-якого бекенду.
1. Транспорт Streamable HTTP
Streamable HTTP — це один HTTP-кінцевий пункт, який приймає запити типу POST із повідомленнями у форматі JSON-RPC. Він замінив попередній транспорт HTTP+SSE, який вимагав постійного потоку серверних подій поруч із звичайними HTTP-запитами.
Один кінцевий пункт типу POST є знайомим для будь-якого зворотного проксі, балансувальника навантаження чи шлюзу API, тому немає проблем із підтримкою довготривалого потоку через nginx чи ALB. Трафік виглядає як звичайний HTTP-API, а блоки налаштувань у nginx залишаються короткими.
location /mcp {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header Authorization $http_authorization;
proxy_http_version 1.1;
proxy_read_timeout 300s;
}
Порада для продакшну: навіть при транспортуванні через один кінцевий пункт зберігайте достатньо великий тайм-аут для читання у проксі. Окремі виклики інструментів можуть закінчуватися довше, ніж звичайний запит API, і занадто короткий тайм-аут призведе до зупинки як дійсно завислих запитів, так і тих, що працюють довше за норму.
2. Аутентифікація за допомогою токена Bearer
У цій схемі фіксований токен знаходиться у заголовку Authorization та перевіряється ще до того, як запит досягне процесу сервера MCP, чи то в nginx, чи в середовищі проміжного програмування додатку. Для будь-якого контенту на публічній URL це найменш безпечний варіант, але це розумний перший крок. Проте токен ніколи не закінчується, не прив’язаний до конкретного сервера та не забезпечує захисту, якщо потрапляє до журналу подій. Він лише свідчить про те, що запит містить облікові дані.
Шаблон проміжного програмування FastAPI починається з імпортів.
from fastapi import Request, HTTPException
Потім йде сама перевірка.
VALID_TOKEN = "your-rotated-secret-token"async def verify_bearer(request: Request):
auth = request.headers.get("authorization", "")
if auth != f"Bearer {VALID_TOKEN}":
raise HTTPException(status_code=401, detail="Unauthorized")
Ще одна порада: не включайте необроблений заголовок Authorization до журналів доступу. Стандартні формати журналів nginx та додатків ускладнюють його помічання, і як тільки він потрапляє туди, файл журналу стає джерелом витоку облікових даних.
3. Безстановість – основа переписування у липні 2026 року
Згідно зі специфікацією 2026-07-28 сеанси на рівні протоколу зникають. Більше не існує заголовка Mcp-Session-Id, а механізм передачі даних також відмовляється від довготривалого потоку GET, який ранні версії використовували для підтримки синхронізації клієнта та сервера під час розмови. Кожен запит містить усе необхідне.
Саме масштабування є ключовим моментом. Старіша версія моделі дозволяла серверу пам’ятати інформацію про клієнта між викликами, і коли за балансувальником навантаження з’являлося кілька інстанцій, ця пам’ять вимагала використання «липких сеансів». Без стану на рівні протоколу будь-яка інстанція може обробляти будь-який запит, що забезпечує справжнє горизонтальне масштабування та звільняє клієнтів від прив’язки до однієї машини.
На практиці сервер, який зберігав інформацію про клієнта в оперативній пам’яті між викликами, не буде працювати за новими специфікаціями. Усі важливі дані слід перемістити у вантаж запиту або у зовнішній сховище, таке як Redis, яке може читати кожна інстанція.
4. Багатократні запити з поверненням даних
Навіть протокол без стану потребує способу, яким сервер може поставити запитання клієнту під час виконання запиту, наприклад, щоб отримати підтвердження чи інформацію про відсутній параметр. Нова специфікація вирішує цю проблему за допомогою багатократних запитів типу Round-Trip. Замість того, щоб підтримувати відкритий потік, сервер відповідає значенням InputRequiredResult, яке містить його запитання разом із незрозумілим значенням requestState. Клієнт збирає відповіді та знову надсилає початковий запит, додаючи ці відповіді та повертаючи стан без змін.
Саме це робить справжню безстановість працездатною. Оскільки все, що потрібно серверу для продовження роботи, передається у вантажі даних, який надсилає клієнт, будь-яка інстанція, не обов’язково та, що розпочала запит, може взяти на себе спробу повторного виконання та завершити його.
Клієнти повинні не чіпати requestState: ні перевірки, ні змін. Зберігайте його та повертайте точно таким, яким він надійшов, інакше процес, який залежить від його цілісності, може зазнати невдачі.
5. Посилення авторизації
Оновлена специфікація спрямовує віддалені сервери MCP до протоколу OAuth 2.1 з підтримкою PKCE та вимагає наявності індикаторів ресурсів (RFC 8707), які пов’язують виданий токен із конкретним сервером, для якого він призначений.
Проблемою є можливість повторної передачі токенів. Якщо статичний токен-носій потрапить у відкритий доступ, будь-який сервер, який його прийме, може стати мішенню. Токен, прив’язаний до конкретного ресурсу, закриває цю проблему, тому токен, виданий для одного сервера MCP, не буде працювати на іншому, навіть якщо обидва використовують одного і того ж постачальника ідентичностей.
Налаштування з використанням токенів-носіїв, з якого починає більшість користувачів, відповідає вимозі «цей кінцевий пункт вимагає автентифікації». Однак воно не задовольняє вимогам до взаємодії та захисту від повторної обробки даних, які очікуються від відповідного віддаленого сервера згідно з новими стандартами, і цю прогалину необхідно усунути, перш ніж URL стане доступним для когось поза основною командою.
6. Політика виходу з ужитку та розширення
Функції, описані в стандарті, тепер проходять формальний цикл життя: Активні, Виходять з ужитку та Видалені, причому між етапами виходу з ужитку та видалення є щонайменше дванадцять місяців. Нові функції, такі як інтерфейс користувача, створений на сервері, чи завдання з довгим часом виконання, спочатку з’являються у вигляді додаткових розширень за бажанням, а не входять безпосередньо до основної частини системи. Деякі старіші функції, зокрема робота з кореневими директоріями, збір зразків даних та журналування у їхній початковій формі, поступово виходять з ужитку на користь їхніх замінників.
Для операторів це означає, що функціональна інтеграція не буде зламана раптово через майбутню зміну специфікації, а нові можливості можна впроваджувати свідомо, а не під тиском необхідності встигати за всім, що додається до специфікації.
Покроковий огляд від початку до кінця
Шлях від локального прототипу до розгортання на EC2 відповідно до поточної специфікації виглядає так:
- Локальні тести. Сервер працює через stdio з локальним клієнтом без мережі та автентифікації, тому ітерації відбуваються швидко.
- Перехід на потоковий HTTP. Сервер переконфігурується для використання потокового протоколу HTTP та розгортається на інстанції EC2.
- nginx як проксі. nginx завершує процес TLS, перевіряє токен-носій перед тим, як будь-що потрапить до процесу MCP, а потім перенаправляє запит.
InputRequiredResult, у якому перелічуються дані, які йому ще потрібні.requestState. Повторна спроба не залежить від конкретної інстанції, тому її може обробити будь-яка машина за nginx.Особливі випадки
Повільні операції. Завдання, які раніше залежали від потоку, що залишався відкритим протягом усього часу виконання, слід перевести на розширення Tasks. Створення постійного з’єднання в транспортному механізмі, побудованому без такого з’єднання, — це неправильний підхід.
Інструменти, які ніколи не будуть працювати віддалено. Для сервера, який завжди працює локально разом із своїм клієнтом, найкраще підходить stdio, і майже жодні з проблем, пов’язаних із розгортанням, тут не застосовуються.
Клієнти різних версій. Оскільки старіші клієнти автоматично переходять на версію 2025-11-25, одне розгортання може на деякий час підтримувати обидві версії протоколу, тож не кожен клієнт мусить оновлюватися одночасно з сервером.
Масштабування та виклики у продакшені
Кілька інстанцій. У рамках моделі сеансів для підтримки зв’язку клієнта з конкретною інстанцією використовувалися «липкі сеанси» або спільне зберігання сеансів. Ця потреба зникла, і для тих, хто керує більш ніж одним сервером, це є основною перевагою переписування коду.
TLS у масштабуючійся мережі. Керування сертифікатом на кожній інстанції EC2 швидко стає складним. Використання керованого балансувальника навантаження з сертифікатом ACM перед мережею вимагає значно менших зусиль, ніж налаштування TLS у nginx на кожному сервері.
Видача та обмін токенами. Статичні токени мають певні обмеження з точки зору безпеки, а також створюють проблеми під час експлуатації після кількох клієнтів, оскільки кожен обмін вимагає ручної координації. Механізм видачі на основі OAuth 2.1 вирішує проблеми безпеки та обміну токенами одночасно.
Відстеження дій під час кількох викликів без сеансів на серверній стороні. Оскільки на сервері немає сеансу, який би поєднував послідовність викликів, ідентифікатори кореляції, що передаються у вантажі запиту, стають єдиним надійним способом відтворення того, що було зроблено під час окремої взаємодії агента у межах кількох викликів.
Обробка продовження виконання на клієнті
Конфігурація nginx та проміжок middleware bearer-check забезпечують транспортування даних та базову автентифікацію. Ось мінімальний шаблон для клієнтської частини багатокрокового запиту.
async def call_tool_with_resume(client, tool_name, params):
result = await client.call_tool(tool_name, params)
if result.get("type") == "InputRequiredResult":
answers = collect_answers(result["questions"])
return await client.call_tool(
tool_name,
{**params, "answers": answers, "requestState": result["requestState"]},
)
return result
Важливою є остання рядок, де значення requestState повертається точно таким, яким його надав сервер.
Поширені помилки
- Вважати, що проект „розгорнутий“ означає, що він готовий до експлуатації.“ nginx разом із токеном-носієм вирішує лише найпростіші вимоги, а не всі. Дотримуйтесь стандартів будь-якого публічного API для кінцевої точки MCP: належна автентифікація, ретельне логування та обмеження частоти запитів перед тим, як URL буде широко поширений.
- Використання оперативної пам’яті сервера. У деяких ранніх версіях проекту мовчки припускалося, що сервер пам’ятатиме клієнта з попереднього запиту. Плануйте на те, що кожен запит надходитиме до іншої інстанції, ніж попередня, адже поточні специфікації саме це дозволяють.
- Фіксація однієї версії протоколу. Клієнт, приєднаний до певної версії, перестає працювати після оновлення сервера. Необхідно вбудувати механізм переговорів із розумним резервним варіантом, щоб оновлення однієї зі сторін не призводило до зупинки роботи іншої.
- Відмова від автентифікації для внутрішнього інструменту. Інструмент, який зараз є внутрішнім, може згодом бути переданий партнерській команді, і тоді статичний токен вже не підійде. Якщо сервер може опинитися поза ноутбуком чи VPC, з самого початку слід використовувати OAuth 2.1 та індикатори ресурсів.
Практики, які ефективні у продакшені
- Уникайте ситуацій, коли ваша інфраструктура не може масштабуватися. Усе, що має зберігатися між викликами, має знаходитися у вантажі даних або зовнішньому сховищі.
- Пов’язуйте кожен токен із конкретним ресурсом, адже токен, прийнятий кожним сервером, якому ви довіряєте, рано чи пізно буде використаний там, де це не потрібно.
- Домовляйтеся щодо версій протоколу та не припускайте наявності певної версії. Клієнти та сервери рідко оновлюються в один і той самий день, тому потрібно враховувати цю різницю.
- Перевіряйте автентифікацію на краю мережі. Коли nginx відхиляє некоректний запит, процес MCP залишається простішим та відкриває менше інформації.
- Не чіпайте непрозорі поля.
requestStateта подібні до нього існують для того, щоб сервер міг покладатися на отримані дані, тому клієнти не повинні їх читати чи змінювати.
Як це впливає на розгортання
У теорії MCP є простим. Розгортання включає рішення, які мають значення, і оновлення специфікації від 28 липня 2026 року їх змінило. Streamable HTTP легше запускати за допомогою звичайної інфраструктури, відсутність стану робить додавання інстанції EC2 реалістичним варіантом замість проекту з управління сеансами, а модель авторизації тепер підходить для серверів, орієнтованих на публіку. Усе це не є складним, як тільки його побачити, і саме цей аспект ігнорують теоретичні описи.
Пов’язана література
- Практичні нотатки: Model Context Protocol (MCP) пояснено: посібник для початківців — Детальний огляд Практичних нотаток: Model Context Protocol (MCP) пояснено: посібник для початківців: контракти, перевірки та шаблони коду для команд, які впроваджують цю схему.
- Практичні нотатки: RAG Systems: Повний посібник від нуля до майстерності (видання 2026 року) — Детальний огляд Практичних нотаток: RAG Systems: Повний посібник від нуля до майстерності (видання 2026 року): контракти, перевірки та шаблони коду для команд, які впроваджують цю схему.