Skip to content

10.13 Integrasi lewat MCP

Topologi: C — Supervisor + Subagent Materi yang dipakai: 6.1, 6.2, 6.3Prasyarat: 10.2, 10.10

Masalahnya

Asisten internal perusahaan perlu dua hal yang berlawanan arah.

Ke luar: ia harus bisa membaca issue GitHub dan mencari di web. Menulis integrasi GitHub sendiri berarti menangani autentikasi, paginasi, rate limit, dan perubahan API — pekerjaan berminggu-minggu untuk sesuatu yang sudah ada.

Ke dalam: tim lain ingin memakai tool data karyawan yang kamu bangun di 10.2 dari editor mereka sendiri — Cursor, Claude Desktop — tanpa kamu membuatkan aplikasi untuk mereka.

Dua kebutuhan, satu protokol.

Keputusan arsitektur

Keputusan 1 — Bangun sendiri atau pakai MCP server yang ada?

Tulis tool sendiriPakai MCP server pihak ketiga
WaktuBerminggu-mingguMenit
Perawatan saat API berubahTanggunganmuTanggungan pemelihara
Kendali atas perilakuPenuhTerbatas
Risiko keamananKode yang kamu tinjauKode yang tidak kamu tinjau
KetergantunganNolPada proyek orang lain

Pilihan: pakai MCP server yang ada untuk GitHub, tulis sendiri untuk data internal.

Pembagiannya mengikuti satu garis: data internal tidak pernah lewat kode pihak ketiga. Tool data karyawan tetap kamu tulis; integrasi GitHub yang sudah ada dipakai apa adanya.

Keputusan 2 — Mengamankan tool yang bukan tulisanmu

Baris keempat tabel di atas bukan formalitas. MCP server pihak ketiga adalah kode yang tidak kamu tinjau dan bisa berubah tanpa memberitahumu.

Tiga lapis pengamanan dari 6.1:

LapisFungsi
requireToolApprovalHentikan tool berisiko sebelum jalan
allowedHostsBatasi host yang boleh dihubungi
forwardInstructions: falseJangan biarkan server menulis ke system prompt-mu

forwardInstructions adalah permukaan serangan

Dokumentasi menyatakan fitur ini mati secara default, dan menyarankan menyalakannya hanya untuk server yang kamu percaya.

Alasannya: instruksi dari server disuntikkan ke system prompt agent-mu. Server yang berniat jahat — atau yang akunnya diretas — bisa menyisipkan instruksi yang mengubah perilaku agent, termasuk membujuknya membocorkan data dari tool lain yang kamu tulis sendiri.

Biarkan mati kecuali kamu mengendalikan server itu.

Keputusan 3 — Apa yang diekspos sebagai MCP server?

Ini keputusan yang paling sering kebablasan.

Layak dieksposJangan diekspos
Tool baca-sajaTool yang menghapus data
Data yang memang boleh dibagiTool yang memakai kredensial pribadimu
Kemampuan yang stabilTool yang mahal dipanggil

Pilihan: hanya tool baca. cek-sisa-cuti dan cek-status-reimburse diekspos; tool yang mengubah keadaan tidak.

Ingat juga dari 6.2: kalau pemakainya hanya agent di aplikasimu sendiri, kamu tidak butuh MCP server sama sekali — cukup import. MCP hanya untuk klien eksternal.

Keputusan 4 — Transport

stdioHTTP/SSE
Cara pakaiKlien menjalankan servermu sebagai subprocessServermu jadi layanan
Cocok untukEditor lokal (Cursor, Claude Desktop)Akses jarak jauh
AutentikasiLewat env subprocessWajib — ini menghadap jaringan

Pilihan: stdio untuk versi pertama. Tim menjalankannya lokal dari editor masing-masing, dan tidak ada yang menghadap internet.

Struktur direktori

text
asisten-internal/
├── src/
│   ├── domain/
│   │   └── karyawan.ts              ← dari 10.2, tidak berubah
│   ├── mastra/
│   │   ├── agents/
│   │   │   └── asisten-agent.ts     ← memakai tool internal + MCP
│   │   ├── tools/
│   │   │   ├── cek-sisa-cuti.ts     ← tool internal
│   │   │   └── cek-status-reimburse.ts
│   │   ├── mcp/
│   │   │   ├── client.ts            ← KE LUAR: pakai server orang lain
│   │   │   └── server.ts            ← KE DALAM: sediakan untuk orang lain
│   │   └── index.ts
│   └── bin/
│       └── mcp-stdio.ts             ← entry point yang dijalankan editor
└── package.json

Dua berkas di mcp/ mewakili dua arah yang berlawanan. Menempatkannya berdampingan membuat perbedaannya jelas bagi pembaca berikutnya.

Alur

Perhatikan TI (tool internal) menunjuk ke dua arah: dipakai agent-mu sendiri, dan diekspos lewat MCP server. Satu definisi tool, dua pemakai.

Implementasi

src/mastra/mcp/client.ts

ts
import { MCPClient } from '@mastra/mcp'

export const mcp = new MCPClient({
  // id wajib diisi — mencegah kebocoran memori saat ada beberapa instance.
  id: 'mcp-eksternal',

  servers: {
    github: {
      command: 'npx',
      args: ['-y', '@modelcontextprotocol/server-github'],
      // Subprocess TIDAK mewarisi env prosesmu. Sebutkan eksplisit.
      env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN! },

      // Tool yang mengubah keadaan butuh persetujuan.
      requireToolApproval: ({ toolName }) =>
        /create|update|delete|merge|close/i.test(toolName),

      // Biarkan mati. Lihat peringatan di keputusan 2.
      forwardInstructions: false,
    },

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

  timeout: 60_000,
})

Tiga hal yang sering salah dan benar di sini.

env disebut eksplisit. Ingat dari 6.1: subprocess Stdio hanya mewarisi daftar putih terkurasi (PATH, HOME, dan beberapa lainnya). GITHUB_TOKEN tidak termasuk.

requireToolApproval berupa fungsi, bukan true. Menyetujui semua tool membuat pengguna lelah dan akhirnya menyetujui tanpa membaca. Batasi pada yang benar-benar mengubah keadaan.

allowedHosts tidak mendukung wildcard. Pencocokannya persis.

src/mastra/agents/asisten-agent.ts

ts
import { Agent } from '@mastra/core/agent'
import { mcp } from '../mcp/client.ts'
import { cekSisaCuti } from '../tools/cek-sisa-cuti.ts'
import { cekStatusReimburse } from '../tools/cek-status-reimburse.ts'

// Pola tahan banting: satu server mati tidak menjatuhkan agent.
const { tools: toolMcp, errors, durations } = await mcp.listToolsWithErrors({
  perServerTimeoutMs: 3_000,
})

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

export const asistenAgent = new Agent({
  id: 'asisten-agent',
  name: 'Asisten Internal',
  instructions: `Kamu asisten internal perusahaan.

Pakai cekSisaCuti dan cekStatusReimburse untuk data karyawan.
Pakai tool github_* untuk pertanyaan tentang issue dan pull request.

Untuk tindakan yang mengubah data di GitHub, jelaskan dulu apa yang akan kamu
lakukan sebelum menjalankannya.`,
  model: 'openai/gpt-5.6-sol',

  // Tool internal dan tool MCP digabung — agent tidak membedakannya.
  tools: { cekSisaCuti, cekStatusReimburse, ...toolMcp },
})

Pakai listToolsWithErrors(), bukan listTools(). Perbedaannya baru terasa di produksi: dengan listTools(), satu MCP server yang sedang mati membuat seluruh agent gagal dibentuk. Dengan versi ini, agent tetap hidup dengan tool yang berhasil dimuat.

durations juga berguna — ia langsung memberitahu server mana yang membuat startup lambat.

src/mastra/mcp/server.ts

ts
import { MCPServer } from '@mastra/mcp'
import { cekSisaCuti } from '../tools/cek-sisa-cuti.ts'
import { cekStatusReimburse } from '../tools/cek-status-reimburse.ts'

export const mcpServer = new MCPServer({
  id: 'hr-internal',
  name: 'HR Internal',
  version: '1.0.0',
  description:
    'Akses baca data kepegawaian: sisa kuota cuti dan status reimbursement.',

  instructions:
    'Pakai tool ini untuk menjawab pertanyaan tentang kuota cuti dan status ' +
    'reimbursement karyawan. Semua tool bersifat baca-saja. NIK berformat K-XXX.',

  // HANYA tool baca. Tool yang mengubah keadaan tidak diekspos.
  tools: { cekSisaCuti, cekStatusReimburse },
})

instructions server ditulis untuk system prompt orang lain

Klien bisa memilih menambahkan teks ini ke system prompt agent mereka lewat forwardInstructions.

Karena itu tulis yang membantu — cara memakai, format yang diharapkan — bukan yang memaksa. Kamu sedang menulis untuk agent yang tidak kamu kendalikan.

src/bin/mcp-stdio.ts

ts
import { mcpServer } from '../mastra/mcp/server.ts'

// Dijalankan oleh editor sebagai subprocess.
await mcpServer.startStdio()

Konfigurasi di sisi editor:

json
{
  "mcpServers": {
    "hr-internal": {
      "command": "node",
      "args": ["/path/ke/asisten-internal/dist/bin/mcp-stdio.js"],
      "env": { "DATABASE_URL": "..." }
    }
  }
}

Nama tool yang dilihat klien

Ini bagian yang paling sering mengejutkan, dan ada di kedua arah.

Saat kamu jadi klien — tool dari MCP server diberi awalan nama server:

text
github_create_issue
github_list_pull_requests
docs_search

Awalan itu mencegah tabrakan saat dua server punya tool bernama sama.

Saat kamu jadi server — pola penamaannya dari 6.2:

Yang didaftarkanMenjadi tool bernama
tools: { cekSisaCuti }cekSisaCuti
agents: { hrAgent }ask_hrAgent
workflows: { approvalWorkflow }run_approvalWorkflow

Karena key menjadi nama publik, pilih key yang deskriptif. ask_a tidak memberitahu klien apa pun.

Bagian ini tidak diverifikasi end-to-end

Berbeda dari kasus lain di bagian ini, jabat tangan MCP langsung tidak dijalankan di sini — ia membutuhkan MCP server eksternal dan editor yang berjalan.

Yang sudah diverifikasi: seluruh bentuk konstruksi MCPClient dan MCPServer di atas lolos type-check terhadap @mastra/mcp 1.17.3, termasuk requireToolApproval berupa fungsi, allowedHosts, dan listToolsWithErrors().

Yang belum: perilaku runtime saat benar-benar tersambung ke server pihak ketiga. Uji sendiri sebelum mengandalkannya di produksi.

A2A: kalau yang di seberang adalah agent utuh

MCP menyeberangi batas ke tool. Kalau yang ingin kamu panggil adalah agent milik tim lain — dengan penalaran, tool, dan memory-nya sendiri yang tetap privat — itu A2A.

ts
import { A2AAgent } from '@mastra/core/a2a'

const agentTimLain = new A2AAgent({
  url: 'https://finance.internal.example.com/api/.well-known/finance-agent/agent-card.json',
})

const hasil = await agentTimLain.generate('Berapa anggaran tersisa untuk Q4?')

Yang menarik: agent Mastra-mu sudah otomatis mengekspos agent card begitu servernya berjalan, di /api/.well-known/<id>/agent-card.json. Kamu tidak perlu menulis kode apa pun untuk menjadi penyedia A2A.

Pakai MCP kalauPakai A2A kalau
Yang diseberang adalah fungsiYang diseberang punya penalaran sendiri
Kamu ingin memakai tool-nyaKamu ingin mendelegasikan pekerjaan
Hasilnya dataHasilnya penilaian

Best practice yang dipakai di sini

1. Data internal tidak lewat kode pihak ketiga. Tool data karyawan ditulis sendiri; integrasi umum dipakai dari ekosistem.

2. forwardInstructions: false untuk server pihak ketiga. Selalu.

3. requireToolApproval sebagai fungsi, bukan true. Persetujuan yang terlalu sering diabaikan pengguna.

4. listToolsWithErrors() di produksi. Satu server mati tidak boleh menjatuhkan agent.

5. Hanya tool baca yang diekspos. Tool yang mengubah keadaan tetap di dalam.

6. Satu definisi tool, dua pemakai. Tool yang sama dipakai agent internal dan diekspos lewat MCP — tidak ada duplikasi.

Kapan pola ini salah

SituasiKenapa gagalKe mana
Pemakainya cuma agent di aplikasi yang samaMCP menambah protokol tanpa manfaatCukup import
Yang diseberang punya penalaran sendiriMCP untuk tool, bukan agentA2A
Butuh menjalankan coding agent di repoBukan wilayah MCP10.14 Coding agent
Server MCP harus diakses dari internetstdio tidak cukupHTTP/SSE + autentikasi, 9.3

Coba sendiri

Sambungkan agent ke satu MCP server nyata (misalnya filesystem atau GitHub), lalu ekspos satu tool buatanmu sendiri dan pakai dari editormu.

Selesai kalau

  • [ ] Tool dari MCP server eksternal muncul dengan awalan nama server
  • [ ] listToolsWithErrors() dipakai, dan kamu sengaja merusak satu server untuk membuktikan agent tetap hidup
  • [ ] durations menunjukkan waktu penemuan tiap server
  • [ ] Tool buatanmu terlihat di editor dan bisa dipanggil dari sana
  • [ ] forwardInstructions mati untuk server pihak ketiga
  • [ ] requireToolApproval hanya memicu untuk tool yang benar-benar berisiko
  • [ ] Kamu bisa menyebutkan satu tool di sistemmu yang sengaja tidak kamu ekspos, beserta alasannya

Kalau macet

MCP server Stdio gagal autentikasi padahal .env sudah benar? Subprocess tidak mewarisi env prosesmu. Sebutkan variabelnya di properti env.

Seluruh aplikasi gagal jalan karena satu server mati? Kamu memakai listTools(). Ganti ke listToolsWithErrors().

Tool-mu tidak muncul di editor? Periksa path di konfigurasi editor menunjuk berkas hasil build, bukan .ts mentah.

Lanjut ke mana

MCP memberi agent akses ke tool orang lain. Kasus berikutnya memberinya sesuatu yang lebih berbahaya dan lebih berguna: tempat kerja nyata — berkas, shell, dan kemampuan menjalankan kode — 10.14 Coding Agent & Sandbox.

Materi belajar mandiri. Bukan dokumentasi resmi Mastra.