Skip to main content
Glama

opzet_hypotheek_doorstromer

Calculate mortgage options for homeowners moving to a new property. Compare existing versus new monthly payments, required financing amounts, and loan components to plan your move effectively.

Instructions

Berekent de hypotheekopzet voor doorstromers met bestaande woning. Output: benodigd bedrag, financiering per component en maandlasten (bestaand versus nieuw).

Invoerbeleid bestaande hypotheek (verplicht expliciet vragen):

  • Stel altijd de vraag: "Wilt u een snelle globale berekening (met een samenvatting van uw hypotheek) of een detailberekening waarbij u alle leningdelen invoert?"

  • Bij snelle globale berekening: laat de gebruiker één samenvattende set waarden geven (totale schuld, gemiddelde rente, resterende looptijd, optioneel huidige maandlast) en vul hiermee één leningdeel.

  • Bij detailberekening: laat de gebruiker alle leningdelen kopiëren/plakken (hoofdsom, rente, looptijd, rentevast, hypotheekvorm) en vul de leningdelen-array één-op-één.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
aanvragerYesGegevens van de (hoofd)aanvrager. Vraag altijd: "Wat is uw leeftijd of geboortedatum?" en gebruik opgegeven leeftijden alleen intern.
waarde_huidige_woningYesMarktwaarde van de huidige woning.
bestaande_hypotheekYesBestaande leningdelen voor doorstromer (detailuitleg: hypotheek://v4/guide/opzet-intake). VRAAG ALTIJD: "Wilt u een snelle globale berekening (met een samenvatting van uw hypotheek) of een detailberekening waarbij u alle leningdelen invoert?"
nieuwe_woningYesKerngegevens nieuwe woning (detailuitleg: hypotheek://v4/guide/opzet-intake).
session_idNoOptioneel sessie-ID vanuit n8n (voor logging).

Implementation Reference

  • Main handler function that processes the tool request: normalizes and validates input, builds API payload for the Replit opzet endpoint, calls the external API, logs success, and returns a formatted response.
    async function handleOpzetDoorstromer(request: any): Promise<ToolResponse> { const rawArgs = requireArguments<OpzetDoorstromerArguments>(request); const normalizedArgs = normalizeOpzetDoorstromerArgs(rawArgs) as OpzetDoorstromerArguments; const logger = createLogger(normalizedArgs.session_id); const aanvrager = requireOpzetAanvrager(normalizedArgs); validateOpzetAanvrager(aanvrager); validateBestaandeHypotheek(normalizedArgs.bestaande_hypotheek); enforceRateLimit(normalizedArgs.session_id); const payload: any = { aanvrager: mapOpzetAanvrager(aanvrager), bestaande_hypotheek: { waarde_huidige_woning: normalizedArgs.waarde_huidige_woning, leningdelen: normalizedArgs.bestaande_hypotheek.leningdelen, }, nieuwe_woning: { waarde_woning: normalizedArgs.nieuwe_woning.waarde_woning, bedrag_verbouwen: normalizedArgs.nieuwe_woning.bedrag_verbouwen ?? 0, bedrag_verduurzamen: normalizedArgs.nieuwe_woning.bedrag_verduurzamen ?? 0, kosten_percentage: normalizedArgs.nieuwe_woning.kosten_percentage ?? 0.05, energielabel: normalizeEnergielabel(normalizedArgs.nieuwe_woning.energielabel || ''), }, }; if (normalizedArgs.session_id) { payload.session_id = normalizedArgs.session_id; } const apiClient = getApiClient(); const { data } = await apiClient.post( REPLIT_API_URL_OPZET, payload, { correlationId: normalizedArgs.session_id } ); logger.info('Toolcall succesvol', { tool: 'opzet_hypotheek_doorstromer' }); return successResponse(formatResponse(data, "opzet_hypotheek_doorstromer")); }
  • src/index.ts:903-938 (registration)
    Tool registration in MCP ListToolsRequestHandler: defines name, detailed description with usage instructions, and complete inputSchema with properties and requirements.
    { name: "opzet_hypotheek_doorstromer", description: `Opzet-berekening voor doorstromers met een CONCRETE nieuwe woning. Gebruik dit zodra er een koopprijs/verbouwing bekend is; hiermee ziet de gebruiker exact hoe bestaand en nieuw samenkomen. Voor algemene verhuis-oriëntatie zonder specifieke woning gebruikt u de maximale-hypotheek tools. **Invoerbeleid bestaande hypotheek (verplicht expliciet vragen):** - Stel altijd de vraag: "Wilt u een snelle globale berekening (met een samenvatting van uw hypotheek) of een detailberekening waarbij u alle leningdelen invoert?" - Bij snelle globale berekening: laat de gebruiker één samenvattende set waarden geven (totale schuld, gemiddelde rente, resterende looptijd, optioneel huidige maandlast) en vul hiermee één leningdeel. - Bij detailberekening: laat de gebruiker alle leningdelen kopiëren/plakken (hoofdsom, rente, looptijd, rentevast, hypotheekvorm) en vul de leningdelen-array één-op-één.`, inputSchema: { type: "object", description: `Gebruik basisintake, huidige woning en bestaande leningdelen; zie ${OPZET_GUIDE_URI} voor detailvelden en defaults.`, properties: { aanvrager: aanvragerSchema, waarde_huidige_woning: { type: "number", description: "Marktwaarde van de huidige woning.", }, bestaande_hypotheek: { ...bestaandeHypotheekSchema, }, nieuwe_woning: { ...nieuweWoningSchema, }, session_id: { type: "string", description: "Optioneel sessie-ID vanuit n8n (voor logging).", }, }, required: [ "aanvrager", "waarde_huidige_woning", "bestaande_hypotheek", "nieuwe_woning", ], }, },
  • TypeScript interface defining the expected input structure for the tool arguments.
    interface OpzetDoorstromerArguments extends OpzetBaseArguments { nieuwe_woning: NieuweWoning; waarde_huidige_woning: number; bestaande_hypotheek: BestaandeHypotheek; } interface Renteklasse { naam: string; lowerbound_ltv_pct: number; higherbound_ltv_pct: number; nhg: boolean; rente_jaarlijks_pct: number; } interface OpzetNieuweLening { looptijd_jaren?: number; rentevast_periode_jaren?: number; nhg?: boolean; renteklassen?: Renteklasse[]; } interface OpzetUitgebreidArguments extends OpzetBaseArguments { nieuwe_woning: NieuweWoning; is_doorstromer?: boolean; waarde_huidige_woning?: number; bestaande_hypotheek?: BestaandeHypotheek; nieuwe_lening?: OpzetNieuweLening; } const OPZET_GUIDE_URI = 'hypotheek://v4/guide/opzet-intake'; const DOORSTROMER_OUTPUT_GUIDANCE = ` **Outputvelden (altijd rechtstreeks gebruiken in de terugkoppeling):** - max_woningbudget → woningbudget inclusief overwaarde en extra leencapaciteit - overwaarde_bedrag → vrijvallende winst uit de huidige woning - huidige_hypotheek_schuld → resterende schuld die moet worden afgelost - extra_leencapaciteit → nieuwe hypotheekruimte bovenop de overwaarde - maandlast_nu, maandlast_straks en verschil_maandlast → huidige, toekomstige en delta maandlast **Presentatie richting gebruiker (één compact blok):** - Toon het woningbudget centraal onder de titel "Uw woningbudget" en licht toe waaruit dit bedrag bestaat in bullets (overwaarde, huidige hypotheek, extra leencapaciteit). - Voeg een tweede blok toe "Uw nieuwe maandlast" met maandlast nu, maandlast straks en het verschil (positief/negatief) op eigen regel. - Gebruik alleen MCP-waarden; geen eigen herberekeningen behalve eenvoudige weergave/afronding. **Invoerkeuze bestaande hypotheek (verplicht expliciet vragen):** 1. Snelle globale berekening → gebruiker geeft een samenvatting (totale schuld, gemiddelde rente/looptijd, eventuele huidige maandlast). Vul één leningdeel met deze totaalwaarden in. 2. Detailberekening → gebruiker levert alle leningdelen (hoofdsom, rente, resterende looptijd, hypotheekvorm). Kopieer ze één-op-één in de leningdelen array. Vraag altijd: "Wilt u een snelle globale berekening (met een samenvatting van uw hypotheek) of een detailberekening waarbij u alle leningdelen invoert?" en volg de gekozen route.`; // BESLISBOOM VOOR AI-AGENTS // 1. Heeft de gebruiker een concrete woning/koopprijs in gedachten? // - Ja → gebruik altijd een OPZET tool (starter, doorstromer of uitgebreid). // - Nee → gebruik een MAXIMALE HYPOTHEEK tool. // 2. Is de gebruiker starter (nog geen eigen woning) of doorstromer (heeft al woning/hypotheek)? // - Kies de starter- of doorstromervariant van het gekozen pad (maximaal of opzet). // 3. Wil de gebruiker specifiek spelen met looptijd/rente/hypotheekvorm? // - Nee → kies de standaard tool (opzet_hypotheek_starter/doorstromer of bereken_hypotheek_starter/doorstromer). // - Ja → gebruik de uitgebreide variant (opzet_hypotheek_uitgebreid of bereken_hypotheek_uitgebreid) en vraag naar de gewenste parameters. // Leeftijd/geboortedatum beleid: // - Vraag eindgebruikers altijd: "Wat is uw leeftijd of geboortedatum?" // - Converteer een opgegeven leeftijd intern naar een geboortedatum in ISO-formaat voor MCP-calls // - Rapporteer bij een gegeven leeftijd uitsluitend die leeftijd terug aan de gebruiker (nooit de afgeleide geboortedatum) const baseIntakeProperties = { inkomen_aanvrager: { type: "number", description: "Bruto jaarinkomen hoofdaanvrager in euro's.", }, geboortedatum_aanvrager: { type: "string", description: "Interne geboortedatum hoofdaanvrager (ISO). Vraag de gebruiker altijd: \"Wat is uw leeftijd of geboortedatum?\" en deel bij een leeftijd alleen die leeftijd terug.", }, heeft_partner: { type: "boolean", description: "Geeft aan of een partner mee leent.", }, inkomen_partner: { type: "number", description: "Optioneel partnerinkomen in euro's.", }, geboortedatum_partner: { type: "string", description: "Optionele interne geboortedatum partner (ISO). Vraag ook hier: \"Wat is uw leeftijd of geboortedatum?\" en houd de afgeleide datum intern.", }, verplichtingen_pm: { type: "number", description: "Optionele maandelijkse verplichtingen in euro's.", default: 0, }, }; const baseIntakeRequired = ["inkomen_aanvrager", "geboortedatum_aanvrager", "heeft_partner"]; const aanvragerSchema = { type: "object", description: 'Gegevens van de (hoofd)aanvrager. Vraag altijd: "Wat is uw leeftijd of geboortedatum?" en gebruik opgegeven leeftijden alleen intern.', properties: { ...baseIntakeProperties, eigen_vermogen: { type: "number", description: "Beschikbaar eigen geld in euro's (optioneel).", default: 0, }, }, required: [...baseIntakeRequired], }; const nieuweWoningSchema = { type: "object", description: `Kerngegevens nieuwe woning (detailuitleg: ${OPZET_GUIDE_URI}).`, properties: { waarde_woning: { type: "number", description: "Koopsom nieuwe woning in euro's.", }, bedrag_verbouwen: { type: "number", description: "Optionele verbouwingskosten in euro's.", default: 0, }, bedrag_verduurzamen: { type: "number", description: "Optionele verduurzamingskosten in euro's.", default: 0, }, kosten_percentage: { type: "number", description: "Optioneel kostenpercentage koper als decimaal.", default: 0.05, }, energielabel: { type: "string", description: "Optioneel energielabel van de woning.", enum: ["A++++ (met garantie)", "A++++", "A+++", "A++", "A+", "A", "B", "C", "D", "E", "F", "G"], }, }, required: ["waarde_woning"], }; // Doorstromer invoerbeleid: // - Laat gebruikers kiezen tussen een snelle globale samenvatting of detailinvoer per leningdeel. // - Snelle invoer: één "leningdeel" dat totale schuld, gemiddelde rente en resterende looptijd samenvat. // - Detailinvoer: meerdere leningdelen rechtstreeks overgenomen uit het hypotheekoverzicht. const bestaandeHypotheekSchema = { type: "object", description: `Bestaande leningdelen voor doorstromer (detailuitleg: ${OPZET_GUIDE_URI}). VRAAG ALTIJD: "Wilt u een snelle globale berekening (met een samenvatting van uw hypotheek) of een detailberekening waarbij u alle leningdelen invoert?"`, properties: { leningdelen: { type: "array", description: "Minimaal één leningdeel. Gebruik één samenvattend leningdeel voor een snelle globale berekening of voeg alle afzonderlijke leningdelen toe voor een nauwkeurige detailberekening.", items: { type: "object", properties: { huidige_schuld: { type: "number", description: "Restschuld in euro's.", }, huidige_rente: { type: "number", description: "Rente als decimaal (bijv. 0.028).", }, resterende_looptijd_in_maanden: { type: "number", description: "Resterende looptijd in maanden.", }, rentevasteperiode_maanden: { type: "number", description: "Resterende rentevaste periode in maanden.", }, hypotheekvorm: { type: "string", description: "Hypotheekvorm van het leningdeel.", enum: ["annuiteit", "lineair", "aflossingsvrij"], }, }, required: ["huidige_schuld", "huidige_rente", "resterende_looptijd_in_maanden", "rentevasteperiode_maanden", "hypotheekvorm"], }, }, }, required: ["leningdelen"], }; const server = new Server( { name: "hypotheek-berekening-server", version: config.serverVersion, }, { capabilities: { tools: {}, resources: {}, prompts: {}, }, } ); type ToolResponse = { content: Array<{ type: "text"; text: string; }>; }; type ToolErrorResponse = ToolResponse & { isError: true }; type ToolHandler = (request: any) => Promise<ToolResponse>; function successResponse(text: string): ToolResponse { return { content: [ { type: "text", text, }, ], }; } function errorResponse(error: unknown, sessionId?: string): ToolErrorResponse { if (error instanceof ValidationError) { return { content: [ { type: "text", text: JSON.stringify(error.toStructured(sessionId), null, 2), }, ], isError: true, }; } if (error instanceof APIError) { return { content: [ { type: "text", text: JSON.stringify(error.toStructured(sessionId), null, 2), }, ], isError: true, }; } const message = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: JSON.stringify( { code: ErrorCode.UNKNOWN_ERROR, message, correlation_id: sessionId, }, null, 2 ), }, ], isError: true, }; } function normalizeSessionIdField(obj: Record<string, unknown>) { if (!obj) return; if (!obj.session_id && typeof obj.sessionId === 'string' && obj.sessionId.trim().length > 0) { obj.session_id = obj.sessionId; } } function requireArguments<T>(request: any): T { if (!request.params?.arguments) { throw new ValidationError( ErrorCode.INVALID_INPUT, 'Arguments zijn verplicht', 'arguments' ); } const args = request.params.arguments as Record<string, unknown>; normalizeSessionIdField(args); return args as unknown as T; } function extractSessionId(args: unknown): string | undefined { if (!args || typeof args !== 'object') { return undefined; } const record = args as Record<string, unknown>; if ('session_id' in record && typeof record.session_id === 'string') { return record.session_id; } if ('sessionId' in record && typeof record.sessionId === 'string') { return record.sessionId; } return undefined; } function mapAanvragers(args: { inkomen_aanvrager: number; geboortedatum_aanvrager: string; heeft_partner: boolean; inkomen_partner?: number; geboortedatum_partner?: string; verplichtingen_pm?: number; }) { return { inkomen_aanvrager: args.inkomen_aanvrager, geboortedatum_aanvrager: args.geboortedatum_aanvrager, heeft_partner: args.heeft_partner, inkomen_partner: args.inkomen_partner ?? 0, geboortedatum_partner: args.geboortedatum_partner ?? null, verplichtingen_pm: args.verplichtingen_pm ?? 0, }; } function validateOpzetAanvrager(aanvrager: OpzetAanvrager) { validateBaseArguments({ inkomen_aanvrager: aanvrager.inkomen_aanvrager, geboortedatum_aanvrager: aanvrager.geboortedatum_aanvrager, heeft_partner: aanvrager.heeft_partner, inkomen_partner: aanvrager.inkomen_partner, geboortedatum_partner: aanvrager.geboortedatum_partner, verplichtingen_pm: aanvrager.verplichtingen_pm, } as BaseArguments); } function requireOpzetAanvrager(container: { aanvrager?: OpzetAanvrager }): OpzetAanvrager { if (!container.aanvrager || typeof container.aanvrager !== 'object') { throw new ValidationError( ErrorCode.INVALID_INPUT, 'aanvrager ontbreekt of is onvolledig', 'aanvrager' ); } return container.aanvrager; } function mapOpzetAanvrager(aanvrager: OpzetAanvrager) { return { inkomen_aanvrager: aanvrager.inkomen_aanvrager, geboortedatum_aanvrager: aanvrager.geboortedatum_aanvrager, heeft_partner: aanvrager.heeft_partner, inkomen_partner: aanvrager.inkomen_partner ?? 0, geboortedatum_partner: aanvrager.geboortedatum_partner ?? null, verplichtingen_pm: aanvrager.verplichtingen_pm ?? 0, eigen_vermogen: aanvrager.eigen_vermogen ?? 0, }; } function buildNieuweLeningPayload(raw: any): any | undefined { if (!raw || typeof raw !== 'object') { return undefined; } const payload: Record<string, unknown> = {}; const looptijdMaanden = raw.looptijd_maanden ?? (typeof raw.looptijd_jaren === 'number' ? raw.looptijd_jaren * 12 : undefined); if (looptijdMaanden) { payload.looptijd_maanden = looptijdMaanden; } const rentevastMaanden = raw.rentevaste_periode_maanden ?? (typeof raw.rentevast_periode_jaren === 'number' ? raw.rentevast_periode_jaren * 12 : undefined); if (rentevastMaanden) { payload.rentevaste_periode_maanden = rentevastMaanden; } if (raw.rente !== undefined) { payload.rente = raw.rente; } if (raw.hypotheekvorm) { payload.hypotheekvorm = raw.hypotheekvorm; } else if (raw.type) { payload.hypotheekvorm = raw.type; } if (raw.energielabel) { payload.energielabel = normalizeEnergielabel(raw.energielabel); } if (raw.nhg !== undefined) { payload.nhg = raw.nhg; } if (raw.ltv !== undefined) { let ltvValue: number | undefined; if (typeof raw.ltv === 'string') { const parsed = parseFloat(raw.ltv.replace('%', '')); ltvValue = Number.isFinite(parsed) ? parsed / 100 : undefined; } else if (typeof raw.ltv === 'number') { ltvValue = raw.ltv; } if (ltvValue !== undefined) { payload.ltv = ltvValue; } } if (Array.isArray(raw.renteklassen) && raw.renteklassen.length > 0) { payload.renteklassen = raw.renteklassen; } return Object.keys(payload).length > 0 ? payload : undefined; } async function handleBerekenStarter(request: any): Promise<ToolResponse> { const args = requireArguments<BaseArguments>(request); const logger = createLogger(args.session_id); validateBaseArguments(args); enforceRateLimit(args.session_id); const payload: any = { aanvragers: mapAanvragers(args), }; if (args.session_id) { payload.session_id = args.session_id; } const apiClient = getApiClient(); const { data } = await apiClient.post( REPLIT_API_URL_BEREKENEN, payload, { correlationId: args.session_id } ); logger.info('Toolcall succesvol', { tool: 'bereken_hypotheek_starter' }); return successResponse(formatResponse(data, "bereken_hypotheek_starter")); } async function handleBerekenDoorstromer(request: any): Promise<ToolResponse> { const rawArgs = requireArguments<DoorstromerArguments>(request); const normalizedArgs = normalizeDoorstromerArgs(rawArgs) as DoorstromerArguments; const logger = createLogger(normalizedArgs.session_id); validateDoorstromerArguments(normalizedArgs); enforceRateLimit(normalizedArgs.session_id); const payload: any = { aanvragers: mapAanvragers(normalizedArgs), bestaande_hypotheek: { waarde_huidige_woning: normalizedArgs.waarde_huidige_woning, leningdelen: normalizedArgs.bestaande_hypotheek.leningdelen, }, }; if (normalizedArgs.session_id) { payload.session_id = normalizedArgs.session_id; } const apiClient = getApiClient(); const { data } = await apiClient.post( REPLIT_API_URL_BEREKENEN, payload, { correlationId: normalizedArgs.session_id } ); logger.info('Toolcall succesvol', { tool: 'bereken_hypotheek_doorstromer' }); return successResponse(formatResponse(data, "bereken_hypotheek_doorstromer")); } async function handleBerekenUitgebreid(request: any): Promise<ToolResponse> { const rawArgs = requireArguments<UitgebreidArguments>(request); const normalizedArgs = rawArgs.is_doorstromer ? (normalizeDoorstromerArgs(rawArgs) as UitgebreidArguments) : rawArgs; const logger = createLogger(normalizedArgs.session_id); validateBaseArguments(normalizedArgs as BaseArguments); if (normalizedArgs.is_doorstromer && normalizedArgs.bestaande_hypotheek) { validateBestaandeHypotheek(normalizedArgs.bestaande_hypotheek); } enforceRateLimit(normalizedArgs.session_id); const payload: any = { aanvragers: mapAanvragers(normalizedArgs), }; if (normalizedArgs.is_doorstromer && normalizedArgs.waarde_huidige_woning && normalizedArgs.bestaande_hypotheek) { payload.bestaande_hypotheek = { waarde_huidige_woning: normalizedArgs.waarde_huidige_woning, leningdelen: normalizedArgs.bestaande_hypotheek.leningdelen, }; } const maatwerk = (normalizedArgs as any).nieuwe_hypotheek ?? (normalizedArgs as any).nieuwe_lening; const nieuweLening = buildNieuweLeningPayload(maatwerk); if (nieuweLening) { payload.nieuwe_lening = nieuweLening; } if (normalizedArgs.session_id) { payload.session_id = normalizedArgs.session_id; } const apiClient = getApiClient(); const { data } = await apiClient.post( REPLIT_API_URL_BEREKENEN, payload, { correlationId: normalizedArgs.session_id } ); logger.info('Toolcall succesvol', { tool: 'bereken_hypotheek_uitgebreid' }); return successResponse(formatResponse(data, "bereken_hypotheek_uitgebreid")); } async function handleActueleRentes(request: any): Promise<ToolResponse> { const sessionId = extractSessionId(request.params?.arguments); if (sessionId) { enforceRateLimit(sessionId); } const apiClient = getApiClient(); const { data } = await apiClient.get(REPLIT_API_URL_RENTES, { correlationId: sessionId }); return successResponse(JSON.stringify(data, null, 2)); } async function handleOpzetStarter(request: any): Promise<ToolResponse> { const rawArgs = requireArguments<OpzetStarterArguments>(request); const normalizedArgs = normalizeOpzetAanvragerShape(rawArgs) as OpzetStarterArguments; const logger = createLogger(normalizedArgs.session_id); const aanvrager = requireOpzetAanvrager(normalizedArgs); validateOpzetAanvrager(aanvrager); enforceRateLimit(normalizedArgs.session_id); const payload: any = { aanvrager: mapOpzetAanvrager(aanvrager), nieuwe_woning: { waarde_woning: normalizedArgs.nieuwe_woning.waarde_woning, bedrag_verbouwen: normalizedArgs.nieuwe_woning.bedrag_verbouwen ?? 0, bedrag_verduurzamen: normalizedArgs.nieuwe_woning.bedrag_verduurzamen ?? 0, kosten_percentage: normalizedArgs.nieuwe_woning.kosten_percentage ?? 0.05, energielabel: normalizeEnergielabel(normalizedArgs.nieuwe_woning.energielabel || ''), }, }; if (normalizedArgs.session_id) { payload.session_id = normalizedArgs.session_id; } const apiClient = getApiClient(); const { data } = await apiClient.post( REPLIT_API_URL_OPZET, payload, { correlationId: normalizedArgs.session_id } ); logger.info('Toolcall succesvol', { tool: 'opzet_hypotheek_starter' }); return successResponse(formatResponse(data, "opzet_hypotheek_starter")); } async function handleOpzetDoorstromer(request: any): Promise<ToolResponse> { const rawArgs = requireArguments<OpzetDoorstromerArguments>(request); const normalizedArgs = normalizeOpzetDoorstromerArgs(rawArgs) as OpzetDoorstromerArguments; const logger = createLogger(normalizedArgs.session_id); const aanvrager = requireOpzetAanvrager(normalizedArgs); validateOpzetAanvrager(aanvrager); validateBestaandeHypotheek(normalizedArgs.bestaande_hypotheek); enforceRateLimit(normalizedArgs.session_id); const payload: any = { aanvrager: mapOpzetAanvrager(aanvrager), bestaande_hypotheek: { waarde_huidige_woning: normalizedArgs.waarde_huidige_woning, leningdelen: normalizedArgs.bestaande_hypotheek.leningdelen, }, nieuwe_woning: { waarde_woning: normalizedArgs.nieuwe_woning.waarde_woning, bedrag_verbouwen: normalizedArgs.nieuwe_woning.bedrag_verbouwen ?? 0, bedrag_verduurzamen: normalizedArgs.nieuwe_woning.bedrag_verduurzamen ?? 0, kosten_percentage: normalizedArgs.nieuwe_woning.kosten_percentage ?? 0.05, energielabel: normalizeEnergielabel(normalizedArgs.nieuwe_woning.energielabel || ''), }, }; if (normalizedArgs.session_id) { payload.session_id = normalizedArgs.session_id; } const apiClient = getApiClient(); const { data } = await apiClient.post( REPLIT_API_URL_OPZET, payload, { correlationId: normalizedArgs.session_id } ); logger.info('Toolcall succesvol', { tool: 'opzet_hypotheek_doorstromer' }); return successResponse(formatResponse(data, "opzet_hypotheek_doorstromer")); }
  • Helper function to normalize doorstromer-specific arguments, ensuring aanvrager shape and existing mortgage fields are canonicalized before validation.
    export function normalizeOpzetDoorstromerArgs(args: any): any { const normalized = normalizeOpzetAanvragerShape(args); if (normalized?.bestaande_hypotheek) { normalized.bestaande_hypotheek = normalizeBestaandeHypotheek(normalized.bestaande_hypotheek); } return normalized; }
  • Helper to normalize existing mortgage data, mapping variant field names to canonical keys and recursively normalizing leningdelen array.
    export function normalizeBestaandeHypotheek(input: any): any { if (!input || typeof input !== 'object') { logger.warn('Invalid bestaande_hypotheek input', { input }); return input; } const normalized: any = {}; for (const [key, value] of Object.entries(input)) { const lowerKey = key.toLowerCase().trim(); const isLeningdelenArray = (lowerKey.includes('lening') && lowerKey.includes('deel')) || (lowerKey.includes('loan') && lowerKey.includes('part')) || lowerKey === 'parts'; if (isLeningdelenArray && Array.isArray(value)) { normalized.leningdelen = value.map((deel, idx) => normalizeLeningdeel(deel, idx)); } else { const canonicalKey = BESTAANDE_HYPOTHEEK_MAPPINGS[lowerKey]; if (canonicalKey) { normalized[canonicalKey] = value; } else { normalized[key] = value; logger.debug('Unknown field in bestaande_hypotheek', { field: key }); } } } return normalized; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pace8/mcp-hypotheken-berekenen'

If you have feedback or need assistance with the MCP directory API, please join our Discord server