MCP - это способ дать агенту то, чего нет в самом репозитории: внешние инструменты и данные по единому протоколу, от поиска в документации до запросов к внутреннему сервису. Соблазн понятен: добавить сервер в общий конфиг, разрешить агенту его инструменты и больше не возвращаться к вопросу. Именно эта лёгкость и создаёт две тихие проблемы - утечку секретов и слишком широкие права, - которые всплывают позже и не там, где их ждёшь.
Наивная модель проста: конфиг один, права выдаются один раз, дальше оно просто работает. Из неё растут два ожидания. Первое - что ключ, вписанный рядом с сервером, останется вашим личным делом. Второе - что разрешение, выданное инструменту однажды, - это разовое удобство без последствий. Оба ожидания ломаются на устройстве MCP в Devin, и лучше знать это до первого подключения, а не после.
Первое, что ломает наивную картину, - расположение конфига изменилось. С релиза Local 3.6 (внутренняя версия v3000.3) MCP переехал из основного конфига в отдельные файлы, а прежние записи из ключа mcpServers мигрируются в них автоматически при старте. Файлов теперь три, и каждый отвечает своей области доверия: проектный .devin/mcp_config.json, который едет в систему контроля версий и виден всей команде; локальный override .devin/mcp_config.local.json, который добавляют в gitignore под личные и чувствительные значения; и пользовательский ~/.config/devin/mcp_config.json (на Windows - в %APPDATA%), общий для всех ваших проектов.
Разделение на три файла - не бюрократия, а перенесённая в конфиг граница доверия. Проектный файл отвечает на вопрос "какие серверы нужны этому репозиторию" и потому публичен по определению. Локальный override отвечает на вопрос "какими ключами и настройками я подключаюсь к ним лично" и потому не должен покидать вашу машину. Пользовательский файл держит то, что не привязано к конкретному проекту. Как только эти три вопроса разведены по трём файлам, ответ на вопрос "куда писать ключ" перестаёт быть предметом гадания.
Запись сервера живёт в объекте mcpServers и бывает двух видов. Удалённый сервер описывается полем url и транспортом transport - http или sse; при необходимости туда же добавляют headers и поля OAuth. Локальный сервер запускается как процесс: command, массив args и объект env с переменными окружения. Любой из них можно временно отключить полем disabled, не удаляя запись. Пример из этой главы показывает оба варианта рядом - удалённый docs и локальный project-tools.
Права - вторая половина дела, и здесь работает принцип минимальных grants. По умолчанию Devin Local спрашивает подтверждение перед каждым вызовом MCP-инструмента. Разрешайте не всё сразу, а по возрастающей: сначала конкретный инструмент, затем, если он вызывается часто, весь сервер; сначала на текущую сессию, и лишь потом - навсегда. Постоянные разрешения удобно держать явным списком в permissions.allow, где шаблон mcp__server__tool разрешает один инструмент, mcp__server__ - весь сервер, а mcp__ - вообще все. Разница между этими шаблонами - это разница в радиусе доверия, а не в удобстве записи.
Секреты требуют отдельной дисциплины, потому что цена ошибки здесь необратима. Ключи и токены не должны попадать в проектный конфиг, который уходит в репозиторий; их место - в локальном override или в подстановке значений, которую поддерживает конфиг: ${env:ИМЯ} берёт переменную окружения, ${file:/путь} читает значение из файла. Если удалённый сервер ходит по OAuth и токен истёк, сервер показывает состояние Needs auth в списке MCP и на карточке; кнопка Authenticate очищает сохранённые данные и заново проходит авторизацию. Это штатный ход, а не ошибка.
Цена невнимания к правам не абстрактна и не ограничивается секретами. Инструмент, безобидный по названию, может возвращать чувствительные данные: "read-only" описывает право на запись, а не безвредность содержимого. Локальный MCP-сервер - это процесс, запущенный на вашей машине с вашими правами, то есть потенциально произвольный код из чужого репозитория. Спецификация MCP прямо трактует это как поверхность атаки и требует минимальных привилегий и осознанного согласия. Поэтому перед выдачей доступа проверяют не только имя инструмента, но и сам сервер, его источник и схему того, что он возвращает.
Проверять результат стоит другим способом, чем настраивали. Открыли конфиг и вписали сервер - подтвердите фактическое состояние в интерфейсе: панель Customizations и список MCP показывают, какие серверы реально загружены и в каком они статусе; сервер в Needs auth ещё не подключён. Пройдите по permissions.allow и убедитесь, что там нет случайного mcp__*, оставшегося от отладки. Загляните в .local.json и в diff проектного файла: ни один ключ не должен оказаться в том, что уходит в репозиторий. Три эти взгляда занимают минуту и снимают обе тихие проблемы разом.
Типичные провалы предсказуемы. Ключ, вписанный в .devin/mcp_config.json вместо .local.json, тихо утекает в историю git - и отзывать его придётся уже как скомпрометированный. Разрешение mcp__* "чтобы не спрашивало" отдаёт агенту весь набор внешних инструментов без разбора. А удивление "сервер вчера работал, сегодня просит заново" почти всегда объясняется истёкшим OAuth и состоянием Needs auth, а не поломкой. Признак всех трёх один: доступ выдали шире и раньше, чем разобрались, что именно сервер приносит и куда уходит ключ. Сначала область и источник - потом grant.
// .devin/mcp_config.json
{
"mcpServers": {
"docs": {
"url": "https://mcp.example.com/mcp",
"transport": "http"
},
"project-tools": {
"command": "node",
"args": ["./tools/mcp-server.js"],
"env": {}
}
}
}