
https://github.com/g-hyeong/MCP-AidFlow
GitHub - g-hyeong/MCP-AidFlow
Contribute to g-hyeong/MCP-AidFlow development by creating an account on GitHub.
github.com
개발 동기
회사에서 개발 AX 프로젝트를 리딩하게 되면서, 첫 번째로 진행한 게 사내 개발 사이클에 맞춘 MCP 서버 구축이었다. MCP 서버를 통해, AI Code Assistant를 통한 개발 시 사내 개발 워크플로우에 맞게 진행되도록 유도해주는 작업이었는데, 기대 이상으로 만족스러운 성능을 보였어서, 퇴사하고 나니 아래와 같은 생각이 들었다.
"이거 충분히 어떤 개발이든 적용시킬 수 있을 것 같은데?"
사내 MCP는 우리 팀의 특정 도구와 컨벤션에 맞춰져 있었다. 그러다 보니 범용성이 없었고, 다른 프로젝트에 가져다 쓸 수가 없었다. 그래서 좀 더 일반적인, 어떤 프로젝트에든 붙일 수 있는 개발 워크플로우 MCP를 만들기로 했다.
이름은 aidflow. AI Development Flow의 약자인데, AI와 함께 개발하는 흐름을 잡아주는 도구라는 의미를 담았다. 세션 기반으로 작업을 관리하면서, 핵심 고안점은 두 가지였다.
- 병렬 작업 용이: 여러 작업을 동시에 진행할 때 세션 단위로 격리
- 새 대화에서의 빠른 컨텍스트 복구: 대화가 끊겨도 AI가 이전 상태를 즉시 파악
MCP란?
MCP는 AI 클라이언트(Claude Code 같은)에 외부 도구를 연결해주는 프로토콜이다. 쉽게 말하면, AI한테 "너 이 도구 쓸 수 있어"라고 알려주는 표준 인터페이스.
MCP 서버를 만들면 AI 클라이언트에 도구(Tool)를 등록할 수 있고, AI는 상황에 맞게 그 도구를 호출한다.
왜 MCP인가
aidflow를 스킬(프롬프트 확장)이 아니라 MCP로 만든 이유가 있다.
MCP의 도구는 실제 코드를 실행할 수 있다. 파일 시스템을 관리하고, 조건을 분기하고, 상태를 추적하는 등 논리적인 처리가 가능하다. 스킬은 결국 프롬프트를 주입하는 것이라 AI의 "의지"에 의존하지만, MCP 도구는 코드 레벨에서 로직을 강제할 수 있다.
예를 들어, "세션 없이는 plan을 만들 수 없다"는 제약을 스킬로는 "부탁"밖에 못 하지만, MCP 도구에서는 코드로 거부할 수 있다. 이 차이가 결정적이었다.
AI 행동 유도를 위한 3 Layer
MCP에는 AI의 행동을 유도할 수 있는 레이어가 세 가지 있다.
- INSTRUCTIONS: 서버가 AI에게 보내는 전역 지시문. MCP 서버가 연결되면 매 대화에 주입된다.
- Tool Description: 각 도구의 설명. AI가 "이 도구를 언제 써야 하는지" 판단하는 근거.
- Handler Return Value: 도구 실행 후 AI에게 돌아가는 결과 메시지.
이 세 가지를 어떻게 설계하느냐에 따라, AI가 내가 의도한 워크플로우를 따를지 말지가 결정된다. aidflow를 만들면서 가장 많이 고민한 부분이 바로 이 부분이었다.
핵심 설계: AI의 행동 유도
MCP Instructions
MCP Instructions은 MCP 서버가 연결될 때 AI 클라이언트에 주입되는 전역 지시문이다. aidflow에서는 이걸 "AI의 기본 동작 프로토콜"로 설계했다.
## On Every New Conversation (CRITICAL)
Before doing ANYTHING, check for existing context:
1. Run `session list` to check for active sessions.
2. If active session exists:
- Run `session status` to understand current progress.
- Read the session's plan.md for full task context.
- Inform the user: "Found active session '{name}'. Resuming where you left off."
핵심은 (CRITICAL)이라는 키워드와, "Before doing ANYTHING"이라는 문구다. AI 모델은 이런 강조 표현에 꽤 잘 반응한다. 실제로 이 INSTRUCTIONS 덕분에, 새 대화를 열면 AI가 먼저 session list를 호출하고, 활성 세션이 있으면 자동으로 상태를 파악한다. 대화 간 컨텍스트 복구가 이렇게 구현된다.
또 하나 신경 쓴 건, 워크플로우의 전체 흐름을 INSTRUCTIONS에 명시한 것이다.
## Development Cycle
1. `session create` to start work. Read SPEC.md for project conventions.
2. `plan create` for medium/large tasks.
3. Work using Claude Code's native tools. Follow plan.md as the execution spec.
4. `/review` before completing.
5. `session complete` to archive.
이렇게 해두면 AI가 "다음에 뭘 해야 하지?"를 판단할 때 이 흐름을 참고한다. 개별 도구의 Handler에서 매번 "다음은 이거야"라고 반복할 필요가 줄어든다.
Tool Description
각 도구의 Description은 AI가 "이 도구를 호출할지 말지" 결정하는 데 쓰인다. 여기서 중요하게 생각한 건 최대한 짧게 유지하는 것이었다.
Tool Description은 항상 AI의 context를 차지한다. MCP 서버가 연결되어 있는 한, 매 요청마다 이 내용이 context에 들어간다. 너무 길면 context를 불필요하게 점유하는 것도 문제지만, 지저분하게 긴 설명이 계속 context에 살아있으면 AI의 판단을 오염시킬 수도 있다고 생각했다.그래서 Description은 핵심 정보만 남기고, 자세한 안내는 Handler Return Value에서 하도록 분리했다.
그러면서도, Description에 AI의 행동 규범을 심을 수 있다는 점은 적극 활용했다.
예를 들어, guide 도구의 Description:
Never auto-select guides. Always ask the user which guide they want to read.
이렇게 "Never", "Always" 같은 강한 표현을 써두면, AI가 가이드를 임의로 선택하지 않고 사용자에게 물어본다. 짧지만 행동 규범이 명확한, 그런 Description을 지향했다.
Handler Return Value
도구가 실행된 후 AI에게 돌아가는 메시지. 여기에 "다음에 뭘 하라"는 힌트를 넣을 수 있다. Description과 달리, Handler Return Value는 도구가 호출된 시점에만 한 번 나오기 때문에 좀 더 상세하게 써도 된다.
개발을 하면 반복되는 상황이 있는데, 그때마다 정성들여 프롬프트를 매번 입력하기란 쉽지 않다. AI 시대에 가장 중요한 것은 어쩌면 개발자들의 손가락 건강이다.
Handler의 응답은, 적재적소에 시스템 프롬프트를 context에 부여해주는 역할을 한다.
session create의 Handler를 보면:
if (hasSpec) {
lines.push(
"Read SPEC.md first for project conventions and engineering foundations."
);
} else {
lines.push(
"**No SPEC.md found.** Use AskUserQuestion to ask the user:",
'"SPEC.md does not exist yet. Run /spec first, or proceed without it?"',
);
}
SPEC.md가 없으면, AI에게 "사용자한테 이렇게 물어봐"라고 질문 템플릿까지 제공한다. AI가 어떤 질문을 해야 할지 고민할 필요 없이, 그대로 따라가면 되도록 설계한 것이다.
session status에서는 plan의 체크박스를 파싱해서 다음 할 일을 자동으로 제안한다.
const nextItem = plan.items.find((item) => !item.completed);
if (nextItem) {
lines.push(`Next suggested item: "${nextItem.text}"`);
}
plan.md에 마크다운 체크박스(- [ ], - [x])를 쓰면, Handler가 정규표현식으로 파싱해서 완료/미완료를 추적하고, 다음 미완료 아이템을 AI에게 알려준다. AI는 이 메시지를 보고 자연스럽게 다음 작업을 이어간다.
각 Layer 별 체감 지침 강제력
여기서 솔직하게 말하자면, 이 세 가지 레이어의 실제 강제력은 다르다.
개발하면서 테스트를 꽤 많이 돌려봤는데, 체감상 이랬다:
| 레이어 | 강제력 | 특징 |
|---|---|---|
| INSTRUCTIONS | 중-강 | 매 대화에 주입되니 꽤 잘 따름 |
| Tool Description | 중 | 도구 선택 시 참고하지만, 세부 규칙은 무시할 때도 있음 |
| Handler Return Value | 약 | "제안" 수준. context가 길어질 수록 강제력은 떨어지는 느낌이지만, 그 상황에서는 사용자의 직접 요청도 마찬가지 |
| 코드 레벨 거부 (에러) | 강 | 강제가 안 될 수 없음 |
Handler의 리턴 메시지에 "다음에는 이 도구를 쓰세요"라고 넣어도, AI가 다른 판단을 하면 그냥 무시한다. 결국 가장 확실한 건 코드 레벨에서 아예 거부하는 것이었다.
그래서 aidflow에서는 핵심적인 워크플로우 제약은 코드로 강제했다.
// 초기화 안 됐으면 모든 도구 거부
if (!isInitialized()) {
return "devpilot is not initialized. Run `init` first.";
}
// 세션 없으면 plan 거부
const sessionName = resolveSession(input.session);
if (!sessionName) {
return "No session found. Create one with `session create` first.";
}
// plan 미완료 상태에서 세션 종료 시도 -> 거부
if (plan.completed < plan.total && !input.force) {
return `Session has incomplete plan items (${plan.completed}/${plan.total}).`;
}
init 안 했으면 다른 도구가 아예 안 되고, 세션 없으면 plan을 만들 수 없고, plan 미완료 상태에서 세션을 종료하려면 force: true를 명시적으로 넣어야 한다. 이런 식으로 "AI가 제안을 무시해도 워크플로우가 깨지지 않는" 구조를 만들었다.
설계할 때 고민했던 건, 메시지 유도와 코드 강제 사이의 균형이었다. 모든 걸 코드로 막아버리면 유연성이 떨어지고, 다 메시지로만 유도하면 AI가 무시할 수 있다. 그래서 "워크플로우 순서"는 코드로 강제하고, "사용자에게 물어봐라" 같은 부분은 메시지로 유도하는 식으로 나눴다.
컨텍스트의 계층화: SPEC.md & plan.md
aidflow에서 컨텍스트를 두 개의 계층으로 나눈 건 의도적인 설계다.
- SPEC.md: 프로젝트 전체의 공통 컨텍스트. 기술 스택, 아키텍처, 코딩 컨벤션 등.
- plan.md: 각 작업(세션)의 개별 컨텍스트. 이번 작업의 목표, 범위, 체크리스트.
위와 같이 구성한 이유는, AI에게 프로젝트 컨텍스트를 줄 때 "이 프로젝트는 어떤 프로젝트인지"와 "지금 내가 하는 작업이 뭔지"는 성격이 다르기 때문이다. SPEC.md는 프로젝트가 살아있는 한 바뀔 일이 적지만, plan.md는 작업마다 생겼다가 아카이빙된다.
이 파일들의 형식을 전부 마크다운으로 한 이유도 있다. 마크다운은 AI가 이해하기 용이하면서도, 사람이 직접 읽고 편집하고 공유하기도 편한 포맷이다. JSON으로 했으면 AI 파싱은 쉬웠겠지만, 사람이 보기 불편했을 거다. 마크다운은 그 중간 지점에 있다.
가이드 문서 시스템을 넣은 것도 같은 맥락이다. .devpilot/guides/에 프로젝트별 가이드를 넣어두면, AI가 필요할 때 참고할 수 있다. 이건 SPEC.md처럼 항상 참조하는 건 아니고, 필요한 시점에 선택적으로 읽는 구조다. guide 도구의 Description에 "Never auto-select, Always ask the user"를 넣은 것도, 불필요한 가이드까지 context에 올리지 않으려는 의도다.
세션 관리
세션 시스템의 구조는 단순하다.
.devpilot/
sessions/
feature-auth/
meta.json # 세션 메타데이터
plan.md # 작업 계획
history/
250304_feature-auth/ # 아카이빙된 세션
meta.json
plan.md
report.md
세션을 만들면 meta.json이 생기고, 작업 계획은 plan.md에 마크다운으로 저장된다. 세션이 끝나면 history/로 이동한다. 파일 시스템에 상태를 저장하니까, 대화가 아무리 끊겨도 상태가 유지된다. DB를 따로 둘 필요도 없고, 마크다운 파일이니까 사용자가 직접 편집할 수도 있다.
컨텍스트 복구
새 대화에서의 컨텍스트 복구는 이렇게 동작한다:
- 새 대화 시작
- INSTRUCTIONS에 의해 AI가
session list호출 - 활성 세션 발견
session status호출 -> meta.json 읽기 + plan.md 체크박스 파싱- AI가 "현재 진행도 3/7, 다음 할 일: XXX"를 파악
- 바로 이어서 작업
plan.md의 체크박스를 Handler에서 정규표현식으로 파싱해서 진행도를 계산한다.
const checkboxes = content.match(/- \[[ x]\] .+/g) ?? [];
const items = checkboxes.map((line) => ({
text: line.replace(/- \[[ x]\] /, ""),
completed: line.startsWith("- [x]"),
}));
- [x] 완료된 일과 - [ ] 남은 일은 AI가 자연스럽게 파싱할 수 있는 형식이면서, 코드로도 추적이 가능한 포맷이다.
git worktree를 통한 병렬 작업
세션마다 git worktree를 만들 수 있게 했다. 이건 선택적 기능인데, 병렬로 여러 작업을 할 때 세션 간 코드 변경이 충돌하지 않게 격리해준다.
const branch = `${config.worktree.branch_prefix}${sessionName}`;
execSync(`git worktree add -b "${branch}" "${worktreePath}"`);
세션을 만들 때 worktree: true를 넘기면, 새 브랜치와 워킹 디렉토리가 자동으로 생긴다. 세션이 끝나면 AI가 사용자에게 "이 브랜치 머지할까? 워크트리 삭제할까?"를 묻는다. 변경 파일 추적도 worktree 경로에서 git diff를 실행해서 자동으로 기록한다.
HITL
aidflow에서 가장 신경 쓴 원칙이 HITL(Human-in-the-Loop)이다. AI가 단독으로 결정하면 안 되고, 핵심적인 의사결정은 사용자의 확인을 거쳐야 한다.
plan 단계를 예로 들면, AI가 plan을 만들 때 단번에 쭉 쓰는 게 아니라, 라운드 기반으로 사용자에게 질문한다.
Round 1 - Core Intent: 목표가 뭔지, 어떤 문제를 해결하는지
Round 2 - Scope & Constraints: 범위와 제약 조건
Round 3 - Details & Edge Cases: 세부 사항과 엣지 케이스
하지만 여기서 중요한 건, 사용자가 일일이 선택하고 싶지 않을 때도 잘 동작해야 한다는 점이다. 그래서 각 라운드마다 "AI auto-research" 옵션을 넣었다.
For each round, always include an 'AI auto-research' option.
When the user is unsure, offer:
- "I'm not sure / Let AI research best practices"
If selected, use WebSearch to find latest stable best practices.
사용자가 기술 스택에 익숙하지 않거나, 그냥 AI한테 맡기고 싶을 때 이 옵션을 선택하면 AI가 WebSearch로 최신 Best Practice를 찾아서 추천해준다. "사용자가 주도하되, 원하면 AI가 주도할 수 있는" 구조를 만든 거다.
session complete에서도 같은 원칙을 적용했다. 세션을 끝내면 AI가 바로 아카이빙하는 게 아니라:
lines.push(
"Post-completion actions:",
"1. Run /report to generate a completion report.",
'2. Ask the user: "Did this session introduce new patterns or conventions?',
' If so, run /spec to update SPEC.md."',
);
"/report 돌려볼까?", "새로운 패턴이 생겼으면 SPEC.md 업데이트할까?"를 사용자에게 묻는다. AI가 알아서 하는 게 아니라, 사용자가 최종 결정권을 갖는 구조다.
MCP vs Skills
aidflow에는 MCP 도구 4개와 Claude Code 스킬 3개가 있다.
| 구분 | 이름 | 역할 |
|---|---|---|
| MCP 도구 | init, session, plan, guide | 워크플로우 골격 |
| 스킬 | /spec, /review, /report | 특정 시점의 작업 |
언뜻 보면 "다 MCP 도구로 만들면 되는 거 아닌가?" 싶을 수 있다. 실제로 처음에는 그렇게 생각했다. 그런데 MCP 도구와 스킬의 성격을 이해하고 나니, 역할을 나누는 게 맞다는 판단이 들었다.
MCP 도구는 AI가 자율적으로 호출하는 것이다. AI가 상황을 판단해서 "지금 이 도구를 써야겠다"고 결정한다. 반면 스킬은 사용자가 명시적으로 트리거하는 것이다. /review라고 입력하면, 미리 작성된 프롬프트가 AI에게 주입되면서 해당 작업을 수행한다.
이 차이가 중요한 이유가 있다. session이나 plan처럼 워크플로우의 골격이 되는 기능은 AI가 자율적으로 호출해야 자연스럽다. 새 대화를 열었을 때 AI가 알아서 session list를 호출하는 것처럼. 반면 /review나 /report는 "지금 이 시점에서 해야 한다"는 판단이 사용자에게 있는 편이 낫다. 워크플로우의 필수 단계가 아니라 선택적 품질 게이트니까.
그리고 스킬에는 MCP 도구에는 없는 장점이 있다. 스킬은 프롬프트 자체이기 때문에, AI의 행동을 아주 상세하게 지정할 수 있다. /review 스킬에는 "코드 리뷰 시 plan.md와 대조하라", "SPEC.md 컨벤션을 확인하라", "TODO/FIXME를 찾아라" 같은 구체적인 체크리스트가 들어있다. 이런 수준의 상세 지침을 MCP 도구의 Description에 넣으면 context 오염이 되지만, 스킬은 호출 시에만 주입되니까 문제가 없다.
session complete의 Handler에서 "/review 한번 돌려보세요"라고 제안은 하지만, 강제하지는 않는다. 이것도 MCP 도구(제안)와 스킬(사용자 트리거)의 역할 분담을 의도한 것이다.
느낀점
MCP 서버를 마치 Agent처럼 구현할 수 있다
이런 MCP 서버 특성상, 코드를 짜는 것보다도, AI에게 보내는 메시지를 설계하는 것이 중요하다. aidflow가 다루는 건 결국 SDLC(Software Development Life Cycle)다. 그 사이클 안에서, 각 단계마다 AI Agent가 적절한 방향으로 움직이도록 지침을 설계해야 한다. INSTRUCTIONS의 문구 하나, Description의 키워드 하나가 AI의 행동을 바꾼다. 각각의 tool이 연계되는 만큼, 전체적인 목표를 바라보며 그 틀 안에서 tool들이 상호작용 하도록 개발했다.
AI가 제안을 무시할 수 있다는 것은 기본적인 사실
결국 이 도구는 개발 시에 사용된다. 개발 중에는 수많은 코드를 읽고 쓰면서 context가 매우 방대해진다. 거기에 MCP의 시스템 프롬프트와 가이드들까지 더해지면 더 그렇다. context가 커질수록, "지침"으로서 주어지는 것들의 강제력은 떨어질 수밖에 없다. 그래서 통제가 필요한 영역은 MCP의 코드 레벨로 격리해뒀고, 나머지 부분은 "강제"가 아닌 "유도"로 방향성을 잡았다. "강제"가 아님에도 사이클이 온전하게 구성될 수 있도록 설계한 부분이 핵심이었다고 생각한다.
파일 시스템과 MCP의 조합
파일 시스템은 작업 관리에 아주 용이한 수단이다. 세션 메타데이터, 작업 계획, 진행 상태를 파일로 관리하면 DB 없이도 상태 추적이 가능하고, git으로 버전 관리도 되고, 사람이 직접 편집하거나 팀원과 공유할 수도 있다. 그리고 MCP는 그 파일 시스템을 작업 사이클 안에서 통제할 수 있는 효과적인 수단이다. 세션 생성 시 디렉토리를 만들고, 상태를 확인할 때 파일을 읽고, 완료 시 아카이빙하는 -- 이 일련의 흐름을 코드로 관리할 수 있다는 게 MCP의 가치라고 생각한다.
GitHub: https://github.com/g-hyeong/MCP-AidFlow
Claude Code 사용 방법
claude mcp add aidflow --scope user -- npx -y aidflow'Projects > MCP' 카테고리의 다른 글
| 손쉽게 로깅 관리하기: Logging Advisor MCP 서버 개발기 (2) | 2025.08.31 |
|---|