강좌
11개 강의 중 6번째 · Model Context Protocol: Advanced TopicsRoots 둘러보기
Sign in to save your progressYou can keep reading without an account, but completed lessons won't be saved.
Not nowSign in

튜토리얼 단계

샘플 프로젝트를 살펴보면서 이 기능을 구현하는 방법을 더 잘 이해해 보겠습니다.

건너뛰기다음
1. 루트 정의하기

이상적으로는 사용자가 MCP 서버가 접근할 수 있는 파일/폴더를 지정해야 합니다.

이 프로그램은 CLI 인수 목록을 받아들이도록 설정되어 있으며, 이는 사용자가 접근을 허용하고자 하는 경로로 해석됩니다.

해당 경로 목록은 42번째 줄에서 MCPClient에 제공됩니다.

2. 루트 객체 생성하기

MCP 사양에 따르면, 모든 루트는 file://로 시작하는 URI를 가져야 합니다.

이 함수는 사용자가 제공한 경로 목록을 받아 Root 객체로 변환합니다.

3. 루트 콜백

클라이언트는 루트 목록을 서버에 즉시 제공하지 않습니다. 대신, 서버는 미래의 어느 시점에 클라이언트에 요청을 보낼 수 있습니다. 서버가 루트를 요청할 때 실행될 콜백을 만듭니다. 이 콜백은 ListRootsResult 객체 안에 루트 목록을 반환해야 합니다.

이 콜백은 58번째 줄에서 ClientSession에 전달됩니다.

4. 루트 사용하기

이제 서버 차례입니다. 서버는 다음 두 가지 시나리오에서 루트를 사용합니다:

  1. 도구가 파일이나 폴더에 접근하려고 할 때마다
  2. LLM(Claude와 같은)이 파일이나 폴더를 전체 경로로 해석해야 할 때. 사용자가 'todos.txt 파일을 읽어줘'라고 말하는 경우를 생각해 보세요 - Claude는 텍스트 파일이 어디에 있는지 파악해야 하며, 루트 목록을 살펴봄으로써 이를 알아낼 수 있습니다

두 번째 경우를 처리하기 위해, 루트를 나열하는 도구를 정의하거나 프롬프트에 직접 주입할 수 있습니다.

5. 루트 접근하기

루트는 ctx.session.list_roots()를 호출하여 접근합니다.

이는 클라이언트로 메시지를 다시 보내며, 이로 인해 루트 목록 콜백이 실행됩니다.

6. 접근 권한 부여하기

기억하세요: MCP SDK는 도구가 읽으려는 파일이나 폴더를 제한하려고 시도하지 않습니다! 이 검사는 직접 구현해야 합니다.

is_path_allowed와 같은 함수를 구현하여, 경로를 루트 목록과 비교함으로써 해당 경로에 접근할 수 있는지 여부를 결정하는 것을 고려하세요.

7. 접근 권한 부여하기

is_path_allowed와 같은 권한 부여 함수를 만들었다면, 요청된 경로에 접근할 수 있는지 확인하기 위해 도구 전반에서 이를 사용하세요.

← 이전다음 →
main.py×
import asyncio
import sys
import os
from dotenv import load_dotenv
from contextlib import AsyncExitStack
from mcp_client import MCPClient
from core.claude import Claude
from core.cli_chat import CliChat
from core.cli import CliApp
load_dotenv()
# Anthropic Config
claude_model = os.getenv("CLAUDE_MODEL", "claude-sonnet-4-5")
anthropic_api_key = os.getenv("ANTHROPIC_API_KEY", "")
assert claude_model, "Error: CLAUDE_MODEL cannot be empty. Update .env"
assert anthropic_api_key, (
"Error: ANTHROPIC_API_KEY cannot be empty. Update .env"
)
async def main():
claude_service = Claude(model=claude_model)
# Get root directories from command line arguments
root_paths = sys.argv[1:]
if not root_paths:
print("Usage: uv run main.py <root1> [root2] ...")
print("Example: uv run main.py /path/to/videos /another/path")
sys.exit(1)
clients = {}
async with AsyncExitStack() as stack:
# Create the MCP client with the provided root directories
doc_client = await stack.enter_async_context(
MCPClient(
command="uv", args=["run", "mcp_server.py"], roots=root_paths
)
)
clients["doc_client"] = doc_client
chat = CliChat(
doc_client=doc_client,
clients=clients,
claude_service=claude_service,
)
cli = CliApp(chat)
await cli.initialize()
await cli.run()
if __name__ == "__main__":
if sys.platform == "win32":
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
asyncio.run(main())