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
          );
        }
      }
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool calculates and outputs specific financial details, but doesn't mention whether it's a read-only operation, if it requires authentication, rate limits, or side effects (e.g., saving data). For a financial calculation tool with complex inputs, this lack of behavioral context is a significant gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core purpose and output. It avoids unnecessary words and gets straight to the point. However, it could be slightly more structured by separating purpose from output details for clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (9 parameters, nested objects) and lack of annotations or output schema, the description is minimally adequate. It states what the tool does and outputs, but doesn't cover behavioral aspects, usage context, or error handling. For a tool with no output schema, it should ideally hint at the return format beyond just listing output types.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description doesn't add any parameter-specific details beyond what's in the schema (e.g., it doesn't explain relationships between parameters like 'heeft_partner' and optional partner fields). Baseline 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: calculating a mortgage setup for starters. It specifies the output (total required amount, financing overview, and monthly costs), which helps distinguish it from siblings like 'bereken_hypotheek_starter' (which might calculate something different). However, it doesn't explicitly differentiate from 'opzet_hypotheek_doorstromer' or 'opzet_hypotheek_uitgebreid' in terms of target audience or scope.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites, target scenarios (e.g., first-time buyers), or compare it to sibling tools like 'bereken_hypotheek_starter' or 'opzet_hypotheek_doorstromer'. Usage is implied by the name and description but not explicitly stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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