MCP 2026-07-28 출시. 에이전트 비용 추적에 미치는 영향
요약
MCP 2026-07-28 사양 확정에 따라 세션 프리미티브가 삭제되고 작업(Tasks) 기능이 비동기 방식으로 전환되었습니다. 이로 인해 에이전트 비용 추적의 책임이 프로토콜에서 애플리케이션 계층으로 이동하게 되었습니다.
핵심 포인트
- 세션 ID 기반의 자동 그룹화가 사라져 애플리케이션에서 직접 범위를 정의해야 함
- 상태 비저장 코어 도입으로 로드 밸런싱 및 확장성 향상
- Tasks 확장 기능이 일급 객체로 격상되어 비동기 작업 핸들 반환 가능
- 비동기 작업 전환에 따라 호출 전 예산 검증(Pre-call guard) 방식의 변화 필요
MCP 2026-07-28 사양(specification)이 오늘 최종 확정되었습니다.
유지 관리자들은 이를 출시 이후 가장 큰 규모의 개정이라고 부릅니다.
대부분의 커버리지는 상태 비저장 코어(stateless core)와 새로운 확장 프레임워크(Extensions framework)에 집중하고 있습니다.
이번 릴리스의 두 가지 변경 사항은 에이전트 비용을 추적하는 방식에 영향을 미치며, 두 가지 모두 책임을 애플리케이션 계층(application layer)으로 넘깁니다.
세션 프리미티브(session primitive)의 삭제
Mcp-Session-Id와 initialize/initialized 핸드셰이크(handshake)가 제거되었습니다.
이제 모든 요청은 자기 기술적(self-describing)입니다.
확장성(scalability)을 위한 올바른 호출 방식: MCP 서버는 일반적인 라운드 로빈(round-robin) 로드 밸런서 뒤에서 실행될 수 있으며, 스티키 세션(sticky sessions)이나 공유 세션 저장소(shared session store)가 필요하지 않습니다.
부작용: 프로토콜 수준에서 호출을 그룹화할 수 있는 세션 범위(session scope)가 더 이상 존재하지 않습니다.
오늘 이전에는 예산 추적을 세션 ID에 고정했던 팀들에게 자연스러운 그룹화 단위가 있었습니다:
typescript
// 2026-07-28 이전: 세션 ID가 프로토콜 프리미티브(primitive)였습니다
const sessionId = request.headers['mcp-session-id'];
const budget = await store.get(budget:${sessionId});
그것은 사라졌습니다.
사양 자체의 가이드는 명확합니다: "만약 서버가 호출 간에 상태를 유지해야 한다면, 도구(tool)로부터 명시적인 핸들(handle)을 생성하고 모델이 이를 인자(argument)로 다시 전달하게 하십시오."
이는 귀하의 예산 범위(budget scope)가 이제 프로토콜이 제공하는 것이 아니라, 귀하가 직접 정의하는 것이 되었음을 의미합니다:
typescript
// 2026-07-28 이후: 귀하가 범위를 소유합니다
const agentRunId = context.runId; // 프로토콜의 것이 아닌 귀하의 구조체
const budget = await store.get(budget:${agentRunId});
이는 더 깔끔한 모델입니다.
또한 선택 사항(opt-in) 작업이기도 합니다.
이전에 세션 범위의 비용을 추적하지 않았다면, 이제는 확실히 자동으로 제공되지 않습니다.
작업(Tasks)의 비용 누적 방식이 비동기(async)로 전환됨
Tasks 확장 기능이 이제 io.modelcontextprotocol/tasks 아래에서 일급 객체(first-class)가 되었습니다.
tools/call은 동기적 결과 대신 작업 핸들(task handle)을 반환할 수 있습니다.
클라이언트는 tasks/get, tasks/update, tasks/cancel을 사용하여 작업을 진행시킵니다.
typescript
const result = await mcp.callTool({ name: 'deep-research', params });
if (result.type === 'task') {
// 호출 전 예산 확인 (Pre-call budget check)은 이미 실행되어 종료되었습니다.
// LLM 작업은 여기서부터 비동기적으로 진행됩니다.
let taskResult;
do {
taskResult = await mcp.tasks.get(result.taskId);
} while (taskResult.status !== 'complete');
}
비용 추적 문제: 호출 전 가드 (pre-call guard)가 tools/call이 나가기 전에 실행되었습니다.
가드는 호출을 감지하고, 세션 예산을 확인한 뒤, 통과시켰습니다.
서버는 이를 Task로 변환했습니다.
실제 LLM 작업 — 잠재적으로 많은 내부 호출과 비용이 많이 드는 모델 사용 — 은 사용자의 폴링 루프 (polling loop)가 기다리는 동안 비동기적으로 실행됩니다.
가드는 제 역할을 다했습니다.
다만 Task 내부에서 어떤 일이 일어나는지에 대한 가시성 (visibility)이 없었을 뿐입니다.
Task는 장기 실행되는 에이전트 작업 (long-running agent work)을 위한 올바른 프리미티브 (primitive)입니다.
하지만 Task는 비용 누적 지점을 호출 레벨의 가드 (call-level-only guards)가 도달할 수 없는 곳으로 이동시킵니다.
해결 방법
- 세션 범위 (session scope)를 명시적으로 정의하십시오 — 프로토콜이 이를 제공할 것이라고 가정하지 마십시오.
에이전트 실행 ID (agent run ID), 사용자 세션 ID, 또는 모델이 도구 호출 간에 스레드를 유지하는 핸들을 사용하십시오.
예산을 범위로 하는 명시적인 구조체를 애플리케이션에 만드십시오:
typescript
const session = {
id: crypto.randomUUID(),
budgetCents: 50,
spentCents: 0,
reservedCents: 0,
};
- Task 폴링 라이프사이클 (polling lifecycle) 동안 예산 추적을 유지하십시오.
매 폴링마다 확인하십시오.
세션 예산을 초과하면 Task를 취소하십시오 — tasks/cancel은 스펙 (spec)의 일부입니다:
typescript
while (taskResult.status !== 'complete') {
await updateSpend(session, taskResult.progressTokens);
if (session.spentCents + session.reservedCents >= session.budgetCents) {
await mcp.tasks.cancel(taskResult.taskId);
throw new BudgetExceededError(session.id);
}
taskResult = await mcp.tasks.get(taskResult.taskId);
}
- Task 확인 시점뿐만 아니라, Task 시작 시점에 예산을 예약(Reserve)하십시오.
tools/call을 통과시키기 전에 Task의 최대 비용을 추정하십시오. 해당 금액을 예약하십시오.
예약 금액이 세션 한도를 초과할 경우, Task가 시작되기 전에 차단하십시오:
typescript
const estimatedMaxCost = estimateTaskCost(toolName, params);
if (session.spentCents + estimatedMaxCost > session.budgetCents) {
throw new BudgetExceededError(Task would exceed session budget);
}
session.reservedCents += estimatedMaxCost;
// 이제 도구/호출을 실행하도록 합니다.
추정치는 불완전할 수 있습니다.
하지만 합리적인 상한선(ceiling)을 설정하는 것이, 전체 비용을 다 쓰며 끝까지 실행되는 태스크(Task)에 대해 아무런 방어책이 없는 것보다 훨씬 낫습니다.
더 넓은 패턴
스펙 블로그(spec blog)에 기재된 Supabase의 노트를 읽어볼 가치가 있습니다. 그들은 새로운 MRTR (Multi Round-Trip Requests)을 사용하여, 새로운 프로젝트를 생성하거나 파괴적인 쿼리(destructive query)를 실행하는 것과 같이 실제 비용이 발생하는 도구(tool)를 실행하기 전에 사용자에게 확인을 요청하고 있습니다.
도구는 실행 중간에 일시 중지되고 명시적인 승인을 받습니다.
그것이 올바른 직관입니다. 동일한 확인 패턴이 예산에도 적용됩니다. 비용이 많이 드는 태스크(Task)를 시작하려는 도구는, 호출 전 확인(pre-call check)만으로 충분하다고 믿는 것이 아니라, 실행을 확정하기 전에 세션이 해당 비용을 감당할 수 있는지 확인해야 합니다.
이것이 AI CostGuard가 구축된 아키텍처 패턴입니다. 즉, 단순히 호출당 토큰 카운터(per-call token counter)가 아니라, 호출 전 결정 지점(pre-call decision point)을 가진 세션 범위 예산(session-scoped budget) 방식입니다.
오늘 MCP 스펙은 세션 프리미티브(session primitive)를 제거하는 동시에 비동기 태스크(async Task) 실행을 일급 객체(first-class)로 만듦으로써, 프로토콜 수준에서 그 차이를 명확히 했습니다.
만약 예산 범위를 위해 Mcp-Session-Id에 의존하고 있었다면, 지금 마이그레이션하십시오.
만약 세션 수준의 비용을 전혀 추적하고 있지 않았다면, 이번 출시는 시작하기에 좋은 시점입니다.
https://github.com/salimassili62-afk/ai-costguard
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기