📝 한줄 요약
MCP 서버의 모든 기능을 터미널 명령어로 쓸 수 있는 aitk CLI를 만들어서, MCP 연결 없이도 팀 스킬을 검색/배포/관리할 수 있게 됐습니다.
바쁘시 면 이것만 읽어도 돼요:
MCP 서버의 15개 도구를 터미널 명령어 15개와 1:1 대응시킨 CLI를 만들었습니다
npm install -g @gpters/aitk한 줄이면 설치 끝CLI 로그인으로 토큰을 한 번 발급받으면 이후 명령어는 바로 실행됩니다
만드는 것보다 기존 버그 찾는 게 더 오래 걸렸습니다 — deploy 하나에 버그 3개가 겹쳐 있었음
Claude Code, OpenCode, Codex, 그리고 이제 터미널에서도 동일한 팀 스킬 경험 제공
🎯 이런 분들께 도움돼요
MCP를 지원하지 않는 AI 도구에서도 팀 스킬을 쓰고 싶은 분
스킬을 만들었는데 배포 과정이 번거로웠던 분
MCP 서버를 운영하면서 "이거 CLI도 있으면 좋겠다"고 생각했던 분
배경: MCP 서버에 CLI 인터페이스를 처음 만들다
이전 글에서 MCP 서버 하나로 Claude Code와 OpenCode를 동시에 지원하는 구조를 소개했습니다. 플러그인을 설치하면 MCP 서버에 자동 연결되고, 에이전트가 스킬을 검색하고 로드하는 방식이었습니다.
이 구조는 대화형 작업에는 매우 편했습니다. 에이전트가 대화 흐름 속에서 필요한 스킬을 찾고 실행할 수 있기 때문입니다.
다만 사용하다 보니 한 가지 아이디어가 떠올랐습니다.
"이 서버 기능을 CLI로도 제공하면 어떨까?"
이유는 단순했습니 다.
에이전트가 도구를 실행할 때는 CLI 형태가 훨씬 다루기 쉽기 때문입니다.
CLI는 구조가 단순합니다.
명령어 실행 → 결과(JSON) 파싱
대화 컨텍스트를 거의 사용하지 않아 토큰 사용량이 가벼움
다른 도구나 에이전트 워크플로우에 도구처럼 연결하기 쉬움
그래서 MCP를 대체하려는 것이 아니라 같은 서버 위에 CLI 인터페이스를 하나 더 추가하기로 했습니다.
웹 애플리케이션 의 구조를 보여주는 다이어그램
이번 글은 MCP 서버에 첫 CLI 클라이언트(aitk)를 처음부터 만든 과정에 대한 기록입니다.
🛠️ 사용한 도구
Claude Code: CLI 전체 개발 및 버그 수정
모델: Claude Opus 4.6
빌드: Bun (번들링) + npm (배포)
백엔드: 기존 MCP 서버 (Next.js + Neon PostgreSQL)
🔧 작업 과정
MCP 서버에 이미 API가 있었다 — CLI 자리는 명확했다
MCP 서버에는 원래 두 가지 API가 있었습니다. 플러그인이 쓰는 JSON-RPC 2.0과, 간단한 호출용 Simple REST API.
꼭 두 개가 "필수"였던 건 아닙니다. 기술적으로는 CLI도 JSON-RPC만으로 전부 구현할 수 있습니다. 다만 당시에는 (1) 플러그인/MCP 경로와 무관하게 가벼운 엔드포인트로 빠르게 호출할 수 있게 하려는 목적, (2) 일부 액션은 tools/call 래핑 없이 HTTP 요청 한 번으로 끝내기 좋다는 이유로 REST를 함께 두었습니다.
이미 서버 로직이 API로 분리되어 있었기 때문에, CLI는 사실상 얇은 클라이언트만 만들면 되는 구조였습니다.
그래서 CLI에서는 기존에 있던 인터페이스를 그대로 활용했습니다.
단순 호출/응답이 깔끔한 작업은 REST
MCP 도구와 1:1로 붙는 작업은 JSON-RPC의
tools/call
즉 "REST가 있어서 CLI가 가능했다"기보다는, 이미 있던 두 인터페이스 중 작업 성격에 맞는 쪽을 골라 쓴 것에 가깝습니다.
어떤 도구를 쓰든, 심지어 아무 도구 없이 터미널만 있어도, 동일한 팀 스킬에 접근할 수 있습니다.
cls의 다이어그램
CLI를 만들다 보니 MCP 도구 6개가 빠져 있었다
처음에는 기본 명령어만 구현했는데, MCP 서버의 tools 목록과 하나씩 비교해 보니 CLI에 아직 구현되지 않은 도구들이 있다는 걸 발견했습니다.
MCP 도구
CLI
상태
semantic_search
aitk search
있음
get_plugin_content
aitk get
있음
deploy_skill
aitk deploy
있음
check_updates
aitk updates
있음
undeploy_skill
—
없음
suggest_improvement
—
없음
list_suggestions
—
없음
resolve_suggestion
—
없음
add_files
—
없음
remove_files
—
없음
스킬 삭제, 개선 제안, 파일 관리 — 전부 MCP에서는 되는데 CLI에서는 안 되는 기능들이었습니다.
6개 명령어를 한 번에 추가했다
패턴은 단순합니다.
토큰 확인
JSON-RPC
tools/call호출응답 JSON 파싱
6개 명령어 파일을 병렬로 만들었습니다.
aitk undeploy my-old-skill
aitk suggest --plugin-id code-reviewer --title "보안 체크 추가" --description "OWASP top 10 포함"
aitk suggestions --plugin-id my-skill --status pending
aitk resolve --suggestion-id abc123 --action accept --comment "좋은 제안!"
aitk add-files --id my-skill script.mjs reference.md
aitk remove-files --id my-skill --files "old-script.mjs"
만드는 건 금방이었습니다. 진짜 문제는 그다음이었죠.
삽질 기록: deploy 하나에 버그가 3개 겹쳐있었다
aitk deploy를 실행했더니 동작하지 않았습니다. 원인을 찾아보니 세 가지 문제가 동시에 있었습니다.
첫 번째: API 라우트 validation에 deploy가 없었습니다.
route.ts의 switch에 case 'deploy'가 빠져 있어서 400 에러가 발생했습니다.
두 번째: create_plugin은 관리자 전용이었습니다.
처음에는 deploy를 action=create로 매핑했는데 이 도구는 admin 전용이라 권한 에러가 났습니다.
세 번째: 응답 포맷이 JSON이 아니었습니다.
deploy_skill 핸들러가 JSON 뒤에 사람이 읽는 텍스트를 붙여서 JSON.parse()가 실패했습니다.
웹이나 MCP에서는 경로가 달라 문제가 드러나지 않았는데, CLI에서는 한 번에 호출되면서 세 개가 동시에 터졌습니다.
문제를 하나씩 고치고 나니 흐름은 이렇게 정리됐습니다.
aitk deploy → REST ?action=deploy → deploy_skill 핸들러 → DB 저장 + 임베딩 생성updates 명령어도 고장나 있었다
aitk updates를 실행했더니 다음 오류가 발생했습니다.
Missing required field: installationscheck_updates MCP 도구는 installations 배열이 필수인데 CLI에서는 {}를 보내고 있었습니다.
그래서 로컬에 설치된 스킬을 ~/.claude/skills/에서 자동으로 탐지해서 배열로 보내도록 수정했습니다.
설치된 스킬이 없으면 API 호출 없이 바로 안내 메시지를 출력합니다.
전체 15개 명령어를 E2E 테스트했다
마지막으로 전체 흐름을 실제로 테스트했습니다.
aitk deploy → aitk search → aitk get → aitk add-files → aitk suggest → aitk suggestions → aitk resolve → aitk remove-files → aitk undeploy15개 명령어가 모두 정상 동작했습니다.
이후 npm에 @gpters/[email protected]로 퍼블리시했습니다.
이번 작업은 대부분 Claude Code와 함께 진행했습니다.
💬 이 과정에서 배운 AI 활용 팁
효과적이었던 것
MCP 서버를 먼저 만들면 CLI는 거의 공짜입니다. 비즈니스 로직이 이미 서버에 있으니까 CLI는 "HTTP 호출 + JSON 파싱"만 하면 됩니다. 6개 명령어 추가에 실제 코딩 시간보다 기존 버그 찾는 시간이 더 길었습니다.
stdout은 JSON, stderr는 사람용으로 나눠야 합니다.
stdout에는 JSON만 내보내고(AI가 파싱),stderr에는 상태 메시지를 내보냅니다. 이렇게 하면aitk search "키워드" | jq '.[]'같은 파이프라인이 자연스럽게 됩니다.병렬 작업을 적극 활용했습니다. 6개 명령어 파일을 한 번에 생성하고, 5개 명령어를 동시에 테스트하는 식으로 진행했습니다. Claude Code가 병렬 도구 호출을 잘 처리합니다.
이렇게 하면 안 돼요
API를 CLI로 감싸면 숨은 버그가 드러납니다. 웹이나 MCP에서 잘 되던 것도 CLI에서 호출하면 안 되는 경우가 많습니다. route validation 누락, 응답 포맷 불일치, 필수 파라미터 미전달 — 전부 CLI를 만들면서 발견한 것들입니다.
VERSION을 하드코딩하면 안 됩니다.
bin/aitk.ts에 VERSION 상수를 직접 넣어뒀더니,package.json만 올리고 소스는 안 바꿔서 빌드된 JS에 옛날 버전이 박히는 문제가 생겼습니 다.
🌍 다른 업무에 적용한다면?
MCP 서버 위에 여러 인터페이스를 얹는 패턴은 다른 서비스에도 그대로 적용됩니다.
이 패턴은 "MCP 서버가 있는데 CLI도 필요한" 모든 상황에 적용됩니다:
사내 API 문서 검색: MCP 서버에 API 스펙을 넣어두고, CLI로도 검색 가능하게
팀 템플릿 관리:
aitk deploy처럼 CLI 한 줄로 팀 공유 템플릿 배포온보딩 자동화: 새 팀원이
npm install -g @gpters/aitk && aitk login두 줄이면 팀 스킬 전체 접근 가능
핵심은 "MCP 서버 하나 + 도구별 클라이언트 껍데기"라는 구조입니다. CLI는 가장 가벼운 껍데기고, 새 도구가 나와도 클라이언트만 추가하면 됩니다.
🚀 앞으로의 계획
현재 @gpters.org 팀 외에도 누구나 사용할 수 있습니다. Welcome 페이지에서 플러그인 및 CLI 연결 방법을 확인하세요!
코딩 에이전트를 위한 AI 툴킷