# Конфликты имён инструментов MCP и более безопасное поведение агентов

Агент читает каталог инструментов MCP не так, как внимательный инженер читает SDK. Он переходит от сжатой инструкции к наиболее вероятному действию. Если вы даёте ему `get_user`, `get_users`, `user_lookup` и `admin_get_user`, а различия пытаетесь объяснить абзацем оговорок, то превращаете вопрос полномочий в игру на угадывание.

Конфликты имён инструментов MCP не сводятся к дублирующимся идентификаторам, из-за которых клиент отклоняет каталог. Хуже семантический конфликт: два вызываемых действия звучат как взаимозаменяемые, но одно обращается к более широкой системе, использует более сильные учётные данные или меняет состояние. Я видел, как команды называли это проблемой промпта после того, как агент выбирал не то действие. В большинстве случаев это проблема интерфейса, который они сами выпустили.

Решение не в гигантской таксономии и не в церемониальном комитете по именованию. Дайте каждому действию имя, из которого понятно, что оно делает, где именно и насколько широки его полномочия. Затем напишите описания, задающие границу, которую имя не может передать. Сделайте неоднозначность заметной в тестах, прежде чем она превратится в карточку на подтверждение, неожиданный API-вызов или тяжёлый разбор инцидента.

## Сначала конфликт возникает семантически, а уже потом синтаксически

Синтаксический конфликт возникает, когда два MCP-сервера публикуют инструмент с именем `search`. В зависимости от клиента одна запись может перезаписать другую, клиент может потребовать пространство имён или показать запутанный каталог. Это нужно исправить, потому что поведение разных клиентов может отличаться.

Семантический конфликт сохраняется, даже если каждый идентификатор технически уникален. Рассмотрим такие инструменты:

```text
search_customer
search_customer_records
lookup_customer
customer_admin_search
```

Все четыре могут выглядеть корректными для компилятора и понятными команде, которая их создала. Но если агент получает запрос «Найди запись клиента Maya Chen и обнови её адрес», они дают слабые сигналы для маршрутизации. Агенту приходится угадывать, какая система хранит достоверные данные, предназначено ли действие только для чтения, может ли оно искать по всему клиентскому пространству и допустимо ли использовать административные учётные данные.

Схемы инструментов не спасают расплывчатый каталог. Модель может изучить имена аргументов, но похожие схемы часто усиливают неоднозначность. И поиск только для чтения в каталоге, и поиск в рабочей CRM могут принимать `query`, `limit` и `organization_id`. То, что один инструмент возвращает данные, а другой может запустить обогащение или записать событие аудита, иногда указано лишь в описании. Модель может придавать ему меньше веса, чем очевидному совпадению с задачей.

Относитесь к этим случаям как к разным дефектам:

- Конфликт идентификаторов: клиент не может последовательно показать два инструмента.
- Конфликт намерений: два инструмента выглядят подходящими для одного и того же запроса пользователя.
- Конфликт полномочий: за инструментом с узким названием скрыты широкие учётные данные.
- Конфликт окружений: похожие обозначения скрывают разные аккаунты, регионы или статус production.

Последние три приводят к самым дорогим ошибкам. Клиент может отклонить дублирующиеся имена. Но он не сможет надёжно сообщить, что `sync_contact` означает «изменить запись в рабочей CRM с помощью токена для всей организации», а `update_contact` означает «записать данные в локальный тестовый шаблон».

## В имени инструмента должны быть ключевые факты для маршрутизации

Хорошее имя сообщает агенту факты, необходимые для выбора, ещё до чтения длинного описания. Для действий, затрагивающих внешние системы, я использую такой порядок: целевая система, объект, глагол, затем область действия, если она меняет полномочия или последствия.

`crm_contact_update` лучше, чем `update_contact`, потому что указывает систему. `crm_production_contact_update` может быть ещё лучше, если в том же каталоге есть sandbox. `github_org_member_remove` понятнее, чем `manage_member`: оно говорит, какой ресурс изменится и что результатом будет удаление.

Не перегружайте имя всеми деталями реализации. Агенту не нужно `crm_v3_contacts_patch_with_bearer_auth`. Ему нужны различия, влияющие на выбор. Версия, транспорт и способ аутентификации обычно должны оставаться в реализации сервера или описании. Аккаунт, окружение, побочный эффект и граница полномочий часто стоит указывать в имени.

Практический шаблон выглядит так:

```text
<system>_<object>_<verb>[_<scope>]
```

Примеры:

```text
billing_invoice_get
billing_invoice_send_customer
billing_production_refund_create
source_control_repo_issue_list
source_control_org_member_remove
warehouse_inventory_adjust
warehouse_inventory_adjust_dry_run
```

Шаблон не священен. Важно, чтобы соседние имена различались именно там, где различается их эффект. Если рядом стоят `billing_invoice_send_customer` и `billing_invoice_preview_email`, глаголы и объекты подсказывают модели, какое действие действительно свяжется с человеком. Если единственная разница спрятана в логическом параметре внутри схемы, каталог слишком много требует от маршрутизации.

Избегайте расплывчатых глаголов вроде `process`, `manage`, `handle`, `run`, `execute`, `sync` и `apply`, если сам объект не делает эффект однозначным. Они популярны, потому что продуктовые команды используют их как зонтичные названия для нескольких операций. Именно поэтому они плохо подходят для инструментов. Модель воспринимает такое зонтичное имя как разрешение выбрать самое широкое толкование, которое позволит выполнить запрос.

## Описания задают границу, а не рекламируют продукт

Спецификация инструментов Model Context Protocol определяет инструмент через имя, описание и схему входных данных. Это контракт интерфейса, а не место для рекламного текста. Описание должно отвечать на четыре практических вопроса: какое действие произойдёт, какая внешняя система его получит, какая область действия применяется и что инструмент отказывается делать.

Сравните эти два описания:

```json
{
  "name": "crm_contact_update",
  "description": "Updates customer contact information in the CRM.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "contact_id": {"type": "string"},
      "address": {"type": "string"}
    },
    "required": ["contact_id"]
  }
}
```

```json
{
  "name": "crm_production_contact_update",
  "description": "Changes address, phone, or email fields for one existing contact in the production CRM. This writes immediately. Use crm_contact_search first when the caller supplies a name rather than a contact ID. It cannot create contacts, merge records, or update more than one contact per call.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "contact_id": {
        "type": "string",
        "description": "Stable production CRM contact ID, not an email address or display name."
      },
      "changes": {
        "type": "object",
        "properties": {
          "address": {"type": "string"},
          "phone": {"type": "string"},
          "email": {"type": "string"}
        },
        "minProperties": 1,
        "additionalProperties": false
      }
    },
    "required": ["contact_id", "changes"],
    "additionalProperties": false
  }
}
```

Второе описание задаёт агенту последовательность действий, называет последствие и исключает заманчивые подмены. Оно также помещает важные для различения детали рядом с действием, а не прячет их в отдельном руководстве по эксплуатации, которое агент может никогда не увидеть.

Прямо говорите о побочных эффектах. Пишите: «немедленно отправляет письмо», «создаёт списание», «удаляет удалённую ветку» или «записывает данные в production». Не пишите: «сохраняет изменения» или «выполняет запрошенную операцию». Такие фразы помогают рецензенту звучать точно, но скрывают единственное, что важно заметить агенту и человеку.

Описания входных полей важны по той же причине. Если поле принимает идентификатор ресурса, укажите, что отображаемое имя недопустимо. Если дата по умолчанию задаётся в UTC, напишите об этом. Нестрогие схемы с необязательными строками переносят смысл в прозу и позволяют агенту придумывать аргументы, которые случайно проходят проверку.

## Широкий доступ никогда не должен выглядеть удобным запасным вариантом

Самый опасный каталог содержит узкий и более широкий инструмент, которые выглядят как решения одного запроса. Широкий инструмент часто нужен по уважительной причине: администратору требуется экстренный доступ, миграции нужен поиск между аккаунтами или службе поддержки нужен режим обхода ограничений. Ошибка в том, что его публикуют как равноправный инструмент с дружелюбным именем.

Представьте такие записи:

```text
support_ticket_get
support_ticket_update
support_admin_query
```

Агенту нужен контекст тикета. `support_admin_query` может искать тикеты, пользователей, историю биллинга, внутренние заметки и удалённые записи. Если его описание начинается со слов «Выполняет запрос к платформе поддержки», агент может выбрать его, потому что широкое покрытие выглядит полезным. Инструмент сделал именно то, что вы ему назвали. Ошибка проектирования произошла ещё до вызова.

Переименуйте и ограничьте его:

```text
support_internal_cross_account_search
```

В описании нужно указать, что он ищет внутренние данные поддержки между аккаунтами, возвращает сведения за пределами записи тикета и требует явной инструкции с указанием границы аккаунта. Если рабочий процесс это допускает, требуйте в схеме идентификатор аккаунта вместо одного лишь запроса в свободной форме.

Я не согласен с распространённой рекомендацией публиковать один «мощный инструмент» ради гибкости. Это удобно, потому что уменьшает объём кода сервера и позволяет опытным операторам делать больше за меньшее число вызовов. Для автономного агента такой подход стирает различие между обычной работой и исключительными полномочиями. Создавайте отдельные инструменты для существенно разных уровней доступа. Дополнительные записи в каталоге дешевле, чем объяснения после того, как широкий поиск раскрыл историю не того клиента.

То же относится к окружениям. Не предлагайте `deploy` с параметром `environment`, который по умолчанию указывает на production. Используйте отдельные имена действий, если ошибочное значение имеет другой радиус последствий:

```text
release_staging_deploy
release_production_deploy
```

Перечисление допустимых значений в схеме тоже помогает, но разные имена делают production заметным на этапе выбора, подтверждения и последующего аудита.

## Параметры не могут передать весь смысл безопасности

Параметр меняет действие после того, как агент уже выбрал инструмент. Имя и описание влияют на сам выбор. Команды смешивают эти задачи, когда создают один универсальный инструмент с большим объектом аргументов.

Такой дизайн выглядит компактно:

```json
{
  "name": "repository_action",
  "description": "Performs repository operations.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "operation": {"enum": ["read_file", "create_branch", "delete_branch", "open_pull_request"]},
      "repository": {"type": "string"},
      "branch": {"type": "string"}
    },
    "required": ["operation", "repository"]
  }
}
```

Но за одним ярлыком маршрутизации здесь скрыты операция чтения, операция записи и разрушительная операция. Когда агент выбрал `repository_action`, он уже пересёк важную границу. Рецензент видит подтверждение непрозрачного зонтичного действия и должен в спешке изучать аргументы.

Разделите инструменты там, где меняется класс действия:

```text
repository_file_read
repository_branch_create
repository_branch_delete
repository_pull_request_create
```

Оставляйте параметры для фактов, которые меняются внутри одного действия: идентификатора репозитория, имени ветки, пути к файлу, сообщения коммита или курсора страницы. Не заставляйте параметр выбирать, будет ли вызов читать, записывать, отправлять, списывать средства, удалять данные или обращаться к production.

То же правило действует для области действия. `report_export` с `scope: all_accounts` превращает безобидный экспорт в извлечение данных между аккаунтами. Если область действия меняет круг затронутых людей или то, какие данные могут покинуть систему, дайте ей отдельный инструмент или потребуйте более строгий путь авторизации. Агент не должен узнавать о разнице в полномочиях только после заполнения поля JSON.

## Для выбора инструментов нужен набор тестов на неоднозначность

Нельзя один раз изучить каталог и объявить его понятным. Проверяйте его на реальных пользовательских запросах, особенно на неполных запросах, из которых агенту приходится выводить область действия.

Создайте небольшой набор тестов выбора для каждого сервера. Его можно запускать вручную с поддерживаемым клиентом агента или передавать каталог и промпты в контролируемый оценочный стенд. Записывайте выбранный инструмент, предложенные аргументы и то, одобрил бы вызов человек. Не оценивайте только то, удалось ли в итоге выполнить задачу. Широкий инструмент, который возвращает правильный ответ, всё равно выбран неправильно, если существовал более узкий вариант.

Используйте, например, такие запросы:

1. «Найди счёт для заказа 1842». Ожидаемый выбор должен быть поиском в биллинге только для чтения, а не общим поиском по бухгалтерской книге.
2. «Обнови номер телефона Priya». Если стабильный идентификатор контакта отсутствует, агент должен спросить, о какой Priya идёт речь, а не найти и изменить наиболее похожую запись.
3. «Разверни исправление». Если в каталоге есть действия для staging и production, агент должен спросить об окружении.
4. «Удали Alex из репозитория». Агент должен отличить членство в репозитории от членства в организации.
5. «Отправь счёт». Агент должен выбрать действие отправки, а не генератор предпросмотра или общий вызов обновления счёта.

Добавляйте враждебные формулировки, похожие на описание плохого инструмента. Если `internal_cross_account_search` выигрывает при запросе «найди всё, что у нас есть об этом клиенте», ваше описание может быть технически честным, но всё же слишком привлекательным. Правильным поведением может быть выбор поиска с ограниченной областью или просьба указать аккаунт.

Сохраняйте расшифровку теста после переименования инструментов. Она показывает регрессии, которых не заметит валидатор схемы. Каталог может оставаться корректным, даже если безобидное переименование `billing_invoice_get` в `get_invoice` создаёт конкуренцию с системами закупок, логистики и юридического отдела.

## На экранах подтверждения повторяйте действие простым языком

Подтверждение человеком является последней проверкой, а не разрешением делать названия инструментов небрежными. Если в подтверждении показан только низкоуровневый запрос вроде `POST /v1/contacts/123`, человеку приходится восстанавливать намерение по конечной точке и содержимому запроса. Это плохой момент для обнаружения того, что агент выбрал рабочую CRM вместо sandbox.

Проводите один и тот же деловой смысл через все уровни. Имя инструмента говорит `crm_production_contact_update`. Описание сообщает, что он немедленно записывает данные в одну существующую рабочую запись. В подтверждении нужно написать, что агент хочет изменить указанное поле у одного рабочего контакта, по возможности назвать целевой аккаунт и показать предлагаемые значения. В событии аудита должны сохраниться идентичность инструмента, фактически использованный канал и цель.

Не делайте формулировку подтверждения более успокаивающей, чем само действие. «Разрешить обновление CRM» скрывает разницу между исправлением номера телефона и заменой адреса электронной почты, который используется для восстановления аккаунта. Показывайте важные аргументы, удаляя секреты. Если аргумент содержит чувствительные данные клиента, отображайте достаточно структуры для проверки с учётом ваших правил обработки данных.

Авторизация Sallyport на уровне сессии может подтвердить, что конкретный процесс агента вправе действовать во время своего запуска, а ключи для отдельных вызовов могут требовать отдельного подтверждения для учётных данных, заслуживающих проверки при каждом использовании. Такое разделение работает лучше всего, когда названия действий сразу и точно объясняют подтверждающему человеку, что именно запрашивает агент.

## Учётные данные и идентичность инструмента решают разные задачи

Если агент не получает учётные данные, предотвращается распространённая ошибка: он не может скопировать API-ключ в журнал, исходный файл, задачу или ответ в чате, потому что никогда не видит секрет. Но это не делает безопасным любой запрос. Агент всё ещё может попросить шлюз выполнить неправильный инструмент с помощью настоящих учётных данных.

При проектировании разделяйте такие вопросы:

- Может ли агент получить или раскрыть учётные данные?
- Может ли агент запросить действие за пределами предполагаемой области запроса пользователя?
- Может ли человек увидеть, какой процесс запросил действие?
- Может ли расследующий специалист проверить, что произошло после запуска?

Каталог инструментов отвечает на второй вопрос. Идентичность сессии и подтверждения отвечают на третий. Защищённая от подмены запись отвечает на четвёртый. У каждого уровня своя задача, и один уровень не заменяет другой.

Sallyport хранит HTTP- и SSH-учётные данные в зашифрованном хранилище и выполняет действие, не передавая секреты агенту. Это снижает риск раскрытия учётных данных, но агенту всё равно нужен каталог, в котором имена не позволяют запросить более широкое действие лишь потому, что оно показалось достаточно похожим.

Эта разница важна, когда команды говорят: «Агент не видит токен, значит, инструмент безопасен». Токен может быть защищён, а действие всё равно обладать чрезмерными полномочиями. Учётные данные только для чтения для отчётности и учётные данные для возврата средств в production не должны находиться за почти одинаковыми записями лишь потому, что модель изолирована от обоих секретов.

## Пространства имён помогают операторам, но не оправдывают расплывчатые действия

Многие клиенты показывают инструменты с префиксом, полученным от сервера, например `crm.search_contacts` или `billing.search_contacts`. Используйте пространство имён, если клиент его поддерживает. Это даёт агенту и оператору дополнительную подсказку для маршрутизации и уменьшает число буквальных дубликатов имён.

Но не полагайтесь на него как на единственную подсказку. Клиенты могут сокращать названия, объединять каталоги серверов или показывать имена серверов, мало что значащие для человека, читающего подтверждение. Инструмент с именем `search_contacts` остаётся расплывчатым, если один сервер обращается к тестовой базе, а другой к рабочим данным клиентов.

Лучше использовать такую пару:

```text
crm_production_contact_search
marketing_audience_contact_search
```

Эти имена остаются понятными после добавления или удаления префикса клиентом. Они также делают смешанные каталоги безопаснее, когда со временем агент подключается к большему числу серверов.

Используйте границы серверов для группировки связанных полномочий, а не для их сокрытия. Сервер с именем `operations`, который публикует возвраты средств, развёртывание в production, экспорт данных клиентов и изменения сотрудников, может быть удобен команде-владельцу. Но он создаёт перегруженный каталог с несвязанными глаголами и широкой поверхностью доступа по учётным данным. Разделяйте серверы, если у разных доменов разные владельцы, учётные данные, требования к подтверждению или процессы проверки.

## Проверка каталога обнаруживает ошибки до развёртывания

Проверяйте каталог инструментов рядом со списком задач на естественном языке, а не отдельно от него. Имя, очевидное для автора, часто опирается на контекст, который исчезает, когда в одной сессии агента появляются тридцать инструментов от шести серверов.

Перед выпуском нового действия проведите такую короткую проверку:

1. Прочитайте только имя. Понятны ли человеку внешняя система, объект, побочный эффект и необычная область действия?
2. Поставьте его рядом со всеми похожими инструментами. Не описывает ли одно имя более широкий доступ с помощью более мягкого глагола?
3. Мысленно уберите описание. Не скрывает ли схема в аргументе различие между чтением и записью, sandbox и production или одной записью и несколькими аккаунтами?
4. Задайте неоднозначный запрос пользователя. Должен ли агент задать уточняющий вопрос, и сделали ли вы этот вариант безопаснее угадывания?
5. Проверьте обозначения в подтверждении и аудите. Сохраняют ли они то же различие, что и имя инструмента?

Часто правильным решением будет отклонить просьбу о удобном универсальном действии. Во время реализации такой отказ один раз кого-то расстроит. Неоднозначный инструмент расстроит людей, которым позже придётся расследовать неожиданный вызов, когда контекст уже исчез.

Делайте имя конкретным, ясно описывайте границу и усложняйте случайный выбор широких полномочий. Агенту не нужно больше правдоподобных вариантов. Ему нужно меньше способов принять одно разрешение за другое.
