byteforce

CPN 한국어 자습서 · 러닝패스 2 / 4 — Building with the Claude API

MCP

리소스 접근하기

Accessing resources

서버에 리소스를 정의했으니, 이제 클라이언트가 그것을 요청할 차례입니다. mcp_client.pyread_resource 함수 하나를 더합니다 — URI를 받아 서버에 요청하고, MIME 타입에 따라 JSON이면 json.loads로 파싱하고 아니면 평문 그대로 돌려줍니다. 그러면 @ 멘션이 동작합니다.

전체 내레이션영상 나레이션 한국어 번역 (전체)

Stephen Grider · Anthropic 기술 스태프

앞에서 MCP 서버에 리소스 둘을 정의했습니다. 이제 클라이언트가 이 리소스들을 요청할 수 있어야 합니다. 그러려면 MCP 클라이언트에 함수 하나를 더합니다. 이 클라이언트에는 우리 애플리케이션의 나머지 부분이 사용하는 기능이 들어 있고, 그 나머지 코드는 이미 만들어져 있습니다. 즉 프로젝트 어딘가에서 우리가 지금 추가할 이 함수를 호출하게 됩니다.

mcp_client.py를 열고 아래로 내려가 read_resource를 찾습니다. 목표는 특정 리소스를 서버에 요청해 읽어 오고, 돌아온 내용을 그 MIME 타입에 따라 파싱한 뒤 데이터를 돌려주는 것입니다. 인자는 uri 하나입니다 — 서버에서 가져올 리소스의 URI죠.

요청을 만들기 전에, 타입을 깔끔히 맞추려고 파일 맨 위에 import 둘을 추가합니다 — json 모듈, 그리고 pydantic에서 AnyUrl을 가져옵니다. 다시 read_resource 함수로 내려와, await self.session().read_resource(AnyUrl(uri))로 결과를 받습니다. AnyUrl로 감싸는 건 타입을 맞추기 위한 것입니다.

그 결과에서 result.contents[0]을 꺼냅니다. 왜 이렇게 하는지 분명히 해 두면 — 조금 전 인스펙터에서 본 응답이 바로 이 result입니다. result에는 contents 리스트가 있고, 그 안에 원소들이 들어 있는데 우리는 맨 첫 번째만 신경 씁니다. 그 첫 원소에서 MIME 타입을 봅니다 — 어떤 종류의 데이터를 받았는지 알려 주기 때문입니다. JSON이면 텍스트를 JSON으로 파싱해 돌려줘야 합니다.

그래서 if isinstance(resource, types.TextResourceContents) and resource.mimeType == "application/json": 분기를 둡니다. 이게 우리 힌트입니다 — 서버가 JSON을 돌려준다고 알려 줬다면, 텍스트 내용을 JSON으로 파싱해야 합니다. 이 경우엔 return json.loads(resource.text)를 돌려줍니다. 그 if에 걸려 일찍 반환하지 않으면, 그냥 return resource.text — 평문 그대로 돌려줍니다. 이건 단일 문서의 내용을 받는 경우에 해당합니다.

이걸로 read_resource가 완성됐습니다. 다시 강조하면, MCP 클라이언트에 우리가 쓰는 코드는 이 코드베이스의 여러 다른 곳에서 호출됩니다. 어딘가에서 방금 만든 이 함수를 불러 문서 이름 목록을 받고, 또 단일 문서 내용을 받아 프롬프트에 넣습니다. 나머지 작업은 이미 끝나 있으므로, 이 시점에서 사실상 전부 동작할 것입니다.

터미널로 돌아가 CLI 앱을 다시 실행해 멘션 기능을 테스트합니다. @를 입력하면 리소스 목록이 보이고, 화살표 키로 스크롤할 수 있습니다. 원하는 리소스에서 멘션을 삽입하면 "@report.pdf 문서에 뭐가 들어 있어?"처럼 됩니다. 이제 그 문서의 내용이 프롬프트에 담겨 Claude로 전송됩니다. 제출하면 즉시 응답이 오고, report.pdf 안에 무엇이 있는지 알려 줍니다. 이번엔 Claude가 문서 내용을 읽으려고 도구를 쓰지 않았습니다 — 우리가 미리 내용을 넣어 줬으니까요. 정리하면, 리소스는 MCP 서버에서 일정량의 정보를 노출하는 데 씁니다.

이 장에서 배우는 것What you'll learn

약 6분
1

클라이언트에 read_resource(uri) 함수 하나를 추가

2

import — json 모듈 · pydantic AnyUrl

3

result = await self.session().read_resource(AnyUrl(uri))

4

result.contents[0] — 첫 콘텐츠만 사용

5

MIME 분기 — application/json이면 json.loads, 아니면 평문

6

@ 멘션 → 문서 내용을 프롬프트에 주입 → Claude가 도구 없이 답

먼저 짚고 갈 용어
read_resource
클라이언트가 URI로 리소스를 요청해 내용을 받아 오는 함수. MIME에 따라 파싱한다.
AnyUrl
pydantic 타입. URI 문자열을 감싸 read_resource에 넘길 때 타입을 맞춘다.
contents[0]
result의 contents 리스트에서 첫 콘텐츠. 우리는 이 하나만 사용한다.
mimeType 분기
application/json이면 json.loads로 역직렬화, 아니면 text 평문 그대로.

클라이언트가 리소스를 요청한다

Add read_resource

서버에 리소스를 정의했으니, 이제 클라이언트가 요청할 수 있어야 합니다. mcp_client.pyread_resource 함수 하나를 더합니다. 목표는 — URI로 서버에 요청해 내용을 받아 오고, MIME 타입에 따라 파싱한 뒤 데이터를 돌려주는 것입니다.

먼저 타입을 깔끔히 맞추려고 파일 맨 위에 import 둘을 추가합니다 — json 모듈과 pydantic의 AnyUrl입니다.

mcp_client.py · import 추가
# mcp_client.py 맨 위 — import 둘 추가
import json
from pydantic import AnyUrl
이 함수는 어디서 쓰이나

이 클라이언트 코드는 프로젝트의 여러 곳에서 호출됩니다. 어딘가에서 이 함수를 불러 문서 이름 목록을 받고, 또 단일 문서 내용을 받아 프롬프트에 넣습니다. 나머지 연결 코드는 이미 작성돼 있습니다.

MIME 타입으로 갈라지는 파서

Branch on mimeType

await self.session().read_resource(AnyUrl(uri))로 결과를 받고, result.contents[0]에서 첫 콘텐츠를 꺼냅니다. 그 콘텐츠의 mimeTypeapplication/json이면 텍스트를 json.loads로 역직렬화하고, 그 외엔 text를 평문 그대로 돌려줍니다.

mcp_client.py · read_resource
async def read_resource(self, uri: str) -> Any:
    result = await self.session().read_resource(AnyUrl(uri))
    resource = result.contents[0]

    if isinstance(resource, types.TextResourceContents) and resource.mimeType == "application/json":
        return json.loads(resource.text)   # JSON 힌트 -> 역직렬화

    return resource.text                    # 그 외 -> 평문 그대로
read_resource · MIME 타입에 따라 갈라지는 파서

application/json

json.loads(resource.text)

그 외 (text/plain)

return resource.text
▼ 반환값 활용

같은 read_resource 한 함수가 들어온 mimeType을 보고 갈라집니다 — application/json이면 json.loads로 역직렬화해 리스트로, 아니면 text를 평문 문자열 그대로 돌려줍니다.

@ 멘션이 동작한다

The mention feature works

나머지 연결 코드는 이미 끝나 있으므로, 이 시점에서 사실상 전부 동작합니다. CLI에서 @를 입력하면 목록 리소스가 문서 이름을 자동완성으로 띄우고, 문서를 골라 제출하면 그 내용이 프롬프트에 주입되어 Claude가 도구 없이 바로 답합니다.

@ 멘션 -> 문서 주입 미니 데모
>

멘션할 문서 (목록 리소스 · 자동완성)

report.pdf
deposition.md
plan.md

입력창에 @를 입력하면 목록 리소스가 문서 이름을 자동완성으로 띄웁니다. 문서를 고르고 제출해 보세요.

main.py · @ 멘션 실행 (개념 출력)
$ uv run main.py

> what's in the @report.pdf document?
   (@ 입력 시 문서 목록 자동완성 -> report.pdf 선택)

[클라이언트] read_resource("docs://documents/report.pdf")
  -> contents[0].mimeType == "text/plain" -> 평문 반환
  -> 문서 내용을 프롬프트에 주입

Claude: report.pdf는 20미터 응축탑의 상태를 다룬 보고서입니다. ...
  (도구 호출 없이 바로 답 — 내용이 이미 프롬프트에 있으므로)

정리 & 점검

Recap & check
핵심 정리
  • 클라이언트에 read_resource(uri) 추가 — 서버에 요청해 내용을 받아 온다.
  • import: json · pydantic AnyUrl. 요청은 read_resource(AnyUrl(uri)).
  • result.contents[0]mimeTypeapplication/json이면 json.loads(text), 아니면 text.
  • @ 멘션 → 문서 내용을 프롬프트에 주입 → Claude가 도구 없이 답한다.

Q1read_resource에서 받은 콘텐츠의 mimeTypeapplication/json일 때 무엇을 하나요?

Q2요청을 만들 때 URI를 무엇으로 감싸 read_resource에 넘기나요?

Q3@로 문서를 멘션해 제출하면 Claude는 어떻게 답하나요?

MEMBER SESSION REQUIRED · REGISTRATION IS FREE

여기부터는 등록한 분에게 열립니다.

전 코스는 계속 무료입니다. 등록하면 이 코스의 남은 76개 레슨을 끝까지 읽을 수 있습니다.

등록하고 이어서 읽기

이미 등록하셨다면 그때 쓰신 이메일을 넣어 주세요.