// Simpan tipe yang digunakan
// Gunakan @ts-ignore untuk bypass errors
// @ts-ignore
const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
// @ts-ignore
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
// @ts-ignore
const { CallToolRequestSchema, ListToolsRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
import { FaraidhApp, HartaData, AhliWarisData } from "./faraidhApp.js";
// Schema definitions untuk Faraidh
const HartaSchema = z.object({
totalHarta: z.number().min(1, "Total harta harus lebih besar dari 0"),
hutang: z.number().optional(),
biayaPemakaman: z.number().optional(),
wasiat: z.number().optional()
});
const AhliWarisSchema = z.object({
// Pasangan
suami: z.number().max(1, "Suami maksimal 1 orang").optional(),
istri: z.number().optional(),
// Keturunan langsung
anakLaki: z.number().optional(),
anakPerempuan: z.number().optional(),
cucuLakiDariAnakLaki: z.number().optional(),
cucuPerempuanDariAnakLaki: z.number().optional(),
// Orang tua dan kakek nenek
ayah: z.number().max(1, "Ayah maksimal 1 orang").optional(),
ibu: z.number().max(1, "Ibu maksimal 1 orang").optional(),
kakekDariAyah: z.number().optional(),
nenekDariIbu: z.number().optional(),
nenekDariAyah: z.number().optional(),
// Saudara
saudaraLakiKandung: z.number().optional(),
saudaraPerempuanKandung: z.number().optional(),
saudaraLakiSeAyah: z.number().optional(),
saudaraPerempuanSeAyah: z.number().optional(),
saudaraLakiSeIbu: z.number().optional(),
saudaraPerempuanSeIbu: z.number().optional(),
// Keponakan dan paman
anakLakiSaudaraKandung: z.number().optional(),
anakLakiSaudaraSeAyah: z.number().optional(),
pamanKandungAyah: z.number().optional(),
anakLakiPamanKandung: z.number().optional()
});
const TambahKasusSchema = z.object({
nama: z.string().min(1, "Nama kasus harus diisi"),
deskripsi: z.string().optional(),
harta: HartaSchema,
ahliWaris: AhliWarisSchema
});
const HitungLangsungSchema = z.object({
harta: HartaSchema,
ahliWaris: AhliWarisSchema
});
const IdKasusSchema = z.object({
id: z.string().min(1, "ID kasus harus diisi")
});
const UpdateKasusSchema = z.object({
id: z.string().min(1, "ID kasus harus diisi"),
nama: z.string().optional(),
deskripsi: z.string().optional(),
harta: HartaSchema.optional(),
ahliWaris: AhliWarisSchema.optional()
});
const TemplateSchema = z.object({
template: z.enum(['keluargaSederhana', 'hanyaAnakPerempuan', 'tanpaKeturunan'], {
errorMap: () => ({ message: "Template harus salah satu dari: keluargaSederhana, hanyaAnakPerempuan, tanpaKeturunan" })
})
});
// Server setup
const server = new Server(
{
name: "faraidh-mcp-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
},
);
// Inisialisasi aplikasi faraidh
const faraidhApp = new FaraidhApp();
// Register tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "tambah-kasus",
description: "Menambahkan kasus faraidh baru dengan data harta dan ahli waris",
inputSchema: zodToJsonSchema(TambahKasusSchema),
},
{
name: "hitung-warisan",
description: "Menghitung pembagian warisan untuk kasus yang sudah tersimpan",
inputSchema: zodToJsonSchema(IdKasusSchema),
},
{
name: "buat-laporan",
description: "Membuat laporan detail pembagian warisan untuk kasus tertentu",
inputSchema: zodToJsonSchema(IdKasusSchema),
},
{
name: "hitung-langsung",
description: "Menghitung pembagian warisan langsung tanpa menyimpan kasus",
inputSchema: zodToJsonSchema(HitungLangsungSchema),
},
{
name: "laporan-langsung",
description: "Membuat laporan pembagian warisan langsung tanpa menyimpan kasus",
inputSchema: zodToJsonSchema(HitungLangsungSchema),
},
{
name: "daftar-kasus",
description: "Mendapatkan daftar semua kasus faraidh yang tersimpan",
inputSchema: { type: "object", properties: {}, required: [] },
},
{
name: "lihat-kasus",
description: "Melihat detail kasus faraidh berdasarkan ID",
inputSchema: zodToJsonSchema(IdKasusSchema),
},
{
name: "hapus-kasus",
description: "Menghapus kasus faraidh berdasarkan ID",
inputSchema: zodToJsonSchema(IdKasusSchema),
},
{
name: "update-kasus",
description: "Mengupdate data kasus faraidh yang sudah tersimpan",
inputSchema: zodToJsonSchema(UpdateKasusSchema),
},
{
name: "template-kasus",
description: "Menggunakan template kasus faraidh yang sudah disediakan",
inputSchema: zodToJsonSchema(TemplateSchema),
},
{
name: "validasi-data",
description: "Memvalidasi data harta dan ahli waris sebelum perhitungan",
inputSchema: zodToJsonSchema(HitungLangsungSchema),
}
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
try {
const { name, arguments: args } = request.params;
switch (name) {
case "tambah-kasus": {
const parsed = TambahKasusSchema.safeParse(args);
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error.message}`);
}
const { nama, deskripsi = "", harta, ahliWaris } = parsed.data;
const kasusBaru = faraidhApp.tambahKasus(nama, deskripsi, harta, ahliWaris);
return {
content: [{
type: "text",
text: `✅ Kasus faraidh berhasil ditambahkan:\n\nID: ${kasusBaru.id}\nNama: ${kasusBaru.nama}\nDeskripsi: ${kasusBaru.deskripsi}\nTanggal: ${kasusBaru.createdAt.toLocaleString('id-ID')}\n\nGunakan "hitung-warisan" dengan ID ${kasusBaru.id} untuk menghitung pembagian warisan.`
}],
};
}
case "hitung-warisan": {
const parsed = IdKasusSchema.safeParse(args);
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error.message}`);
}
const hasil = faraidhApp.hitungWarisan(parsed.data.id);
return {
content: [{
type: "text",
text: `📊 Hasil perhitungan warisan:\n\n${JSON.stringify(hasil, null, 2)}\n\nGunakan "buat-laporan" untuk mendapatkan laporan yang lebih detail dan mudah dibaca.`
}],
};
}
case "buat-laporan": {
const parsed = IdKasusSchema.safeParse(args);
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error.message}`);
}
const laporan = faraidhApp.buatLaporan(parsed.data.id);
return {
content: [{
type: "text",
text: `📋 Laporan Perhitungan Faraidh:\n\n${laporan}`
}],
};
}
case "hitung-langsung": {
const parsed = HitungLangsungSchema.safeParse(args);
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error.message}`);
}
const hasil = faraidhApp.hitungLangsung(parsed.data.harta, parsed.data.ahliWaris);
return {
content: [{
type: "text",
text: `📊 Hasil perhitungan warisan langsung:\n\n${JSON.stringify(hasil, null, 2)}`
}],
};
}
case "laporan-langsung": {
const parsed = HitungLangsungSchema.safeParse(args);
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error.message}`);
}
const laporan = faraidhApp.buatLaporanLangsung(parsed.data.harta, parsed.data.ahliWaris);
return {
content: [{
type: "text",
text: `📋 Laporan Perhitungan Faraidh Langsung:\n\n${laporan}`
}],
};
}
case "daftar-kasus": {
const kasus = faraidhApp.semuaKasus();
if (kasus.length === 0) {
return {
content: [{
type: "text",
text: "📝 Belum ada kasus faraidh yang tersimpan.\n\nGunakan 'tambah-kasus' untuk menambahkan kasus baru atau 'template-kasus' untuk menggunakan template yang tersedia."
}],
};
}
const daftarText = kasus.map(k =>
`ID: ${k.id}\nNama: ${k.nama}\nDeskripsi: ${k.deskripsi}\nTanggal: ${k.createdAt.toLocaleString('id-ID')}\nStatus: ${k.hasil ? 'Sudah dihitung' : 'Belum dihitung'}`
).join('\n\n---\n\n');
return {
content: [{
type: "text",
text: `📋 Daftar Kasus Faraidh (${kasus.length} kasus):\n\n${daftarText}`
}],
};
}
case "lihat-kasus": {
const parsed = IdKasusSchema.safeParse(args);
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error.message}`);
}
const kasus = faraidhApp.dapatkanKasus(parsed.data.id);
if (!kasus) {
return {
content: [{
type: "text",
text: `❌ Kasus dengan ID ${parsed.data.id} tidak ditemukan.`
}],
};
}
return {
content: [{
type: "text",
text: `📖 Detail Kasus Faraidh:\n\n${JSON.stringify(kasus, null, 2)}`
}],
};
}
case "hapus-kasus": {
const parsed = IdKasusSchema.safeParse(args);
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error.message}`);
}
const berhasil = faraidhApp.hapusKasus(parsed.data.id);
if (!berhasil) {
return {
content: [{
type: "text",
text: `❌ Kasus dengan ID ${parsed.data.id} tidak ditemukan.`
}],
};
}
return {
content: [{
type: "text",
text: `✅ Kasus dengan ID ${parsed.data.id} berhasil dihapus.`
}],
};
}
case "update-kasus": {
const parsed = UpdateKasusSchema.safeParse(args);
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error.message}`);
}
const { id, nama, deskripsi, harta, ahliWaris } = parsed.data;
const kasusUpdate = faraidhApp.updateKasus(id, nama, deskripsi, harta, ahliWaris);
if (!kasusUpdate) {
return {
content: [{
type: "text",
text: `❌ Kasus dengan ID ${id} tidak ditemukan.`
}],
};
}
return {
content: [{
type: "text",
text: `✅ Kasus berhasil diupdate:\n\n${JSON.stringify(kasusUpdate, null, 2)}`
}],
};
}
case "template-kasus": {
const parsed = TemplateSchema.safeParse(args);
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error.message}`);
}
const templates = faraidhApp.templateKasus();
const selectedTemplate = templates[parsed.data.template];
if (!selectedTemplate) {
return {
content: [{
type: "text",
text: `❌ Template ${parsed.data.template} tidak ditemukan.`
}],
};
}
// Tambahkan kasus dari template
const kasusBaru = faraidhApp.tambahKasus(
`Template: ${parsed.data.template}`,
selectedTemplate.deskripsi,
selectedTemplate.harta,
selectedTemplate.ahliWaris
);
return {
content: [{
type: "text",
text: `✅ Kasus dari template "${parsed.data.template}" berhasil ditambahkan:\n\nID: ${kasusBaru.id}\nDeskripsi: ${selectedTemplate.deskripsi}\n\nGunakan "hitung-warisan" dengan ID ${kasusBaru.id} untuk menghitung atau "buat-laporan" untuk laporan detail.`
}],
};
}
case "validasi-data": {
const parsed = HitungLangsungSchema.safeParse(args);
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error.message}`);
}
const { harta, ahliWaris } = parsed.data;
const errorHarta = faraidhApp.validasiHarta(harta);
const errorAhliWaris = faraidhApp.validasiAhliWaris(ahliWaris);
const allErrors = [...errorHarta, ...errorAhliWaris];
if (allErrors.length === 0) {
return {
content: [{
type: "text",
text: `✅ Data valid! Tidak ada kesalahan yang ditemukan.\n\nData siap untuk perhitungan faraidh.`
}],
};
}
return {
content: [{
type: "text",
text: `❌ Ditemukan ${allErrors.length} kesalahan dalam data:\n\n${allErrors.map((err, i) => `${i + 1}. ${err}`).join('\n')}\n\nSilakan perbaiki data sebelum melakukan perhitungan.`
}],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [{ type: "text", text: `❌ Error: ${errorMessage}` }],
isError: true,
};
}
});
// Start server
export async function startMcpServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
}