Підключення майже готове. Агент зібрав конфіг, назвав сервіс і зупинився на одному полі - API key. Сервіс видав довгий рядок, і з нього самого не зрозуміло, куди його покласти. У листування? У файл поруч із кодом? Чи одразу в конфіг, аби вже напевно працювало?
Розберемо це на одній моделі. Подивимося, де значення задають, який процес його отримує і як переконатися, що все налаштовано, не показуючи сам секрет. Модель та сама для локального запуску, для підключення MCP і для опублікованого застосунку - змінюється лише те, чиє середовище ми налаштовуємо.
Що дає ключ доступу
Ключ пов'язує запит з обліковим записом або проєктом і з тими правами, які вам видали. За ним сервіс розуміє, хто звернувся, що цьому користувачеві дозволено і скільки запитів він уже надіслав. Що саме означає ключ, визначає конкретний сервіс. Десь це доступ до всього акаунта, десь лише до одного проєкту і тільки на читання.

Поруч зі словом "ключ" в інструкціях трапляється токен. У повсякденних текстах їх часто вживають як одне й те саме, але деталі механізму відрізняються. Ідеться про спосіб отримання, строк дії і набір прав. Повними синонімами їх краще не вважати - що саме видає сервіс, написано в його документації.
Навіщо сервісу взагалі вас розрізняти, простіше побачити на побутовому прикладі. Ви завели акаунт, обрали тариф із лімітом запитів на місяць і отримали ключ. Далі сервіс за цим ключем рахує ваші запити, звіряє їх із лімітом і, якщо тариф платний, виставляє рахунок. Ключ, що потрапив до сторонніх, означає, що ліміт і рахунок дістаються комусь іншому.
Ключ не ухвалює рішення сам. Він дає сервісу посилання на налаштування власника, а вже сервіс вирішує, чи дозволено конкретний запит і чи треба списати його з ліміту.
Бувають і значення, які сервіс спеціально позначає як публічні - саме їх і треба покласти в код сторінки. Відрізнити публічне значення від секретного за іменем змінної не вийде. Призначення написано в документації. туди й дивіться. У прикладах нижче всюди стоять очевидні заглушки на кшталт fc-EXAMPLE-0000000000, справжній ключ має інший вигляд.
Де застосунок може зберігати секрет
Уявіть таку ситуацію. Ви додали ключ безпосередньо в код сторінки, запустили проєкт і отримали відповідь від API. Усе працює. Після публікації застосунок теж відкривається, запити проходять - здається, завдання розв'язане.
Але разом із кодом браузер кожного відвідувача отримує і ключ. Достатньо відкрити інструменти розробника, знайти його в JavaScript або в запиті і скопіювати. Після цього людина може надсилати свої запити від вашого імені. І витрачати ваш ліміт. Аж доки сервіс не почне відхиляти вже ваші власні запити.
Проблема не в конкретному інструменті збірки. Усе, що сторінка передала браузеру, доступне його власнику: HTML, стилі, JavaScript, адреси й заголовки запитів. Навіть якщо під час збірки ключ узяли з локального .env, після підстановки в JavaScript він поїхав до відвідувача разом з рештою коду.
Backend - це код, який працює на сервері. Відвідувач отримує його відповіді, але не вихідний код і не змінні середовища серверного процесу. Тому backend може зберігати ключ у себе і звертатися до зовнішнього сервісу від імені застосунку.
Схема запиту змінюється. Браузер надсилає звичайний запит вашому backend без секретного ключа. Backend читає ключ зі свого середовища, додає його в окремий запит до API і повертає браузеру лише результат.
Тепер відвідувач бачить адресу backend і дані відповіді, але не ключ зовнішнього сервісу. Секрет лишається за процесом, який ним користується.
Секрет може мати двох власників:
- локальний процес - Claude Code з підключеною інтеграцією, скрипт або dev-сервер. Він запускається на вашому комп'ютері й отримує ключ там само;
- backend опублікованого застосунку - код на сервері, який сам звертається до зовнішнього API. Його середовище налаштовують у панелі хостингу.
Це два окремі налаштування. Ключ із середовища вашого комп'ютера не з'явиться на хостингу сам, навіть якщо сервіс та ім'я змінної збігаються.
Перенесення ключа на backend ховає його від браузера, але публічний backend все одно треба захищати від чужих запитів.
Перш ніж Claude Code попросить задати значення, попросіть його назвати дві речі: який процес використовуватиме ключ і яким способом значення до цього процесу дійде. Якщо відповіді немає, налаштовувати поки нічого.
Як працює змінна середовища
Змінна середовища - це іменоване значення, яке програма отримує під час запуску. Вона має ім'я, наприклад FIRECRAWL_API_KEY, і значення, той самий довгий рядок. У конфігах і в коді пишуть ім'я; значення живе окремо і поруч із кодом не зберігається.
Звідки програма його бере. Програма, запущена з терміналу, успадковує середовище цього терміналу. Задали змінну у вікні, запустили з нього Claude Code - Claude Code її бачить. Відкрили друге вікно і задали змінну там - на процес, що вже працює, це не впливає, середовище йому дісталося під час старту.
Звідси найчастіша причина скарги "ключ же задано, а не працює". Значення задали не в тому вікні або вже після запуску. Лікується перезапуском процесу з того терміналу, де змінна є, от і все.
Навчальний варіант, з явно фіктивним значенням:
# bash: змінна для поточного терміналу
export FIRECRAWL_API_KEY=fc-EXAMPLE-0000000000# PowerShell: те саме
$env:FIRECRAWL_API_KEY = "fc-EXAMPLE-0000000000"Із заглушкою в навчальному прикладі так писати можна. Справжній ключ у такий спосіб набирати не варто. Рядок повністю піде в історію команд і лишиться там після того, як ви закриєте термінал. Для справжнього значення є прихований ввід - ви вставляєте рядок, на екрані нічого не з'являється, в історію потрапляє лише сама команда:
# bash: значення вводиться приховано
read -s -r FIRECRAWL_API_KEY
export FIRECRAWL_API_KEY# PowerShell: прихований ввід і розпакування значення в змінну середовища
$secure = Read-Host "FIRECRAWL_API_KEY" -AsSecureString
$env:FIRECRAWL_API_KEY = [System.Net.NetworkCredential]::new('', $secure).PasswordПеревіряти налаштування треба так, щоб значення не опинилося на екрані, у логах чи на скриншоті. Щоб відповісти на запитання "налаштовано чи ні", вистачає двох рядків:
# bash
[ -n "$FIRECRAWL_API_KEY" ] && echo "задано" || echo "не задано"# PowerShell
if ($env:FIRECRAWL_API_KEY) { "задано" } else { "не задано" }Ні довжину, ні перші символи друкувати не треба. Для перевірки це нічого не додає, а фрагмент ключа - уже частина секрету.
Способів задати змінну назавжди багато, і в кожній системі вони свої. Розбиратися в них зараз не потрібно. Для локальної роботи вистачає терміналу, а на хостингу змінні задають у його ж панелі.
Змінна середовища - це спосіб не писати секрет у коді, а не засіб захисту. Значення не шифрується, і програма, запущена в тому самому середовищі, вільно його прочитає. Саме тому важливо, чий процес ви налаштовуєте.
Навіщо потрібні .env і .env.example
Задавати змінні вручну в кожному новому терміналі швидко набридає, тому значення складають у текстовий файл .env. Один рядок у ньому зберігає одну пару: ім'я і значення.
Найчастіше пропускають одну деталь, і досить важливу. Сам по собі файл нічого не налаштовує. Хтось має його прочитати й передати значення в середовище процесу - застосунок, бібліотека, або launcher, який запускає вашу програму. Створити .env і вважати, що тепер його підхопить будь-яка програма, не можна. Що саме завантажує файл у вашому випадку, варто з'ясувати напряму, а не припускати.
А поруч із ним, як правило, лежать ще два файли. Приблизно так:
# .env - лишається тільки на вашій машині
FIRECRAWL_API_KEY=fc-EXAMPLE-0000000000# .env.example - шаблон для тих, хто відкриє проєкт
# значення порожні, тут лише імена
FIRECRAWL_API_KEY=# .gitignore
.env| Файл | Навіщо потрібен | Що всередині |
|---|---|---|
.env | локальні значення для запуску | справжні імена і значення, лише на вашому комп'ютері |
.env.example | підказка тому, хто відкриває проєкт | ті самі імена з порожніми значеннями, іноді короткий коментар |
.gitignore | правило, щоб локальний файл не потрапив у репозиторій | рядок з іменем файлу |
Сенс поділу простий. .env.example відповідає на запитання "які змінні потрібні цьому проєкту" і спокійно живе в репозиторії, бо значень у ньому немає. Справжні ключі в нього не переносять навіть тимчасово.
Якщо ви поки не знайомі з Git, тут достатньо запам'ятати роль .gitignore. Він перелічує файли, які не треба додавати в репозиторій. Докладно роботу з Git і .gitignore розберемо на рівні 13.
А тепер пастка з .gitignore, на яку натрапляють регулярно. Правило діє лише на файли, які Git ще не відстежує. Якщо .env колись закомітили, додавання рядка в .gitignore його не відв'яже. Файл лишиться під контролем версій, і його зміни й далі потраплятимуть у коміти. У документації Git це сказано прямо. Файлів, які Git уже відстежує, правила ігнорування не стосуються. Документація gitignore.
Головна умова на схемі - посередині. Той самий рядок у .gitignore допомагає лише доти, доки .env не відстежується. Для файлу, який уже відстежується, потрібен окремий крок.
Тому перш ніж записати у файл справжній секрет, варто переконатися, що Git про нього не знає. git status покаже, чи потрапляє файл у зміни. Якщо файл уже відстежується, його знімають з відстеження командою git rm --cached .env і комітять цю зміну - файл лишиться на диску, але вийде з-під контролю версій.
Тільки врахуйте, що це впливає на майбутні коміти. Значення, яке вже потрапило в історію, там і лишиться, і розбиратися з ним треба інакше - про це нижче.
Як передати ключ підключенню MCP
MCP - спосіб підключити зовнішній інструмент до Claude Code. Підключення описують у файлі .mcp.json, і цей файл зазвичай лежить у репозиторії поруч із проєктом, тобто його бачить уся команда. Отже, секрету там не місце.
Мінімальне підключення сервісу Firecrawl через HTTP має такий вигляд:
{
"mcpServers": {
"firecrawl": {
"type": "http",
"url": "https://mcp.firecrawl.dev/v2/mcp",
"headers": {
"Authorization": "Bearer ${FIRECRAWL_API_KEY}"
}
}
}
}Ключа у файлі немає. У заголовку стоїть ${FIRECRAWL_API_KEY} - посилання на змінну середовища за іменем. Claude Code підставляє сюди значення із середовища того процесу, в якому він запущений. У репозиторій їде ім'я, а значення в кожного своє. Таку підстановку Claude Code вміє робити в полях url, headers, env, command і args. Claude Code про MCP.
На схемі видно, що в заголовку запиту сходяться дві речі з різних місць. Шаблон заголовка приходить зі спільного конфігу, а значення підставляється із середовища процесу. Ні на якому кроці ключ не записується у файл, який їде в репозиторій.
Адресу і спосіб передавання ключа беруть з документації самого сервісу. У Firecrawl це заголовок Authorization, і документація окремо просить не вставляти ключ в MCP-адресу. Причина проста. Адреса - це звичайний текст, вона потрапляє в конфіг, у логи та в історію команд, а разом із нею туди потрапить і ключ.
Деякі сервіси підтримують вхід через браузер. Клієнт сам відкриває сторінку входу, і передавати API key вручну взагалі не доводиться. У Firecrawl для цього передбачено окрему адресу. Документація Firecrawl про MCP.
Інструмент мовчить? Тоді перевірте по черзі три речі:
- ім'я змінної в конфігу та ім'я, яке ви задали, збігаються до останнього символу;
- значення задано в тому процесі, з якого запущено Claude Code;
- процес перезапущено після того, як змінну задали.
Коли змінної немає, Claude Code не падає мовчки. Він завантажує конфіг, лишає текст ${FIRECRAWL_API_KEY} без підстановки і пише попередження з іменем змінної, якої бракує. Саме значення при цьому ніде не друкується, тож діагностика безпечна. Виводити все середовище цілком, щоб знайти проблему, точно не треба.
Цей приклад потрібен, щоб розібратися в уже готовому підключенні. Як сервер обирають, додають і які права він отримує - окрема тема; тут важливо лише те, де в цій конструкції живе секрет.
Якщо ключ уже потрапив назовні
Ситуація звичайна. Ключ опинився в повідомленні, у коміті або на скриншоті. Панікувати нема чого, але й відкладати на завтра не варто. Порядок дій такий:
- відкликати або замінити ключ в особистому кабінеті сервісу. Це єдиний крок, після якого старий рядок більше нічого не відкриває;
- задати нове значення там, де воно потрібне: локально, на хостингу, в налаштуваннях підключення;
- розібратися з місцем публікації - видалити повідомлення, виправити файл, закрити доступ;
- подивитися в кабінеті, як ключ використовували, і що сервіс показує щодо витрат.
Порядок саме такий, і перший крок не переставляється. Видалене повідомлення чи новий коміт поверх старого не роблять попередній секрет безпечним. Повідомлення могли прочитати, репозиторій клонувати, історію зберегти. Поки ключ діє, він діє в усіх, у кого він є.
Про репозиторій окремо. Якщо секрет потрапив у коміт, GitHub має посібник із видалення таких даних. Видалення конфіденційних даних з репозиторію. Тільки майте на увазі, що повне очищення історії переписує вже наявні коміти, а отже зачіпає всіх, хто з цим репозиторієм працює. Таку операцію погоджують із власниками, а не запускають самотужки за навчальною статтею. А от ключ замінюють одразу, ні з ким цього не погоджуючи.
Що доручити Claude Code
Агент бере на себе майже всю механіку з цієї статті, крім одного. Сам секрет вводите ви. Що можна спокійно віддати агентові:
- підготувати конфіг з іменем змінної, а не зі значенням;
- назвати, що саме завантажує змінну в процес і в якому процесі вона має опинитися;
- перевірити, що секрету немає в клієнтській збірці та у файлах, які відстежує Git;
- виконати перевірку доступу і повідомити результат. Без виводу значення, звісно.
За вами лишається ввід справжнього ключа через механізм, який для цього передбачено: прихований ввід у терміналі, локальний файл, панель хостингу. У листування, скриншот чи звіт агента секрет не потрапляє ніколи, навіть "на секунду, щоб перевірити".
Доручення може звучати приблизно так:
Підключи сервіс через API. Ключ я задам сам.
Візьми з документації адресу, метод і спосіб передавання ключа.
У конфіг поклади ім'я змінної середовища, не значення.
Скажи, який процес має отримати змінну і що її завантажує.
Перевір, що ключа немає в клієнтській збірці та у файлах, які відстежує Git.
Перевірку доступу зроби без виводу значення і його частин.
Повідом результат: налаштовано, не задано або доступ відхилено.І останнє, про читання звіту. "Змінну задано" і "доступ працює" - різні твердження, підміняти одне іншим не варто. Відсутність значення теж відрізняється від відмови сервісу. У першому випадку запит нема чим підписати, у другому сервіс відповів, але доступу не дав. Якщо агент каже просто "готово", уточніть, що саме він перевірив.
Секрет не надсилають у чат, не показують на скриншоті й не вставляють у файл, який іде в репозиторій. Усе інше в цій статті - способи дотриматися цього правила і при цьому не заважати роботі.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ