DCF 평가를 위한 Peer-Group-Search MCP 서버 개발
최근 클로드 엑셀(Claude for Excel) 을 활용한 DCF 평가 자동화 워크플로우를 구축하고 있습니다. DCF 평가를 하다보면 평가대상회사의 유사회사(Peer Group)을 선정하고 해당 유사회사에 대한 데이터를 수집하는 것에 상당한 시간을 쓰게 됩니다. 이번 글에서는 이를 자동화하기 위해 직접 구축한 MCP 서버의 개발 과정을 소개하고자 합니다.
최근 클로드 엑셀(Claude for Excel) 을 활용한 DCF 평가 자동화 워크플로우를 구축하고 있습니다. DCF 평가를 하다보면 평가대상회사의 유사회사(Peer Group)을 선정하고 해당 유사회사에 대한 데이터를 수집하는 것에 상당한 시간을 쓰게 됩니다. 이번 글에서는 이를 자동화하기 위해 직접 구축한 MCP 서버의 개발 과정을 소개하고자 합니다.
1. 개발 동기
가치평가(Valuation) 실무에서 DCF(현금흐름할인법) 모델을 구축할 때, 사전 작업 중 가장 공수가 많이 드는 작업은 외부 데이터 수집입니다. 특히 자기자본비용(Ke) 산정을 위해 유사회사(Peer Group)를 선정하고, 그들의 재무 데이터를 하나하나 수집하는 과정은 그야말로 '단순 노가다'에 가깝습니다.
실제로 국내 소재 기업 기준으로 유사회사 한 곳을 조사할 때마다 다음과 같은 지표들을 일일이 찾아야 합니다.
- 베타(Beta): 블룸버그 혹은 한국공인회계사회(KICPA) 등에서 제공하는 베타값 조회
- 이자부부채(IBD): DART 사업보고서를 열어 최근 재무제표 상 이자발생부채 및 비지배지분 금액 조회
- 시가총액: 평가기준일 기준의 주가와 유통주식수 조회
보통 한 프로젝트당 유사회사가 적게는 10개 내외에서 많게는 2~30개까지 선정되는데, 만약 평가해야 할 대상 회사가 여러 곳이라면 확인해야 할 데이터 포인트는 순식간에 수백 개로 불어납니다. 이를 해결하기 위해, 클로드가 직접 이 데이터들에 접근할 수 있는 MCP 서버를 개발하게 되었습니다.
2. 기술 스택
현재 널리 쓰이는 MCP 서버들은 주로 로컬 NodeJS나 Python 환경에서 작동하는 경우가 많습니다. 하지만 제 목표는 Claude Excel 등 외부 클라이언트에서 웹을 통해 언제든 안정적으로 접근하는 것이었습니다.
이를 위해 구성한 기술 환경은 다음과 같습니다.
| 구분 | 사용 기술 및 도구 |
|---|---|
| 웹 프레임워크 | Next.js 15 |
| 배포 환경 | Vercel (Serverless Edge 환경) |
| MCP 연동 | @modelcontextprotocol/sdk, mcp-handler |
| 언어 및 파싱 도구 | TypeScript, adm-zip(DART 원문 압축), fast-xml-parser |
3. 주요 기능
본 MCP 서버는 방대한 금융 데이터를 효율적으로 처리하기 위해 **오프라인 캐시(Pre-collected)**와 실시간 API(Live) 조회를 유기적으로 결합하여 제공합니다.
3.1. 캐시 기반 데이터 조회 (고성능/토큰 최적화)
실제로 대부분의 가치평가 데이터는 기준일이 정해지면 변하지 않는 확정 수치입니다. 사전에 정제되어 서버에 적재된 데이터를 사용함으로써 응답 속도를 높이고 LLM의 컨텍스트 토큰 소모를 획기적으로 줄여줍니다.
| 도구명 | 주요 기능 및 특징 |
|---|---|
valuation_get_data | 가치평가 핵심 데이터 통합 패키지. 베타, IBD, 주식수, 주가를 한 번에 응답합니다. **각 분기말(3/31, 6/30, 9/30, 12/31)**의 경우 사전에 캐시된 데이터를 우선 조회하며, 이외의 경우 실시간 API를 통해 조회합니다. |
get_business_content | '사업보고서 - 주요 제품 및 서비스' 원문 추출. 방대한 사업보고서 내용 중 유사 BM 판단에 필요한 핵심 섹션만 마크다운으로 추출하여 캐싱해 두었고, 이를 조회합니다. |
search_by_industry | 업종별 상장사 검색. 전체 상장사의 표준산업분류(KSIC) 매핑 데이터를 기반으로, 특정 업종의 Peer Group 리스트를 즉시 반환합니다. |
search_stock | 기업 정보 및 코드 매핑. 종목명/코드와 DART 고유번호 간의 매핑 정보를 오프라인에서 조회하여 기업 검색의 속도를 높입니다. |
3.2. 실시간 API 연동 도구 (최신 데이터 확보)
최신 공시 기록이나 시장 지표 등 실시간성이 중요한 항목은 관련 외부 기관의 API를 통해 직접 데이터를 가져옵니다.
| 도구명 | 주요 기능 및 특징 |
|---|---|
kicpa_get_beta | 한공회 실질/조정베타. 한국공인회계사회 베타조회 서비스를 연동하여 공신력 있는 베타 데이터를 조회합니다. |
dart_get_financials | DART XBRL/재무 데이터. OpenDART API를 실시간 호출하여 최신 공시 시스템상의 재무 수치를 가치평가 목적에 맞춰 추출합니다. |
naver_get_market_data | 실시간 시장 데이터. 네이버금융을 통해 현재가, 시가총액, PER, PBR 등 시장 상황에 따라 실시간으로 변하는 지표를 조회합니다. |
4. 사용 방법
구축된 MCP 서버를 실제 업무 환경(Claude Desktop 또는 Claude for Excel)에서 사용하는 방법입니다.
4.1. 커스텀 MCP 서버로 등록하기
별도의 로컬 환경 구축 없이, 배포된 서버의 URL을 통해 커스텀 MCP로 등록할 수 있습니다.
- Claude 설정: Claude 데스크톱 앱 혹은 웹에서
사용자 지정>커넥터>커스텀 커넥터 추가메뉴를 통해 등록합니다. - URL:
https://kicpa-beta-mcp.vercel.app/api/mcp(Vercel 배포 주소)
4.2. 클로드 엑셀(Claude for Excel) 등에서 요청하기
서버가 성공적으로 연결되면 채팅창에서 아래와 같이 자연어로 유사회사 데이터 수집을 요청할 수 있습니다.
- 유사회사 리스트 출력: "평가대상회사와 동일한 업종인 회사 리스트를 출력하고 주요 제품 및 서비스를 요약해줘."
- 데이터 수집 및 정리: "유사기업으로 선정된 회사들에 대해 20251231 기준 베타, 이자부부채, 비지배지분, 세전이익, 시가총액 데이터를 찾아서 표로 정리해줘."
5. 주요 이슈사항
개발 과정에서 여러 난관에 부딪혔습니다. 특히 인증 문제와 LLM이 한 번에 처리할 수 있는 정보량(Token)의 한계가 주요 허들이었습니다. 어떻게 이 문제들을 풀어나갔는지 살펴보겠습니다.
5.1. 토큰 효율화를 위한 데이터 정제 및 캐싱 전략
개발 초기 단계에서 마주한 가장 현실적인 난관은 토큰(Token) 사용량이었습니다. 클로드 엑셀이 MCP 서버를 호출하여 작업을 수행하는 과정에서 예상보다 훨씬 많은 토큰이 소모되는 문제가 발생했습니다.
상황
기업 가치평가를 위해서는 베타, 이자부부채(IBD), 시가총액 등 다양한 데이터를 여러 API로부터 각각 가져와야 합니다. 수많은 API를 호출하고 결과를 해석하는 과정에서 컨텍스트 토큰이 기하급수적으로 늘어나 성능 저하로 이어졌습니다. 또한, 유사회사 검증을 위해 DART 보고서 원문 전체를 LLM 모델에 전달하려 했으나, 방대한 XML 구조로 인해 토큰 초과 오류와 파싱 에러가 빈번했습니다.
해결 방법
이를 해결하기 위해 **서버 측 데이터 캐싱(Caching)**과 텍스트 정제(Refinement) 전략을 통합하여 도입했습니다.
- 1. 데이터 통합 및 캐싱: 실시간 조회가 필요 없는 재무 데이터 등을 서버 엔진에서 미리 수집하여 JSON 형태로 적재했습니다. 클로드가 개별 API를 수십 번 호출하는 대신, 정제된 데이터를 한 번의 호출(
valuation_get_data)로 묶어 받아가도록 구조를 변경하여 토큰 효율성을 극대화했습니다. - 2. 사업보고서 파싱 최적화: 보고서 전체가 아닌 핵심 섹션인 "II. 사업의 내용" 챕터만 추출하는 자체 파서(
document-parser.ts)를 구현했습니다. 특히 복잡한 HTML 표 형식을 마크다운 표로 변환하고 불필요한 태그를 제거하여 토큰 사이즈를 획기적으로 줄였습니다.
5.2. 회사마다 다른 XBRL Taxonomy
valuation_get_data 구현 중 가장 까다로웠던 항목은 **이자부부채(Interest-Bearing Debt, IBD)**였습니다. 기업마다 XBRL 계정 구조와 택소노미 확장 방식이 달라 단순 계정명 매칭으로는 데이터 추출의 한계가 컸습니다.
상황
처음에는 fnlttSinglAcntAll API로 받은 계정 레벨 데이터를 단순히 표준 IFRS 계정 ID 화이트리스트 (ifrs-full_ShorttermBorrowings, ifrs-full_LongtermBorrowings, dart_BondsIssued 등)로 매칭해서 뽑았습니다. 삼성전자·현대차 같은 대형사는 이 방식이 통했지만, 중소형사로 내려가면 다음과 같은 문제가 줄줄이 터졌습니다.
- 계정 ID가 회사별 커스텀: 어떤 회사는 "유동성장기차입금"을
ifrs-full_CurrentPortionOfLongtermBorrowings로 쓰고, 어떤 회사는dart_CurrentPortionOfLongtermBorrowings를, 또 어떤 회사는company_ShortTermPortionOfLongTermLoans같은 자체 확장 계정으로 씁니다. - 같은 이름, 다른 의미: "장기차입금"이라는 계정명 하나가 어떤 회사에서는 총액을, 다른 회사에서는 비유동 부분만을 의미하기도 합니다. 삼성전자의 경우
ifrs-full_NoncurrentPortionOfNoncurrentLoansReceived처럼 "비유동 부분의 비유동 부분" 같은 이중 네이밍을 씁니다. - 리스부채 분리: IFRS 16 도입 이후 리스부채를 별도 표시하는 회사와, 장기차입금에 포함해 표시하는 회사가 혼재합니다.
- 회사채 세분화: 일반 사채·전환사채·신주인수권부사채·교환사채가 모두 IBD인데, 회사마다 이를 나누는 기준이 다릅니다.
- 계정과목 언더카운트: 계정 단위 API는 "재무활동으로 인한 부채" 중 일부만 명시적으로 나열되어 있어, 재무상태표(BS) 계정에서 긁어모으는 방식으로는 기본적으로 언더카운트가 발생합니다.
결과적으로 화이트리스트를 아무리 늘려도 100개 종목 중 20~30개에서는 계산이 어긋나거나 아예 0이 나왔습니다. 계정 ID 수준에서 정규화하는 접근은 본질적으로 한계가 있다는 결론을 내렸습니다.
해결 방법
계정 ID가 아니라 XBRL 차원(Axis) 에서 접근하는 전략으로 선회했습니다. K-IFRS 택소노미에는 재무활동 현금흐름 주석용으로 정의된 LiabilitiesArisingFromFinancingActivitiesAxis라는 표준 Axis가 있는데, 이 Axis 아래에는 회사가 자신의 재무구조를 어떻게 분류했는지가 통일된 스키마로 들어갑니다. 즉 계정 ID는 회사마다 달라도, 이 Axis의 멤버 구조는 훨씬 일관성이 있습니다. 이 사실을 이용해 xbrl-parser.ts에 아래 파이프라인을 직접 구현했습니다.
1. 원본 XBRL 다운로드 (fnlttXbrl.xml API)
fnlttSinglAcntAll대신 XBRL 원문 ZIP을 받아.xbrl파일을 직접 파싱합니다. 사업보고서(reprt_code=11011) 기준.
2. 컨텍스트 필터링: 엔티티 스코프 + 당기 기말
- XBRL은 같은 계정에 대해 개별/연결, 당기/전기, 기초/기말 등 수십 개의 context가 달립니다. 이 중 당기(
CFY{year}) + 기말(eFY) 조건을 전제로 엔티티 스코프는 연결(ConsolidatedMember) 우선, 연결 결과가 비어있으면 별도(SeparateMember)로 자동 폴백하도록 구성했습니다. - 처음에는 연결만 필터링하면 충분하다고 보고
ConsolidatedMember만 하드 필터링했는데, 종속기업이 없어 별도재무제표만 제출하는 소형 상장사는 XBRL에도ConsolidatedMembercontext가 아예 존재하지 않아 Axis 경로가 통째로 실패하고 모든 케이스가 계정 기반 fallback으로 넘어가는 문제가 있었습니다. extractScope()헬퍼 함수로 스코프별 추출 로직을 분리하고, 바깥에서Consolidated → Separate순으로 호출해 첫 번째 성공 결과를 채택하는 구조로 리팩터했습니다. 연결 재무제표가 있는 회사는 여전히 연결 기준으로, 별도 only 회사는 별도 기준으로 각각 정확히 추출됩니다.- 다만 소형 상장사 중에는
LiabilitiesArisingFromFinancingActivitiesAxis자체를 공시에 포함시키지 않는 사례가 여전히 존재하는데, 이런 케이스는 연결/별도 어느 쪽에서도 Axis가 없으므로 최종적으로 계정 기반 fallback(8번)으로 흘러가도록 했습니다.
3. 1차 Axis — LiabilitiesArisingFromFinancingActivitiesAxis
- 이 Axis에 속한 멤버들과 각 멤버의
LiabilitiesArisingFromFinancingActivities기말 잔액을 쌍으로 추출합니다. - 같은 멤버가 여러 context에 걸쳐 나타나면 절댓값이 가장 큰 값을 채택합니다 (유동·비유동 혼재 시 더 포괄적인 수치 선호).
4. 2차 Axis — ClassesOfFinancialLiabilitiesAxis 로 리스부채 보완
- 일부 회사는 리스부채를 재무활동 부채 Axis에 싣지 않고 금융부채 분류 Axis에만 노출합니다. 1차 Axis 결과에
lease키워드를 포함하는 멤버가 없을 때만 2차 Axis를 스캔해 리스부채 멤버를 보충합니다.
5. 멤버 이름 기반 IBD 판별 (화이트리스트가 아니라 키워드 + 네거티브 필터)
- 멤버 이름(원본 영문)을 소문자로 정규화한 뒤, IBD 키워드가 하나라도 있으면 포함, 네거티브 키워드가 있으면 우선 제외합니다. 매입채무(
trade payable)·보증금(guarantee deposit)처럼 이름은 부채지만 이자가 발생하지 않는 항목을 거르는 용도입니다. - 이 방식으로 회사가 자체 정의한 멤버(예:
CompanyName_LongTermBorrowingsFromRelatedPartiesMember)도 자동으로 IBD로 식별됩니다.
6. 유동/비유동 분류
- 멤버 이름에
shortterm,currentportion,current키워드가 있으면 유동, 그 외는 비유동으로 분류. 회사마다 Axis 멤버명에 유동/비유동 구분이 녹아있다는 규칙성을 활용한 것입니다.
7. 한글 라벨 매핑
- 표준 멤버 ID(예:
ShortTermBorrowingsMember)는 하드코딩 테이블로 "단기차입금", "장기차입금", "리스부채", "사채", "전환사채" 등으로 매핑합니다. - 회사 자체 확장 멤버는 키워드 추론으로 라벨을 붙입니다 —
debenture + longterm이면 "사채·장기차입금",shortterm + currentportion이면 "단기차입금·유동성장기부채" 같은 식으로. - 알 수 없는 멤버는 원본 이름에서
Member·Of...접미사만 떼어 그대로 노출해, 디버깅 시 추적 가능하게 남겨 둡니다.
8. 2단 Fallback: XBRL 실패 시 계정 기반 추출로 복귀
valuation_get_data는 항상 XBRL 파싱을 1차로 시도하고, XBRL 결과가 없거나 합계가 0이면 기존 계정 기반 방식(extractDebtSummary)으로 자동 폴백합니다. XBRL이 안 먹히는 구형 보고서나 예외 구조는 기존 방식이 받쳐 주도록 해서 Taxonomy의 두 계층 모두에서 보험을 들어두는 설계입니다.
5.3. 에이전트의 도구 선택 오류 — 워크플로우 문서와 description 보강
마지막 난관은 LLM 쪽이었습니다. 기능을 다 만들어 놓아도 클로드 엑셀이 "어떤 도구를 어떤 순서로 호출해야 하는가"를 잘못 판단하는 사례가 반복됐습니다.
상황
에이전트가 도구를 호출할 때 다음과 같은 세 가지 주요 비효율 패턴을 보이며 성능 저하와 데이터 불일치를 야기했습니다.
- 1. 불필요한 개별 호출:
valuation_get_data하나로 끝날 일을 개별 API(kicpa_get_beta,dart_get_financials등)로 쪼개서 호출하여 토큰과 시간을 낭비함. - 2. 파라미터 오해: 평가기준일(valuation_date)과 데이터의 기준연도(year)를 혼동하여 엉뚱한 기간의 데이터를 불러옴.
- 3. 대량 콘텐츠 호출:
get_business_content와 같은 장문 콘텐츠를 여러 종목에 동시 호출하여 컨텍스트 창을 불필요하게 부풀림.
해결 방법
세 문제 모두 별도 로직 변경 없이 **"에이전트에게 읽히는 가이드"**를 보강하여 해결했습니다.
- 1.
docs/PEER_GROUP_WORKFLOW.md신규 문서: Peer Group 선정부터 데이터 추출까지의 정규 호출 시퀀스를 레시피 형식으로 문서화하고 예시를 포함했습니다. - 2. 도구 description 교차 참조: 통합 도구와 개별 조회 도구의 설명란에 양방향 가이드를 삽입하여 에이전트의 올바른 선택을 유도했습니다.
- 3. 파라미터 규칙 명시:
year파라미터에 대한 엄격한 규칙과 잘못된 예시를 description에 직접 추가하여 오판 가능성을 원천 차단했습니다.
6. 마치며
단순히 엑셀 서식을 자동으로 채워보고자 시작한 이 작은 프로젝트가, 점진적으로 DART 원문 파싱과 데이터 정제 과정이 추가되며 꽤 본격적인 MCP 서버로 발전했습니다. 다만 현재 추출한 데이터가 모든 케이스에서 완벽하다고 볼 수는 없습니다. 이 MCP 서버를 활용하시는 분들이 있다면, 실무에서의 피드백을 통해 함께 데이터를 보완하고 정교하게 다듬어 나갈 수 있기를 기대합니다.
사람의 주관이 개입되어 완전 자동화가 어렵다고 여겨지던 가치평가(Valuation) 영역에서, 이처럼 MCP와 LLM의 결합이 실무의 한계를 어디까지 넓혀줄 수 있을지 개인적으로도 무척 기대됩니다.
앞으로 수집한 데이터를 바탕으로 실제 DCF 템플릿에 연결하여 결과를 산출하는 과정을 클로드 엑셀 가이드에서 구체적으로 다루어 보겠습니다.

