번역 자료 / / 2025. 5. 22. 15:32

MCP 리소스

원문: https://modelcontextprotocol.io/docs/concepts/resources

서버에서 LLM으로 데이터와 콘텐츠를 노출하기

리소스(Resources)는 Model Context Protocol(MCP)의 핵심 기본 요소로, 서버가 클라이언트가 읽고 LLM 상호작용의 컨텍스트로 사용할 수 있는 데이터와 콘텐츠를 노출할 수 있게 해줍니다.

리소스는 애플리케이션 제어(application-controlled)가 되도록 설계되었습니다. 이는 클라이언트 애플리케이션이 언제 어떻게 리소스를 사용할지 결정할 수 있다는 의미입니다. 다양한 MCP 클라이언트는 리소스를 다르게 처리할 수 있습니다. 예를 들면:

  • Claude Desktop은 현재 사용자가 리소스를 사용하기 전에 명시적으로 선택해야 합니다
  • 다른 클라이언트는 휴리스틱을 기반으로 자동으로 리소스를 선택할 수 있습니다
  • 일부 구현은 AI 모델 자체가 어떤 리소스를 사용할지 결정하도록 허용할 수도 있습니다

서버 개발자는 리소스 지원을 구현할 때 이러한 모든 상호작용 패턴을 처리할 준비가 되어 있어야 합니다. 모델에 자동으로 데이터를 노출하려면 서버 개발자는 도구(Tools)와 같은 모델 제어(model-controlled) 기본 요소를 사용해야 합니다.

개요

리소스는 MCP 서버가 클라이언트에 제공하고자 하는 모든 종류의 데이터를 나타냅니다. 여기에는 다음이 포함될 수 있습니다:

  • 파일 내용
  • 데이터베이스 레코드
  • API 응답
  • 라이브 시스템 데이터
  • 스크린샷 및 이미지
  • 로그 파일
  • 기타 등등

각 리소스는 고유한 URI로 식별되며 텍스트 또는 바이너리 데이터를 포함할 수 있습니다.

리소스 URI

리소스는 다음 형식을 따르는 URI를 사용하여 식별됩니다:

[protocol]://[host]/[path]

예를 들면:

  • file:///home/user/documents/report.pdf
  • postgres://database/customers/schema
  • screen://localhost/display1

프로토콜 및 경로 구조는 MCP 서버 구현에 의해 정의됩니다. 서버는 자체 사용자 정의 URI 체계를 정의할 수 있습니다.

리소스 유형

리소스는 두 가지 유형의 콘텐츠를 포함할 수 있습니다:

텍스트 리소스

텍스트 리소스는 UTF-8로 인코딩된 텍스트 데이터를 포함합니다. 이는 다음에 적합합니다:

  • 소스 코드
  • 설정 파일
  • 로그 파일
  • JSON/XML 데이터
  • 일반 텍스트

바이너리 리소스

바이너리 리소스는 base64로 인코딩된 원시 바이너리 데이터를 포함합니다. 이는 다음에 적합합니다:

  • 이미지
  • PDF
  • 오디오 파일
  • 비디오 파일
  • 기타 비텍스트 형식

리소스 검색

클라이언트는 두 가지 주요 방법을 통해 사용 가능한 리소스를 검색할 수 있습니다:

직접 리소스

서버는 resources/list 엔드포인트를 통해 구체적인 리소스 목록을 노출합니다. 각 리소스는 다음을 포함합니다:

{
  uri: string;           // 리소스의 고유 식별자
  name: string;          // 사람이 읽을 수 있는 이름
  description?: string;  // 선택적 설명
  mimeType?: string;     // 선택적 MIME 타입
}

리소스 템플릿

동적 리소스의 경우, 서버는 클라이언트가 유효한 리소스 URI를 구성하는 데 사용할 수 있는 URI 템플릿을 노출할 수 있습니다:

{
  uriTemplate: string;   // RFC 6570을 따르는 URI 템플릿
  name: string;          // 이 유형의 사람이 읽을 수 있는 이름
  description?: string;  // 선택적 설명
  mimeType?: string;     // 모든 일치하는 리소스에 대한 선택적 MIME 타입
}

리소스 읽기

리소스를 읽으려면 클라이언트는 리소스 URI와 함께 resources/read 요청을 보냅니다.

서버는 리소스 내용 목록으로 응답합니다:

{
  contents: [
    {
      uri: string;        // 리소스의 URI
      mimeType?: string;  // 선택적 MIME 타입

      // 다음 중 하나:
      text?: string;      // 텍스트 리소스용
      blob?: string;      // 바이너리 리소스용(base64로 인코딩됨)
    }
  ]
}

서버는 하나의 resources/read 요청에 대해 여러 리소스를 반환할 수 있습니다. 예를 들어, 디렉토리를 읽을 때 디렉토리 내의 파일 목록을 반환하는 데 사용될 수 있습니다.

리소스 업데이트

MCP는 두 가지 메커니즘을 통해 리소스의 실시간 업데이트를 지원합니다:

목록 변경

서버는 notifications/resources/list_changed 알림을 통해 사용 가능한 리소스 목록이 변경되었을 때 클라이언트에 알릴 수 있습니다.

내용 변경

클라이언트는 특정 리소스에 대한 업데이트를 구독할 수 있습니다:

  1. 클라이언트가 리소스 URI와 함께 resources/subscribe를 보냅니다
  2. 리소스가 변경되면 서버는 notifications/resources/updated를 보냅니다
  3. 클라이언트는 resources/read로 최신 내용을 가져올 수 있습니다
  4. 클라이언트는 resources/unsubscribe로 구독을 취소할 수 있습니다

구현 예시

다음은 MCP 서버에서 리소스 지원을 구현하는 간단한 예시입니다:

  • TypeScript
  • Python
const server = new Server({
  name: "example-server",
  version: "1.0.0"
}, {
  capabilities: {
    resources: {}
  }
});

// 사용 가능한 리소스 나열
server.setRequestHandler(ListResourcesRequestSchema, async () => {
  return {
    resources: [
      {
        uri: "file:///logs/app.log",
        name: "애플리케이션 로그",
        mimeType: "text/plain"
      }
    ]
  };
});

// 리소스 내용 읽기
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
  const uri = request.params.uri;

  if (uri === "file:///logs/app.log") {
    const logContents = await readLogFile();
    return {
      contents: [
        {
          uri,
          mimeType: "text/plain",
          text: logContents
        }
      ]
    };
  }

  throw new Error("리소스를 찾을 수 없습니다");
});
app = Server("example-server")

@app.list_resources()
async def list_resources() -> list[types.Resource]:
    return [
        types.Resource(
            uri="file:///logs/app.log",
            name="애플리케이션 로그",
            mimeType="text/plain"
        )
    ]

@app.read_resource()
async def read_resource(uri: AnyUrl) -> str:
    if str(uri) == "file:///logs/app.log":
        log_contents = await read_log_file()
        return log_contents

    raise ValueError("리소스를 찾을 수 없습니다")

# 서버 시작
async with stdio_server() as streams:
    await app.run(
        streams[0],
        streams[1],
        app.create_initialization_options()
    )

모범 사례

리소스 지원을 구현할 때:

  1. 명확하고 설명적인 리소스 이름과 URI 사용
  2. LLM의 이해를 돕기 위한 유용한 설명 포함
  3. 알려진 경우 적절한 MIME 타입 설정
  4. 동적 콘텐츠를 위한 리소스 템플릿 구현
  5. 자주 변경되는 리소스에 대한 구독 사용
  6. 명확한 오류 메시지로 오류를 우아하게 처리
  7. 대규모 리소스 목록에 대한 페이지 매김 고려
  8. 적절한 경우 리소스 내용 캐싱
  9. 처리하기 전에 URI 검증
  10. 사용자 정의 URI 체계 문서화

보안 고려사항

리소스를 노출할 때:

  • 모든 리소스 URI 검증
  • 적절한 접근 제어 구현
  • 디렉토리 트래버설을 방지하기 위해 파일 경로 정제
  • 바이너리 데이터 처리에 주의
  • 리소스 읽기에 대한 속도 제한 고려
  • 리소스 접근 감사
  • 전송 중인 민감한 데이터 암호화
  • MIME 타입 검증
  • 장시간 실행되는 읽기에 대한 타임아웃 구현
  • 적절하게 리소스 정리 처리
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유