Hubungkan sistem internal perusahaan ke Claude Code via MCP — dari server fetch generik hingga menulis MCP Server khusus, mencakup platform deploy, monitoring, tiket, dan prinsip desain tool.
Artikel sebelumnya membahas koneksi ke database. Database punya protokol standar, jadi integrasinya relatif mudah. Tapi sebagian besar tim sehari-hari juga bergantung pada berbagai sistem internal: platform deployment, dashboard monitoring, sistem tiket, API internal, pusat konfigurasi.
Sistem-sistem ini biasanya tidak punya MCP Server siap pakai, tapi hampir semuanya menyediakan HTTP API. Artikel ini membahas cara menggunakan MCP untuk menghubungkan tool internal ini ke Claude Code, sehingga Claude bisa langsung membantu Anda mengecek monitoring, melihat status deployment, dan mengelola tiket.
Ada dua cara untuk mengintegrasikan tool internal:
Cara pertama: Gunakan MCP Server HTTP generik
Komunitas menyediakan beberapa MCP Server generik yang bisa membungkus REST API apa pun menjadi tool MCP. Anda cukup menulis file deskripsi API, dan server akan mengubahnya menjadi tool yang bisa dipanggil Claude. Cocok untuk skenario dengan struktur API sederhana yang tidak memerlukan logika kompleks.
Cara kedua: Buat MCP Server sendiri
Gunakan MCP SDK TypeScript atau Python untuk membuat Server khusus, dengan kontrol penuh atas definisi tool, validasi parameter, dan penanganan error. Cocok untuk skenario yang memerlukan penggabungan beberapa API, transformasi data, atau penambahan logika bisnis.
Artikel ini membahas kedua cara tersebut, dimulai dari yang sederhana.
Solusi paling ringan adalah menggunakan @anthropic-ai/mcp-server-fetch dari Anthropic, yang memungkinkan Claude mengirim HTTP request secara langsung. Konfigurasinya sangat simpel:
{
"mcpServers": {
"fetch": {
"command": "npx",
"args": ["-y", "@anthropic-ai/mcp-server-fetch"]
}
}
}
Setelah dikonfigurasi, Claude bisa langsung memanggil API internal:
Tolong cek status terkini dari platform deployment https://deploy.internal.com/api/v1/services/user-service
Claude akan mengirim GET request, menerima respons, dan menampilkan hasilnya untuk Anda.
Tapi cara ini punya keterbatasan yang jelas:
Cocok untuk penggunaan sementara, tidak cocok untuk solusi jangka panjang.
Ketika Anda perlu menggunakan sistem internal secara berulang, membuat MCP Server khusus adalah pilihan yang lebih baik. Berikut demo menggunakan skenario nyata: mengintegrasikan platform deployment perusahaan.
Misalkan platform deployment Anda menyediakan API berikut:
GET /api/v1/services — Daftar semua layananGET /api/v1/services/:name/status — Lihat status layananPOST /api/v1/services/:name/deploy — Trigger deploymentGET /api/v1/services/:name/logs — Lihat log deployment terbaruInisialisasi project terlebih dahulu:
mkdir mcp-deploy && cd mcp-deploy
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
npx tsc --init
Kode inti src/index.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const API_BASE = process.env.DEPLOY_API_URL!;
const API_TOKEN = process.env.DEPLOY_API_TOKEN!;
async function api(path: string, method = "GET", body?: unknown) {
const res = await fetch(`${API_BASE}${path}`, {
method,
headers: {
Authorization: `Bearer ${API_TOKEN}`,
"Content-Type": "application/json",
},
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) {
throw new Error(`API error: ${res.status} ${await res.text()}`);
}
return res.json();
}
const server = new McpServer({
name: "deploy-platform",
version: "1.0.0",
});
// Daftar semua layanan
server.tool("list_services", "列出部署平台上的所有服务及其状态", {}, async () => {
const data = await api("/api/v1/services");
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
});
// Lihat status layanan tertentu
server.tool(
"service_status",
"查看指定服务的当前部署状态、版本号和健康检查结果",
{ name: z.string().describe("服务名称,如 user-service") },
async ({ name }) => {
const data = await api(`/api/v1/services/${name}/status`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
// Lihat log deployment
server.tool(
"deploy_logs",
"查看指定服务最近的部署日志",
{
name: z.string().describe("服务名称"),
limit: z.number().optional().default(10).describe("返回条数,默认 10"),
},
async ({ name, limit }) => {
const data = await api(`/api/v1/services/${name}/logs?limit=${limit}`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
// Trigger deployment
server.tool(
"trigger_deploy",
"触发指定服务的部署。这是一个写操作,会实际影响生产环境",
{
name: z.string().describe("服务名称"),
version: z.string().describe("要部署的版本号或 git ref"),
},
async ({ name, version }) => {
const data = await api(`/api/v1/services/${name}/deploy`, "POST", {
version,
});
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
const transport = new StdioServerTransport();
server.connect(transport);
Kompilasi:
npx tsc
{
"mcpServers": {
"deploy": {
"command": "node",
"args": ["/path/to/mcp-deploy/dist/index.js"],
"env": {
"DEPLOY_API_URL": "https://deploy.internal.com",
"DEPLOY_API_TOKEN": "your-api-token"
}
}
}
}
Simpan token di .claude/settings.local.json (tidak di-commit ke git), dan URL di .claude/settings.json (di-commit ke git, dibagikan ke tim).
Setelah konfigurasi selesai, percakapan menjadi lebih natural:
Apa status user-service sekarang?
→ Claude memanggil service_status("user-service")
→ Hasil: berjalan, versi v2.3.1, deployment terakhir 2 jam lalu, health check semua lolos
Apakah ada deployment yang gagal belakangan ini?
→ Claude memanggil deploy_logs("user-service", 20)
→ Menganalisis log, memberi tahu bahwa deployment ke-3 di-rollback karena health check timeout
Deploy user-service ke v2.3.2
→ Claude memanggil trigger_deploy("user-service", "v2.3.2")
→ Karena deskripsi tool mencantumkan "operasi tulis", Claude akan meminta konfirmasi terlebih dahulu
Ini adalah pertanyaan yang perlu dipertimbangkan dengan serius.
Operasi baca aman untuk diintegrasikan. Mengecek status, melihat log, mencari tiket — operasi ini tidak punya efek samping, tidak ada kerugian jika Claude melakukan kesalahan.
Operasi tulis terbagi dua:
Operasi tulis berisiko rendah bisa diintegrasikan, tapi cantumkan dengan jelas di deskripsi tool. Claude secara otomatis akan meminta konfirmasi pengguna untuk operasi yang ditandai memiliki efek samping. Misalnya membuat tiket, mengirim pesan, memperbarui konfigurasi.
Operasi tulis berisiko tinggi sebaiknya tidak diintegrasikan. Menghapus resource, trigger rollback, mengubah permission — konsekuensi operasi ini serius dan tidak bisa dibatalkan, lebih aman dilakukan secara manual.
Jika Anda tetap ingin mengintegrasikan operasi tulis, minimal lakukan dua hal:
| Sistem | Tool yang Diekspos | Catatan |
|---|---|---|
| Platform Deployment (K8s / Kamal) | Cek status layanan, lihat log, trigger deployment | Tambahkan konfirmasi untuk operasi tulis |
| Sistem Monitoring (Grafana / Datadog) | Lihat metrik, lihat riwayat alert | Batasi rentang waktu query, hindari menarik terlalu banyak data |
| Sistem Tiket (Jira / Linear) | Cari tiket, buat tiket, perbarui status | Membuat tiket adalah operasi tulis, tapi berisiko rendah |
| Dokumentasi Internal (Notion / Confluence) | Cari dokumen, baca isi halaman | Perhatikan paginasi, jangan menarik terlalu banyak sekaligus |
| Pusat Konfigurasi (Consul / etcd) | Baca konfigurasi, bandingkan perbedaan antar environment | Hanya lakukan baca, jangan integrasikan operasi tulis |
| CI/CD (GitHub Actions / Jenkins) | Lihat status build, trigger build | Trigger build termasuk operasi tulis risiko menengah |
Menulis tool MCP berbeda dengan menulis API. API ditujukan untuk programmer, tool ditujukan untuk AI. Ada beberapa prinsip yang perlu diperhatikan:
Nama tool harus jelas dan langsung
✗ get_svc_stat — Claude belum tentu bisa menebak arti singkatan
✓ service_status — Langsung tahu fungsinya
Deskripsi ditulis untuk AI
Deskripsi tool bukan dokumentasi untuk manusia, melainkan acuan bagi Claude untuk menentukan "kapan harus memanggil tool ini". Deskripsi harus menjelaskan: apa yang dilakukan tool ini, apa yang dikembalikan, dan kapan harus digunakan.
✗ "Ambil status layanan"
✓ "Lihat status deployment terkini, nomor versi, dan hasil health check dari layanan yang ditentukan. Gunakan ketika pengguna menanyakan apakah suatu layanan berjalan normal"
Definisikan parameter dengan jelas menggunakan zod
Parameter yang dilengkapi .describe() membantu Claude mengetahui apa yang harus diisi. Tanpa deskripsi, Claude hanya bisa menebak berdasarkan nama parameter.
Kembalikan data terstruktur
Tool MCP mengembalikan teks, tapi usahakan mengembalikan JSON yang terformat. Claude memproses data terstruktur jauh lebih akurat dibanding teks biasa.
Granularitas yang tepat
Jangan memasukkan alur kerja kompleks ke dalam satu tool. Juga jangan memecah satu query sederhana menjadi tiga tool. Prinsipnya: satu tool menyelesaikan satu operasi mandiri yang bermakna.
Kode MCP Server bisa ditempatkan di beberapa lokasi:
Di dalam repository project (direkomendasikan untuk memulai)
your-project/
├── .claude/settings.json
├── mcp-servers/
│ └── deploy/
│ ├── src/index.ts
│ ├── package.json
│ └── tsconfig.json
└── ...
Keuntungannya adalah kode dan konfigurasi berada di satu tempat, tim tinggal clone dan install dependensi untuk langsung bisa pakai.
Repository terpisah
Ketika MCP Server digunakan di beberapa project, simpan di repository terpisah dan publikasikan sebagai npm package atau Docker image.
{
"mcpServers": {
"deploy": {
"command": "npx",
"args": ["-y", "@yourcompany/mcp-deploy-server"]
}
}
}
Instalasi global
Untuk MCP Server yang digunakan di seluruh perusahaan (misalnya integrasi dengan autentikasi terpadu atau platform logging terpadu), install secara global dan konfigurasi di ~/.claude/settings.json.
Masalah yang paling umum saat mengembangkan MCP Server adalah "Claude tidak memanggil tool saya" atau "dipanggil tapi error".
Pastikan Server sudah berjalan
Setelah restart Claude Code, ketik /mcp untuk melihat daftar MCP Server yang terhubung. Jika Server Anda tidak ada di daftar, periksa apakah command dan args sudah benar.
Tes Server secara mandiri
MCP Server berkomunikasi via stdio, bisa langsung diuji di terminal:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node dist/index.js
Jika mengembalikan daftar tool, berarti Server itu sendiri tidak bermasalah.
Lihat log pemanggilan tool Claude
Claude Code menampilkan input dan output setiap pemanggilan tool. Jika parameter salah, biasanya deskripsi tool atau definisi parameter kurang jelas sehingga Claude salah memahami.
Mari kita rangkai semuanya dengan kasus nyata. Misalkan Anda ingin mengintegrasikan Sentry ke Claude Code agar bisa langsung memeriksa error di production.
server.tool(
"search_errors",
"在 Sentry 中搜索最近的错误。用于排查线上问题、查看错误趋势",
{
query: z.string().describe("搜索关键词,如错误信息、函数名"),
hours: z.number().optional().default(24).describe("查看最近多少小时的错误"),
},
async ({ query, hours }) => {
const since = new Date(Date.now() - hours * 3600000).toISOString();
const data = await api(
`/api/0/projects/${ORG}/${PROJECT}/issues/?query=${encodeURIComponent(query)}&start=${since}&sort=date`
);
const summary = data.map((issue: any) => ({
title: issue.title,
count: issue.count,
firstSeen: issue.firstSeen,
lastSeen: issue.lastSeen,
link: issue.permalink,
}));
return {
content: [{ type: "text", text: JSON.stringify(summary, null, 2) }],
};
}
);
server.tool(
"error_details",
"查看 Sentry 中某个错误的详细信息,包括堆栈和最近一次事件",
{ issueId: z.string().describe("Sentry issue ID") },
async ({ issueId }) => {
const [issue, latest] = await Promise.all([
api(`/api/0/issues/${issueId}/`),
api(`/api/0/issues/${issueId}/events/latest/`),
]);
return {
content: [
{
type: "text",
text: JSON.stringify(
{
title: issue.title,
count: issue.count,
users: issue.userCount,
stacktrace: latest.entries?.find(
(e: any) => e.type === "exception"
),
},
null,
2
),
},
],
};
}
);
Setelah terintegrasi, percakapan untuk menyelidiki masalah production menjadi seperti ini:
Apakah ada error 500 baru dalam 4 jam terakhir?
→ Claude mencari di Sentry
→ Menemukan 3 issue baru, yang paling parah mempengaruhi 120 pengguna
→ Otomatis menarik stack trace, menemukan null pointer exception
→ Menemukan lokasi yang sesuai di kode, memberikan solusi perbaikan
Dari menemukan masalah hingga menemukan kode yang bermasalah, seluruh proses selesai dalam satu percakapan.
Artikel ini membahas cara menggunakan MCP untuk mengintegrasikan tool internal. Ide intinya adalah: sistem internal punya HTTP API → buat MCP Server sebagai pembungkus → Claude bisa langsung menggunakannya.
Semua contoh dalam artikel ini membungkus API yang sudah ada — platform deployment dan Sentry sudah punya antarmuka sendiri, MCP Server hanya melakukan penerusan dan adaptasi. Artikel berikutnya akan membahas skenario yang berbeda: ketika kemampuan yang Anda butuhkan tidak memiliki API siap pakai, bagaimana membangun MCP Server dari nol, mengimplementasikan logika sendiri, mengelola state, dan menangani interaksi multi-langkah yang kompleks.