Головна / Статті

Два способи збою серверів MCP та шар захисту для їх запобігання

Сервери MCP зазнають невдач через занадто широкі токени та інструменти, які не враховують завдання. Обмежені токени, перевірка аудиторії, очищені помилки та ліміти інструментів створюють захисний шар.

Видавець: REACTAPP.TOP8 хв читання1896 слів

Джерела: MCP Servers Fail in Two Ways, and Both Are Preventable. Here’s the Guardrail Layer.

Більшість серверів MCP не виходять з ладу через збої. Вони виходять з ладу через надання більшої довіри, ніж це потрібно для виконання завдання, у двох аспектах: у правах, які вони надають, та в інтерфейсі, який вони відкривають. Більш розумна модель цього не виправить. Проте шар захисту, розташований між агентом та інструментом, це зробить, і нижченаведені розділи показують, що містить такий шар, разом із кодом.

Очищення, якого ніхто не просив

Користувач просить агента скопіювати базу даних у продакшн-середовищі до середовища передпродакшну. Обробляючи це запит, агент вирішує, що крок очищення буде корисним, і спочатку видаляє старі тестові дані. Ніхто цього не просив. Якщо сервер MCP передає власні облікові дані користувача, а у цього користувача є права адміністратора, видалення відбувається, і база даних у продакшні зникає.

AWS наводить саме цей сценарій у своїх рекомендаціях щодо керування MCP, пояснюючи, чому не можна ігнорувати токени обмеження доступу.

Сервер є межею довіри

Люди зазвичай описують сервер MCP так само, як і будь-який засіб інтеграції: як щось, що поєднує агента з системою, з кількома доступними інструментами, і це вже все. Насправді саме тут рішення моделі про дію стає справжнім побічним ефектом, коли використовується реальна інфраструктура та справжні облікові дані. Усі кроки до цього є лише логічними міркуваннями. Сервер — це та точка, де міркування мають наслідки.

Отже, сервер є межею довіри, незалежно від того, чи був він спроєктований саме так чи ні. Якщо подивитися на продакшн-сервери з цієї точки зору, їхні помилки можна розділити на дві групи.

Клас помилок один: занадто великі права

Історія з базою даних є загальним випадком. Керівні принципи управління AWS вказують на рішення: кожен наступний запит має містити токен, спеціально створений для цього запиту та маючий відповідні обмеження, а не облікові дані, які ініціювали агента. Також зазначається, що слід перевіряти значення claim audience токена, що запобігає використанню токена, створеного для одного сервера MCP, на іншому.

Лише деякі команди застосовують цей підхід на ранніх етапах, оскільки це вимагає більше налаштувань, ніж просто передача токена викликаючої сторони. Проте саме це визначає різницю між невдалим запитом до інструменту, який безпечно зазнає помилки, та запитом, який справді потрапляє у продакшн.

Клас помилок два: поганий інтерфейс

Друга група є більш тонкою. Не відбувається жодних драматичних інцидентів. Надійність просто поступово знижується, і визначити причину цього складно. Керівні принципи розробки Azure MCP від Microsoft вказують на основну причину: сервери проектуються як REST API, де кожна операція представлена як окремий інструмент, тоді як проектування мало б відповідати тому, чого хоче досягти агент. Ілюстрація чітко ілюструє цю ідею. Замість чотирьох інструментів під назвами create_ticket, add_comment, assign_ticket та notify_user один інструмент file_support_request виконує всі чотири кроки всередині себе.

У посібнику вказано ще два симптоми цієї звички. Описи можуть обіцяти те, чого інструмент не може надати, тому агент довіряє тексту, якого код ніколи не дотримується. Крім того, повідомлення про помилки можуть бути марними (“Помилка”) або занадто інформативними: сліди виконання коду, внутрішні шляхи до файлів чи підтвердження наявності певного користувача у вашій базі даних, що саме по собі дозволяє здійснювати атаки типу енумерації.

Асиметрія у спрямуванні підозр

Замисліться, скільки зусиль вкладається у остаточну відповідь агента: перевірка структурованого виводу, захисні механізми, повторні спроби та кроки верифікації. Вивід моделі вважається підозрілим, і це цілком обґрунтовано.

Тепер розгляньмо шар викликів інструментів, який знаходиться під цією відповіддю. Токен викликаючого проходить прямо через систему. Список інструментів розширюється, причому ніхто не перевіряє, чи може модель все ще ефективно ними користуватися. Опис інструменту приймається таким, яким він написаний, і жоден тест не демонструє, що агент правильно використовує цей інструмент. Величезні зусилля докладаються для сумніву в тому, що каже модель, тоді як майже жодних зусиль не докладається для сумніву в тому, що вона робить.

Це нерівномірне ставлення є справжньою проблемою обох груп. Ні більш розумна логіка дозволів, ні кращі назви інструментів самі по собі не зможуть її вирішити. Потрібен шар, який би перевіряв кожен виклик інструменту, а не лише кожну відповідь моделі, перш ніж надавати їй довіру.

Створення шару

Основною одиницею тут є виклик інструменту. Не дозволяйте необробленому запиту потрапляти до бекенду — спочатку його потрібно обгорнути.

from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Optional

@dataclass
class ScopedToken:
 audience: str # which MCP server this token is valid for
 permissions: list[str] # e.g. ["read", "create"] - never assume "all"
 issued_at: datetime
 expires_at: datetime
 source_user: str # who originally triggered this, for audit

Кожне поле в цьому обгортку існує для того, щоб наступний рівень міг визначити, наскільки можна довіряти цьому запиту. Тепер переходимо до елементів керування.

Обмежте токен перед запуском інструменту

Не використовуйте знову облікові дані, якими користувач прийшов, для подальших дій. Видається вужчий токен, який охоплює лише те, що має зробити інструмент.

def issue_scoped_token(user_token: ScopedToken, tool_name: str,
 required_permissions: list[str]) -> ScopedToken:
 # Never grant more than the tool declares it needs
 granted = [p for p in required_permissions if p in user_token.permissions]
 if set(required_permissions) - set(granted):
 raise PermissionError(
 f"{tool_name} requires {required_permissions}, "
 f"caller only has {user_token.permissions}"
 )
 return ScopedToken(
 audience=tool_name,
 permissions=granted,
 issued_at=datetime.utcnow(),
 expires_at=datetime.utcnow() + timedelta(minutes=5),
 source_user=user_token.source_user,
 )

Це та частина, яка забезпечує безпечну невдачу сценарію бази даних. Запит, призначений лише для читання та створення, просто не може виконати операцію видалення, незалежно від рішення моделі.

Перевіряйте аудиторію при кожному виклику

Токен, створений для одного інструменту, має бути відхилений кожним іншим інструментом.

def enforce_audience(token: ScopedToken, expected_tool: str) -> None:
 if token.audience != expected_tool:
 raise PermissionError(
 f"Token issued for '{token.audience}' cannot be used on '{expected_tool}'"
 )

Формуйте інструмент відповідно до завдання

Це використовує шаблон file_support_request з посібника Azure:

def file_support_request(customer_email: str, issue_type: str, description: str) -> dict:
 ticket = create_ticket(issue_type, description)
 add_comment(ticket.id, f"Filed by {customer_email}")
 assign_ticket(ticket.id, team=route_by_type(issue_type))
 notify_user(customer_email, ticket.id)
 return {
 "ticket_id": ticket.id,
 "status": "open",
 "assigned_team": ticket.team,
 }

Один дзвінок несе одну чітку мету, тому агенту ніколи не доводиться самостійно правильно визначати послідовність чотирьох операцій.

Очищуйте помилки перед тим, як агент їх побачить

Зберігайте повну інформацію на сервері та надавайте агенту лише те, що йому потрібно для відновлення.

def safe_error(internal_message: str, request_id: str) -> dict:
 # internal_message goes to your logs, never to the model
 log.error(internal_message, extra={"request_id": request_id})
 return {
 "content": [{
 "type": "text",
 "text": f"Unable to complete the request. Request ID: {request_id}. "
 f"Try again or contact support."
 }],
 "isError": True,
 }

Обмежте кількість інструментів на сервері

Дізнайтеся про це обмеження раніше, ніж це станеться на практиці. У посібниках Microsoft вказано цифру: не більше 10–15 інструментів на одному сервері, розподілених за доменами, і не слід продовжувати збільшувати кількість інструментів на одному сервері.

MAX_TOOLS_PER_SERVER = 15

def register_tool(server, tool):
 if len(server.tools) >= MAX_TOOLS_PER_SERVER:
 raise ValueError(
 f"{server.name} already has {len(server.tools)} tools. "
 f"Split into a domain-specific server instead of adding more."
 )
 server.tools.append(tool)

Три швидкі перевірки існуючого сервера

Жодна з них не вимагає повної перебудови.

Перевіряйте діапазон дії токенів, а не лише їх наявність. Візьміть десять останніх викликів інструменту та порівняйте права, які мав кожен токен, із тими, які були потрібні інструменту. Якщо ці дані зазвичай відрізняються, це означає, що використовуються токени ширшого діапазону, ніж необхідно, і саме така ситуація, ймовірно, існує зараз.

Розрахуйте кількість токенів для вашого власного інструменту. Згідно з рекомендаціями AWS, лише на описи потрібно від 5 000 до 10 000 токенів на кожен виклик, коли кількість інструментів на сервері перевищує 20. Підрахуйте кількість інструментів, які у вас є. Сервер, який значно перевищує цей показник без фільтрації чи розділення даних, страждає від надмірного обсягу контексту, що не можна виправити жодними змінами моделі.

Спробуйте тест на рівні агента, який, ймовірно, ще не проводився. Візьміть три найбільш використовувані інструменти та перевірьте, чи зможуть вони, використовуючи справжнього агента, а не тест на одиницю, вибрати правильний варіант зі звичайного запиту, сформульованого природною мовою. У посібниках Microsoft цей рівень описується як той, який майже всі ігнорують. Пройшовши тести на одиницю та протокол, не провівши цього тесту, залишається невідомим, чи справді інструменти функціонують.

Як це пов’язано з рештою

Жоден з цих елементів не замінює існуючу роботу з забезпечення надійності, а існує поруч із нею. Токени з визначеним діапазоном дії діють за тим самим принципом, що й перевірка вхідних даних на кожному рівні довіри. Обмеження кількості інструментів та орієнтація на завдання відображають принципи версіонування API та проектування інтерфейсів, але стосуються поверхні, яку читає модель, а не людський розробник. Кожен сервер MCP є ще одним місцем, де система вирішує, наскільки можна довіряти запитувачу, тому він потребує такої ж уваги, як і решта.

Команди використовують MCP тому, що це надає агентам більше можливостей: один стандартний шлях до реальних систем замість сотні індивідуальних інтеграцій. Проблема полягає у тому, що модель виконує погано сфокусований запит так само точно, як і добре сфокусований, оскільки не розрізняє між ними різниці. Лише шар захисту це розуміє.

Проблеми з дозволами та проблеми з інтерфейсом здаються непов’язаними: одні стосуються доступу, а інші — дизайну. Обидві виникають через одне недбале припущення, а саме що виклик інструменту заслуговує на довіру, яку насправді ніхто не надав. Більшість систем ШІ спрямовують свою підозру на результат виконання завдання. Але сторона виклику інструменту також заслуговує на увагу.

Це залишає одне запитання до кожного використовуваного сервера MCP: що наразі заважає агенту діяти на виклик, який містить надмірну довіру, і чи буде подано попередження, якщо ця довіра вже існує?

Швидкі відповіді

Що таке обмеження токенів MCP? Це означає надання кожному виклику інструменту власного вузькоспрямованого токена для цієї мети, без передавання початкових облікових даних викликаючого. Рекомендації AWS щодо управління рекомендують це робити, щоб хибні чи ненавмисні дії не могли відбутися лише через те, що базові облікові дані це дозволяли.

Скільки інструментів має надавати один сервер MCP? Згідно з рекомендаціями Azure, як орієнтовна цифра вказується 10–15, після чого сервер слід розділити за доменами. При цьому агенти значно рідше обирають правильний інструмент, а рекомендації AWS оцінюють вартість довгого списку інструментів у 5 000–10 000 токенів за кожен виклик лише для описів, якщо їх більше 20.

Чим відрізняється проектування інструментів, орієнтованих на завдання, від проектування системних API? У проектуванні системних API кожна операція бекенду стає окремим інструментом, що імітує REST API. У проектуванні, орієнтованому на завдання, пропонується один інструмент, який реалізує те, що намагається досягти користувач, обробляючи всі кроки внутрішньо. Прикладом від Microsoft є єдиний інструмент file_support_request замість чотирьох операцій з обробки заявок.

Чому потрібно очищувати повідомлення про помилки MCP? Сліди виконання коду, внутрішні шляхи чи підказки про наявність певної запису надають зловмисникам інформацію, яку вони не повинні отримувати, що є принаймні слабкістю у форматі енумерації. Безпечніший підхід полягає у записуванні повної інформації до журналів на стороні сервера та поверненні агенту лише того, що йому потрібно для відновлення, разом із ідентифікатором взаємозв’язку.

Чи виявляють єдиничні тести ці проблеми? Ні. Єдиничні тести охоплюють лише логіку інструменту, а тести протоколу — дотримання стандартів MCP, але жоден з них не доводить, що агент може знайти, вибрати та правильно використати інструмент у реальних умовах. Для цього потрібен третій рівень тестування, який базується на реальному агенті, але більшість команд його ігнорують.

Пов’язана література

  • Поновне вимірювання податку на токени MCP: два описи інструментів спричинили зростання — 13-денний повторний запуск дев’яти серверів MCP показав, що кількість токенів у визначеннях інструментів зросла з 36 320 до 38 738, причому майже все це сталося через те, що один із серверів переписав вже наявні описи.