08. MCP 프로바이더 설정과 도구 관리
학습 목표
이 튜토리얼을 마치면 다음을 할 수 있습니다:
- MCP(Model Context Protocol)의 역할과 euleragent에서의 위치를 이해한다
workspace.yaml에 MCP 서버를 설정하고 연결한다euleragent mcp sync/mcp show로 카탈로그를 관리한다- MCP 소스가
web.search를 통해 투명하게 라우팅되는 구조를 이해한다 kind: source_enable승인 흐름을 처리한다- 카탈로그 스냅샷으로 검색 소스 구성의 재현성을 보장한다
- fail-closed 동작(list_changed → 소스 비활성화)을 이해한다
- 신뢰 수준별 다중 MCP 서버를 구성한다
사전 준비
- 워크스페이스 초기화 완료 (
euleragent init) - 07_web_rag.md의 RAG 기본 개념 이해
- 에이전트 생성:
euleragent new research-agent --template personal-assistant
- Ollama 실행 (또는
default_llm_profile: fake):
ollama serve
Step 1: MCP란 무엇인가?
MCP(Model Context Protocol) 개요
MCP는 JSON-RPC 2.0 기반 프로토콜로, 외부 도구와 데이터 소스를 표준화된 인터페이스로 연결합니다. euleragent에서 MCP는 다음과 같은 역할을 합니다:
- 다양한 검색 소스 통합: Tavily, Brave, 커스텀 API 등을 하나의 인터페이스로 관리
- 도구 카탈로그 관리: 각 MCP 서버가 제공하는 도구 목록을 자동으로 수집
- 보안 게이트: deny-all 정책에 따라 모든 MCP 소스는 승인 후 사용
euleragent에서 MCP의 위치
MCP는 LLM에게 직접 노출되지 않습니다. 에이전트는 단일 web.search 도구만 호출하고, 내부의 SearchRouter가 설정된 소스 중 적절한 곳으로 라우팅합니다:
┌─────────────────────────────────────────────────────┐
│ 에이전트 (LLM) │
│ │ │
│ ▼ │
│ web.search("API gateway 비교") │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ SearchRouter │ ← 소스 라우팅 결정 │
│ └──────┬──────┘ │
│ │ │
│ ┌────┴────┬──────────┐ │
│ ▼ ▼ ▼ │
│ [Native] [MCP: [MCP: │
│ Brave Tavily] 내부 KB] │
│ │
│ 각 소스는 독립적으로 승인/차단/stale 처리 │
└─────────────────────────────────────────────────────┘
핵심: LLM은 개별 소스의 존재를 모릅니다.
SearchRouter가 enabled/approved 소스 중에서 자동으로 선택하고, 결과를 표준 형식(SearchResult)으로 통합하여 반환합니다.
MCP 프로토콜 기본 동작
MCP 서버는 두 가지 핵심 메서드를 제공합니다:
| 메서드 | 설명 | 용도 |
|---|---|---|
tools/list |
서버가 제공하는 도구 목록 반환 | 카탈로그 동기화 시 |
tools/call |
특정 도구 실행 | 실제 검색 실행 시 |
// tools/list 응답 예시
{
"tools": [
{
"name": "web_search",
"description": "Search the web using Tavily API",
"inputSchema": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}
]
}
Step 1.5: 데모 MCP 서버로 빠르게 시작하기
euleragent 저장소에 포함된 데모 MCP 서버를 사용하면 외부 서비스 없이 MCP 통합을 체험할 수 있습니다. 이 서버는 stdlib만 사용하며 두 가지 도구를 제공합니다:
sqlite.query— SQLite DB에 대한 읽기 전용 SQL 실행demo.echo— 메시지 에코 (테스트용)
1. 샘플 데이터베이스 생성 및 서버 시작
# 샘플 DB 생성
python -c "
import sqlite3, pathlib
pathlib.Path('.euleragent/state').mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect('.euleragent/state/demo.db')
conn.execute('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)')
conn.execute(\"INSERT OR IGNORE INTO users VALUES (1, 'Alice', 'alice@example.com')\")
conn.execute(\"INSERT OR IGNORE INTO users VALUES (2, 'Bob', 'bob@example.com')\")
conn.commit(); conn.close()
print('Demo database created.')
"
# 데모 MCP 서버 시작 (포트 9020)
python examples/mcp/sqlite_tools_server.py
2. workspace.yaml에서 MCP 서버 주석 해제
euleragent init으로 생성된 workspace.yaml에는 데모 서버 설정이 주석으로 포함되어 있습니다. 주석을 해제하고 enabled: true로 변경하세요:
mcp:
enabled: true
servers:
- id: demo_sqlite
url: http://localhost:9020/mcp
allow_tools: [sqlite.query, demo.echo]
cost_tier: free
risk_default: low
require_approval_enable: false
timeout_seconds: 30
3. 카탈로그 동기화 및 확인
euleragent mcp sync
euleragent mcp show
4. curl로 직접 테스트
# 도구 목록 확인
curl -s -X POST http://localhost:9020/mcp \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}' | python -m json.tool
# 쿼리 실행
curl -s -X POST http://localhost:9020/mcp \
-d '{"jsonrpc":"2.0","method":"tools/call","id":2,"params":{"name":"sqlite.query","arguments":{"sql":"SELECT * FROM users"}}}' | python -m json.tool
보안: 데모 서버는
SELECT와PRAGMA만 허용하며,INSERT/UPDATE/DELETE/DROP등 쓰기 작업은 거부합니다. SQLite 연결도?mode=ro로 읽기 전용으로 열립니다.
Step 2: workspace.yaml에 MCP 설정 추가
기본 MCP 설정
workspace.yaml의 mcp: 섹션에서 MCP 서버를 설정합니다:
# .euleragent/config/workspace.yaml
mcp:
enabled: true
catalog_path: .euleragent/state/mcp_catalog.json
servers:
- id: tavily_search
url: http://localhost:3001/mcp
allow_tools:
- web_search
cost_tier: paid
risk_default: high
require_approval_enable: true # source_enable 승인 필요
- id: internal_kb
url: http://localhost:3002/mcp
allow_tools:
- document_search
cost_tier: free
risk_default: medium
require_approval_enable: false # 내부 소스, 승인 불필요
설정 필드 설명
| 필드 | 필수 | 설명 |
|---|---|---|
id |
예 | 서버 고유 식별자 (카탈로그에서 참조용) |
url |
예 | MCP 서버의 JSON-RPC endpoint URL |
allow_tools |
아니오 | 허용할 도구 이름 목록 (비어있으면 전체 허용) |
cost_tier |
아니오 | 비용 등급: free, paid, unknown |
risk_default |
아니오 | 기본 위험 수준: low, medium, high |
require_approval_enable |
아니오 | true: 사용 전 source_enable 승인 필요 (기본값: true) |
환경변수로 API 키 보호
API 키가 필요한 MCP 서버는 환경변수 보간을 사용합니다:
mcp:
enabled: true
servers:
- id: tavily_search
url: ${TAVILY_MCP_URL} # 환경변수에서 URL 로드
allow_tools:
- web_search
cost_tier: paid
# 환경변수 설정
export TAVILY_MCP_URL="http://localhost:3001/mcp"
export TAVILY_API_KEY="tvly-YOUR_API_KEY"
보안: API 키를
workspace.yaml에 직접 기록하지 마세요.${ENV_VAR}구문을 사용하면 런타임에 환경변수에서 해석됩니다.workspace.yaml은 버전 관리에 포함될 수 있으므로 이 패턴이 중요합니다.
Step 3: mcp sync — 카탈로그 동기화
MCP 설정을 추가한 후 카탈로그를 동기화합니다. 이 과정에서 각 서버에 tools/list를 호출하여 제공 가능한 도구 목록을 가져옵니다.
실행
euleragent mcp sync
예상 출력
MCP catalog sync completed.
Servers: 2
[OK] tavily_search (web_search, connected)
[OK] internal_kb (document_search, connected)
Snapshot: .euleragent/state/mcp_catalog.json
동기화 중 오류 발생 시
MCP catalog sync completed.
Servers: 2
[OK] internal_kb (document_search, connected)
[FAIL] tavily_search (connection refused: http://localhost:3001/mcp)
Snapshot: .euleragent/state/mcp_catalog.json
Warnings: 1 server(s) unreachable
오류가 발생한 서버의 소스는 카탈로그에 포함되지 않습니다. 연결 문제를 해결한 후 mcp sync를 다시 실행하세요.
일반적인 오류와 해결 방법
| 오류 | 원인 | 해결 |
|---|---|---|
connection refused |
MCP 서버가 실행 중이 아님 | 서버 프로세스 시작 확인 |
timeout after 30s |
서버 응답 지연 | timeout_seconds 설정 증가 |
MCP error -32601 |
지원하지 않는 메서드 | MCP 서버 버전 확인 |
tool X not in allow_tools |
허용 목록에 없는 도구 | allow_tools 수정 |
Step 4: mcp show — 카탈로그 상태 확인
동기화된 카탈로그의 현재 상태를 확인합니다:
euleragent mcp show
예상 출력
MCP Catalog
Servers:
NAME URL STATUS TOOLS
tavily_search http://localhost:3001/mcp OK 1 (web_search)
internal_kb http://localhost:3002/mcp OK 1 (document_search)
Source Sets:
default: tavily_search, internal_kb
offline: internal_kb
Snapshot Hash: sha256:a3f8d21...
Last Sync: 2026-02-24 14:30:15
출력 해석
- STATUS:
OK(연결 정상),STALE(카탈로그 변경 감지),FAIL(연결 실패) - TOOLS: 해당 서버에서
allow_tools로 허용된 도구 수와 이름 - Snapshot Hash: 현재 카탈로그의 SHA-256 해시 (재현성 검증에 사용)
- Last Sync: 마지막
mcp sync실행 시각
Step 5: 첫 번째 MCP 검색 실행
MCP 소스가 설정된 상태에서 검색 태스크를 실행합니다.
태스크 실행
euleragent run research-agent \
--task "2025년 API 게이트웨이 솔루션 비교 리포트 작성" \
--mode plan
실행 중 발생하는 일
- Runner가 시작되면
SearchCatalogSnapshot을 생성합니다 - 에이전트가
web.search를 호출합니다 SearchRouter가 enabled 소스를 확인합니다- 외부 MCP 소스(
tavily_search)는 승인이 필요합니다 kind: source_enable승인 레코드가 생성됩니다
예상 출력
[run] Starting plan mode for research-agent
[run] SearchCatalog: 2 sources (1 native, 1 mcp)
[run] MCP source 'tavily_search' requires approval (source_enable)
[llm] Generating plan...
[approval] Created: kind=source_enable, tool=web.search, source=mcp_web_search
[run] Plan generated: 1 file(s)
└─ plan.md (645 tokens)
[run] Run finalized: a1b2c3d4
승인 확인
euleragent approve show
Pending Approvals:
ID KIND TOOL SOURCE RISK STATUS
ap_001 source_enable web.search mcp_web_search high pending
Step 6: source_enable 승인 흐름 상세
승인 레코드 구조
source_enable 승인 레코드는 다음과 같은 구조를 가집니다:
euleragent approve show ap_001
{
"id": "ap_001",
"kind": "source_enable",
"tool_name": "web.search",
"tool_params": {
"query": "API gateway comparison 2025"
},
"risk_level": "high",
"side_effects": ["external_network"],
"status": "pending",
"resolved_source_id": "mcp_web_search",
"candidate_sources": ["mcp_web_search", "mcp_document_search"],
"routing_reason": "mcp_source_selected"
}
필드 설명
| 필드 | 설명 |
|---|---|
kind |
source_enable — MCP 소스 활성화 승인 |
resolved_source_id |
SearchRouter가 선택한 소스 ID |
candidate_sources |
라우팅 후보였던 모든 소스 목록 |
routing_reason |
라우팅 결정 사유 (예: mcp_source_selected) |
승인 수락
euleragent approve accept ap_001 --actor "user:you"
Accepted: ap_001
Kind: source_enable
Source: mcp_web_search (tavily via MCP)
승인 거부
소스를 신뢰할 수 없다면 거부합니다. 해당 소스는 사용되지 않고 다른 eligible 소스로 폴백합니다:
euleragent approve deny ap_001 --reason "Tavily 비용 초과 우려"
--edit-params로 소스 변경
승인 시 라우팅 대상 소스를 변경할 수 있습니다. SearchRouter가 mcp_web_search를 선택했지만, 다른 소스를 사용하고 싶은 경우:
euleragent approve accept ap_001 \
--actor "user:you" \
--edit-params '{"resolved_source_id": "mcp_document_search"}'
이렇게 하면 resolved_source_id가 mcp_document_search로 변경되어, 다음 실행에서 해당 소스가 우선 사용됩니다.
참고:
--edit-params로 변경한 값은 승인 레코드의final_params에 기록되어 감사 추적이 가능합니다.
Step 7: source_sets로 검색 소스 그룹 관리
다양한 용도에 맞게 검색 소스를 그룹으로 묶어 관리할 수 있습니다.
workspace.yaml에 source_sets 설정
# .euleragent/config/workspace.yaml
mcp:
enabled: true
servers:
- id: tavily_search
url: http://localhost:3001/mcp
allow_tools: [web_search]
cost_tier: paid
- id: internal_kb
url: http://localhost:3002/mcp
allow_tools: [document_search]
cost_tier: free
- id: arxiv_search
url: http://localhost:3003/mcp
allow_tools: [paper_search]
cost_tier: free
source_sets:
default:
- internal_kb
- tavily_search
academic:
- internal_kb
- arxiv_search
offline:
- internal_kb
news:
- tavily_search
--source-set으로 실행별 소스 집합 지정
# 학술 리서치 → 내부 KB + arXiv만 사용
euleragent run research-agent \
--task "트랜스포머 아키텍처 최신 논문 분석" \
--mode plan \
--source-set academic
# 뉴스 수집 → Tavily만 사용
euleragent run research-agent \
--task "오늘의 AI 뉴스 요약" \
--mode plan \
--source-set news
# 오프라인 모드 → 내부 KB만 사용
euleragent run research-agent \
--task "내부 문서 기반 FAQ 생성" \
--mode plan \
--source-set offline
--source-set을 지정하지 않으면 workspace.yaml의 source_sets.default가 사용됩니다.
Step 8: 카탈로그 스냅샷과 재현성
스냅샷의 목적
모든 실행은 시작 시점의 검색 소스 구성을 스냅샷으로 고정합니다. 이를 통해:
- 실행 결과를 정확히 재현할 수 있습니다
- 어떤 소스가 사용 가능했는지 감사 추적이 가능합니다
- MCP 서버 변경이 기존 실행 결과에 영향을 미치지 않습니다
스냅샷 파일 확인
실행 완료 후 아티팩트 디렉토리에서 스냅샷을 확인합니다:
# 검색 소스 스냅샷
cat .euleragent/runs/a1b2c3d4/artifacts/search_sources_snapshot.json
{
"sources": [
{
"id": "brave",
"type": "native",
"provider_name": "brave",
"enabled": true,
"require_approval": true,
"risk_level": "high",
"cost_tier": "paid"
},
{
"id": "mcp_web_search",
"type": "mcp",
"provider_name": "mcp",
"enabled": false,
"require_approval": true,
"risk_level": "high",
"cost_tier": "paid",
"mcp_server_id": "tavily_search",
"mcp_tool_name": "web_search"
}
],
"created_at": 1740400000.0,
"hash": "sha256:a3f8d21..."
}
# MCP 카탈로그 스냅샷
cat .euleragent/runs/a1b2c3d4/artifacts/mcp_catalog_snapshot.json
{
"catalogs": [
{
"server_id": "tavily_search",
"tools": [
{
"name": "web_search",
"description": "Search the web using Tavily API",
"input_schema": {"type": "object", "properties": {"query": {"type": "string"}}},
"server_id": "tavily_search"
}
],
"fetched_at": 1740400000.0,
"hash": "sha256:b4c9e32...",
"stale": false
}
],
"snapshot_hash": "sha256:a3f8d21..."
}
두 실행의 스냅샷 비교
# 해시로 빠르게 비교
diff <(jq '.hash' .euleragent/runs/RUN1/artifacts/search_sources_snapshot.json) \
<(jq '.hash' .euleragent/runs/RUN2/artifacts/search_sources_snapshot.json)
해시가 동일하면 두 실행에서 동일한 검색 소스 구성을 사용한 것입니다.
Step 9: fail-closed 동작
list_changed란?
MCP 서버는 도구 목록이 변경되면 list_changed 알림을 보냅니다. euleragent는 이 알림을 받으면 해당 카탈로그를 stale(유효기간 만료)로 표시하고, 해당 소스의 검색을 차단합니다.
fail-closed 정책
euleragent는 fail-closed(폐쇄 실패) 정책을 따릅니다. 알 수 없는 변경이 감지되면 차단하고 사람의 개입을 요구합니다:
[run] MCP catalog stale for server 'tavily_search' (list_changed=true)
[run] Source 'mcp_web_search' blocked: mcp_catalog_stale
[run] Falling back to native sources only
stale 상태의 의미
| 상태 | SearchRouter 동작 | 필요한 조치 |
|---|---|---|
stale: false |
정상적으로 소스 사용 | 없음 |
stale: true |
해당 소스 자동 skip | mcp sync 실행 필요 |
복구 방법
# 카탈로그 재동기화
euleragent mcp sync
동기화 후 새로운 도구 목록이 반영되고, stale 상태가 해제됩니다. 만약 도구가 추가되거나 변경되었다면 새로운 카탈로그 해시가 생성됩니다.
보안적 의미
fail-closed 정책은 다음을 방지합니다:
- 도구 변경 공격: MCP 서버가 예상치 못한 도구를 추가하는 것을 차단
- 스키마 변경: 기존 도구의 입력 스키마가 변경된 경우 차단
- 자동 실행 방지: 변경된 도구가 승인 없이 실행되는 것을 방지
원칙: "모르는 것은 차단한다." euleragent의 deny-all 정책은 MCP 소스에도 동일하게 적용됩니다.
Step 10: 다중 MCP 서버와 신뢰 수준
신뢰 수준별 서버 구성
실전 환경에서는 내부 서버와 외부 서버를 구분하여 관리합니다:
# .euleragent/config/workspace.yaml
mcp:
enabled: true
servers:
# 내부 서버 — 승인 불필요, 항상 사용 가능
- id: company_kb
url: http://internal-mcp.company.local:3000/mcp
allow_tools:
- document_search
- code_search
cost_tier: free
risk_default: low
require_approval_enable: false # 내부 서버이므로 자동 활성화
# 외부 유료 서버 — 승인 필요
- id: tavily_search
url: http://localhost:3001/mcp
allow_tools:
- web_search
cost_tier: paid
risk_default: high
require_approval_enable: true # 외부 서비스, 승인 필수
# 외부 무료 서버 — 승인 필요 (데이터 유출 위험)
- id: duckduckgo_search
url: http://localhost:3004/mcp
allow_tools:
- web_search
cost_tier: free
risk_default: medium
require_approval_enable: true # 외부 전송이므로 승인 필요
source_sets:
default:
- company_kb
- tavily_search
internal_only:
- company_kb
full:
- company_kb
- tavily_search
- duckduckgo_search
서버별 승인 동작 차이
| 서버 | require_approval_enable |
동작 |
|---|---|---|
company_kb |
false |
즉시 사용 가능, 승인 불필요 |
tavily_search |
true |
첫 사용 시 source_enable 승인 필요 |
duckduckgo_search |
true |
첫 사용 시 source_enable 승인 필요 |
allow_tools로 도구 범위 제한
MCP 서버가 많은 도구를 제공하더라도, allow_tools로 euleragent에서 사용할 도구를 제한할 수 있습니다:
- id: multi_tool_server
url: http://localhost:3005/mcp
allow_tools:
- web_search # 허용
- news_search # 허용
# image_generate 등 나머지 도구는 차단됨
allow_tools에 포함되지 않은 도구는 카탈로그에 등록되지 않으며, SearchRouter가 라우팅하지 않습니다. 이는 euleragent의 deny-all 원칙과 일치합니다.
실전 예제: 비용 인식 라우팅
SearchRouter는 소스 선택 시 cost_tier를 고려합니다. 기본적으로 free 소스를 우선 시도하고, 결과가 부족하면 paid 소스로 확장합니다:
# 비용 민감 태스크 — 내부 KB 우선
euleragent run research-agent \
--task "사내 API 문서에서 인증 방법 찾기" \
--mode plan \
--source-set internal_only
# 종합 리서치 — 모든 소스 활용
euleragent run research-agent \
--task "경쟁사 API 게이트웨이 비교 분석" \
--mode plan \
--source-set full
검색 라우팅 결정 추적
모든 검색 라우팅 결정은 search_routing.jsonl에 기록됩니다:
cat .euleragent/runs/a1b2c3d4/artifacts/search_routing.jsonl
{
"query": "API gateway comparison 2025",
"selected_sources": ["mcp_web_search"],
"candidate_sources": ["mcp_web_search", "mcp_document_search"],
"routing_reason": "mcp_source_selected",
"timestamp": 1740400123.456
}
이 로그를 통해 각 검색이 어떤 소스로 라우팅되었는지, 다른 후보는 무엇이었는지 사후에 확인할 수 있습니다.
전체 흐름 요약
1. workspace.yaml에 mcp 설정 추가
│
▼
2. euleragent mcp sync
(서버별 tools/list 호출 → 카탈로그 생성)
│
▼
3. euleragent mcp show
(소스 상태 확인)
│
▼
4. euleragent run --task "..." --source-set default
(SearchRouter가 소스 선택 → 승인 필요 시 source_enable 생성)
│
▼
5. euleragent approve accept <id> --actor "user:you"
(외부 소스 승인)
│
▼
6. 재실행 → 승인된 소스로 검색 수행
│
▼
7. 결과 확인:
- search_routing.jsonl (라우팅 결정)
- search_sources_snapshot.json (소스 구성)
- mcp_catalog_snapshot.json (MCP 카탈로그)
자주 묻는 질문
Q: MCP 서버를 직접 구축할 수 있나요?
네. MCP는 JSON-RPC 2.0 표준을 따르므로, tools/list와 tools/call을 구현하는 HTTP 서버를 만들면 됩니다. Python으로 간단한 MCP 서버를 구축하는 예시:
# 간단한 MCP 서버 (Flask 기반)
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/mcp", methods=["POST"])
def handle_rpc():
data = request.json
method = data.get("method")
if method == "tools/list":
return jsonify({
"jsonrpc": "2.0",
"id": data["id"],
"result": {
"tools": [
{
"name": "custom_search",
"description": "Search internal database",
"inputSchema": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
}
]
},
})
elif method == "tools/call":
# 실제 검색 로직 구현
query = data["params"]["arguments"]["query"]
return jsonify({
"jsonrpc": "2.0",
"id": data["id"],
"result": {
"content": [{"type": "text", "text": f"Results for: {query}"}]
},
})
return jsonify({"jsonrpc": "2.0", "id": data["id"], "error": {"code": -32601, "message": "Method not found"}})
Q: MCP 없이도 웹 검색을 사용할 수 있나요?
네. MCP는 선택 사항입니다. mcp.enabled: false(기본값)이면 기존의 네이티브 검색 프로바이더(Brave, Tavily 직접 연결)를 그대로 사용할 수 있습니다. MCP는 여러 검색 소스를 통합 관리하고 싶을 때 사용합니다. 네이티브 검색 설정은 07_web_rag.md를 참조하세요.
Q: source_enable 승인을 일괄 처리할 수 있나요?
네. accept-all을 사용하면 됩니다:
# web.search 관련 소스 승인만 일괄 수락
euleragent approve accept-all --tool web.search --actor "user:you"
다른 도구(file.write, shell.exec 등)의 승인은 영향받지 않습니다.
Q: 카탈로그가 stale 상태인데 무시하고 실행하고 싶습니다.
보안상 stale 소스는 강제로 사용할 수 없습니다. 반드시 euleragent mcp sync로 재동기화한 후 실행하세요. 이것은 euleragent의 fail-closed 정책입니다.
자주 하는 실수 (순서 어긋남)
| 증상 | 원인 | 복구 |
|---|---|---|
MCP is not enabled. Set mcp.enabled: true in: <path> |
MCP 비활성화 | workspace.yaml에서 mcp.enabled: true 설정 |
No MCP servers configured. |
서버 미설정 | workspace.yaml → mcp.servers 섹션 추가 |
| MCP 카탈로그 stale | 동기화 미실행 | euleragent mcp sync |
다음 단계: 09_scoped_llm_profile.md — 스코프별 LLM 프로필 설정과 외부 LLM 승인 워크플로우를 학습합니다.