MCP (2) Resources in Tools

김동하·2025년 12월 31일

MCP

목록 보기
2/3
post-thumbnail

Embedded Resources

지금까지 Tools와 Resources에 대해 알아봤다. Tools은 LLM이 호출하는 함수이고 Resources는 앱이 관리하는 컨텍스트 객체라고 할 수 있다.

그럼 이제, Tool의 결과 안에 Resource를 섞어 넣어 Tool을 단순한 메시지 생성기가 아니라 리소스 그래프를 생성하는 노드로 만들어보자.

먼저 Embedded Resources는 말그대로 리소스 전체를 결과 안에 직접 포함시키는 것이다.

{
  "type": "resource",
  "resource": {
    "uri": "gym://equipment/beam-001",
    "mimeType": "application/json",
    "text": "{...}"
  }
}

그럼 클라이언트(앱)는 리소스를 포함한 툴의 결과물을 보고 이 리소스를 리소스 목록에 추가하고 변경을 감지하거나 컨텍스트를 최신화한다.
즉, 툴 결과를 상태 객체로 만드는 것이다.

그럼 인스펙터에서 툴 리스트를 검색해보자

이 중에서 get_entry를 선택하고 id가 1번인 entry를 고르면

이와 같이 JSON을 받게 된다.

{
  "id": 1,
  "title": "First day at new job",
  "content": "Started my new job today. Met the team and set up my workspace.",
  "mood": "excited",
  "location": "office",
  "weather": "sunny",
  "isPrivate": 0,
  "isFavorite": 1,
  "createdAt": 1767064472,
  "updatedAt": 1767064472,
  "tags": [
    {
      "id": 6,
      "name": "learning"
    },
    {
      "id": 1,
      "name": "work"
    }
  ]
}

이러한 결과는 단지 텍스트일뿐이다. LLM은 이 데이터가 무엇인지 알지만 앱은 알지 못 한다. 즉, 앱의 입장에서는 어디서 나온 데이터고 나중에 다시 참조할 수 있는지, 리소스를 구독이나 추적해야 하는지 모른다.

그럼 이제 Tool의 결과를 반환하는 부분에 resource를 섞어보자

text였던 타입을

resource로 변경하고 리소스의 uri을 넣어준다. 이제 툴의 결과물은 단순한 JSON이 아니라epicme://entries/{id} 라는 리소스의 정식 표현이다.

클라이언트는 이제 이 URI를 기억하여 리소스 목록에 추가하고 다음 대화 컨텍스트에 쉽게 재사용 가능하다. 즉, MCP 서버가 상태 관리 시스템이 된다.

인스펙터에서 확인해보자

툴에서 id가 1인 entry를 찾으니 리소스를 추가한 결과를 반환한다.

Linked Resources

Linked Resources는 전체 데이터를 넣지 않고 링크만 참조하는 방식이다.

{
 "type": "resource_link",
 "uri": "gym://equipment/vault-002",
 "name": "Vault Table",
 "mimeType": "application/json"
}

위와 같은 형태로 uri를 넣어서 뱉어주면 된다.

코드 상에서도 간다하게 entryLinks 객체를 넣어주면 된다.

Linked Resources는 Embedded Resources에 비해서 토큰 소모가 적다는 장점이 있다. Embedded가 LLM에 바로 읽히는 것에 비해 Linked는 클릭을 해야 읽힌다.

Prompt

프롬프트란 재사용 가능한 AI 워크플로라고 할 수 있다. 사용자의 대부분은 프롬프트 엔지니어가 아니다.

그렇기 때문에 좋은 프롬프트를 작성하는 법을 알 필요가 없기에, MCP는 서버가 프롬프트 템플릿을 기능처럼 제공한다. 사용자는 템플릿을 선택하고 필요한 인자만 채우면 된다.

LLM은 Prompt가 뭔지 모른다. Prompt는 LLM 기능이라기보다 클라 사이드의 UX 기능에 가깝다.

그럼, 사용자가 entry를 하나 작성하면 거기에 맞는 태그를 추천해주는 프롬프트를 만든다고 가정하자.

Tools, Resource를 했던 것처럼 MCP 서버에 일단 레지스터를 하면 된다.

그리고 디스크립션과 스키마를 작성해주는데 이것은 사용자를 위한 것이다. LLM용이 아님!

그리고 콜백에 message 배열로 이 메시지가 누구로부터 왔는데 role과 원하는 프롬프트 작성한다.

content의 type의 경우는 resource를 사용할 수도 있다. 이제 인스펙터로 가보자

프롬프트 패널에 가보면 방금 생성한 프롬프트가 하나 있다. id가 1로 검색하면

LLM에게 어떤 툴을 가져와서 무엇을 해달라는 프롬프트가 조회된다. 추천한 태그가 존재하지 않을 시 어떤 도구를 생성하여 새로운 태그를 만들고 엔트리에 추가해달라고도 말했다.

Prompt 최적화

현재 프롬프트에선 사용자가 어떤 툴을 사용하라고 명시하고 있다. LLM이 위 프롬프트를 실행하면

  • entry 가져오기(get_entry or resource read)

  • tags 목록 가져오기(list_tags or tags resource)

위 과정을 거치고 그 다음에야 태그 추천을 한다. 하지만 이렇게 되면 데이터를 얻기 위해 여러 툴을 사용하면서 느려질 수 있다.

여기서 좀 더 최적화하면 어떤 툴을 사용하라고 지시하는 것이 아니라 태그들의 리소스와 해당 엔트리의 리소스를 프롬프트에 넣으면 좀 더 LLM이 빠르게 추천할 수 있을 것이다.

그럼 코드를 수정해보자.

기존에 추가했던 프롬프트 관련 콜백에 툴의 결과를 가져와서 리소스에 추가할 것이다.

먼저 엔트리와 태그를 가져오고

이런 식으로 새로운 리소스 객체를 추가할 것이다.

이제 인스펙터를 실행해보자

프롬프트와 리소스를 잘 가져온다! 메시지를 구성하는 순서는 원하는 작업을 말하고 컨텍스트를 전해주는 것이 좋다.

profile
프론트엔드 개발

0개의 댓글