1부에서 MCP(Model Context Protocol)의 개념·특징·API와의 차이를 포괄적으로 정리한다. 2부에서 vibe-investing / AMQS-AI-Infra 퀀트 전략을 소재로 동작하는 MCP 서버를 직접 만들고, 테스트하고, Claude Desktop에 연결하는 전 과정을 다룬다. 동봉 코드(
server.py)는 stdio 핸드셰이크와 tool 호출까지 실제 검증 완료.
1부 — MCP 개념 이해
1. MCP란 무엇인가?
MCP(Model Context Protocol)는 2024년 11월 Anthropic이 발표한 개방형 표준 프로토콜로, AI 모델(특히 LLM)이 외부 데이터 소스나 도구와 연결될 수 있도록 해주는 통신 규약이다. 이후 OpenAI, Google, Microsoft 등 주요 IT 기업이 채택하면서 AI 업계의 사실상 표준(de facto standard)으로 자리잡았다.
1.1 왜 MCP가 필요한가? — 문제의 계보
| 단계 | 상황 | 한계 |
|---|---|---|
| 1. LLM 단독 | 학습 시점 지식으로만 응답 | 실시간 정보("지금 날씨") 불가, 외부 세계와 상호작용 불가 |
| 2. Agent 프레임워크 | Langchain·CrewAI 등이 LLM을 외부 도구(검색·API·DB)에 연결 | 프레임워크마다 도구별 개별 SDK 제작 필요 — M×N 통합 문제 |
| 3. MCP | 단일 표준 프로토콜로 통합 | M+N으로 축소 — 도구 제공자는 MCP 서버 1개만 만들면 모든 호스트에서 사용 가능 |
M×N 문제를 구체적으로 보면: Agent 프레임워크 M개 × 외부 도구 N개 조합마다 커넥터를 각각 개발해야 했다. 도구 제공자는 모든 프레임워크와 개별 협업해야 하는 비효율에 시달렸다. MCP는 이를 AI 분야의 USB-C 포트 같은 표준 연결 방식으로 해결한다 — MCP를 지원하는 어떤 AI 애플리케이션이든, MCP를 지원하는 어떤 데이터 소스와도 즉시 연결된다.
1.2 MCP 아키텍처
| 구성 요소 | 역할 | 예시 |
|---|---|---|
| MCP 호스트(Host) | LLM이 포함된 AI 애플리케이션 환경 | Claude Desktop, Cursor, Windsurf |
| MCP 클라이언트(Client) | 호스트 내에서 LLM과 MCP 서버 간 통신 중개. 서버당 1:1 연결 | 호스트에 내장 |
| MCP 서버(Server) | 외부 서비스(DB·웹 API·파일시스템 등)와 연결해 LLM에 컨텍스트와 기능 제공 | slack-mcp-server, notion-mcp-server,본 문서의 AMQS 서버 |
| 전송 계층(Transport) | JSON-RPC 2.0 기반 메시지 교환 | stdio(로컬), Streamable HTTP(원격) |
┌─────────────── Host (Claude Desktop) ───────────────┐
│ LLM ↔ Client A ── stdio ──→ Server A (AMQS 신호) │
│ Client B ── stdio ──→ Server B (Slack) │
│ Client C ── HTTP ───→ Server C (원격 DB) │
└──────────────────────────────────────────────────────┘
호스트 하나가 여러 서버에 동시 연결되며, LLM은 대화 맥락에 따라 어느 서버의 어떤 기능을 쓸지 스스로 결정한다.
2. MCP의 핵심 특징
| 특징 | 내용 |
|---|---|
| 개방형 표준 (Open Standard) | 스펙과 SDK가 오픈소스로 공개. 특정 AI 모델·벤더에 종속되지 않으며, 누구나 서버·클라이언트를 구현할 수 있다 |
| 양방향 통신 (Bidirectional) | 일회성 요청-응답이 아닌 지속 세션 기반. 서버가 클라이언트에 알림(notification)을 보내거나, 서버가 역으로 LLM 추론을 요청(sampling)하는 것도 스펙에 포함 |
| 범용성 (Universality) | 한 번 MCP 표준을 구현하면 다양한 데이터 소스 접근이 표준화. 서버는 호스트가 Claude든 Cursor든 신경 쓸 필요 없음 |
| 동적 발견 (Dynamic Discovery) | 클라이언트가 런타임에 list_tools로 서버 기능을 조회 — 사전 코딩 없이 AI가 도구를 자동 탐색·상호작용 |
| 조합성 (Composability) | 여러 서버의 tool을 LLM이 자유롭게 조합해 멀티스텝 워크플로우 수행 |
3. MCP vs 기존 API — 무엇이 다른가?
| 구분 | 기존 API 직접 통합 | MCP |
|---|---|---|
| 연결 방식 | 서비스마다 개별 연결·개별 SDK | 단일 표준 프로토콜로 여러 도구 접근 |
| 도구 발견 | 개발자가 문서 읽고 수동 코딩 | AI가 런타임에 자동 탐색 (list_tools) |
| 통신 모델 | 일회성 요청-응답 (stateless) | 지속 세션 기반 양방향 실시간 통신 |
| 호출 주체 | 애플리케이션 코드가 호출 시점 결정 | LLM이 대화 맥락에서 호출 여부·순서·조합을 결정 |
| 인터페이스 명세 | OpenAPI/Swagger — 사람·코드용 | JSON Schema + 자연어 description — LLM용 |
| 통합 비용 | M(앱)×N(도구) 커넥터 | M+N (도구당 서버 1개) |
| 관계 | — | MCP는 API를 대체하지 않는다 — 서버 내부는 여전히 REST API·DB를 호출. MCP는 그 위의 LLM 친화적 어댑터 계층 |
마지막 행이 가장 중요하다. MCP 서버 개발이란 결국 "기존 API·라이브러리·계산 로직을 LLM이 이해하고 호출할 수 있는 형태로 감싸는 것"이다. 2부에서 만들 AMQS 서버도 내부적으로는 yfinance API와 pandas 계산을 쓰되, 이를 MCP tool로 노출한다.
4. MCP 사용 예 — 개념에서 실전으로
예시 1: 뉴스 요약 및 슬랙 전송
"오늘 아침 뉴스 3개 요약해서 슬랙 AI news 채널에 올려줘" → AI가 검색 MCP 서버로 뉴스 수집 → 핵심 요약 → slack-mcp-server로 지정 채널에 게시. 서로 다른 서버 2개를 LLM이 자율 조합하는 것이 핵심이다.
예시 2: 유튜브 채널 분석 및 노션 보고서
"최근 영상 10개 분석해서 노션에 보고서 만들어줘" → 유튜브 애널리틱스 MCP로 시청 지속률·이탈 지점 분석 → 인사이트 문서 작성 → notion-mcp-server로 리포트 페이지 생성.
예시 3: 업무 자동화 서버 생태계
slack-mcp-server(메시지 전송/조회) · notion-mcp-server(DB 관리) · google-calendar-mcp-server(일정 관리) 등 — 이미 수천 개의 공개 서버가 존재한다.
예시 4: 퀀트 투자 신호 서버 (본 문서의 주제)
"지금 레짐 확인하고 RISK_ON이면 Top 10 뽑아줘, 내 MU 손절선도 봐줘" → AMQS MCP 서버의 tool 3개를 LLM이 순차 조합. 예시 1~3이 남이 만든 서버를 쓰는 것이라면, 2부는 이런 서버를 직접 만드는 방법이다.
2부 — MCP 서버 개발 실전 - AMQS-AI-Infra Signal Server
5. 왜 AMQS가 MCP 샘플로 적합한가?
AMQS-AI-Infra 원본 문서에는 이미 Python / LLM 역할 분담표가 존재한다. 이 표가 그대로 MCP 서버 설계도가 될 수 있다.
| AMQS 역할 분담 | 담당 | MCP 매핑 |
|---|---|---|
| 4-Factor 모멘텀 · 손절 · 레짐 · Top-N 선별 | Python (자동) | Tools — LLM이 호출하는 결정론적 계산 |
| Universe · 전략 파라미터 (정적 참조) | Python | Resources — 읽기 전용 컨텍스트 |
| Revenue Acceleration · 13F · EPS Revision · 내러티브 | LLM (지식·검색) | Prompts — 교차검증 워크플로우 템플릿 |
즉, "Python은 기술 신호를 계산하고 LLM은 펀더멘털을 검토한다"는 AMQS 철학("LLM은 엑셀이지 오라클이 아니다")이 MCP의 3대 primitive(Tools / Resources / Prompts)와 1:1 대응한다. 결정론적 계산은 코드에, 판단은 모델에 두는 구조 — MCP 서버 설계의 정석이다.
6. 개발자 관점의 아키텍처 재해석
1부의 Host / Client / Server 구조를 개발자 시각으로 다시 보자.
| 구성 요소 | 개발자가 실제로 만지는 것 |
|---|---|
| Host | 만들지 않음 (기성 앱 사용) |
| Client | 만들지 않음 (호스트 내장) — 프로토콜 테스트용으로만 작성 |
| Server | 우리가 만드는 것 — 이 문서의 전부 |
| 전송 계층 | SDK가 처리 — transport 종류만 선택 |
6.1 3대 Primitive
| Primitive | 제어 주체 | 용도 | AMQS 샘플에서 |
|---|---|---|---|
| Tools | model-controlled (LLM이 호출 결정) | 함수 실행, 부수효과 가능 | get_regime, get_momentum_score, get_top_signals, check_stop_loss |
| Resources | application-controlled (호스트가 관리) | 읽기 전용 데이터, URI로 식별 | amqs://universe |
| Prompts | user-controlled (사용자가 선택) | 재사용 가능한 프롬프트 템플릿 | cross_validate_ticker |
6.2 Transport 선택
| Transport | 통신 | 용도 | 비고 |
|---|---|---|---|
| stdio | 표준입출력 | 로컬 (Claude Desktop, Cursor) | 시작은 무조건 이것. 배포·인증 불필요 |
| Streamable HTTP | 단일 HTTP 엔드포인트 | 원격 서버 | 2025-03 스펙부터 SSE를 대체하는 현행 표준 |
| SSE (legacy) | HTTP + Server-Sent Events | 원격 (구형) | deprecated — 신규 개발에서 사용하지 말 것 |
초기 자료의 "stdio(로컬) 또는 SSE(원격)" 서술은 구 스펙 기준이다. 현행 스펙에서 원격 transport는 Streamable HTTP로 통합되었다.
6.3 SDK 선택
| 기준 | Python (mcp + FastMCP) |
TypeScript (@modelcontextprotocol/sdk) |
|---|---|---|
| 퀀트·데이터 작업 | pandas / numpy / yfinance 생태계 직결 | 별도 브리지 필요 |
| 코드량 | 데코레이터 3종으로 끝 | 다소 장황 |
| 배포 | uv / pip |
npx 원커맨드 배포 유리 |
| 본 샘플 | 채택 | — |
퀀트 신호 서버는 데이터 스택이 Python에 있으므로 Python SDK가 자연스럽다. FastMCP는 함수 시그니처와 docstring에서 tool schema를 자동 생성한다 — docstring이 곧 LLM에게 보여지는 tool description이므로 docstring 품질이 tool 호출 정확도를 좌우한다.
7. 프로젝트 구조와 환경 설정
amqs-mcp-server/
├── server.py # MCP 서버 본체 (단일 파일)
├── requirements.txt
├── claude_desktop_config.example.json # Claude Desktop 연결 예시
└── MCP_Server_Getting_Started.md # 본 문서
python -m venv venv && source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
# 즉시 데모 (네트워크·API 키 불필요, 합성 가격)
AMQS_MOCK=1 python server.py
# 실데이터 (yfinance)
python server.py
AMQS_MOCK=1은 재현 가능한 합성 가격으로 동작하는 오프라인 모드다. Toss 대시보드의 MOCK 모드와 같은 취지 — 키 없이도 로직 시연이 가능해야 문서와 CI가 살아있는 문서가 된다.
8. 서버 구현 단계별 해부
server.py 전문은 동봉 파일 참조. 핵심 패턴만 짚는다.
Step 1 — 서버 인스턴스와 instructions
from mcp.server.fastmcp import FastMCP
mcp = FastMCP(
"amqs-ai-infra",
instructions=(
"AMQS-AI-Infra 퀀트 전략 신호 서버. ... "
"모든 출력은 연구용 참고 신호이며 투자 권유가 아니다."
),
)
instructions는 클라이언트가 서버 전체의 성격을 이해하는 시스템 힌트다. 금융 도구라면 디스클레이머를 서버 레벨에 박아두는 것이 각 tool 출력에 반복하는 것보다 안전하다 (본 샘플은 둘 다 한다).
Step 2 — 계층 분리: 데이터 / 전략 / MCP
데이터 계층 get_prices() yfinance 또는 합성 데이터 + 캐시
전략 계층 composite_scores() 4-Factor z-score → 0~100 점수 (MCP와 무관한 순수 함수)
MCP 계층 @mcp.tool() 전략 함수를 얇게 감싸 JSON으로 직렬화
전략 로직을 MCP 데코레이터와 분리하면 (1) 단위 테스트가 쉽고 (2) 동일 로직을 CLI·백테스트·Signal Bot에서 재사용할 수 있다. 원본 레포의 script/strategy.py(엔진) / script/amqs_ai_infra.py(CLI) 분리와 같은 원리다. 이것이 3장에서 말한 "MCP는 API의 어댑터 계층"의 코드 구현이다.
Step 3 — Tool 정의: docstring = LLM용 명세
@mcp.tool()
def get_top_signals(top_n: int = 10) -> str:
"""AMQS Top-N 매수 후보를 반환한다. 서브테마당 최대 4종 캡을 적용해
GPU 등 단일 테마 과집중을 방지한다. 거시 레짐이 RISK_ON 이 아니면 경고를 포함한다."""
체크리스트
| 항목 | 이유 |
|---|---|
타입 힌트 필수 (top_n: int = 10) |
JSON Schema 자동 생성의 근거 |
| docstring에 동작·제약·전제 명시 | LLM이 언제 이 tool을 쓸지 판단하는 유일한 근거 |
| 반환은 구조화된 JSON 문자열 | LLM 파싱 안정성 (ensure_ascii=False로 한글 보존) |
| 입력 검증 + 에러를 JSON으로 반환 | universe 밖 ticker 요청 시 예외 대신 {"error": ..., "universe": [...]} — LLM이 스스로 복구 가능 |
상한 클램프 (min(top_n, len(UNIVERSE))) |
LLM이 top_n=999를 넣어도 안전 |
Step 4 — 도메인 규칙을 tool 내부에 강제
서브테마 캡(테마당 최대 4종)은 LLM에게 "지켜달라"고 부탁하는 것이 아니라 코드가 강제한다:
for t, row in df.iterrows():
theme = row["subtheme"]
if theme_count.get(theme, 0) >= SUBTHEME_CAP:
continue # GPU 쏠림을 코드 레벨에서 차단
검증 결과(Top-10, mock): compute 4 / server 3 / software 2 / network 1 — 캡 정상 작동. 리스크 규칙은 프롬프트가 아니라 코드에 둔다. 이것이 "LLM은 엑셀" 원칙의 MCP 버전이다.
Step 5 — Resource와 Prompt
@mcp.resource("amqs://universe") # URI 스킴은 자유 — 서버 네임스페이스 관례
def universe_resource() -> str: ...
@mcp.prompt()
def cross_validate_ticker(ticker: str) -> str: ...
Prompt는 "Python이 못 하는 일(매출가속·13F·EPS Revision)을 LLM에게 시키는 표준 지시문"을 서버가 배포하는 채널이다. 원본 레포 prompts/AMQS_AI_Infra_kr.MD를 복사-붙여넣기하던 워크플로우가, MCP에서는 호스트 UI에서 원클릭 호출로 바뀐다.
9. 테스트 — 3단계 검증 파이프라인
| 단계 | 방법 | 검증 대상 |
|---|---|---|
| 1. 단위 | 모듈 import 후 함수 직접 호출 | 전략 로직 (MCP 무관) |
| 2. 프로토콜 | MCP 클라이언트로 stdio 핸드셰이크 | initialize → list_tools → call_tool |
| 3. 대화형 | MCP Inspector | 실제 호스트 관점 UI 검증 |
9.1 단위 테스트
import os; os.environ["AMQS_MOCK"] = "1"
import server
print(server.get_top_signals(10)) # 데코레이터를 거쳐도 함수로 직접 호출 가능
9.2 프로토콜 테스트 (실행 검증 완료)
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
params = StdioServerParameters(command="python", args=["server.py"],
env={"AMQS_MOCK": "1"})
async with stdio_client(params) as (r, w):
async with ClientSession(r, w) as s:
await s.initialize()
tools = await s.list_tools() # 4개 tool 확인
out = await s.call_tool("get_regime", {})
실행 결과
TOOLS: ['get_regime', 'get_momentum_score', 'get_top_signals', 'check_stop_loss']
RESOURCES: ['amqs://universe']
PROMPTS: ['cross_validate_ticker']
CALL get_regime OK: {"regime": "RISK_ON", ...}
이 테스트가 1부 2장의 "동적 발견(Dynamic Discovery)" 특징의 실증이다 — 클라이언트는 서버 코드를 전혀 모른 채 런타임 조회만으로 기능 목록과 schema를 얻는다.
9.3 MCP Inspector
npx @modelcontextprotocol/inspector python server.py
브라우저 UI에서 tool 목록·schema·호출 결과를 대화형으로 확인한다. Claude Desktop 연결 전 필수 관문.
흔한 함정: stdio 서버에서
print()디버깅 금지. stdout은 JSON-RPC 채널이므로 로그는 반드시logging(stderr)으로 보낼 것. 이것이 stdio 서버 오작동 원인 1위다.
10. Claude Desktop 연결
claude_desktop_config.json 위치:
| OS | 경로 |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
{
"mcpServers": {
"amqs-ai-infra": {
"command": "python",
"args": ["/ABSOLUTE/PATH/TO/amqs-mcp-server/server.py"],
"env": { "AMQS_MOCK": "0" }
}
}
}
주의사항: (1) 경로는 반드시 절대경로, (2) venv 사용 시 command를 venv 내 python 절대경로로, (3) 수정 후 Claude Desktop 완전 재시작. 연결되면 다음과 같은 대화가 가능해진다:
사용자: 지금 레짐 확인하고, RISK_ON이면 Top 10 뽑아서 MU가 몇 위인지 알려줘.
그리고 내 MU 평단 200불인데 손절선 걸렸는지도 봐줘.
Claude: [get_regime] → RISK_ON
[get_top_signals(10)] → MU 3위, tier SATELLITE
[check_stop_loss("MU", 200.0)] → pnl -17.95%, action EXIT
→ "손절선 -12%를 하회했습니다. AMQS 규칙상 EXIT 신호입니다..."
1부 4장의 "뉴스 요약 → 슬랙 전송" 예시가 그랬듯, 사용자는 자연어로 말하고 LLM이 tool 조합을 스스로 결정한다. 이 tool orchestration이 MCP의 실질 가치다.
11. 원격 배포 (Streamable HTTP)
stdio에서 원격으로 전환은 마지막 한 줄 변경이 전부다:
mcp.run(transport="streamable-http") # 기본 http://localhost:8000/mcp
| 항목 | stdio | Streamable HTTP |
|---|---|---|
| 사용자 | 본인 1명 | 팀·외부 공개 |
| 인증 | 불필요 (로컬 프로세스) | OAuth 2.1 필수 (스펙 요구사항) |
| 배포 | — | Docker + reverse proxy, 또는 Cloudflare Workers 등 |
| 상태 관리 | 프로세스 = 세션 | 세션 ID 기반, 서버 스케일아웃 고려 |
Signal Bot 웹뷰(python -m http.server 8011)를 팀에 공유하던 것처럼, MCP 서버도 원격화하면 팀원 모두의 Claude가 동일한 AMQS 신호 소스를 쓰게 된다 — 단 인증 없는 공개는 절대 금물 (다음 절).
12. 보안 고려사항 (CTI 관점)
MCP는 새로운 공격 표면이다. 서버를 만드는 순간 방어자 관점도 함께 가져가야 한다.
| 위협 | 내용 | 본 샘플의 대응 / 권고 |
|---|---|---|
| Tool Poisoning | 악성 서버가 tool description에 숨은 지시를 심어 LLM 조종 | 서버 제작자로서: description을 기능 서술로 한정. 사용자로서: 서드파티 서버 description 감사 후 설치 |
| Prompt Injection (간접) | tool이 반환한 외부 데이터(뉴스·웹) 안의 지시문을 LLM이 실행 | 반환값을 구조화 JSON으로 한정, 자유 텍스트 최소화 |
| Confused Deputy | LLM이 사용자의 의도를 넘는 tool 호출 수행 | 부수효과 있는 tool(주문 실행 등)은 만들지 않음 — 본 서버는 read-only 신호 전용. 실거래 연동 시 human-in-the-loop 승인 필수 |
| Secrets 노출 | config의 env에 API 키 평문 저장 |
KIS API 등 연동 시 OS keychain / secret manager 사용. .gitignore + pre-commit secret scan (LAON VaultGuard 계열 도구) |
| Rug Pull | 신뢰 후 서버 업데이트로 tool 동작 변경 | 버전 고정 설치, 변경분 diff 리뷰 |
| 무인증 원격 노출 | Streamable HTTP 서버 공개 시 무단 사용·데이터 유출 | OAuth 2.1 + rate limit, 내부망은 최소 mTLS |
금융 신호 서버 특유의 리스크 한 가지: LLM이 tool 출력을 과잉 해석해 확정적 매매 조언으로 바꾸는 것. 대응은 3중이다 — 서버 instructions에 디스클레이머, 각 tool 출력에 disclaimer 필드, prompt 템플릿 말미에 고지 강제.
13. 확장 로드맵
| 단계 | 내용 | 난이도 |
|---|---|---|
| 1 | backtest.py 연동 — run_backtest(start, end) tool 추가 |
하 |
| 2 | Signal Bot의 signals.json을 Resource로 노출 (amqs://signals/latest) |
하 |
| 3 | ARDS-X 레짐 분류기를 별도 MCP 서버로 — AMQS/ARDS 핸드오프를 LLM이 오케스트레이션 | 중 |
| 4 | Toss Open API / KIS API 연동 — 단 실주문 tool은 승인 게이트 필수 | 상 |
| 5 | 크립토 뉴스 파이프라인(web3paper) 연동 — 뉴스 수집·요약 tool로 CTI/투자 통합 | 중 |
| 6 | Streamable HTTP + OAuth로 팀 배포, 다국어(KR/EN/ZH/JP) tool description | 중 |
14. 요약
개념 (1부)
- MCP는 LLM과 외부 도구의 M×N 통합 문제를 M+N으로 줄이는 개방형 표준 — 'AI의 USB-C 포트'.
- Host / Client / Server 구조에서 개발자가 만드는 것은 Server뿐이다.
- MCP는 API를 대체하지 않는다 — 기존 API 위에 얹는 LLM 친화적 어댑터 계층이며, 핵심 차이는 동적 발견과 LLM 주도 호출이다.
개발 (2부)4. MCP 서버 개발 =결정론적 계산을 Tools로, 정적 데이터를 Resources로, LLM 워크플로우를 Prompts로 노출하는 일이다. 5. AMQS의 Python/LLM 역할 분담표는 그대로 MCP 설계도가 된다 — 리스크 규칙(서브테마 캡, 손절)은 프롬프트가 아니라 코드에 강제한다. 6. 개발 순서: FastMCP + stdio → 단위 테스트 → 프로토콜 테스트 → Inspector → Claude Desktop → (필요 시) Streamable HTTP + OAuth. 7. 서버를 만드는 순간 공격 표면도 만든다 — Tool Poisoning, Injection, Secrets를 설계 단계에서 고려한다.
참고 링크
- MCP 공식 스펙·문서: https://modelcontextprotocol.io
- Python SDK: https://github.com/modelcontextprotocol/python-sdk
- MCP Inspector: https://github.com/modelcontextprotocol/inspector
- AMQS-AI-Infra 원본: https://github.com/gameworkerkim/vibe-investing/tree/main/01.Trading%20Strategy/Adaptive%20Momentum%20Quant%20Strategy%20(AMQS)%20for%20AI%20Infra
본 문서와 코드는 연구·교육 목적이며 투자 권유가 아니다. AMQS는 고위험 퀀트 전략으로 원금 손실 가능성이 있다. License: MIT — "Built on AMQS by Dennis Kim, vibe-investing repository."