Skip to content

6.1 MCP sebagai Client

Tujuan bab

Setelah bab ini kamu bisa:

  • Menyambungkan Mastra ke satu atau beberapa MCP server dengan MCPClient
  • Menjelaskan perbedaan transport Stdio dan HTTP, serta bagaimana Mastra memilihnya
  • Menyerahkan tool dari MCP server ke agent lewat listTools()
  • Menangani server yang gagal terhubung tanpa menjatuhkan seluruh agent
  • Menerapkan pengamanan untuk tool yang tidak kamu tulis sendiri

Prasyarat

  • Bagian 2 selesai
  • Paket @mastra/mcp terpasang: npm install @mastra/mcp@latest

Kenapa memakai tool orang lain

Ada banyak kemampuan yang tidak masuk akal untuk kamu tulis sendiri: integrasi GitHub, pencarian web, akses basis data, kontrol browser. MCP (Model Context Protocol) adalah standar terbuka yang membuat kemampuan semacam itu bisa dibagikan antar sistem — dan ekosistemnya sudah cukup besar.

Untuk Mastra, memakai MCP server berarti agent-mu mendapat tool tambahan tanpa kamu menulis satu pun createTool().

Menyambung

ts
// src/mastra/mcp/client.ts
import { MCPClient } from '@mastra/mcp'

export const mcp = new MCPClient({
  id: 'mcp-eksternal',
  servers: {
    github: {
      command: 'npx',
      args: ['-y', '@modelcontextprotocol/server-github'],
      env: {
        GITHUB_TOKEN: process.env.GITHUB_TOKEN!,
      },
    },
    docs: {
      url: new URL('https://contoh.example.com/mcp'),
    },
  },
  timeout: 60_000,
})

Beberapa hal yang perlu dipahami dari potongan ini.

servers adalah objek, dan key-nya menjadi nama server. Nama itu dipakai untuk memberi awalan pada nama tool — akan terlihat sebentar lagi.

Transport dipilih otomatis dari bentuk konfigurasinya:

Kalau kamu memberiTransport yang dipakai
commandStdio — Mastra menjalankan server sebagai subprocess
urlHTTP — mencoba Streamable HTTP dulu, lalu jatuh ke SSE lama bila gagal

id bukan sekadar label. Dokumentasi menyebut kegunaannya secara khusus: mencegah kebocoran memori saat kamu membuat beberapa instance dengan konfigurasi yang identik. Isi selalu.

timeout default-nya 60 detik, berlaku untuk semua server kecuali ditimpa per server.

Catatan tentang environment pada server Stdio

Ini detail yang berdampak nyata dan jarang disadari. Untuk server Stdio, subprocess tidak mewarisi seluruh environment prosesmu. Ia memulai dari daftar putih terkurasi milik MCP SDK:

  • Di POSIX: HOME, LOGNAME, PATH, SHELL, TERM, USER
  • Di Windows: APPDATA, HOMEDRIVE, HOMEPATH, LOCALAPPDATA, PATH, PROCESSOR_ARCHITECTURE, SYSTEMDRIVE, SYSTEMROOT, TEMP, USERNAME, USERPROFILE

Artinya API key-mu tidak otomatis sampai ke sana — kamu harus menyebutkannya secara eksplisit di env, seperti pada contoh di atas.

Ada juga inheritDefaultEnv yang default-nya true. Menyetelnya ke false berarti hanya variabel di env yang diteruskan. Dokumentasi memberi peringatan yang layak diperhatikan: subprocess tanpa PATH bisa gagal menjalankan perintah yang bukan path absolut.

Menyerahkan tool ke agent

ts
// src/mastra/agents/dev-agent.ts
import { Agent } from '@mastra/core/agent'
import { mcp } from '../mcp/client.ts'

export const devAgent = new Agent({
  id: 'dev-agent',
  name: 'Dev Agent',
  instructions: 'Bantu pengembang mengelola repositori dan mencari dokumentasi.',
  model: 'openai/gpt-5.6-sol',
  tools: await mcp.listTools(),
})

listTools() mengambil semua tool dari semua server yang dikonfigurasi.

Nama tool diberi awalan nama server, dengan format namaServer_namaTool — misalnya github_create_issue. Ini mencegah tabrakan nama saat dua server kebetulan punya tool dengan nama sama.

Menangani server yang gagal

Contoh di atas punya kelemahan yang baru terasa di produksi: kalau salah satu server tidak bisa dihubungi, listTools() bermasalah dan agent-mu ikut gagal dibuat.

Untuk itu ada listToolsWithErrors():

ts
// src/mastra/agents/dev-agent.ts
import { Agent } from '@mastra/core/agent'
import { mcp } from '../mcp/client.ts'

const { tools, errors, errorDetails, durations } = await mcp.listToolsWithErrors({
  perServerTimeoutMs: 3_000,
})

if (Object.keys(errors).length > 0) {
  console.warn('Sebagian MCP server tidak dapat dihubungi:', errorDetails)
}

export const devAgent = new Agent({
  id: 'dev-agent',
  name: 'Dev Agent',
  instructions: 'Bantu pengembang mengelola repositori dan mencari dokumentasi.',
  model: 'openai/gpt-5.6-sol',
  tools,
})

Yang kamu dapat:

NilaiIsinya
toolsTool dari server yang berhasil dalam batas waktu
errorsPeta pesan error per server, berupa string
errorDetailsPesan yang sama plus httpStatus dan code transport bila tersedia
durationsLama penemuan tiap server dalam milidetik

perServerTimeoutMs membatasi berapa lama penemuan menunggu tiap server. Server yang selesai dalam batas itu tetap masuk tools; yang melewati batas masuk errors.

Pola ini yang sebaiknya kamu pakai begitu ada lebih dari satu server. Agent yang kehilangan satu dari empat sumber tool masih jauh lebih berguna daripada agent yang gagal dibuat sama sekali.

durations berguna untuk diagnosis

Kalau startup aplikasimu terasa lambat, durations langsung memberitahu server mana yang menahan. Ini jenis informasi yang biasanya harus kamu ukur sendiri dengan susah payah.

Mengamankan tool yang bukan tulisanmu

Ini bagian yang paling penting dari bab ini. Tool dari MCP server eksternal adalah kode yang tidak kamu tulis, tidak kamu tinjau, dan bisa berubah tanpa memberitahumu. Mastra menyediakan beberapa lapis pengamanan.

Persetujuan sebelum eksekusi

ts
export const mcp = new MCPClient({
  id: 'mcp-eksternal',
  servers: {
    github: {
      command: 'npx',
      args: ['-y', '@modelcontextprotocol/server-github'],
      env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN! },
      requireToolApproval: true,
    },
  },
})

requireToolApproval bekerja dengan alur human-in-the-loop yang sudah kamu kenal dari 2.3 — tool berhenti, memancarkan permintaan persetujuan, dan menunggu.

Ia juga bisa berupa fungsi, sehingga keputusannya dinamis:

ts
requireToolApproval: ({ toolName }) => toolName.startsWith('delete_'),

Fungsi ini menerima nama tool, argumen, request context, dan anotasi yang diiklankan server — cukup untuk memutuskan berdasarkan risiko sebenarnya, bukan menyetujui semuanya atau tidak sama sekali.

Membatasi host yang boleh dihubungi

Untuk server HTTP, allowedHosts adalah daftar putih host yang boleh dihubungi klien atas nama server itu:

ts
docs: {
  url: new URL('https://contoh.example.com/mcp'),
  allowedHosts: ['contoh.example.com'],
},

Beberapa aturan pencocokannya yang perlu diketahui: cocokkan persis, tidak peka huruf besar-kecil pada hostname, tidak mendukung wildcard, dan skema URL tidak diperiksa. Array kosong menolak semua permintaan; kalau tidak diisi sama sekali, tidak ada pembatasan.

forwardInstructions — hati-hati di sini

MCP server bisa mengiklankan instruksi, dan Mastra bisa menambahkannya ke system prompt agent-mu. Fitur ini mati secara default, dan dokumentasi memberi alasan yang tegas:

aktifkan hanya untuk server yang kamu percaya, karena instruksinya disuntikkan ke dalam system prompt agent.

Ini permukaan serangan, bukan sekadar fitur kenyamanan

Menyalakan forwardInstructions berarti pihak ketiga bisa menulis ke dalam system prompt agent-mu. Server yang berniat jahat — atau yang diretas — bisa menyisipkan instruksi yang mengubah perilaku agent, termasuk membujuknya membocorkan data dari tool lain.

Biarkan mati kecuali kamu benar-benar mengendalikan server itu. instructionsMaxLength (default 512 karakter) membatasi panjangnya, tapi batas panjang bukan batas niat.

Gambaran keseluruhan

Kesalahan umum

Gejala: MCP server Stdio gagal jalan atau melaporkan kredensial tidak ditemukan, padahal variabelnya jelas ada di .env aplikasimu. Penyebab: Subprocess Stdio tidak mewarisi seluruh environment prosesmu — hanya daftar putih terkurasi seperti PATH dan HOME. API key-mu tidak termasuk. Perbaikan: Sebutkan variabel yang dibutuhkan secara eksplisit di properti env pada definisi server itu.

Gejala: Seluruh aplikasi gagal dijalankan karena satu MCP server sedang tidak bisa dihubungi. Penyebab: Memakai listTools(), yang tidak memisahkan kegagalan per server. Perbaikan: Ganti ke listToolsWithErrors() dengan perServerTimeoutMs, lalu catat errorDetails dan lanjutkan dengan tool yang berhasil dimuat.

Coba sendiri

Tantangan: Sambungkan agent ke dua MCP server sekaligus — satu Stdio dan satu HTTP — lalu buktikan bahwa aplikasimu tetap hidup saat salah satunya mati.

Ketentuan:

  1. Pasang @mastra/mcp dan konfigurasikan MCPClient dengan id yang diisi
  2. Server pertama memakai transport Stdio, dan membutuhkan sebuah environment variable yang kamu teruskan lewat env
  3. Server kedua memakai url, dan dibatasi dengan allowedHosts
  4. Pakai listToolsWithErrors() dengan perServerTimeoutMs: 3000, dan cetak errors, errorDetails, serta durations
  5. Jalankan sekali dengan kedua server sehat, dan catat nama-nama tool yang muncul — perhatikan awalannya
  6. Rusak sengaja server kedua: ubah URL-nya ke host yang tidak ada. Jalankan lagi, dan pastikan agent tetap terbentuk dengan tool dari server pertama
  7. Tambahkan requireToolApproval berupa fungsi yang hanya meminta persetujuan untuk tool yang namanya mengandung kata berisiko
  8. Hapus satu variabel dari env server Stdio dan amati apa yang terjadi

Checklist penerimaan:

  • [ ] Nama tool yang muncul berawalan nama server, misalnya github_...
  • [ ] Setelah langkah 6, aplikasi tetap jalan dan errorDetails memuat penjelasan kegagalan server kedua
  • [ ] durations menunjukkan angka berbeda untuk kedua server
  • [ ] Fungsi requireToolApproval benar-benar memicu jeda hanya untuk tool yang kamu maksud, bukan semuanya
  • [ ] Langkah 8 memperlihatkan bahwa variabel di env memang tidak diwariskan otomatis
  • [ ] Kamu bisa menjelaskan kenapa forwardInstructions sebaiknya tetap mati untuk server yang bukan milikmu

Petunjuk: Langkah 6 adalah inti latihan ini. Di lingkungan pengembangan, semua server biasanya sehat, sehingga perbedaan antara listTools() dan listToolsWithErrors() tidak terlihat sama sekali — dan baru terasa di produksi pada saat yang paling tidak tepat. Rusakkan sekarang, selagi murah.

Ikhtisar

  • MCPClient menyambungkan Mastra ke satu atau beberapa MCP server. Selalu isi id untuk mencegah kebocoran memori.
  • Transport dipilih dari bentuk konfigurasi: command → Stdio, url → HTTP (Streamable HTTP dulu, jatuh ke SSE bila gagal).
  • Subprocess Stdio tidak mewarisi seluruh environment — hanya daftar putih terkurasi. Kredensial harus disebut eksplisit di env.
  • listTools() menyerahkan semua tool ke agent, dengan nama berawalan namaServer_. listToolsWithErrors() menambahkan ketahanan terhadap server yang gagal, plus errorDetails dan durations.
  • Tool eksternal adalah kode yang tidak kamu tinjau. Amankan dengan requireToolApproval (boolean atau fungsi) dan allowedHosts — yang cocoknya persis dan tidak mendukung wildcard.
  • forwardInstructions mati secara default, dan sebaiknya tetap begitu. Menyalakannya berarti mengizinkan pihak ketiga menulis ke system prompt agent-mu.

Lanjut ke mana

Sekarang arah sebaliknya — membuat tool, agent, dan workflow-mu bisa dipakai sistem lain: 6.2 Mastra sebagai MCP Server.

Materi belajar mandiri. Bukan dokumentasi resmi Mastra.