Episode ini membedah fitur headline spesifikasi 2026-07-28: Multi-Round-Trip Requests. Pelajari peran messageId dan routingId untuk interaksi mid-call seperti konfirmasi user dan authorization step-up, lengkap dengan best practice mendesain flow interaktif.

Episode 7 menutup lingkaran dasar: client di TypeScript dan Python sudah bisa menghubungi server buatan episode 6, plus MCP Inspector untuk menguji semuanya. Sekarang kita masuk ke fitur yang menjadi salah satu headline spesifikasi 2026-07-28: Multi-Round-Trip Requests (MRTR). Fitur ini mengubah cara server dan client berinteraksi di tengah satu pemanggilan tool.
Roadmap episode ini: pahami masalah yang coba diselesaikan MRTR, bedah anatomi request-nya (messageId dan routingId), ikuti pola dua langkah minta-konfirmasi sampai lanjutkan eksekusi, lihat implementasinya di sisi server, lalu tutup dengan best practice mendesain flow interaktif.
Sebelum 2026-07-28, skenario "server butuh konfirmasi user di tengah eksekusi" sangat merepotkan. Salah satu caranya adalah menahan koneksi SSE tetap terbuka berjam-jam sampai user merespons — mahal untuk resource, rapuh terhadap timeout dan proxy, dan menyulitkan multi-hop routing. Belum lagi alur step-up authorization yang butuh beberapa pertukaran pesan secara sekuensial.
MRTR menjawabnya dengan satu ide sederhana: setiap request sekarang bisa membawa identitas yang sama di beberapa putaran. Dengan begitu, satu pemanggilan tool yang "masih berjalan" bisa dilanjutkan lewat request berikutnya — tanpa menahan koneksi streaming apa pun.
Info
MRTR tidak menghidupkan kembali sesi stateful. Core tetap stateless; yang berubah adalah identitas yang memungkinkan request-request terpisah di-route menuju satu pekerjaan yang sedang berlangsung.
Dua nilai kunci membedakan request dalam alur MRTR:
messageId — pengenal unik untuk satu pemanggilan logis. Request awal dan semua lanjutan (follow-up) yang berhubungan memakai nilai yang sama.routingId — identitas routing yang dipakai infrastruktur untuk mengarahkan follow-up ke instance yang sedang menangani request. Saat lewat HTTP, ia dikirim sebagai header Mcp-Routing-Id.Request awal membawa messageId dan method yang dijalankan:
{
"jsonrpc": "2.0",
"id": 3,
"messageId": "transfer-1",
"method": "tools/call",
"params": {
"name": "transfer-funds",
"arguments": {
"to": "123-456-789",
"amount": 500000
}
}
}Server menjawab bahwa pekerjaan belum selesai dan menunggu konfirmasi:
{
"jsonrpc": "2.0",
"id": 3,
"messageId": "transfer-1",
"result": {
"pending": true,
"requested": "confirmation",
"permission": "transfer-funds.approve"
}
}Kemudian client mengirim balasan dengan messageId yang sama:
{
"jsonrpc": "2.0",
"id": 7,
"messageId": "transfer-1",
"method": "confirm",
"params": {
"approved": true
}
}Dengan messageId yang konsisten, server (atau load balancer di depannya) tahu persis lanjutan ini milik pekerjaan transfer-1. Tidak ada koneksi yang dipegang selama menunggu — semuanya request/response biasa.
Pola paling umum MRTR adalah konfirmasi dua langkah:
tools/call; server membalas status pending plus jenis izin yang diminta, misalnya konfirmasi transfer atau approval scope baru.messageId yang sama.Flow yang sama berlaku untuk authorization step-up: request awal ternyata butuh scope yang lebih tinggi, server membalas permintaan otorisasi tambahan, client menjalankan alur OAuth (episode 10), lalu mengirim kembali hasilnya dengan messageId yang sama.
Secara konseptual, di sisi server kalian menggantung eksekusi tool pada promise yang diselesaikan oleh follow-up:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const pending = new Map();
server.registerTool(
"transfer-funds",
{
description: "Transfer antar rekening",
inputSchema: {
type: "object",
properties: {
to: { type: "string" },
amount: { type: "number" },
},
required: ["to", "amount"],
},
},
async ({ to, amount }, context) => {
const messageId = context.request.messageId;
const approved = await waitForConfirmation(messageId, { to, amount });
if (!approved) {
return { content: [{ type: "text", text: "Transfer dibatalkan" }] };
}
return { content: [{ type: "text", text: "Transfer berhasil" }] };
}
);Di balik layar, waitForConfirmation(messageId, ...) mencatat promise di Map, dan handler untuk method confirm akan me-resolve promise itu dengan messageId yang cocok. Detail API tepatnya bergantung versi SDK, tapi polanya selalu sama: simpan state in-flight per messageId, selesaikan saat follow-up tiba.
Merancang flow interaktif butuh kedisiplinan supaya tidak menjadi panggung bug:
pending harus menyebut izin apa yang diminta supaya host bisa menampilkan dialog yang informatif.MRTR adalah pembeda besar spesifikasi 2026-07-28: interaksi mid-call — konfirmasi user, step-up authorization, pengumpulan data tambahan — kini bisa dilakukan dengan request/response biasa yang membawa messageId dan routingId, tanpa menahan SSE. Kalian sudah memahami anatominya, pola dua langkahnya, dan bagaimana menempatkannya di server SDK.
Inti yang harus dibawa pulang:
messageId menyatukan satu pemanggilan logis lintas beberapa request.routingId mengarahkan follow-up ke instance yang benar, bahkan di belakang load balancer.Di episode 9 berikutnya kita bawa server keluar dari lokal: deployment server MCP sebagai service HTTP dengan Express, Fastify, dan Next.js, kontainerisasi dengan Docker, discovery lewat .well-known, dan eksploitasi sifat stateless untuk scaling horizontal. Sampai jumpa!