Title98/100
Recommend mortgage packages
The title clearly communicates the tool’s action and subject. Keep it concise.
Description98/100
Rank Singapore mortgage packages for a borrower. Returns `recommended` (up to 3 picks, each with a package id and `reasoning`; ranked by the LLM when `userContext` is given, otherwise the cheapest package of each rate structure), `lowestRateIds` (up to 3 package ids, cheapest eligible first), `isPersonalised` (whether `userContext` shaped the ranking), and `packages`, a lookup of full package details for every id referenced. Core inputs (gather these first; ask the borrower if missing — do not guess): `dealKind` (PURCHASE | REFINANCE); `currentBankId` (required only for REFINANCE — use `list_banks` to resolve the lender name into its UUID); `propertyType` (HDB | PRIVATE | COMMERCIAL); `propertyStatus`; `loanAmount`. `propertyStatus` depends on `propertyType` — only these pairings are valid: HDB → COMPLETED (resale) or BTO (Build-To-Order, new); PRIVATE → COMPLETED (ready-built) or BUC (Built-Under-Construction, new); COMMERCIAL → OWNEROCCUPIED, INVESTMENT or PERSONAL. Do not mix across types (e.g. HDB + BUC or PRIVATE + BTO are invalid). Optional inputs (only refine the ranking): `ratesType` (FIXED | FLOATING) and `userContext` (free-text borrower priorities, timeline, bank relationships, citizenship, etc.). Pass `formats: ["pdf"]` to also generate a one-page comparison document of the recommended set (with per-package verdicts); the download URL comes back under `documents`. Optional `tenureYears` (default 25) sets the document's instalment assumptions. Stateless: if any input or preference changes, call this tool again with the full updated set — never infer an updated answer from a previous response.
The description explains the capability, when to use it, and what to expect.
Inputs96/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
propertyType
The kind of property securing the loan: an HDB flat, a private home or a commercial property. Only packages offered for it are considered, and it decides which `propertyStatus` values are valid.
Clear enough to supply this argument.
propertyStatus
Stage or use of the property, which must suit `propertyType`. For HDB: COMPLETED for a resale flat, BTO for a new Build-To-Order flat. For PRIVATE: COMPLETED for a ready-built home, BUC for one still under construction. For COMMERCIAL: OWNEROCCUPIED, INVESTMENT or PERSONAL.
Clear enough to supply this argument.
dealKind
PURCHASE (new loan) or REFINANCE (switching lender).
Clear enough to supply this argument.
loanAmount
Amount the borrower wants to borrow, in SGD. Packages whose loan-size range does not cover it are excluded.
Clear enough to supply this argument.
ratesType
Optional rate type: FIXED or FLOATING. This is a hard filter on the candidate pool, not a ranking hint. Omit it when the borrower stated no preference, so a mix of rate types stays eligible.
Clear enough to supply this argument.
fixedDurationYrs
Optional filter: keep only packages whose fixed-rate period lasts this many years. Pass an array to accept any of several. Only applied when `ratesType` is FIXED.
Clear enough to supply this argument.
lockInPeriodYrs
Optional filter: the longest lock-in, in years, the borrower will accept. Packages with a longer lock-in are excluded; packages without one always qualify.
Clear enough to supply this argument.
currentBankId
The borrower's current lender, as the bank `id` returned by `list_banks`. Needed for REFINANCE deals, where that bank's packages are left out; ignored for PURCHASE.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
userContext
Optional free-text summary of the borrower's priorities, timeline, bank relationships, citizenship, employment, family changes, etc. Only refines the LLM ranking; omit if there are no stated priorities.
Clear enough to supply this argument.
candidateLimit
How many candidate packages the ranker weighs for each of its slots (floating, 2-year fixed, longest fixed); values above 8 have no further effect. Leave unset unless deliberately narrowing the choice.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
tenureYears
Repayment period, in years, for the instalments shown in the comparison document; only used with `formats`. 25 when omitted.
Clear enough to supply this argument.
formats
Document formats to generate for the recommended set (currently only "pdf"); the download URL is returned under `documents`. Omit/empty for data only.
Clear enough to supply this argument.
Annotations100/100
All four behavior hints are declared.
{
"title": "Recommend mortgage packages",
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}Output schema85/100
The output schema explains the structured result.
View output schema
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"packages",
"recommended",
"lowestRateIds",
"isPersonalised"
],
"properties": {
"packages": {
"type": "object",
"description": "Lookup of full package details keyed by package id. Every id referenced in `recommended` and `lowestRateIds` has an entry here.",
"propertyNames": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"additionalProperties": {
"type": "object",
"required": [
"id",
"createdAt",
"updatedAt",
"bankId",
"maskIdentity",
"propertyType",
"propertyStatus",
"dealKinds",
"status",
"ratesType",
"minSize",
"maxSize",
"fixedDurationYrs",
"lockInPeriodYrs",
"freeConversion",
"partialRedemption",
"fullRedemption",
"redemptionComment",
"waiversDueToSale",
"cancellation",
"specialTerms",
"validFrom",
"validUntil",
"effectiveYear1",
"effectiveYear2",
"effectiveYear3",
"effectiveYear4",
"effectiveYear5",
"effectiveThereafter",
"effectiveAverageRate",
"effectiveAt",
"importHash"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"bank": {
"type": "object",
"required": [
"id",
"name",
"slug",
"logoUrl",
"website",
"maskIdentity"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"masked": {
"type": "boolean"
},
"logoUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"website": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"maskIdentity": {
"type": "boolean",
"default": false
}
},
"additionalProperties": false
},
"bankId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"source": {
"enum": [
"SHEET",
"MANUAL"
],
"type": "string"
},
"status": {
"enum": [
"ACTIVE",
"INACTIVE"
],
"type": "string"
},
"maxSize": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991
},
"minSize": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991
},
"rateLegs": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"createdAt",
"updatedAt",
"packageId",
"sequence",
"durationMonths",
"legType",
"fixedPct",
"spreadPct",
"indexId"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"index": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"slug",
"name",
"currency"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"currency": {
"type": "string"
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"indexId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
]
},
"legType": {
"enum": [
"FIXED",
"FLOATING"
],
"type": "string"
},
"fixedPct": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"sequence": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"packageId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"spreadPct": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"latestQuote": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"marketIndexId",
"asOfDate",
"rate"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"rate": {
"type": "number"
},
"asOfDate": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"marketIndexId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"computedRate": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"durationMonths": {
"anyOf": [
{
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"dealKinds": {
"type": "array",
"items": {
"enum": [
"PURCHASE",
"REFINANCE"
],
"type": "string"
}
},
"ratesType": {
"enum": [
"FIXED",
"FLOATING"
],
"type": "string"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"validFrom": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"importHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"validUntil": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"effectiveAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"cancellation": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"maskIdentity": {
"type": "boolean",
"default": false
},
"propertyType": {
"enum": [
"HDB",
"PRIVATE",
"COMMERCIAL"
],
"type": "string"
},
"specialTerms": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"effectiveYear1": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"effectiveYear2": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"effectiveYear3": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"effectiveYear4": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"effectiveYear5": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"freeConversion": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"fullRedemption": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"propertyStatus": {
"anyOf": [
{
"enum": [
"BTO",
"OWNEROCCUPIED",
"INVESTMENT",
"PERSONAL",
"COMPLETED",
"BUC"
],
"type": "string"
},
{
"type": "null"
}
]
},
"lockInPeriodYrs": {
"anyOf": [
{
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991
},
{
"type": "null"
}
]
},
"fixedDurationYrs": {
"anyOf": [
{
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991
},
{
"type": "null"
}
]
},
"waiversDueToSale": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"partialRedemption": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"redemptionComment": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"effectiveThereafter": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"effectiveAverageRate": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}
},
"documents": {
"type": "object",
"required": [
"pdfUrl"
],
"properties": {
"pdfUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Public PDF download URL, or null if not generated."
}
},
"description": "Comparison document for the recommended set; only present when `formats` was requested.",
"additionalProperties": false
},
"recommended": {
"type": "array",
"items": {
"type": "object",
"required": [
"rank",
"packageId",
"reasoning"
],
"properties": {
"rank": {
"type": "integer",
"maximum": 3,
"minimum": 1,
"description": "1-based rank (1 = best fit for this borrower)."
},
"packageId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Id of the ranked package; look it up in `packages`."
},
"reasoning": {
"type": "string",
"description": "Short LLM rationale for why this package fits the borrower."
}
},
"additionalProperties": false
},
"maxItems": 3,
"description": "Up to 3 picks. Ranked by the LLM when `userContext` is given and ranking succeeds, otherwise the cheapest package of each structure; fewer when fewer candidates exist after filtering."
},
"lowestRateIds": {
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"maxItems": 3,
"description": "Up to 3 package ids, cheapest first by effective average rate. A fixed package whose lock-in outlives its fixed period is ranked behind eligible ones and only included to fill the list."
},
"isPersonalised": {
"type": "boolean",
"description": "True when `userContext` was supplied and the ranking reflects it."
}
},
"additionalProperties": false
}