
Bifrost 심층 분석: Virtual Keys, MCP 및 Skills
요약
AI 게이트웨이인 Bifrost의 핵심 기능인 Virtual Keys, MCP, Skills를 심층 분석합니다. 가상 API 키를 통해 토큰 및 요청 제한을 커스텀 설정하고, 보안을 유지하며 다양한 코딩 하네스에 통합하는 방법을 다룹니다.
핵심 포인트
- Virtual Keys를 통한 토큰 및 요청 수 커스텀 제한 설정 가능
- 가상 키를 별도 파일로 관리하여 Git 노출 없이 보안 유지
- OpenCode 등 다양한 코딩 하네스와의 유연한 통합 지원
- 백그라운드 작업을 위한 저렴한 모델(small_model) 설정 기능
이전 블로그에서 저는 rate limit(속도 제한)에 걸릴 때마다 제공업체를 교체하는 것에 얼마나 지쳤는지, 그리고 이를 실제로 해결해 주는 Bifrost를 어떻게 찾게 되었는지 언급했습니다. 아직 읽어보지 않으셨다면, 여기에서 꼭 확인해 보세요!
그 이후로 저는 OpenCode, jcode, Pi 등을 포함한 여러 코딩 하네스(harness)와 함께 Bifrost를 매일 사용해 왔습니다. 처음에는 fallback(폴백) 메커니즘과 complexity routing(복잡도 라우팅)만으로도 충분히 만족했습니다. 하지만 대시보드를 훑어볼 때마다 다른 기능들이 계속 저의 호기심을 자극했습니다.
그래서 저는 자리에 앉아 이 기능들을 하나씩 탐구해 보고, 제가 아직 인지하지 못한 문제들을 해결할 수 있는 기능이 있는지 확인해 보기로 했습니다. 그럼 시작해 봅시다!
Virtual Keys를 통한 제한 설정
Virtual API key(가상 API 키)를 생성하여 정해진 시간 동안 허용할 토큰 및 요청 수를 커스텀 제한(custom limits)으로 설정할 수 있습니다.
대시보드를 통해 설정하는 방법은 매우 간단합니다.
이름과 만료 기간을 지정한 다음, 이 키를 통해 허용하려는 제공업체(providers)와 모델(models)을 추가하기만 하면 됩니다. 그러면 끝입니다! 이전 블로그에서 논의했던 방식과 유사하게, 이 키를 가져와 원하는 하네스에 설정할 수 있습니다.
하지만 이번에는 키를 위한 별도의 파일을 만들고, 이를 OpenCode 설정 파일과 동일한 디렉토리에 보관해야 합니다. 저는 제 파일의 이름을 bifrost-virtual-key라고 지었으며, 여기에는 키 자체 외에는 아무것도 들어있지 않습니다. OpenCode는 {file:...} 구문을 사용하여 파일에서 값을 직접 읽어올 수 있으며, 여기에 사용된 상대 경로(relative path)는 터미널을 실행하는 위치가 아니라 설정 파일 자체의 디렉토리를 기준으로 해석됩니다. 따라서 키를 설정 파일 내부에 두거나 Git에 올릴 필요가 전혀 없습니다.
그 후, 설정 파일에 다음 내용을 붙여넣습니다:
{
"model": "bifrost-local/gemini/gemini-2.5-flash",
"small_model": "bifrost-local/gemini/gemini-2.5-flash",
...
여기서 small_model 필드는 무심코 지나치기 쉽지만 언급할 가치가 있습니다. OpenCode는 세션 제목 생성과 같은 저렴한 백그라운드 작업을 위해 이 필드를 사용하며, 만약 설정하지 않으면 스스로 더 저렴한 모델을 찾아 나섭니다. 이를 동일한 모델로 지정하면 모든 작업이 하나의 가상 키 (Virtual Key)를 통해 흐르게 되는데, 이것이 바로 이 작업의 핵심 목적입니다.
이 기능은 다양한 모델을 실험할 때나, 모델이 무한히 실행되어 루프에 빠져 비싼 토큰 (tokens)을 낭비하는 것을 방지해야 하는 장기 작업 (long-horizon tasks) 시에 매우 유용합니다. 또는 하네스 (harness)가 접근할 수 있는 MCP, 도구 (tools) 또는 기술 (skills)을 미세하게 제어하고 싶을 때도 유용합니다.
그럼 의도적으로 아주 작은 제한을 설정하고, 그것이 작동하는 것을 지켜봅시다.
보시는 바와 같이, 저는 최대 1000 토큰과 최대 10개의 요청으로 제한을 설정했으며, 두 설정 모두 매시간 초기화됩니다. 토큰 예산(token budget)을 의도적으로 작게 설정한 이유는 기다리기보다 빠르게 한계에 도달하고 싶기 때문입니다. 저는 이미 이 키를 제 OpenCode 설정에 연결해 두었으므로, 일반적인 메시지 몇 개만 보내도 차단되는 것을 확인할 수 있을 것입니다.
그리고 정확히 그런 일이 일어났습니다. 다만 그 일이 일어난 방식이 흥미로운 부분입니다. 저의 첫 번째 메시지는 627 토큰 (tokens)을 소모했고 문제없이 통과되었습니다. 두 번째 메시지 역시 통과되었지만, 그 자체로 조용히 12.94K 토큰을 소모했습니다. 그 이후의 모든 요청은 거부되었으며, 이것이 성공률이 12개 중 단 2개의 요청, 즉 16.67%에 머무는 이유입니다.
따라서 제한(limit)은 당신이 막 하려는 요청의 크기를 기준으로 체크되는 것이 아니라, 이미 소비한 양을 기준으로 체크됩니다. 요청이 이루어지는 순간 예산(budget) 범위 내에 있다면 통과되지만, 그 요청 자체가 한도를 단번에 초과해 버릴 수 있습니다. 실제로 중요하게 생각하는 예산을 설정하기 전에 이 점을 알아두는 것이 가치가 있습니다. 왜냐하면 상한선(cap)은 총 얼마를 쓸 수 있는지가 아니라, 언제 차단이 시작될지를 결정하기 때문입니다.
해당 스크린샷에서 한 가지 더 주목할 점은, 실패한 각 시도가 gemini와 big-pickle에 대해 각각 한 번씩, 총 두 번씩 나타난다는 것입니다. 이는 지난 블로그에서 다룬 폴백(fallback) 규칙이 제 역할을 하고 있음을 보여줍니다. Bifrost가 폴백을 시도했고, 그 폴백마저 동일한 가상 키(virtual key)에 의해 거부되었습니다. 이것이 바로 우리가 원하는 동작입니다.
이제, 우리가 가진 것들을 더 살펴봅시다!
MCP 게이트웨이 (The MCP Gateway)
이것은 제가 더 일찍 시도하지 않은 것이 후회될 정도입니다.
MCP 게이트웨이를 사용하면 모든 MCP를 한 곳에 연결할 수 있으며, 그 후 가상 키를 사용하여 우리의 하네스(harnesses)에 대한 액세스를 선택적으로 허용할 수 있습니다. 따라서 모든 하네스가 각자의 MCP 설정 복사본을 가지고 있는 대신, Bifrost가 이 모든 것을 보유하며, 각 키는 당신이 허용한 부분(slice)만을 볼 수 있게 됩니다.
MCP 서버 라이브ary에는 이미 엄청난 수의 MCP가 존재하며, 제가 이 글을 쓰는 시점 기준으로 487개가 등록되어 있습니다. 클릭 한 번이면 바로 설치할 수 있습니다. 아래 스크린샷에서 볼 수 있듯이, 저는 이미 Context7 MCP를 설치했습니다. 이제 MCP 클라이언트 설정 (MCP Client Configuration) 옵션에서 이를 제 가상 키 (virtual key)에 추가하기만 하면 준비가 끝납니다.

이미 OpenCode 하네스 (harness)에 가상 키를 추가했으므로, 이제 그곳에서 Context7 MCP를 쉽게 사용할 수 있습니다. Bifrost는 설치된 모든 MCP를 단일 엔드포인트인 /mcp로 노출하며, Bearer 토큰으로 전송하는 가상 키가 어떤 도구 (tools)를 반환할지 결정합니다. 따라서 OpenCode 입장에서는 뒤에 얼마나 많은 MCP가 설치되어 있든 상관없이, 하나의 일반적인 원격 MCP 서버처럼 보이게 됩니다.
설정 파일에 추가할 블록은 다음과 같습니다:
{
"mcp": {
"bifrost": {
...
oauth: false 라인은 보기보다 중요합니다. 원격 MCP 서버가 401 응답을 보낼 때 OpenCode는 자체적으로 OAuth 흐름 (OAuth flow)을 시작하려고 시도합니다. 하지만 우리는 대신 고정된 키 (fixed key)로 인증할 것이기 때문에, 여기서는 시도하지 말라고 알려줘야 합니다.
실시간 문서를 가져와야만 답변할 수 있는 질문을 OpenCode에 던져서 테스트해 보고, 도구 호출 (tool calls)이 실제로 Bifrost 측에 나타나는지 확인해 보겠습니다.

결과가 나왔습니다. resolve-library-id에 이어 query-docs라는 두 개의 도구 호출이 있었으며, 둘 다 Context7 서버를 대상으로 했고 모두 성공했습니다. 따라서 MCP는 의도한 대로 완벽하게 작동하고 있으며, 저는 OpenCode에 Context7 설정을 전혀 입력할 필요가 없었습니다.
이제 Bifrost는 프롬프트와 스킬(skills)에 대해서도 비슷한 기능을 제공하므로, 이 부분들도 살펴보겠습니다!
Prompt 및 Skills 저장소
먼저 스킬에 대해 이야기하기 전에, 프롬프트 저장소(Prompt Repository)에 대해 말씀드리겠습니다. 여기서는 다양한 모델을 대상으로 우리의 프롬프트를 테스트하고, 결과를 확인하며, 수정할 수 있습니다. 이는 매우 중요한데, 제가 여기서 제 프롬프트를 철저히 테스트한 후에 스킬로 변환했기 때문입니다.
보시다시피, 저는 언어(language), 검토 초점(review focus), 코드 조각(code snippet)을 입력받아 상세한 리뷰를 제공하고, 코드를 수정해야 할 경우 수정된 코드까지 제공하는 코드 리뷰 프롬프트를 만들었습니다.
여기서 변수(variables)가 정말 편리합니다. 프롬프트 안에 {{ code }}나 {{ language }}와 같이 작성하는 모든 것은 자동으로 인식되어 채울 수 있는 필드로 바뀌기 때문에, 무언가를 시도할 때마다 프롬프트를 다시 작성할 필요 없이 입력값만 변경하면 됩니다.
또한 여기서의 프롬프트는 버전 관리(versioned)가 되는데, 저는 예상하지 못했습니다. 세션에서 계속 편집하다가 실제로 좋은 것이 있으면 버전을 커밋(commit)하게 되고, 따라서 애플리케이션이 나중에 호출하는 것은 5분 전에 우연히 입력하던 내용이 아니라 의도적으로 배포한 버전이 됩니다.
이제 프롬프트 테스트를 마쳤다면, 그것을 스킬로 만들어서 스킬 저장소에 추가할 수도 있습니다.
저는 코드베이스의 버그를 수정하는 데 사용될 수 있는 safe-bug-fix라는 스킬을 만들었습니다. 이것은 일반적인 SKILL.md 파일이며,
스킬을 생성한 후에는 OpenCode도 실제로 스킬을 사용할 수 있도록 설정해야 합니다.
이를 위해 OpenCode 설정(config)에 다음 코드 스니펫을 붙여넣을 수 있습니다:
{
"permission": {
"skill": {
...
여기서 *는 모든 스킬이 허용됨을 의미합니다. 동일한 블록에서 allow와 함께 deny 및 ask를 사용할 수 있고, 키(key)가 와일드카드(wildcard)를 허용하므로, 원한다면 더 까다롭게 설정할 수도 있습니다. 예를 들어 internal-*와 같은 패턴은 거부(deny)하면서 나머지 모든 것은 열어둘 수 있습니다.
그러면 언제든지 OpenCode 내부에서 이를 사용할 수 있습니다!
이제 제가 발견한 다음으로 유용한 기능은 커스텀 로그 헤더(custom log headers)를 설정하는 것입니다. 그럼 그것에 대해 이야기해 봅시다!
커스텀 로그 헤더 (Custom Log Headers)
제가 주로 OpenCode에서 키를 사용하고 있었지만, jcode에서도 사용하기 시작했습니다. 하지만 로그를 통해 어떤 요청이 어떤 하네스(harness)에서 오는지 식별할 수 없었습니다.
그래서 해결책을 찾기로 했고, Bifrost의 로그 설정(Log settings)에서 커스텀 로그 헤더를 생성할 수 있다는 것을 발견했습니다. 설정하는 방법은 매우 간단했습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기


