@sfranalytics/mcp 0.6.3 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/http.js +69 -3
  2. package/dist/http.js.map +1 -1
  3. package/dist/landing.js +17 -4
  4. package/dist/landing.js.map +1 -1
  5. package/dist/server.js +5 -3
  6. package/dist/server.js.map +1 -1
  7. package/dist/tools/formatters.js +6 -1
  8. package/dist/tools/formatters.js.map +1 -1
  9. package/dist/tools/plr/borrowerProfile.js +5 -2
  10. package/dist/tools/plr/borrowerProfile.js.map +1 -1
  11. package/dist/tools/plr/borrowerSearch.js +1 -1
  12. package/dist/tools/plr/borrowerSearch.js.map +1 -1
  13. package/dist/tools/plr/loansNearby.js +100 -84
  14. package/dist/tools/plr/loansNearby.js.map +1 -1
  15. package/dist/tools/plr/transactionHistory.js +67 -50
  16. package/dist/tools/plr/transactionHistory.js.map +1 -1
  17. package/dist/tools/sfr/bestBuyers.js +58 -41
  18. package/dist/tools/sfr/bestBuyers.js.map +1 -1
  19. package/dist/tools/sfr/buyerProfile.js +12 -5
  20. package/dist/tools/sfr/buyerProfile.js.map +1 -1
  21. package/dist/tools/sfr/getProperty.js +147 -124
  22. package/dist/tools/sfr/getProperty.js.map +1 -1
  23. package/dist/tools/sfr/propertyBatch.js +3 -1
  24. package/dist/tools/sfr/propertyBatch.js.map +1 -1
  25. package/dist/tools/sfr/propertyComps.js +51 -34
  26. package/dist/tools/sfr/propertyComps.js.map +1 -1
  27. package/dist/tools/sfr/propertyTransactions.js +48 -31
  28. package/dist/tools/sfr/propertyTransactions.js.map +1 -1
  29. package/dist/tools/sfr/rentalComparables.js +83 -66
  30. package/dist/tools/sfr/rentalComparables.js.map +1 -1
  31. package/dist/tools/sfr/rentalMarketAnalysis.js +5 -2
  32. package/dist/tools/sfr/rentalMarketAnalysis.js.map +1 -1
  33. package/dist/tools/sfr/searchProperties.js +3 -1
  34. package/dist/tools/sfr/searchProperties.js.map +1 -1
  35. package/dist/tools/sfr/topBuyers.js +13 -10
  36. package/dist/tools/sfr/topBuyers.js.map +1 -1
  37. package/dist/tools/sfr/zipDetail.js +83 -72
  38. package/dist/tools/sfr/zipDetail.js.map +1 -1
  39. package/dist/tools/sfr/zipFinder.js +24 -15
  40. package/dist/tools/sfr/zipFinder.js.map +1 -1
  41. package/dist/tools/support.d.ts +3 -0
  42. package/dist/tools/support.js +60 -0
  43. package/dist/tools/support.js.map +1 -0
  44. package/package.json +1 -1
@@ -3,10 +3,13 @@ import { structuredResult, markdownTable } from "../formatters.js";
3
3
  import { registerToolSafe } from "../registerToolSafe.js";
4
4
  import { autoFillPlrDates } from "../dateHelper.js";
5
5
  import { action } from "../nextActions.js";
6
+ import { ApiError } from "../../services/httpClient.js";
6
7
  const Input = z.object({
7
8
  propertyAddress: z
8
9
  .string()
9
- .describe("Full property address, e.g. '123 Main St, Phoenix, AZ 85004'"),
10
+ .describe("Full property address (e.g. '123 Main St, Phoenix, AZ 85004'). " +
11
+ "Used for geocoding — must be close enough to resolve. " +
12
+ "If geocoding fails, try a nearby address or use plr_borrower_search with geographic filters."),
10
13
  radius: z.coerce.number().default(1).describe("Search radius in miles (default 1)"),
11
14
  limit: z.coerce.number().default(25).describe("Maximum results (default 25)"),
12
15
  startDate: z.string().optional().describe("Start date, e.g. '2023-01-01'"),
@@ -22,92 +25,105 @@ export function registerLoansNearbyTool(server, plr) {
22
25
  "For a specific property's lending history, use plr_transaction_history.",
23
26
  inputSchema: Input,
24
27
  }, async (args) => {
25
- const query = {
26
- property_address: args.propertyAddress,
27
- radius: args.radius,
28
- limit: args.limit,
29
- };
30
- if (args.startDate)
31
- query.start_date = args.startDate;
32
- if (args.endDate)
33
- query.end_date = args.endDate;
34
- autoFillPlrDates(query);
35
- const data = await plr.loansNearby(query);
36
- const rawItems = data?.results ?? data?.data ?? (Array.isArray(data) ? data : []);
37
- // Deduplicate: API returns duplicate rows (same lender+borrower+address+amount+date)
38
- const seen = new Set();
39
- const items = rawItems.filter((l) => {
40
- const key = `${l.lender}|${l.buyer_borrower1_name}|${l.full_street_address}|${l.mtg_amt ?? l.loan_amount}|${l.recording_date}`;
41
- if (seen.has(key))
42
- return false;
43
- seen.add(key);
44
- return true;
45
- });
46
- const lines = [
47
- `## Loans Nearby — ${args.propertyAddress}`,
48
- `**Radius:** ${args.radius} mi | **Found:** ${items.length}`,
49
- "",
50
- ];
51
- // Distinguish "address not geocoded" from "no loans in area"
52
- if (items.length === 0 && data?.property_details === null) {
53
- lines.push("_Could not geocode this address the property was not found in the SFR database. " +
54
- "Try a nearby address or use plr_borrower_search with geographic filters instead._");
55
- return structuredResult(lines.join("\n"), { items, total: items.length, page: 1 });
56
- }
57
- if (items.length > 0) {
58
- const fmt$ = (v) => v != null && Number(v) > 0 ? `$${Number(v).toLocaleString()}` : "—";
59
- const rows = items.slice(0, 25).map((l) => [
60
- l.full_street_address ?? l.address ?? "—",
61
- `${l.city ?? ""}, ${l.state ?? ""}`.replace(/^, |, $/, "") || "—",
62
- l.lender ?? "—",
63
- l.buyer_borrower1_name ?? l.borrower ?? "—",
64
- fmt$(l.loan_amount ?? l.mtg_amt),
65
- l.recording_date ?? "—",
66
- ]);
67
- lines.push(markdownTable(["Address", "Location", "Lender", "Borrower", "Amount", "Date"], rows));
68
- // Show subject property details if available at top level
69
- if (data?.property_details) {
70
- lines.push("");
71
- lines.push("### Subject Property Details");
72
- const pd = data.property_details;
73
- const detailLines = [
74
- pd.avm_value ? `**AVM:** $${Number(pd.avm_value).toLocaleString()}` : null,
75
- pd.market_value ? `**Market Value:** $${Number(pd.market_value).toLocaleString()}` : null,
76
- pd.owner ? `**Owner:** ${pd.owner}` : null,
77
- pd.owner_occupied != null ? `**Owner Occupied:** ${pd.owner_occupied}` : null,
78
- pd.pfc_flag ? `**Pre-Foreclosure:** Yes (${pd.pfc_recording_date ?? ""})` : null,
79
- pd.vacant_flag ? `**Vacant:** Yes` : null,
80
- ].filter(Boolean);
81
- lines.push(detailLines.join(" | "));
28
+ try {
29
+ const query = {
30
+ property_address: args.propertyAddress,
31
+ radius: args.radius,
32
+ limit: args.limit,
33
+ };
34
+ if (args.startDate)
35
+ query.start_date = args.startDate;
36
+ if (args.endDate)
37
+ query.end_date = args.endDate;
38
+ autoFillPlrDates(query);
39
+ const data = await plr.loansNearby(query);
40
+ const rawItems = data?.results ?? data?.data ?? (Array.isArray(data) ? data : []);
41
+ // Deduplicate: API returns duplicate rows (same lender+borrower+address+amount+date)
42
+ const seen = new Set();
43
+ const items = rawItems.filter((l) => {
44
+ const key = `${l.lender}|${l.buyer_borrower1_name}|${l.full_street_address}|${l.mtg_amt ?? l.loan_amount}|${l.recording_date}`;
45
+ if (seen.has(key))
46
+ return false;
47
+ seen.add(key);
48
+ return true;
49
+ });
50
+ const lines = [
51
+ `## Loans Nearby ${args.propertyAddress}`,
52
+ `**Radius:** ${args.radius} mi | **Found:** ${items.length}`,
53
+ "",
54
+ ];
55
+ // Distinguish "address not geocoded" from "no loans in area"
56
+ if (items.length === 0 && data?.property_details === null) {
57
+ lines.push("_Could not geocode this address the property was not found in the SFR database. " +
58
+ "Try a nearby address or use plr_borrower_search with geographic filters instead._");
59
+ return structuredResult(lines.join("\n"), { items, total: items.length, page: 1 });
60
+ }
61
+ if (items.length > 0) {
62
+ const fmt$ = (v) => v != null && Number(v) > 0 ? `$${Number(v).toLocaleString()}` : "—";
63
+ const rows = items.slice(0, 25).map((l) => [
64
+ l.full_street_address ?? l.address ?? "—",
65
+ `${l.city ?? ""}, ${l.state ?? ""}`.replace(/^, |, $/, "") || "—",
66
+ l.lender ?? "—",
67
+ l.buyer_borrower1_name ?? l.borrower ?? "—",
68
+ fmt$(l.loan_amount ?? l.mtg_amt),
69
+ l.recording_date ?? "—",
70
+ ]);
71
+ lines.push(markdownTable(["Address", "Location", "Lender", "Borrower", "Amount", "Date"], rows));
72
+ // Show subject property details if available at top level
73
+ if (data?.property_details) {
74
+ lines.push("");
75
+ lines.push("### Subject Property Details");
76
+ const pd = data.property_details;
77
+ const detailLines = [
78
+ pd.avm_value ? `**AVM:** $${Number(pd.avm_value).toLocaleString()}` : null,
79
+ pd.market_value ? `**Market Value:** $${Number(pd.market_value).toLocaleString()}` : null,
80
+ pd.owner ? `**Owner:** ${pd.owner}` : null,
81
+ pd.owner_occupied != null ? `**Owner Occupied:** ${pd.owner_occupied}` : null,
82
+ pd.pfc_flag ? `**Pre-Foreclosure:** Yes (${pd.pfc_recording_date ?? ""})` : null,
83
+ pd.vacant_flag ? `**Vacant:** Yes` : null,
84
+ ].filter(Boolean);
85
+ lines.push(detailLines.join(" | "));
86
+ }
82
87
  }
88
+ else {
89
+ lines.push("_No loans found within radius._");
90
+ }
91
+ // Slim items for structured output
92
+ const slimItems = items.slice(0, 25).map((l) => ({
93
+ address: l.full_street_address,
94
+ city: l.city,
95
+ state: l.state,
96
+ zip: l.zip_code,
97
+ lender: l.lender,
98
+ borrower: l.buyer_borrower1_name,
99
+ amount: l.mtg_amt ?? l.loan_amount,
100
+ recording_date: l.recording_date,
101
+ loan_type: l.loan_type ?? l.land_use_category,
102
+ }));
103
+ const firstBorrower = items[0]?.buyer_borrower1_name ?? items[0]?.borrower;
104
+ const actions = [
105
+ ...(firstBorrower ? [action("plr_borrower_profile", "Profile nearest borrower", { borrowerName: firstBorrower })] : []),
106
+ action("sfr_get_property", "Full property details", { address: args.propertyAddress }),
107
+ action("plr_transaction_history", "Property lending history", { propertyAddress: args.propertyAddress }),
108
+ ];
109
+ return structuredResult(lines.join("\n"), {
110
+ items: slimItems,
111
+ total: items.length,
112
+ deduped: rawItems.length - items.length > 0 ? rawItems.length - items.length : undefined,
113
+ page: 1,
114
+ }, actions);
83
115
  }
84
- else {
85
- lines.push("_No loans found within radius._");
116
+ catch (err) {
117
+ if (err instanceof ApiError && err.status === 404) {
118
+ const zipMatch = args.propertyAddress.match(/\b(\d{5})\b/);
119
+ const zip = zipMatch?.[1];
120
+ return structuredResult(`## Loans Nearby — ${args.propertyAddress}\n\n` +
121
+ `_Address not found or could not be geocoded. Verify the exact address format. ` +
122
+ `Use sfr_search_properties with the zip code to find the canonical address, ` +
123
+ `or use plr_borrower_search with geographic filters instead._`, { address: args.propertyAddress, error: "not_found" }, [action("sfr_search_properties", "Search by zip to find correct address", zip ? { search_type: "zip", zips: zip } : undefined)]);
124
+ }
125
+ throw err;
86
126
  }
87
- // Slim items for structured output
88
- const slimItems = items.slice(0, 25).map((l) => ({
89
- address: l.full_street_address,
90
- city: l.city,
91
- state: l.state,
92
- zip: l.zip_code,
93
- lender: l.lender,
94
- borrower: l.buyer_borrower1_name,
95
- amount: l.mtg_amt ?? l.loan_amount,
96
- recording_date: l.recording_date,
97
- loan_type: l.loan_type ?? l.land_use_category,
98
- }));
99
- const firstBorrower = items[0]?.buyer_borrower1_name ?? items[0]?.borrower;
100
- const actions = [
101
- ...(firstBorrower ? [action("plr_borrower_profile", "Profile nearest borrower", { borrowerName: firstBorrower })] : []),
102
- action("sfr_get_property", "Full property details", { address: args.propertyAddress }),
103
- action("plr_transaction_history", "Property lending history", { propertyAddress: args.propertyAddress }),
104
- ];
105
- return structuredResult(lines.join("\n"), {
106
- items: slimItems,
107
- total: items.length,
108
- deduped: rawItems.length - items.length > 0 ? rawItems.length - items.length : undefined,
109
- page: 1,
110
- }, actions);
111
127
  });
112
128
  }
113
129
  //# sourceMappingURL=loansNearby.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"loansNearby.js","sourceRoot":"","sources":["../../../src/tools/plr/loansNearby.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC;IACrB,eAAe,EAAE,CAAC;SACf,MAAM,EAAE;SACR,QAAQ,CAAC,8DAA8D,CAAC;IAC3E,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,oCAAoC,CAAC;IACnF,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,8BAA8B,CAAC;IAC7E,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;IAC1E,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC;CACpD,CAAC,CAAC;AAEH,MAAM,UAAU,uBAAuB,CAAC,MAAiB,EAAE,GAAc;IACvE,gBAAgB,CAAC,MAAM,EACrB,kBAAkB,EAClB;QACE,KAAK,EAAE,+BAA+B;QACtC,WAAW,EACT,8EAA8E;YAC9E,6EAA6E;YAC7E,iFAAiF;YACjF,kDAAkD;YAClD,yEAAyE;QAC3E,WAAW,EAAE,KAAK;KACnB,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACX,MAAM,KAAK,GAA4B;YACrC,gBAAgB,EAAE,IAAI,CAAC,eAAe;YACtC,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;QACF,IAAI,IAAI,CAAC,SAAS;YAAE,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;QACtD,IAAI,IAAI,CAAC,OAAO;YAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;QAChD,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAExB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,KAAK,CAAQ,CAAC;QACjD,MAAM,QAAQ,GAAG,IAAI,EAAE,OAAO,IAAI,IAAI,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAElF,qFAAqF;QACrF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE;YACvC,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,oBAAoB,IAAI,CAAC,CAAC,mBAAmB,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;YAC/H,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO,KAAK,CAAC;YAChC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG;YACZ,qBAAqB,IAAI,CAAC,eAAe,EAAE;YAC3C,eAAe,IAAI,CAAC,MAAM,oBAAoB,KAAK,CAAC,MAAM,EAAE;YAC5D,EAAE;SACH,CAAC;QAEF,6DAA6D;QAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,EAAE,gBAAgB,KAAK,IAAI,EAAE,CAAC;YAC1D,KAAK,CAAC,IAAI,CACR,oFAAoF;gBACpF,mFAAmF,CACpF,CAAC;YACF,OAAO,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QACrF,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;YAC7F,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC;gBAC9C,CAAC,CAAC,mBAAmB,IAAI,CAAC,CAAC,OAAO,IAAI,GAAG;gBACzC,GAAG,CAAC,CAAC,IAAI,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,GAAG;gBACjE,CAAC,CAAC,MAAM,IAAI,GAAG;gBACf,CAAC,CAAC,oBAAoB,IAAI,CAAC,CAAC,QAAQ,IAAI,GAAG;gBAC3C,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC;gBAChC,CAAC,CAAC,cAAc,IAAI,GAAG;aACxB,CAAC,CAAC;YAEH,KAAK,CAAC,IAAI,CAAC,aAAa,CACtB,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,EAC/D,IAAI,CACL,CAAC,CAAC;YAEH,0DAA0D;YAC1D,IAAI,IAAI,EAAE,gBAAgB,EAAE,CAAC;gBAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACf,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;gBAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC;gBACjC,MAAM,WAAW,GAAG;oBAClB,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI;oBAC1E,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,sBAAsB,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI;oBACzF,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI;oBAC1C,EAAE,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC,CAAC,uBAAuB,EAAE,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,IAAI;oBAC7E,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,6BAA6B,EAAE,CAAC,kBAAkB,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI;oBAChF,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI;iBAC1C,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAClB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QAChD,CAAC;QAED,mCAAmC;QACnC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YACpD,OAAO,EAAE,CAAC,CAAC,mBAAmB;YAC9B,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,GAAG,EAAE,CAAC,CAAC,QAAQ;YACf,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,QAAQ,EAAE,CAAC,CAAC,oBAAoB;YAChC,MAAM,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,WAAW;YAClC,cAAc,EAAE,CAAC,CAAC,cAAc;YAChC,SAAS,EAAE,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,iBAAiB;SAC9C,CAAC,CAAC,CAAC;QAEJ,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,oBAAoB,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;QAC3E,MAAM,OAAO,GAAG;YACd,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,sBAAsB,EAAE,0BAA0B,EAAE,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACvH,MAAM,CAAC,kBAAkB,EAAE,uBAAuB,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC;YACtF,MAAM,CAAC,yBAAyB,EAAE,0BAA0B,EAAE,EAAE,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC;SACzG,CAAC;QAEF,OAAO,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YACxC,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,OAAO,EAAE,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;YACxF,IAAI,EAAE,CAAC;SACR,EAAE,OAAO,CAAC,CAAC;IAChB,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"loansNearby.js","sourceRoot":"","sources":["../../../src/tools/plr/loansNearby.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAExD,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC;IACrB,eAAe,EAAE,CAAC;SACf,MAAM,EAAE;SACR,QAAQ,CACP,iEAAiE;QACjE,wDAAwD;QACxD,8FAA8F,CAC/F;IACH,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,oCAAoC,CAAC;IACnF,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,8BAA8B,CAAC;IAC7E,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;IAC1E,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC;CACpD,CAAC,CAAC;AAEH,MAAM,UAAU,uBAAuB,CAAC,MAAiB,EAAE,GAAc;IACvE,gBAAgB,CAAC,MAAM,EACrB,kBAAkB,EAClB;QACE,KAAK,EAAE,+BAA+B;QACtC,WAAW,EACT,8EAA8E;YAC9E,6EAA6E;YAC7E,iFAAiF;YACjF,kDAAkD;YAClD,yEAAyE;QAC3E,WAAW,EAAE,KAAK;KACnB,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC;YACH,MAAM,KAAK,GAA4B;gBACrC,gBAAgB,EAAE,IAAI,CAAC,eAAe;gBACtC,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;aAClB,CAAC;YACF,IAAI,IAAI,CAAC,SAAS;gBAAE,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;YACtD,IAAI,IAAI,CAAC,OAAO;gBAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;YAChD,gBAAgB,CAAC,KAAK,CAAC,CAAC;YAExB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,KAAK,CAAQ,CAAC;YACjD,MAAM,QAAQ,GAAG,IAAI,EAAE,OAAO,IAAI,IAAI,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAElF,qFAAqF;YACrF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE;gBACvC,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,oBAAoB,IAAI,CAAC,CAAC,mBAAmB,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;gBAC/H,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,OAAO,KAAK,CAAC;gBAChC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACd,OAAO,IAAI,CAAC;YACd,CAAC,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,qBAAqB,IAAI,CAAC,eAAe,EAAE;gBAC3C,eAAe,IAAI,CAAC,MAAM,oBAAoB,KAAK,CAAC,MAAM,EAAE;gBAC5D,EAAE;aACH,CAAC;YAEF,6DAA6D;YAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,EAAE,gBAAgB,KAAK,IAAI,EAAE,CAAC;gBAC1D,KAAK,CAAC,IAAI,CACR,oFAAoF;oBACpF,mFAAmF,CACpF,CAAC;gBACF,OAAO,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;YACrF,CAAC;YAED,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,MAAM,IAAI,GAAG,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;gBAC7F,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC;oBAC9C,CAAC,CAAC,mBAAmB,IAAI,CAAC,CAAC,OAAO,IAAI,GAAG;oBACzC,GAAG,CAAC,CAAC,IAAI,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,GAAG;oBACjE,CAAC,CAAC,MAAM,IAAI,GAAG;oBACf,CAAC,CAAC,oBAAoB,IAAI,CAAC,CAAC,QAAQ,IAAI,GAAG;oBAC3C,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC;oBAChC,CAAC,CAAC,cAAc,IAAI,GAAG;iBACxB,CAAC,CAAC;gBAEH,KAAK,CAAC,IAAI,CAAC,aAAa,CACtB,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,EAC/D,IAAI,CACL,CAAC,CAAC;gBAEH,0DAA0D;gBAC1D,IAAI,IAAI,EAAE,gBAAgB,EAAE,CAAC;oBAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACf,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;oBAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC;oBACjC,MAAM,WAAW,GAAG;wBAClB,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI;wBAC1E,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,sBAAsB,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI;wBACzF,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI;wBAC1C,EAAE,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC,CAAC,uBAAuB,EAAE,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,IAAI;wBAC7E,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,6BAA6B,EAAE,CAAC,kBAAkB,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI;wBAChF,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI;qBAC1C,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;oBAClB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBACtC,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;YAChD,CAAC;YAED,mCAAmC;YACnC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,CAAC,CAAC,mBAAmB;gBAC9B,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,GAAG,EAAE,CAAC,CAAC,QAAQ;gBACf,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,QAAQ,EAAE,CAAC,CAAC,oBAAoB;gBAChC,MAAM,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,WAAW;gBAClC,cAAc,EAAE,CAAC,CAAC,cAAc;gBAChC,SAAS,EAAE,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,iBAAiB;aAC9C,CAAC,CAAC,CAAC;YAEJ,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,oBAAoB,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;YAC3E,MAAM,OAAO,GAAG;gBACd,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,sBAAsB,EAAE,0BAA0B,EAAE,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvH,MAAM,CAAC,kBAAkB,EAAE,uBAAuB,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC;gBACtF,MAAM,CAAC,yBAAyB,EAAE,0BAA0B,EAAE,EAAE,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC;aACzG,CAAC;YAEF,OAAO,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBACxC,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,KAAK,CAAC,MAAM;gBACnB,OAAO,EAAE,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;gBACxF,IAAI,EAAE,CAAC;aACR,EAAE,OAAO,CAAC,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;gBAC3D,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC1B,OAAO,gBAAgB,CACrB,qBAAqB,IAAI,CAAC,eAAe,MAAM;oBAC/C,gFAAgF;oBAChF,6EAA6E;oBAC7E,8DAA8D,EAC9D,EAAE,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,WAAW,EAAE,EACrD,CAAC,MAAM,CAAC,uBAAuB,EAAE,uCAAuC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAChI,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -2,8 +2,12 @@ import { z } from "zod";
2
2
  import { structuredResult, markdownTable, truncatedJson } from "../formatters.js";
3
3
  import { registerToolSafe } from "../registerToolSafe.js";
4
4
  import { action } from "../nextActions.js";
5
+ import { ApiError } from "../../services/httpClient.js";
5
6
  const Input = z.object({
6
- propertyAddress: z.string().describe("Full property address, e.g. '123 Main St, Phoenix, AZ 85004'"),
7
+ propertyAddress: z.string().describe("Full property address exactly as recorded (e.g. '123 Main St, Phoenix, AZ 85004'). " +
8
+ "Must match dataset precisely — abbreviations (St/Ave/Blvd), directional prefixes (N/S/E/W), " +
9
+ "and unit numbers matter. If unsure, use sfr_search_properties with the zip code first to find " +
10
+ "the canonical address."),
7
11
  page: z.coerce.number().default(1).describe("Page number (default 1)"),
8
12
  pageSize: z.coerce.number().default(10).describe("Results per page (default 10, max 25)"),
9
13
  });
@@ -16,59 +20,72 @@ export function registerTransactionHistoryTool(server, plr) {
16
20
  "Complements sfr_property_transactions which shows deeds/sales.",
17
21
  inputSchema: Input,
18
22
  }, async (args) => {
19
- const body = {
20
- property_address: args.propertyAddress,
21
- page: args.page,
22
- page_size: Math.min(args.pageSize, 25),
23
- };
24
- const data = await plr.transactionHistory(body);
25
- const items = data?.results ?? data?.data ?? (Array.isArray(data) ? data : []);
26
- const total = data?.count ?? items.length;
27
- const lines = [
28
- `## Private Lending History — ${args.propertyAddress}`,
29
- `**Total:** ${total} transactions`,
30
- "",
31
- ];
32
- if (items.length > 0) {
33
- const fmt$ = (v) => v != null && Number(v) > 0 ? `$${Number(v).toLocaleString()}` : "—";
34
- const rows = items.slice(0, 20).map((t) => [
35
- t.recording_date ?? "—",
36
- t.tx_type ?? t.transaction_type_clean ?? "—",
37
- fmt$(t.sale_amt),
38
- t.buyer_borrower1_name ?? "—",
39
- t.seller1_name ?? "—",
40
- t.lender_clean ?? t.first_mtg_lender_name ?? "—",
41
- fmt$(t.first_mtg_amt),
42
- ]);
43
- lines.push(markdownTable(["Date", "Type", "Sale Price", "Buyer", "Seller", "Lender", "Mortgage"], rows));
44
- if (data.property_details) {
45
- const pd = data.property_details;
46
- lines.push("", "### Property Details");
47
- const detailLines = [
48
- pd.avm_value ? `**AVM:** $${Number(pd.avm_value).toLocaleString()}` : null,
49
- pd.market_value ? `**Market Value:** $${Number(pd.market_value).toLocaleString()}` : null,
50
- pd.owner ? `**Owner:** ${pd.owner}` : null,
51
- pd.property_type ? `**Type:** ${pd.property_type}` : null,
52
- pd.beds ? `**Beds:** ${pd.beds}` : null,
53
- pd.total_sqft ? `**Sqft:** ${pd.total_sqft}` : null,
54
- ].filter(Boolean);
55
- lines.push(detailLines.join(" | "));
23
+ try {
24
+ const body = {
25
+ property_address: args.propertyAddress,
26
+ page: args.page,
27
+ page_size: Math.min(args.pageSize, 25),
28
+ };
29
+ const data = await plr.transactionHistory(body);
30
+ const items = data?.results ?? data?.data ?? (Array.isArray(data) ? data : []);
31
+ const total = data?.count ?? items.length;
32
+ const lines = [
33
+ `## Private Lending History — ${args.propertyAddress}`,
34
+ `**Total:** ${total} transactions`,
35
+ "",
36
+ ];
37
+ if (items.length > 0) {
38
+ const fmt$ = (v) => v != null && Number(v) > 0 ? `$${Number(v).toLocaleString()}` : "—";
39
+ const rows = items.slice(0, 20).map((t) => [
40
+ t.recording_date ?? "—",
41
+ t.tx_type ?? t.transaction_type_clean ?? "—",
42
+ fmt$(t.sale_amt),
43
+ t.buyer_borrower1_name ?? "—",
44
+ t.seller1_name ?? "—",
45
+ t.lender_clean ?? t.first_mtg_lender_name ?? "—",
46
+ fmt$(t.first_mtg_amt),
47
+ ]);
48
+ lines.push(markdownTable(["Date", "Type", "Sale Price", "Buyer", "Seller", "Lender", "Mortgage"], rows));
49
+ if (data.property_details) {
50
+ const pd = data.property_details;
51
+ lines.push("", "### Property Details");
52
+ const detailLines = [
53
+ pd.avm_value ? `**AVM:** $${Number(pd.avm_value).toLocaleString()}` : null,
54
+ pd.market_value ? `**Market Value:** $${Number(pd.market_value).toLocaleString()}` : null,
55
+ pd.owner ? `**Owner:** ${pd.owner}` : null,
56
+ pd.property_type ? `**Type:** ${pd.property_type}` : null,
57
+ pd.beds ? `**Beds:** ${pd.beds}` : null,
58
+ pd.total_sqft ? `**Sqft:** ${pd.total_sqft}` : null,
59
+ ].filter(Boolean);
60
+ lines.push(detailLines.join(" | "));
61
+ }
56
62
  }
63
+ else {
64
+ lines.push("_No private lending transactions found for this property._");
65
+ if (data && typeof data === "object") {
66
+ lines.push("```json");
67
+ lines.push(truncatedJson(data, 2000));
68
+ lines.push("```");
69
+ }
70
+ }
71
+ const actions = [
72
+ action("sfr_get_property", "Full property details & valuation", { address: args.propertyAddress }),
73
+ action("plr_loans_nearby", "Other loans in the area", { propertyAddress: args.propertyAddress }),
74
+ action("sfr_property_transactions", "Deed & sale history", { address: args.propertyAddress }),
75
+ ];
76
+ return structuredResult(lines.join("\n"), { items, total, page: args.page }, actions);
57
77
  }
58
- else {
59
- lines.push("_No private lending transactions found for this property._");
60
- if (data && typeof data === "object") {
61
- lines.push("```json");
62
- lines.push(truncatedJson(data, 2000));
63
- lines.push("```");
78
+ catch (err) {
79
+ if (err instanceof ApiError && err.status === 404) {
80
+ const zipMatch = args.propertyAddress.match(/\b(\d{5})\b/);
81
+ const zip = zipMatch?.[1];
82
+ return structuredResult(`## Private Lending History — ${args.propertyAddress}\n\n` +
83
+ `_Property not found at this address. The address must exactly match the dataset ` +
84
+ `(including directional prefixes like 'E'/'W', abbreviations like 'St'/'Ave', and unit numbers). ` +
85
+ `Use sfr_search_properties with the zip code to find the canonical address._`, { address: args.propertyAddress, error: "not_found" }, [action("sfr_search_properties", "Search by zip to find correct address", zip ? { search_type: "zip", zips: zip } : undefined)]);
64
86
  }
87
+ throw err;
65
88
  }
66
- const actions = [
67
- action("sfr_get_property", "Full property details & valuation", { address: args.propertyAddress }),
68
- action("plr_loans_nearby", "Other loans in the area", { propertyAddress: args.propertyAddress }),
69
- action("sfr_property_transactions", "Deed & sale history", { address: args.propertyAddress }),
70
- ];
71
- return structuredResult(lines.join("\n"), { items, total, page: args.page }, actions);
72
89
  });
73
90
  }
74
91
  //# sourceMappingURL=transactionHistory.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transactionHistory.js","sourceRoot":"","sources":["../../../src/tools/plr/transactionHistory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transactionHistory.js","sourceRoot":"","sources":["../../../src/tools/plr/transactionHistory.ts"],"names":[],"mappings":"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"}
@@ -2,8 +2,12 @@ import { z } from "zod";
2
2
  import { structuredResult, markdownTable, truncatedJson } from "../formatters.js";
3
3
  import { action } from "../nextActions.js";
4
4
  import { registerToolSafe } from "../registerToolSafe.js";
5
+ import { ApiError } from "../../services/httpClient.js";
5
6
  const Input = z.object({
6
- address: z.string().describe("Full property address, e.g. '123 Main St, Phoenix, AZ 85004'"),
7
+ address: z.string().describe("Full property address exactly as recorded (e.g. '123 Main St, Phoenix, AZ 85004'). " +
8
+ "Must match dataset precisely — abbreviations (St/Ave/Blvd), directional prefixes (N/S/E/W), " +
9
+ "and unit numbers matter. If unsure, use sfr_search_properties with the zip code first to find " +
10
+ "the canonical address."),
7
11
  });
8
12
  export function registerBestBuyersTool(server, sfr) {
9
13
  registerToolSafe(server, "sfr_best_buyers", {
@@ -13,48 +17,61 @@ export function registerBestBuyersTool(server, sfr) {
13
17
  "Ideal for sellers, wholesalers, or agents matching properties to active buyers.",
14
18
  inputSchema: Input,
15
19
  }, async (args) => {
16
- const data = await sfr.getBestBuyers(args.address);
17
- const buyers = data?.data ?? data?.buyers ?? (Array.isArray(data) ? data : []);
18
- const lines = [
19
- `## Best Buyers — ${args.address}`,
20
- `**Matches:** ${buyers.length} investors`,
21
- "",
22
- ];
23
- if (buyers.length > 0) {
24
- // API returns {name, matchScore, totalAcquisitions, recentPurchasesCount, matchReasons}
25
- const rows = buyers.slice(0, 25).map((b, i) => [
26
- i + 1,
27
- b.name ?? b.buyer_name ?? "—",
28
- b.totalAcquisitions ?? b.acquisitions ?? "—",
29
- b.recentPurchasesCount ?? "—",
30
- b.matchScore != null ? `${b.matchScore}%` : "—",
31
- Array.isArray(b.matchReasons) ? b.matchReasons[0] ?? "—" : "—",
32
- ]);
33
- lines.push(markdownTable(["#", "Buyer", "Total Acq.", "Recent", "Match", "Reason"], rows));
20
+ try {
21
+ const data = await sfr.getBestBuyers(args.address);
22
+ const buyers = data?.data ?? data?.buyers ?? (Array.isArray(data) ? data : []);
23
+ const lines = [
24
+ `## Best Buyers — ${args.address}`,
25
+ `**Matches:** ${buyers.length} investors`,
26
+ "",
27
+ ];
28
+ if (buyers.length > 0) {
29
+ // API returns {name, matchScore, totalAcquisitions, recentPurchasesCount, matchReasons}
30
+ const rows = buyers.slice(0, 25).map((b, i) => [
31
+ i + 1,
32
+ b.name ?? b.buyer_name ?? "—",
33
+ b.totalAcquisitions ?? b.acquisitions ?? "—",
34
+ b.recentPurchasesCount ?? "—",
35
+ b.matchScore != null ? `${b.matchScore}%` : "—",
36
+ Array.isArray(b.matchReasons) ? b.matchReasons[0] ?? "—" : "—",
37
+ ]);
38
+ lines.push(markdownTable(["#", "Buyer", "Total Acq.", "Recent", "Match", "Reason"], rows));
39
+ }
40
+ else {
41
+ lines.push("```json");
42
+ lines.push(truncatedJson(data, 3000));
43
+ lines.push("```");
44
+ }
45
+ // Slim buyers — keep match-specific fields, strip URL slugs
46
+ const slimBuyers = buyers.slice(0, 25).map((b) => ({
47
+ name: b.name ?? b.buyer_name,
48
+ totalAcquisitions: b.totalAcquisitions ?? b.acquisitions,
49
+ recentPurchasesCount: b.recentPurchasesCount,
50
+ purchasesWithinQuarterMile: b.purchasesWithinQuarterMile || undefined,
51
+ matchScore: b.matchScore,
52
+ matchReasons: b.matchReasons,
53
+ hasProfile: b.hasProfilePage || undefined,
54
+ hasContact: b.hasContactInfo || undefined,
55
+ }));
56
+ const firstName = buyers[0]?.name ?? buyers[0]?.buyer_name;
57
+ const actions = [
58
+ ...(firstName ? [action("sfr_buyer_profile", "Profile top match", { name: firstName })] : []),
59
+ ...(firstName ? [action("sfr_buyer_growth", "Growth trend for top match", { name: firstName })] : []),
60
+ action("sfr_get_property", "Full property details", { address: args.address }),
61
+ ];
62
+ return structuredResult(lines.join("\n"), { items: slimBuyers, total: buyers.length, page: 1 }, actions);
34
63
  }
35
- else {
36
- lines.push("```json");
37
- lines.push(truncatedJson(data, 3000));
38
- lines.push("```");
64
+ catch (err) {
65
+ if (err instanceof ApiError && err.status === 404) {
66
+ const zipMatch = args.address.match(/\b(\d{5})\b/);
67
+ const zip = zipMatch?.[1];
68
+ return structuredResult(`## Best Buyers — ${args.address}\n\n` +
69
+ `_Property not found at this address. The address must exactly match the dataset ` +
70
+ `(including directional prefixes like 'E'/'W', abbreviations like 'St'/'Ave', and unit numbers). ` +
71
+ `Use sfr_search_properties with the zip code to find the canonical address._`, { address: args.address, error: "not_found" }, [action("sfr_search_properties", "Search by zip to find correct address", zip ? { search_type: "zip", zips: zip } : undefined)]);
72
+ }
73
+ throw err;
39
74
  }
40
- // Slim buyers — keep match-specific fields, strip URL slugs
41
- const slimBuyers = buyers.slice(0, 25).map((b) => ({
42
- name: b.name ?? b.buyer_name,
43
- totalAcquisitions: b.totalAcquisitions ?? b.acquisitions,
44
- recentPurchasesCount: b.recentPurchasesCount,
45
- purchasesWithinQuarterMile: b.purchasesWithinQuarterMile || undefined,
46
- matchScore: b.matchScore,
47
- matchReasons: b.matchReasons,
48
- hasProfile: b.hasProfilePage || undefined,
49
- hasContact: b.hasContactInfo || undefined,
50
- }));
51
- const firstName = buyers[0]?.name ?? buyers[0]?.buyer_name;
52
- const actions = [
53
- ...(firstName ? [action("sfr_buyer_profile", "Profile top match", { name: firstName })] : []),
54
- ...(firstName ? [action("sfr_buyer_growth", "Growth trend for top match", { name: firstName })] : []),
55
- action("sfr_get_property", "Full property details", { address: args.address }),
56
- ];
57
- return structuredResult(lines.join("\n"), { items: slimBuyers, total: buyers.length, page: 1 }, actions);
58
75
  });
59
76
  }
60
77
  //# sourceMappingURL=bestBuyers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bestBuyers.js","sourceRoot":"","sources":["../../../src/tools/sfr/bestBuyers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bestBuyers.js","sourceRoot":"","sources":["../../../src/tools/sfr/bestBuyers.ts"],"names":[],"mappings":"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"}
@@ -16,10 +16,12 @@ export function registerBuyerProfileTool(server, sfr) {
16
16
  "target property type, price range), geographic focus, related LLCs, and contact info. " +
17
17
  "Includes flip vs hold percentages and target margin ranges. " +
18
18
  "Returns relatedLLCs — use these names with sfr_flip_activity(buyer_name=) to find flip transactions across all entities. " +
19
+ "Note: relatedLLCs may return simplified/slug names rather than exact deed-recorded entity names for some investors. " +
19
20
  "For iBuyer/flipper margin analysis, chain: buyer_profile → get relatedLLCs → sfr_flip_activity per LLC. " +
20
- "Only major institutional investors have profiles use sfr_top_buyers to discover names. " +
21
+ "Profiles exist for investors with significant recorded transaction volume (typically 50+ acquisitions). " +
22
+ "Smaller investors won't have profiles — use sfr_top_buyers to discover names. " +
21
23
  "For acquisition trends over time, use sfr_buyer_growth. " +
22
- "CHAIN: Use plr_borrower_search with the investor name to check if they borrow from private lenders.",
24
+ "CHAIN: Use plr_borrower_profile with the investor name to check private lending activity and get contact info (requires PLR API key).",
23
25
  inputSchema: Input,
24
26
  }, async (args) => {
25
27
  try {
@@ -108,7 +110,7 @@ export function registerBuyerProfileTool(server, sfr) {
108
110
  const topZip = Array.isArray(data.portfolio?.topZipCodes) ? data.portfolio.topZipCodes[0] : undefined;
109
111
  const actions = [
110
112
  action("sfr_buyer_growth", "Acquisition trend over time", { name: args.name }),
111
- action("plr_borrower_search", "Check if investor borrows privately", { search: args.name }),
113
+ action("plr_borrower_profile", "Lending activity + contacts (PLR key required)", { borrowerName: args.name }),
112
114
  ...(topZip ? [action("sfr_zip_detail", "Deep-dive top zip", { zipCode: topZip })] : []),
113
115
  ...(topZip ? [action("sfr_search_properties", "Recent purchases in top zip", { search_type: "zip", zips: topZip })] : []),
114
116
  ];
@@ -152,8 +154,13 @@ export function registerBuyerProfileTool(server, sfr) {
152
154
  if (err instanceof ApiError && err.status === 404) {
153
155
  const slug = slugifyBuyerName(args.name);
154
156
  return structuredResult(`## Investor Profile — ${args.name}\n\n` +
155
- `_No profile found (slug: "${slug}"). Only major institutional investors have profiles. ` +
156
- `Use sfr_top_buyers to discover names with profiles._`, { name: args.name, slug, error: "not_found" }, [action("sfr_top_buyers", "Find buyers with profiles", {})]);
157
+ `_No SFR profile found (slug: "${slug}"). SFR profiles exist only for large institutional investors ` +
158
+ `(typically 50+ acquisitions)._ ` +
159
+ `For smaller investors, plr_borrower_profile provides lending activity and contact info (phone, email, principals). ` +
160
+ `Requires a PLR API key — reach out at support@sfranalytics.com or chat at https://mcp.sfranalytics.com to get access.`, { name: args.name, slug, error: "not_found" }, [
161
+ action("plr_borrower_profile", "Check PLR for lending activity + contacts", { borrowerName: args.name }),
162
+ action("sfr_top_buyers", "Find institutional buyers with profiles"),
163
+ ]);
157
164
  }
158
165
  throw err;
159
166
  }