Skip to main content
Glama

opzet_hypotheek_starter

Calculate mortgage setup for first-time homebuyers including total required amount, financing overview, and monthly payments based on income, property details, and financial obligations.

Instructions

Berekent de hypotheekopzet voor starters. Output: totaal benodigd bedrag, financieringsoverzicht en maandlast.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
inkomen_aanvragerYesBruto jaarinkomen hoofdaanvrager in euro's.
geboortedatum_aanvragerYesGeboortedatum hoofdaanvrager (YYYY-MM-DD).
heeft_partnerYesGeeft aan of een partner mee leent.
inkomen_partnerNoOptioneel partnerinkomen in euro's.
geboortedatum_partnerNoOptionele geboortedatum partner (YYYY-MM-DD).
verplichtingen_pmNoOptionele maandelijkse verplichtingen in euro's.
eigen_vermogenNoOptioneel beschikbaar eigen geld in euro's.
nieuwe_woningYesKerngegevens nieuwe woning (detailuitleg: hypotheek://v4/guide/opzet-intake).
session_idNoOptioneel sessie-ID vanuit n8n (voor logging).

Implementation Reference

  • Main handler function: normalizes arguments, validates aanvrager data, constructs payload for Replit opzet API, calls the external API, logs success, and returns formatted response.
    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"));
    }
  • src/index.ts:880-901 (registration)
    Tool registration in ListToolsRequestHandler: defines name, description, and input schema referencing aanvragerSchema and nieuweWoningSchema.
    {
      name: "opzet_hypotheek_starter",
      description: `Opzet-berekening voor starters met een CONCRETE woning. Gebruik dit zodra de gebruiker een huis/koopprijs noemt en wil weten “kan ik deze woning kopen, hoe ziet de financiering eruit?”. Voor louter oriëntatie zonder woning blijft u bij de maximale-hypotheek tools. Dit is de standaardvariant; kies opzet_hypotheek_uitgebreid wanneer de gebruiker expliciet scenario’s/leningdelen wil tweaken.`,
      inputSchema: {
        type: "object",
        description: `Gebruik basisintake plus woninginfo; zie ${OPZET_GUIDE_URI} voor detailvelden en defaults.`,
        properties: {
          aanvrager: aanvragerSchema,
          nieuwe_woning: {
            ...nieuweWoningSchema,
          },
          session_id: {
            type: "string",
            description: "Optioneel sessie-ID vanuit n8n (voor logging).",
          },
        },
        required: [
          "aanvrager",
          "nieuwe_woning",
        ],
      },
    },
  • Input schema definitions: aanvragerSchema (applicant data) and nieuweWoningSchema (new property details), used in tool inputSchema.
    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"],
    };
  • Normalization helper: converts legacy flat argument structure to nested 'aanvrager' object expected by the handler.
    export function normalizeOpzetAanvragerShape(args: any): any {
      if (!args || typeof args !== 'object') {
        return args;
      }
    
      if (args.aanvrager && typeof args.aanvrager === 'object') {
        return args;
      }
    
      const legacy = { ...args };
      if (
        typeof legacy.inkomen_aanvrager === 'number' &&
        typeof legacy.geboortedatum_aanvrager === 'string' &&
        typeof legacy.heeft_partner === 'boolean'
      ) {
        legacy.aanvrager = {
          inkomen_aanvrager: legacy.inkomen_aanvrager,
          geboortedatum_aanvrager: legacy.geboortedatum_aanvrager,
          heeft_partner: legacy.heeft_partner,
          inkomen_partner: legacy.inkomen_partner,
          geboortedatum_partner: legacy.geboortedatum_partner,
          verplichtingen_pm: legacy.verplichtingen_pm,
          eigen_vermogen: legacy.eigen_vermogen,
        };
      }
    
      return legacy;
    }
  • Validation helper called by validateOpzetAanvrager: validates base applicant data including income ranges, date formats, ages (18-75), partner completeness.
    export function validateBaseArguments(args: unknown): void {
      // Type check
      if (typeof args !== 'object' || args === null) {
        throw new ValidationError(
          ErrorCode.INVALID_INPUT,
          'Arguments moet een object zijn',
          'arguments'
        );
      }
    
      const input = args as Record<string, unknown>;
    
      // Valideer verplichte velden
      if (typeof input.inkomen_aanvrager !== 'number') {
        throw new ValidationError(
          ErrorCode.INVALID_INPUT,
          'inkomen_aanvrager moet een getal zijn',
          'inkomen_aanvrager',
          input.inkomen_aanvrager
        );
      }
    
      if (typeof input.geboortedatum_aanvrager !== 'string') {
        throw new ValidationError(
          ErrorCode.INVALID_INPUT,
          'geboortedatum_aanvrager moet een string zijn',
          'geboortedatum_aanvrager',
          input.geboortedatum_aanvrager
        );
      }
    
      if (typeof input.heeft_partner !== 'boolean') {
        throw new ValidationError(
          ErrorCode.INVALID_INPUT,
          'heeft_partner moet een boolean zijn',
          'heeft_partner',
          input.heeft_partner
        );
      }
    
      // Valideer inkomen range
      if (input.inkomen_aanvrager < ValidationConstraints.INKOMEN.MIN || 
          input.inkomen_aanvrager > ValidationConstraints.INKOMEN.MAX) {
        throw new ValidationError(
          ErrorCode.INCOME_OUT_OF_RANGE,
          `Inkomen aanvrager moet tussen €${ValidationConstraints.INKOMEN.MIN} en €${ValidationConstraints.INKOMEN.MAX} liggen`,
          'inkomen_aanvrager',
          input.inkomen_aanvrager
        );
      }
    
      // Valideer leeftijd aanvrager
      validateAge(input.geboortedatum_aanvrager, 'aanvrager');
    
      // Valideer partner gegevens indien heeft_partner = true
      if (input.heeft_partner) {
        if (input.inkomen_partner !== undefined) {
          if (typeof input.inkomen_partner !== 'number') {
            throw new ValidationError(
              ErrorCode.INVALID_INPUT,
              'inkomen_partner moet een getal zijn',
              'inkomen_partner',
              input.inkomen_partner
            );
          }
    
          if (input.inkomen_partner < ValidationConstraints.INKOMEN.MIN || 
              input.inkomen_partner > ValidationConstraints.INKOMEN.MAX) {
            throw new ValidationError(
              ErrorCode.INCOME_OUT_OF_RANGE,
              `Inkomen partner moet tussen €${ValidationConstraints.INKOMEN.MIN} en €${ValidationConstraints.INKOMEN.MAX} liggen`,
              'inkomen_partner',
              input.inkomen_partner
            );
          }
        }
    
        if (input.geboortedatum_partner) {
          if (typeof input.geboortedatum_partner !== 'string') {
            throw new ValidationError(
              ErrorCode.INVALID_INPUT,
              'geboortedatum_partner moet een string zijn',
              'geboortedatum_partner',
              input.geboortedatum_partner
            );
          }
          validateAge(input.geboortedatum_partner, 'partner');
        }
      }
    
      // Valideer verplichtingen
      if (input.verplichtingen_pm !== undefined) {
        if (typeof input.verplichtingen_pm !== 'number') {
          throw new ValidationError(
            ErrorCode.INVALID_INPUT,
            'verplichtingen_pm moet een getal zijn',
            'verplichtingen_pm',
            input.verplichtingen_pm
          );
        }
    
        if (input.verplichtingen_pm < ValidationConstraints.VERPLICHTINGEN.MIN || 
            input.verplichtingen_pm > ValidationConstraints.VERPLICHTINGEN.MAX) {
          throw new ValidationError(
            ErrorCode.INVALID_INPUT,
            `Verplichtingen moet tussen €${ValidationConstraints.VERPLICHTINGEN.MIN} en €${ValidationConstraints.VERPLICHTINGEN.MAX} liggen`,
            'verplichtingen_pm',
            input.verplichtingen_pm
          );
        }
      }
    }

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/Test'

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