캐싱
캐싱 (Caching)
MCP가 일부 결과에 캐싱을 지원하는 방법을 설명하는 페이지예요. 클라이언트가 응답을 캐시해 불필요한 재조회를 줄일 수 있고, 서버는 TTL과 cache scope 힌트로 캐시의 신선도와 공유 범위를 알려줘요.
출처: 문서
본문
Model Context Protocol (MCP)은 일부 결과에 대한 캐싱을 지원해요. 이를 통해 클라이언트가 응답을 캐시하고 불필요한 재조회를 줄일 수 있어요. 캐싱은 변경 알림과 상호 보완적이에요. 두 메커니즘은 공존할 수 있어요.
캐시 가능 결과 (Cacheable Results)
서버는 MUST 다음 작업이 반환하는 resultType: "complete" 결과에 캐싱 힌트를 포함해야 해요:
server/discovertools/listprompts/listresources/listresources/templates/listresources/read
resultType: "input_required"(다중 왕복 요청 참조)인 중간 결과는 캐시할 수 없고 캐싱 힌트를 담지 않아요.
캐시 키 (Cache Key)
캐시된 응답은 요청 메서드와 결과에 영향을 주는 요청 파라미터(예: resources/read의 uri, 페이지네이션된 목록 요청의 cursor)로 식별돼요. 클라이언트는 MUST NOT 그것을 생성한 요청과 메서드나 파라미터가 다른 요청에 캐시된 응답을 서비스해서는 안 돼요.
다중 왕복 요청 메커니즘을 통해 요청을 재시도해서 생긴 결과, 즉 inputResponses나 requestState를 담은 요청의 결과는 MUST NOT 캐시되지 말아야 해요. 캐시 키의 일부가 아닌 입력에 의존하기 때문이다.
캐시 가능 모델 (Cacheable Model)
MCP의 캐시 가능 결과는 클라이언트에게 캐싱 힌트를 제공하기 위해 두 필드를 사용해요:
- Time-to-live (TTL) 필드
ttlMs는 클라이언트가 결과를 신선하다고 간주할 수 있는 시간(밀리초)을 지정하는 정수 값이에요. - 캐시 범위 필드 (Cache Scope Field)
cacheScope는 캐시된 응답의 의도된 범위를 나타내며,"public"또는"private"이에요.
Time-to-Live (TTL) 필드
ttlMs 필드는 서버가 클라이언트가 결과를 신선하다고 간주할 수 있는 시간(밀리초)을 나타내는 힌트예요. 의미론은 HTTP Cache-Control: max-age와 유사해요.
ttlMs가0이면 응답은 SHOULD 즉시 오래된 것으로 간주되어야 한다. 클라이언트는 MAY 결과가 필요할 때마다 다시 가져올 수 있다.ttlMs가 양수이면 클라이언트는 SHOULD 응답을 받은 후 그 밀리초 동안 결과를 신선하다고 간주해야 한다.ttlMs가 없으면 클라이언트는 SHOULD 기본값0(즉시 오래된)을 가정하고 자신의 캐싱 휴리스틱이나 알림에 의존해야 한다. 이는 이전 서버 버전에서만 일어나야 한다.ttlMs가 음수이면 클라이언트는 SHOULD 그것을 무시하고0으로 취급해야 한다.
서버는 MUST >= 0인 ttlMs 값을 제공해야 해요.
참고 (Note): TTL은 신선도 힌트이지 보장이 아니에요. 서버는 TTL이 만료되기 전에 기본 데이터를 변경할 수 있어요. TTL은 클라이언트가 합리적으로 재조회를 피할 수 있는 기간을 알려주지, 데이터가 얼마나 오래 변경되지 않음이 보장되는지를 알려주지 않아요.
신선도 계산 (Freshness Calculation)
클라이언트는 응답을 받은 로컬 시간(t_received)을 기록해요. 응답은 다음 동안 신선(fresh) 하다고 간주돼요:
now < t_received + ttlMs
TTL이 만료되면 응답은 오래된(stale) 것이고 클라이언트는 SHOULD 다음 접근 시 다시 가져와야 해요.
클라이언트는 SHOULD NOT TTL을 자동 백그라운드 재조회를 트리거하는 폴링 간격으로 취급해서는 안 돼요. TTL은 신선도 힌트예요. 클라이언트는 데이터가 필요할 때 신선도를 확인하고, 오래된 경우에만 다시 가져와요. 폴링을 선택하는 구현은 MUST 지터(jitter)와 백오프(backoff)를 적용해야 해요.
클라이언트는 MAY 데이터가 바뀌었다고 믿을 이유가 있으면 TTL 만료 전에 다시 가져올 수 있어요 (예: 도구 호출에서 메서드를 찾을 수 없거나 파라미터가 유효하지 않다는 예상치 못한 오류를 받은 경우).
클라이언트는 MAY 재조회 중 오류가 발생하면(예: 네트워크 문제, 서버 다운) 오래된 응답을 서비스할 수 있어요.
캐시 범위 필드 (Cache Scope Field)
cacheScope 필드는 누가 응답을 캐시할 수 있는지 제어해요. HTTP Cache-Control: public과 Cache-Control: private에 유사해요.
| Value | Meaning |
|---|---|
"public" |
The response does not contain user-specific data. Any client, shared gateway, or caching proxy MAY store and serve the cached response to any user. |
"private" |
The response contains private data that is not meant to be shared between callers. Cached responses MAY be reused for the same authorization context. Caches MUST NOT be shared across authorization contexts (e.g. a different access token requires a different cache). |
캐시 범위 선택 (Choosing a Cache Scope)
"public"은 도구, 프롬프트, 리소스 템플릿 목록이 모든 사용자에게 동일할 때 적절해요."private"는 인증된 사용자에 의존하는resources/read결과나 사용자별로 달라지는 필터링된 목록 결과에 적절해요.
알림과의 상호작용 (Interaction with Notifications)
TTL과 서버 푸시 알림은 상호 보완적이에요:
- 서버는 MAY 기능에서
listChanged: true를 광고하지 않고ttlMs를 제공할 수 있어요. 이 경우 클라이언트는 전적으로 TTL 기반 신선도에 의존해요. - 서버는 MAY
listChanged: true를 광고하면서ttlMs도 제공할 수 있어요. 이 경우 클라이언트는 알림 사이의 불필요한 재조회를 피하려고 TTL을 사용하고, 알림은 즉각적인 무효화 신호로 작용해요.
캐시된 응답이 여전히 신선한 동안 관련 알림을 받으면, 그 알림은 캐시된 응답을 무효화(invalidates) 하고 즉시 오래된 것으로 간주해야 해요.
sequenceDiagram
participant Client
participant Server
Client->>Server: tools/list
Server-->>Client: { tools: [...], ttlMs: 300000 }
Note over Client: Cache response, fresh for 5 min
Note over Client: 2 minutes later...
Client->>Client: Need tools list → cache still fresh, use cached
Note over Client: 3 minutes later (TTL expired)...
Client->>Client: Need tools list → cache stale
Client->>Server: tools/list
Server-->>Client: { tools: [...], ttlMs: 300000 }
Note over Server: Tools change before TTL expires
Server-->>Client: notifications/tools/list_changed
Note over Client: Invalidate cache immediately
Client->>Server: tools/list
Server-->>Client: { tools: [...], ttlMs: 300000 }
페이지네이션과의 상호작용 (Interaction with Pagination)
목록 결과가 페이지네이션되면, 각 페이지는 독립적으로 캐시 가능한 응답이에요. 이는 HTTP Cache-Control이 페이지네이션된 리소스를 취급하는 방식과 일관돼요.
- 각 페이지 응답은 자체
ttlMs값을 담아요. 각 페이지의 신선도 시계는 그 페이지를 받은 시각에 시작돼요. - 서버는 MAY 서로 다른 페이지에 서로 다른
ttlMs값을 반환할 수 있어요 (예: 안정적인 목록의 앞쪽 페이지에 더 긴 TTL, 마지막 페이지에 더 짧은 TTL). - 캐시된 페이지가 만료되면 클라이언트는 SHOULD 자신의 커서를 사용해 그 페이지를 다시 가져와야 해요.
- 페이지 간 일관성 보장은 없어요. 페이지 가져오기 사이에 기본 데이터가 바뀌면, 클라이언트는 중복이나 누락을 관찰할 수 있어요.
- 전체 목록의 일관된 스냅샷이 필요한 클라이언트는 SHOULD 처음부터(커서 없이) 다시 가져와야 해요.
- 커서가 무효해지면(예: 서버가 이전에 유효했던 커서에 오류 반환), 클라이언트는 SHOULD 모든 캐시된 페이지를 버리고 처음부터 다시 가져와야 해요.
서버는 MUST 주어진 목록 요청에 대해 모든 응답 페이지에 같은 cacheScope를 적용해야 해요. 예를 들어 tools/list 응답의 첫 페이지가 cacheScope: "private"이면, 그 요청의 모든 후속 페이지도 MUST "private"이어야 해요.
보안 고려 사항 (Security Considerations)
cacheScope가 "public"이면 응답이 사용자 특정 데이터를 담지 않고 안전하게 공유될 수 있음을 나타내요. 서버는 "public" cacheScope의 응답이 인증된 엔드포인트에서 온 것이라도 호출자들 사이에 공유될 수 있다는 점을 인지해야 MUST 해요. 예를 들어 "public" cacheScope의 인증된 tools/list 호출 결과는 클라이언트가 캐시할 수 있고, 초기 요청의 권한 부여 컨텍스트 밖에서 공유될 수 있어요. (즉, 서로 다른 접근 토큰이 같은 캐시를 활용할 수 있음).
서버 구현자는:
cacheScope가 해당 프리미티브의 의도된 가시성을 올바르게 반영하도록 해야 한다.- 적절한 프리미티브별 접근 통제를 MUST 적용하고, 프리미티브에 대한 무단 접근을 막기 위해
cacheScope만 의존해서는 MUST NOT 안 된다.