@sfranalytics/mcp 0.6.0
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.
- package/LICENSE +24 -0
- package/README.md +147 -0
- package/dist/config.d.ts +28 -0
- package/dist/config.js +101 -0
- package/dist/config.js.map +1 -0
- package/dist/http.d.ts +2 -0
- package/dist/http.js +252 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +213 -0
- package/dist/server.js.map +1 -0
- package/dist/services/httpClient.d.ts +19 -0
- package/dist/services/httpClient.js +73 -0
- package/dist/services/httpClient.js.map +1 -0
- package/dist/services/plr.d.ts +26 -0
- package/dist/services/plr.js +75 -0
- package/dist/services/plr.js.map +1 -0
- package/dist/services/sfr.d.ts +33 -0
- package/dist/services/sfr.js +124 -0
- package/dist/services/sfr.js.map +1 -0
- package/dist/services/snowflake.d.ts +12 -0
- package/dist/services/snowflake.js +71 -0
- package/dist/services/snowflake.js.map +1 -0
- package/dist/tools/dateHelper.d.ts +26 -0
- package/dist/tools/dateHelper.js +86 -0
- package/dist/tools/dateHelper.js.map +1 -0
- package/dist/tools/formatters.d.ts +66 -0
- package/dist/tools/formatters.js +219 -0
- package/dist/tools/formatters.js.map +1 -0
- package/dist/tools/health.d.ts +5 -0
- package/dist/tools/health.js +38 -0
- package/dist/tools/health.js.map +1 -0
- package/dist/tools/nextActions.d.ts +10 -0
- package/dist/tools/nextActions.js +23 -0
- package/dist/tools/nextActions.js.map +1 -0
- package/dist/tools/plr/borrowerContacts.d.ts +3 -0
- package/dist/tools/plr/borrowerContacts.js +73 -0
- package/dist/tools/plr/borrowerContacts.js.map +1 -0
- package/dist/tools/plr/borrowerLoans.d.ts +3 -0
- package/dist/tools/plr/borrowerLoans.js +115 -0
- package/dist/tools/plr/borrowerLoans.js.map +1 -0
- package/dist/tools/plr/borrowerProfile.d.ts +3 -0
- package/dist/tools/plr/borrowerProfile.js +201 -0
- package/dist/tools/plr/borrowerProfile.js.map +1 -0
- package/dist/tools/plr/borrowerRankings.d.ts +3 -0
- package/dist/tools/plr/borrowerRankings.js +123 -0
- package/dist/tools/plr/borrowerRankings.js.map +1 -0
- package/dist/tools/plr/borrowerSearch.d.ts +3 -0
- package/dist/tools/plr/borrowerSearch.js +128 -0
- package/dist/tools/plr/borrowerSearch.js.map +1 -0
- package/dist/tools/plr/churnedBorrowers.d.ts +3 -0
- package/dist/tools/plr/churnedBorrowers.js +86 -0
- package/dist/tools/plr/churnedBorrowers.js.map +1 -0
- package/dist/tools/plr/lenderBorrowers.d.ts +3 -0
- package/dist/tools/plr/lenderBorrowers.js +71 -0
- package/dist/tools/plr/lenderBorrowers.js.map +1 -0
- package/dist/tools/plr/lenderRankings.d.ts +3 -0
- package/dist/tools/plr/lenderRankings.js +74 -0
- package/dist/tools/plr/lenderRankings.js.map +1 -0
- package/dist/tools/plr/loansNearby.d.ts +3 -0
- package/dist/tools/plr/loansNearby.js +113 -0
- package/dist/tools/plr/loansNearby.js.map +1 -0
- package/dist/tools/plr/marketTrends.d.ts +3 -0
- package/dist/tools/plr/marketTrends.js +95 -0
- package/dist/tools/plr/marketTrends.js.map +1 -0
- package/dist/tools/plr/msaRankings.d.ts +3 -0
- package/dist/tools/plr/msaRankings.js +74 -0
- package/dist/tools/plr/msaRankings.js.map +1 -0
- package/dist/tools/plr/negativeRemarks.d.ts +3 -0
- package/dist/tools/plr/negativeRemarks.js +94 -0
- package/dist/tools/plr/negativeRemarks.js.map +1 -0
- package/dist/tools/plr/ownerSearch.d.ts +3 -0
- package/dist/tools/plr/ownerSearch.js +57 -0
- package/dist/tools/plr/ownerSearch.js.map +1 -0
- package/dist/tools/plr/portfolioSummary.d.ts +3 -0
- package/dist/tools/plr/portfolioSummary.js +99 -0
- package/dist/tools/plr/portfolioSummary.js.map +1 -0
- package/dist/tools/plr/topBorrowers.d.ts +3 -0
- package/dist/tools/plr/topBorrowers.js +69 -0
- package/dist/tools/plr/topBorrowers.js.map +1 -0
- package/dist/tools/plr/topLenders.d.ts +3 -0
- package/dist/tools/plr/topLenders.js +75 -0
- package/dist/tools/plr/topLenders.js.map +1 -0
- package/dist/tools/plr/transactionHistory.d.ts +3 -0
- package/dist/tools/plr/transactionHistory.js +74 -0
- package/dist/tools/plr/transactionHistory.js.map +1 -0
- package/dist/tools/prompts.d.ts +7 -0
- package/dist/tools/prompts.js +157 -0
- package/dist/tools/prompts.js.map +1 -0
- package/dist/tools/registerToolSafe.d.ts +29 -0
- package/dist/tools/registerToolSafe.js +36 -0
- package/dist/tools/registerToolSafe.js.map +1 -0
- package/dist/tools/sfr/activityHighlights.d.ts +3 -0
- package/dist/tools/sfr/activityHighlights.js +70 -0
- package/dist/tools/sfr/activityHighlights.js.map +1 -0
- package/dist/tools/sfr/bestBuyers.d.ts +3 -0
- package/dist/tools/sfr/bestBuyers.js +60 -0
- package/dist/tools/sfr/bestBuyers.js.map +1 -0
- package/dist/tools/sfr/buyerGrowth.d.ts +3 -0
- package/dist/tools/sfr/buyerGrowth.js +68 -0
- package/dist/tools/sfr/buyerGrowth.js.map +1 -0
- package/dist/tools/sfr/buyerProfile.d.ts +3 -0
- package/dist/tools/sfr/buyerProfile.js +162 -0
- package/dist/tools/sfr/buyerProfile.js.map +1 -0
- package/dist/tools/sfr/distressSearch.d.ts +3 -0
- package/dist/tools/sfr/distressSearch.js +173 -0
- package/dist/tools/sfr/distressSearch.js.map +1 -0
- package/dist/tools/sfr/flipActivity.d.ts +3 -0
- package/dist/tools/sfr/flipActivity.js +110 -0
- package/dist/tools/sfr/flipActivity.js.map +1 -0
- package/dist/tools/sfr/flipStats.d.ts +3 -0
- package/dist/tools/sfr/flipStats.js +98 -0
- package/dist/tools/sfr/flipStats.js.map +1 -0
- package/dist/tools/sfr/getProperty.d.ts +3 -0
- package/dist/tools/sfr/getProperty.js +142 -0
- package/dist/tools/sfr/getProperty.js.map +1 -0
- package/dist/tools/sfr/institutionalOwners.d.ts +3 -0
- package/dist/tools/sfr/institutionalOwners.js +88 -0
- package/dist/tools/sfr/institutionalOwners.js.map +1 -0
- package/dist/tools/sfr/investorActivity.d.ts +3 -0
- package/dist/tools/sfr/investorActivity.js +130 -0
- package/dist/tools/sfr/investorActivity.js.map +1 -0
- package/dist/tools/sfr/marketHighlights.d.ts +3 -0
- package/dist/tools/sfr/marketHighlights.js +100 -0
- package/dist/tools/sfr/marketHighlights.js.map +1 -0
- package/dist/tools/sfr/msaResolver.d.ts +15 -0
- package/dist/tools/sfr/msaResolver.js +109 -0
- package/dist/tools/sfr/msaResolver.js.map +1 -0
- package/dist/tools/sfr/propertyBatch.d.ts +3 -0
- package/dist/tools/sfr/propertyBatch.js +73 -0
- package/dist/tools/sfr/propertyBatch.js.map +1 -0
- package/dist/tools/sfr/propertyComps.d.ts +3 -0
- package/dist/tools/sfr/propertyComps.js +56 -0
- package/dist/tools/sfr/propertyComps.js.map +1 -0
- package/dist/tools/sfr/propertyTransactions.d.ts +3 -0
- package/dist/tools/sfr/propertyTransactions.js +50 -0
- package/dist/tools/sfr/propertyTransactions.js.map +1 -0
- package/dist/tools/sfr/rentalComparables.d.ts +3 -0
- package/dist/tools/sfr/rentalComparables.js +91 -0
- package/dist/tools/sfr/rentalComparables.js.map +1 -0
- package/dist/tools/sfr/rentalMarketAnalysis.d.ts +3 -0
- package/dist/tools/sfr/rentalMarketAnalysis.js +134 -0
- package/dist/tools/sfr/rentalMarketAnalysis.js.map +1 -0
- package/dist/tools/sfr/rentalStats.d.ts +3 -0
- package/dist/tools/sfr/rentalStats.js +118 -0
- package/dist/tools/sfr/rentalStats.js.map +1 -0
- package/dist/tools/sfr/searchProperties.d.ts +3 -0
- package/dist/tools/sfr/searchProperties.js +157 -0
- package/dist/tools/sfr/searchProperties.js.map +1 -0
- package/dist/tools/sfr/topBuyers.d.ts +3 -0
- package/dist/tools/sfr/topBuyers.js +91 -0
- package/dist/tools/sfr/topBuyers.js.map +1 -0
- package/dist/tools/sfr/zipDetail.d.ts +3 -0
- package/dist/tools/sfr/zipDetail.js +85 -0
- package/dist/tools/sfr/zipDetail.js.map +1 -0
- package/dist/tools/sfr/zipFinder.d.ts +3 -0
- package/dist/tools/sfr/zipFinder.js +79 -0
- package/dist/tools/sfr/zipFinder.js.map +1 -0
- package/dist/tools/slugHelper.d.ts +2 -0
- package/dist/tools/slugHelper.js +5 -0
- package/dist/tools/slugHelper.js.map +1 -0
- package/dist/tools/snowflake/compareMarkets.d.ts +2 -0
- package/dist/tools/snowflake/compareMarkets.js +95 -0
- package/dist/tools/snowflake/compareMarkets.js.map +1 -0
- package/dist/tools/snowflake/hviTrend.d.ts +2 -0
- package/dist/tools/snowflake/hviTrend.js +77 -0
- package/dist/tools/snowflake/hviTrend.js.map +1 -0
- package/dist/tools/snowflake/investorActivity.d.ts +2 -0
- package/dist/tools/snowflake/investorActivity.js +138 -0
- package/dist/tools/snowflake/investorActivity.js.map +1 -0
- package/dist/tools/snowflake/marketSnapshot.d.ts +2 -0
- package/dist/tools/snowflake/marketSnapshot.js +185 -0
- package/dist/tools/snowflake/marketSnapshot.js.map +1 -0
- package/dist/tools/snowflake/marketTrends.d.ts +2 -0
- package/dist/tools/snowflake/marketTrends.js +81 -0
- package/dist/tools/snowflake/marketTrends.js.map +1 -0
- package/dist/tools/snowflake/rankRentalZips.d.ts +2 -0
- package/dist/tools/snowflake/rankRentalZips.js +94 -0
- package/dist/tools/snowflake/rankRentalZips.js.map +1 -0
- package/dist/tools/snowflake/rankZips.d.ts +2 -0
- package/dist/tools/snowflake/rankZips.js +86 -0
- package/dist/tools/snowflake/rankZips.js.map +1 -0
- package/dist/tools/snowflake/rentalMarket.d.ts +2 -0
- package/dist/tools/snowflake/rentalMarket.js +111 -0
- package/dist/tools/snowflake/rentalMarket.js.map +1 -0
- package/dist/tools/snowflake/rentalYield.d.ts +2 -0
- package/dist/tools/snowflake/rentalYield.js +143 -0
- package/dist/tools/snowflake/rentalYield.js.map +1 -0
- package/dist/tools/snowflake/zipProfile.d.ts +2 -0
- package/dist/tools/snowflake/zipProfile.js +110 -0
- package/dist/tools/snowflake/zipProfile.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { structuredResult, markdownTable, truncatedJson, fmtDollars } from "../formatters.js";
|
|
3
|
+
import { registerToolSafe } from "../registerToolSafe.js";
|
|
4
|
+
import { autoFillPlrDates } from "../dateHelper.js";
|
|
5
|
+
import { action } from "../nextActions.js";
|
|
6
|
+
const Input = z.object({
|
|
7
|
+
search: z.string().optional().describe("Search text for borrower name"),
|
|
8
|
+
states: z.string().optional().describe("Filter by entity registration state (where the LLC is registered, NOT where they do deals). Comma-separated, e.g. 'AZ,TX'. Use MSA filter for geographic activity."),
|
|
9
|
+
msas: z.string().optional().describe("Semicolon-separated MSA names (use ; because MSA names contain commas), e.g. 'Los Angeles-Long Beach-Anaheim, CA; Dallas-Fort Worth-Arlington, TX'. When user says a city like 'LA' or 'Phoenix', use the full MSA name."),
|
|
10
|
+
minLoans: z.coerce.number().optional().describe("Minimum total loan count"),
|
|
11
|
+
maxLoans: z.coerce.number().optional().describe("Maximum total loan count"),
|
|
12
|
+
minBridgeLoans: z.coerce.number().optional().describe("Minimum bridge loan count"),
|
|
13
|
+
minDscrLoans: z.coerce.number().optional().describe("Minimum DSCR loan count"),
|
|
14
|
+
hasPhone: z.boolean().optional().describe("Require phone number"),
|
|
15
|
+
hasEmail: z.boolean().optional().describe("Require email address"),
|
|
16
|
+
verifiedPhone: z.boolean().optional().describe("Require verified phone number"),
|
|
17
|
+
startDate: z.string().optional().describe("Start date for activity window, e.g. '2024-01-01'"),
|
|
18
|
+
endDate: z.string().optional().describe("End date for activity window"),
|
|
19
|
+
nextDueStart: z.string().optional().describe("Earliest loan maturity date, e.g. '2025-01-01'. Filters borrowers whose next maturing loan is on or after this date."),
|
|
20
|
+
nextDueEnd: z.string().optional().describe("Latest loan maturity date, e.g. '2025-12-31'. Filters borrowers whose next maturing loan is on or before this date. Use with nextDueStart for 'maturing in next 6 months' queries."),
|
|
21
|
+
ordering: z.string().optional().describe("Sort field: 'num_loans', 'borrower_name', or 'most_recent_recording_date'. Prefix with - for descending, e.g. '-num_loans'"),
|
|
22
|
+
page: z.coerce.number().default(1),
|
|
23
|
+
pageSize: z.coerce.number().default(25).describe("Results per page (default 25, max 25)"),
|
|
24
|
+
});
|
|
25
|
+
export function registerBorrowerSearchTool(server, plr) {
|
|
26
|
+
registerToolSafe(server, "plr_borrower_search", {
|
|
27
|
+
title: "Build targeted borrower lists with advanced filters (PLR)",
|
|
28
|
+
description: "The most flexible borrower search — filter by loan count (min/max), loan type (bridge/DSCR), " +
|
|
29
|
+
"geography (state/MSA), contact availability (phone/email/verified), activity window, " +
|
|
30
|
+
"and loan maturity dates (nextDueStart/nextDueEnd for 'loans maturing soon' queries). " +
|
|
31
|
+
"Best for 'top borrowers in [city/MSA]' queries and building targeted outreach lists. " +
|
|
32
|
+
"Defaults to last 1 year if no dates provided. " +
|
|
33
|
+
"Different from plr_borrower_rankings (growth trends) and plr_top_borrowers (nationwide only, no MSA). " +
|
|
34
|
+
"CHAIN: borrower names -> plr_borrower_loans (lender details) -> plr_borrower_contacts (phone/email/principals).",
|
|
35
|
+
inputSchema: Input,
|
|
36
|
+
}, async (args) => {
|
|
37
|
+
const body = {
|
|
38
|
+
page: args.page,
|
|
39
|
+
page_size: Math.min(args.pageSize, 25),
|
|
40
|
+
};
|
|
41
|
+
if (args.search)
|
|
42
|
+
body.search = args.search;
|
|
43
|
+
if (args.states)
|
|
44
|
+
body.states = args.states.split(",").map((s) => s.trim());
|
|
45
|
+
if (args.msas)
|
|
46
|
+
body.msas = args.msas.split(";").map((s) => s.trim());
|
|
47
|
+
if (args.minLoans !== undefined)
|
|
48
|
+
body.min_loans = args.minLoans;
|
|
49
|
+
if (args.maxLoans !== undefined)
|
|
50
|
+
body.max_loans = args.maxLoans;
|
|
51
|
+
if (args.minBridgeLoans !== undefined)
|
|
52
|
+
body.min_bridge_loans = args.minBridgeLoans;
|
|
53
|
+
if (args.minDscrLoans !== undefined)
|
|
54
|
+
body.min_dscr_loans = args.minDscrLoans;
|
|
55
|
+
if (args.hasPhone !== undefined)
|
|
56
|
+
body.has_phone = args.hasPhone;
|
|
57
|
+
if (args.hasEmail !== undefined)
|
|
58
|
+
body.has_email = args.hasEmail;
|
|
59
|
+
if (args.verifiedPhone !== undefined)
|
|
60
|
+
body.verified_phone = args.verifiedPhone;
|
|
61
|
+
if (args.startDate)
|
|
62
|
+
body.start_date = args.startDate;
|
|
63
|
+
if (args.endDate)
|
|
64
|
+
body.end_date = args.endDate;
|
|
65
|
+
if (args.nextDueStart)
|
|
66
|
+
body.next_due_start = args.nextDueStart;
|
|
67
|
+
if (args.nextDueEnd)
|
|
68
|
+
body.next_due_end = args.nextDueEnd;
|
|
69
|
+
if (args.ordering)
|
|
70
|
+
body.ordering = args.ordering;
|
|
71
|
+
autoFillPlrDates(body);
|
|
72
|
+
const data = await plr.borrowerListBuilder(body);
|
|
73
|
+
const items = data?.results ?? data?.data ?? (Array.isArray(data) ? data : []);
|
|
74
|
+
const total = data?.count ?? items.length;
|
|
75
|
+
const lines = [
|
|
76
|
+
`## Borrower Search Results`,
|
|
77
|
+
`**Total:** ${total} borrowers | **Page:** ${args.page} | **Showing:** ${items.length}`,
|
|
78
|
+
"",
|
|
79
|
+
];
|
|
80
|
+
if (items.length > 0) {
|
|
81
|
+
const rows = items.slice(0, 25).map((b) => [
|
|
82
|
+
b.borrower_name ?? "—",
|
|
83
|
+
b.state ?? "—",
|
|
84
|
+
b.num_loans ?? "—",
|
|
85
|
+
b.bridge_loans ?? "—",
|
|
86
|
+
b.dscr_loans ?? "—",
|
|
87
|
+
fmtDollars(b.total_volume ?? b.volume),
|
|
88
|
+
b.first_phone ? "Y" : "N",
|
|
89
|
+
b.first_email ? "Y" : "N",
|
|
90
|
+
b.most_recent_recording_date ?? b.last_recording_date ?? "—",
|
|
91
|
+
]);
|
|
92
|
+
lines.push(markdownTable(["Borrower", "Entity State", "Loans", "Bridge", "DSCR", "Volume", "Phone", "Email", "Last Active"], rows));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
lines.push("_No borrowers found matching criteria._ " +
|
|
96
|
+
"Try relaxing filters: lower minBridgeLoans, widen date range, or check MSA name spelling. " +
|
|
97
|
+
"Use plr_msa_rankings to discover correct PLR MSA names (different from SFR MSA names).");
|
|
98
|
+
if (typeof data === "object" && !Array.isArray(data)) {
|
|
99
|
+
lines.push("```json");
|
|
100
|
+
lines.push(truncatedJson(data, 2000));
|
|
101
|
+
lines.push("```");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Slim items for structured output — raw API items contain huge MSA/zip/address arrays
|
|
105
|
+
const slimItems = items.slice(0, 25).map((b) => ({
|
|
106
|
+
borrower_name: b.borrower_name,
|
|
107
|
+
state: b.state,
|
|
108
|
+
num_loans: b.num_loans,
|
|
109
|
+
bridge_loans: b.bridge_loans,
|
|
110
|
+
dscr_loans: b.dscr_loans,
|
|
111
|
+
has_phone: !!b.first_phone,
|
|
112
|
+
has_email: !!b.first_email,
|
|
113
|
+
total_volume: b.total_volume ?? b.volume ?? undefined,
|
|
114
|
+
verified_phone: b.has_good_phone ?? false,
|
|
115
|
+
last_active: b.most_recent_recording_date ?? b.last_recording_date,
|
|
116
|
+
most_active_msa: b.most_active_msa,
|
|
117
|
+
owner_name: b.owner_name,
|
|
118
|
+
}));
|
|
119
|
+
const firstName = items[0]?.borrower_name;
|
|
120
|
+
const actions = [
|
|
121
|
+
...(firstName ? [action("plr_borrower_profile", "Full profile for top result", { borrowerName: firstName })] : []),
|
|
122
|
+
...(firstName ? [action("plr_borrower_loans", "Loan history for top result", { borrowerNames: firstName })] : []),
|
|
123
|
+
...(firstName ? [action("plr_borrower_contacts", "Contact info for top result", { borrowerNames: firstName })] : []),
|
|
124
|
+
];
|
|
125
|
+
return structuredResult(lines.join("\n"), { items: slimItems, total, page: args.page }, actions);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=borrowerSearch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"borrowerSearch.js","sourceRoot":"","sources":["../../../src/tools/plr/borrowerSearch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { structuredResult, markdownTable, fmtDollars } from "../formatters.js";
|
|
3
|
+
import { registerToolSafe } from "../registerToolSafe.js";
|
|
4
|
+
import { action } from "../nextActions.js";
|
|
5
|
+
const Input = z.object({
|
|
6
|
+
lenders: z
|
|
7
|
+
.string()
|
|
8
|
+
.describe("Comma-separated lender names. Use short names from the 'lender' field (e.g. 'Dominion'), not display 'clean_name' (e.g. 'Dominion Financial Services')"),
|
|
9
|
+
search: z.string().optional().describe("Search within churned borrower names"),
|
|
10
|
+
page: z.coerce.number().default(1).describe("Page number (default 1)"),
|
|
11
|
+
pageSize: z.coerce.number().default(25).describe("Results per page (default 25, max 25)"),
|
|
12
|
+
});
|
|
13
|
+
export function registerChurnedBorrowersTool(server, plr) {
|
|
14
|
+
registerToolSafe(server, "plr_churned_borrowers", {
|
|
15
|
+
title: "Find churned borrowers (PLR)",
|
|
16
|
+
description: "Find borrowers who have LEFT a specific lender (churned to a competitor). " +
|
|
17
|
+
"Returns their current lender, loan recency (1/2/3yr), contact info, and owner names. " +
|
|
18
|
+
"Essential for competitive intelligence and win-back campaigns. " +
|
|
19
|
+
"For current borrowers, use plr_lender_borrowers instead. " +
|
|
20
|
+
"CHAIN: To check if churned borrowers are in distress, pass names as owner_name to sfr_distress_search.",
|
|
21
|
+
inputSchema: Input,
|
|
22
|
+
}, async (args) => {
|
|
23
|
+
const body = {
|
|
24
|
+
lenders: args.lenders.split(",").map((s) => s.trim()),
|
|
25
|
+
page: args.page,
|
|
26
|
+
page_size: Math.min(args.pageSize, 25),
|
|
27
|
+
};
|
|
28
|
+
if (args.search)
|
|
29
|
+
body.search = args.search;
|
|
30
|
+
const data = await plr.churnedBorrowers(body);
|
|
31
|
+
const items = data?.results ?? data?.data ?? (Array.isArray(data) ? data : []);
|
|
32
|
+
const total = data?.count ?? items.length;
|
|
33
|
+
const lines = [
|
|
34
|
+
`## Churned Borrowers — ${args.lenders}`,
|
|
35
|
+
`**Total:** ${total} | **Page:** ${args.page} | **Showing:** ${items.length}`,
|
|
36
|
+
"",
|
|
37
|
+
];
|
|
38
|
+
if (items.length > 0) {
|
|
39
|
+
const rows = items.slice(0, 25).map((b) => [
|
|
40
|
+
b.borrower ?? b.borrower_name ?? "—",
|
|
41
|
+
b.last_lender ?? "—",
|
|
42
|
+
b.total_number_of_loans ?? "—",
|
|
43
|
+
fmtDollars(b.total_origination_volume),
|
|
44
|
+
b.loans_in_last_1_year ?? "—",
|
|
45
|
+
b.loans_in_last_2_years ?? "—",
|
|
46
|
+
b.loans_in_last_3_years ?? "—",
|
|
47
|
+
b.first_phone ? "Y" : "N",
|
|
48
|
+
b.first_email ? "Y" : "N",
|
|
49
|
+
]);
|
|
50
|
+
lines.push(markdownTable(["Borrower", "Current Lender", "Loans", "Volume", "1yr", "2yr", "3yr", "Phone", "Email"], rows));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
lines.push("_No churned borrowers found._ " +
|
|
54
|
+
"Verify the lender name uses the short 'lender' field format (e.g. 'Kiavi' not 'Kiavi Funding Inc'). " +
|
|
55
|
+
"Use plr_lender_rankings to discover correct lender names.");
|
|
56
|
+
}
|
|
57
|
+
// Slim items — raw API has 5 owner_name fields, 3 phone fields, 3 email fields
|
|
58
|
+
const slimItems = items.slice(0, 25).map((b) => {
|
|
59
|
+
const owners = [b.owner_name_1, b.owner_name_2, b.owner_name_3, b.owner_name_4, b.owner_name_5]
|
|
60
|
+
.filter(Boolean);
|
|
61
|
+
return {
|
|
62
|
+
borrower: b.borrower ?? b.borrower_name,
|
|
63
|
+
last_lender: b.last_lender,
|
|
64
|
+
total_loans: b.total_number_of_loans,
|
|
65
|
+
total_volume: b.total_origination_volume,
|
|
66
|
+
last_origination_date: b.last_origination_date,
|
|
67
|
+
loans_1yr: b.loans_in_last_1_year,
|
|
68
|
+
loans_2yr: b.loans_in_last_2_years,
|
|
69
|
+
loans_3yr: b.loans_in_last_3_years,
|
|
70
|
+
lenders_used: b.lenders_used,
|
|
71
|
+
owners: owners.length > 0 ? owners : undefined,
|
|
72
|
+
phone: b.first_phone || undefined,
|
|
73
|
+
verified_phone: b.has_good_phone_transform ?? false,
|
|
74
|
+
email: b.first_email || undefined,
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
const firstName = items[0]?.borrower ?? items[0]?.borrower_name;
|
|
78
|
+
const actions = [
|
|
79
|
+
...(firstName ? [action("plr_borrower_profile", "Profile top churned borrower", { borrowerName: firstName })] : []),
|
|
80
|
+
...(firstName ? [action("plr_borrower_contacts", "Contact info for outreach", { borrowerNames: firstName })] : []),
|
|
81
|
+
action("plr_lender_borrowers", "Current borrowers for comparison", { lenders: args.lenders }),
|
|
82
|
+
];
|
|
83
|
+
return structuredResult(lines.join("\n"), { items: slimItems, total, page: args.page }, actions);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=churnedBorrowers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"churnedBorrowers.js","sourceRoot":"","sources":["../../../src/tools/plr/churnedBorrowers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { structuredResult, markdownTable, fmtDollars } from "../formatters.js";
|
|
3
|
+
import { registerToolSafe } from "../registerToolSafe.js";
|
|
4
|
+
import { autoFillPlrDates } from "../dateHelper.js";
|
|
5
|
+
import { action } from "../nextActions.js";
|
|
6
|
+
const Input = z.object({
|
|
7
|
+
lenders: z.string().describe("Comma-separated lender names. Use short names from 'lender' field (e.g. 'Kiavi,Lima One'), not display 'clean_name'"),
|
|
8
|
+
search: z.string().optional().describe("Search within borrower names"),
|
|
9
|
+
states: z.string().optional().describe("Comma-separated state filter (filters by entity registration state, not loan geography — results may include entities registered in a state but doing deals elsewhere)"),
|
|
10
|
+
startDate: z.string().optional().describe("Start date, e.g. '2024-01-01'"),
|
|
11
|
+
endDate: z.string().optional().describe("End date"),
|
|
12
|
+
page: z.coerce.number().default(1).describe("Page number (default 1)"),
|
|
13
|
+
pageSize: z.coerce.number().default(25).describe("Results per page (default 25, max 25)"),
|
|
14
|
+
});
|
|
15
|
+
export function registerLenderBorrowersTool(server, plr) {
|
|
16
|
+
registerToolSafe(server, "plr_lender_borrowers", {
|
|
17
|
+
title: "Borrowers of a specific lender (PLR)",
|
|
18
|
+
description: "Find all borrowers who have taken loans from specific lenders. " +
|
|
19
|
+
"Returns borrower names, loan counts, and amounts for each lender's customer base. " +
|
|
20
|
+
"Complementary to plr_churned_borrowers (which shows borrowers who LEFT a lender). " +
|
|
21
|
+
"CHAIN: To find borrowers in distress, pass names as owner_name to sfr_distress_search.",
|
|
22
|
+
inputSchema: Input,
|
|
23
|
+
}, async (args) => {
|
|
24
|
+
const body = {
|
|
25
|
+
lenders: args.lenders.split(",").map((s) => s.trim()),
|
|
26
|
+
page: args.page,
|
|
27
|
+
page_size: Math.min(args.pageSize, 25),
|
|
28
|
+
};
|
|
29
|
+
if (args.search)
|
|
30
|
+
body.search = args.search;
|
|
31
|
+
if (args.states)
|
|
32
|
+
body.states = args.states.split(",").map((s) => s.trim());
|
|
33
|
+
if (args.startDate)
|
|
34
|
+
body.start_date = args.startDate;
|
|
35
|
+
if (args.endDate)
|
|
36
|
+
body.end_date = args.endDate;
|
|
37
|
+
autoFillPlrDates(body);
|
|
38
|
+
const data = await plr.lenderBorrowers(body);
|
|
39
|
+
const items = data?.results ?? data?.data ?? (Array.isArray(data) ? data : []);
|
|
40
|
+
const total = data?.count ?? items.length;
|
|
41
|
+
const filters = [args.states, args.search].filter(Boolean);
|
|
42
|
+
const filterLabel = filters.length > 0 ? ` (${filters.join(", ")})` : "";
|
|
43
|
+
const lines = [
|
|
44
|
+
`## Lender Borrowers — ${args.lenders}${filterLabel}`,
|
|
45
|
+
`**Total:** ${total} | **Page:** ${args.page} | **Showing:** ${items.length}`,
|
|
46
|
+
"",
|
|
47
|
+
];
|
|
48
|
+
if (items.length > 0) {
|
|
49
|
+
const rows = items.slice(0, 25).map((b) => [
|
|
50
|
+
b.buyer_borrower1_name ?? b.borrower_name ?? "—",
|
|
51
|
+
b.num_loans ?? b.loan_count ?? "—",
|
|
52
|
+
fmtDollars(b.volume),
|
|
53
|
+
]);
|
|
54
|
+
lines.push(markdownTable(["Borrower", "Loans", "Volume"], rows));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
lines.push("_No borrowers found for these lenders._ " +
|
|
58
|
+
"Verify the lender name uses the short 'lender' field format (e.g. 'Kiavi' not 'Kiavi Funding Inc'). " +
|
|
59
|
+
"Use plr_lender_rankings to discover correct lender names. " +
|
|
60
|
+
"Note: the states filter applies to entity registration state, not loan geography.");
|
|
61
|
+
}
|
|
62
|
+
const firstName = items[0]?.buyer_borrower1_name ?? items[0]?.borrower_name;
|
|
63
|
+
const actions = [
|
|
64
|
+
...(firstName ? [action("plr_borrower_profile", "Profile top borrower", { borrowerName: firstName })] : []),
|
|
65
|
+
...(firstName ? [action("plr_borrower_contacts", "Contact info for top borrower", { borrowerNames: firstName })] : []),
|
|
66
|
+
action("plr_churned_borrowers", "Borrowers who left this lender", { lenders: args.lenders }),
|
|
67
|
+
];
|
|
68
|
+
return structuredResult(lines.join("\n"), { items, total, page: args.page }, actions);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=lenderBorrowers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lenderBorrowers.js","sourceRoot":"","sources":["../../../src/tools/plr/lenderBorrowers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { structuredResult, markdownTable, truncatedJson, fmtDollars } from "../formatters.js";
|
|
3
|
+
import { registerToolSafe } from "../registerToolSafe.js";
|
|
4
|
+
import { autoFillPlrDates } from "../dateHelper.js";
|
|
5
|
+
import { action } from "../nextActions.js";
|
|
6
|
+
const Input = z.object({
|
|
7
|
+
search: z.string().optional().describe("Search text for lender name (matches both short 'lender' and display 'clean_name')"),
|
|
8
|
+
startDate: z.string().optional().describe("Start date, e.g. '2024-01-01'"),
|
|
9
|
+
endDate: z.string().optional().describe("End date"),
|
|
10
|
+
loanTypes: z.string().optional().describe("Loan types filter: 'DSCR', 'Bridge', 'Unknown'"),
|
|
11
|
+
ordering: z.string().optional().describe("Sort field, e.g. '-current_count'"),
|
|
12
|
+
page: z.coerce.number().default(1).describe("Page number (default 1)"),
|
|
13
|
+
pageSize: z.coerce.number().default(25).describe("Results per page (default 25, max 25)"),
|
|
14
|
+
});
|
|
15
|
+
export function registerLenderRankingsTool(server, plr) {
|
|
16
|
+
registerToolSafe(server, "plr_lender_rankings", {
|
|
17
|
+
title: "Rank lenders by loan COUNT — nationwide only (PLR)",
|
|
18
|
+
description: "Rank private lenders NATIONWIDE by number of loans originated. " +
|
|
19
|
+
"Filters: loan type, date range, and search text only — NO geographic filters. " +
|
|
20
|
+
"For lender rankings WITH geographic filters (state/MSA), use plr_top_lenders instead. " +
|
|
21
|
+
"Returns lender names, loan counts, volumes, avg loan size, and HQ location. " +
|
|
22
|
+
"To see a lender's borrowers, use plr_lender_borrowers.",
|
|
23
|
+
inputSchema: Input,
|
|
24
|
+
}, async (args) => {
|
|
25
|
+
const body = {
|
|
26
|
+
page: args.page,
|
|
27
|
+
page_size: Math.min(args.pageSize, 25),
|
|
28
|
+
};
|
|
29
|
+
if (args.search)
|
|
30
|
+
body.search = args.search;
|
|
31
|
+
if (args.startDate)
|
|
32
|
+
body.start_date = args.startDate;
|
|
33
|
+
if (args.endDate)
|
|
34
|
+
body.end_date = args.endDate;
|
|
35
|
+
if (args.loanTypes)
|
|
36
|
+
body.loan_types = args.loanTypes.split(",").map((s) => s.trim());
|
|
37
|
+
if (args.ordering)
|
|
38
|
+
body.ordering = args.ordering;
|
|
39
|
+
autoFillPlrDates(body);
|
|
40
|
+
const data = await plr.lenderRankings(body);
|
|
41
|
+
const items = data?.results ?? data?.data ?? (Array.isArray(data) ? data : []);
|
|
42
|
+
const total = data?.count ?? items.length;
|
|
43
|
+
const lines = [
|
|
44
|
+
`## Lender Rankings`,
|
|
45
|
+
`**Total:** ${total} lenders | **Page:** ${args.page}`,
|
|
46
|
+
"",
|
|
47
|
+
];
|
|
48
|
+
if (items.length > 0) {
|
|
49
|
+
const rows = items.slice(0, 25).map((l, i) => [
|
|
50
|
+
i + 1 + (args.page - 1) * args.pageSize,
|
|
51
|
+
l.clean_name ?? l.lender ?? "—",
|
|
52
|
+
l.loan_count != null ? Number(l.loan_count).toLocaleString() : "—",
|
|
53
|
+
fmtDollars(l.total_volume),
|
|
54
|
+
fmtDollars(l.avg_loan_amount),
|
|
55
|
+
l.headquarters_location ?? "—",
|
|
56
|
+
]);
|
|
57
|
+
lines.push(markdownTable(["#", "Lender", "Loans", "Volume", "Avg Loan", "HQ"], rows));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
lines.push("_No lenders found._");
|
|
61
|
+
lines.push("```json");
|
|
62
|
+
lines.push(truncatedJson(data, 2000));
|
|
63
|
+
lines.push("```");
|
|
64
|
+
}
|
|
65
|
+
const firstName = items[0]?.lender ?? items[0]?.clean_name;
|
|
66
|
+
const actions = [
|
|
67
|
+
...(firstName ? [action("plr_lender_borrowers", "Borrowers of top lender", { lenders: firstName })] : []),
|
|
68
|
+
...(firstName ? [action("plr_churned_borrowers", "Churned borrowers from top lender", { lenders: firstName })] : []),
|
|
69
|
+
action("plr_top_lenders", "Rank by volume (with geo filters)", {}),
|
|
70
|
+
];
|
|
71
|
+
return structuredResult(lines.join("\n"), { items, total, page: args.page }, actions);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=lenderRankings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lenderRankings.js","sourceRoot":"","sources":["../../../src/tools/plr/lenderRankings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { structuredResult, markdownTable } from "../formatters.js";
|
|
3
|
+
import { registerToolSafe } from "../registerToolSafe.js";
|
|
4
|
+
import { autoFillPlrDates } from "../dateHelper.js";
|
|
5
|
+
import { action } from "../nextActions.js";
|
|
6
|
+
const Input = z.object({
|
|
7
|
+
propertyAddress: z
|
|
8
|
+
.string()
|
|
9
|
+
.describe("Full property address, e.g. '123 Main St, Phoenix, AZ 85004'"),
|
|
10
|
+
radius: z.coerce.number().default(1).describe("Search radius in miles (default 1)"),
|
|
11
|
+
limit: z.coerce.number().default(25).describe("Maximum results (default 25)"),
|
|
12
|
+
startDate: z.string().optional().describe("Start date, e.g. '2023-01-01'"),
|
|
13
|
+
endDate: z.string().optional().describe("End date"),
|
|
14
|
+
});
|
|
15
|
+
export function registerLoansNearbyTool(server, plr) {
|
|
16
|
+
registerToolSafe(server, "plr_loans_nearby", {
|
|
17
|
+
title: "Find loans near address (PLR)",
|
|
18
|
+
description: "Geospatial search: find private lender loans within a radius of an address. " +
|
|
19
|
+
"Returns each loan with distance, lender, borrower, amount, date, and type. " +
|
|
20
|
+
"Includes subject property details (AVM, owner, distress flags) when available. " +
|
|
21
|
+
"Great for understanding local lending activity. " +
|
|
22
|
+
"For a specific property's lending history, use plr_transaction_history.",
|
|
23
|
+
inputSchema: Input,
|
|
24
|
+
}, 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(" | "));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
lines.push("_No loans found within radius._");
|
|
86
|
+
}
|
|
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
|
+
});
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=loansNearby.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loansNearby.js","sourceRoot":"","sources":["../../../src/tools/plr/loansNearby.ts"],"names":[],"mappings":"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"}
|