@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.
Files changed (198) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +147 -0
  3. package/dist/config.d.ts +28 -0
  4. package/dist/config.js +101 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/http.d.ts +2 -0
  7. package/dist/http.js +252 -0
  8. package/dist/http.js.map +1 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +41 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/server.d.ts +3 -0
  13. package/dist/server.js +213 -0
  14. package/dist/server.js.map +1 -0
  15. package/dist/services/httpClient.d.ts +19 -0
  16. package/dist/services/httpClient.js +73 -0
  17. package/dist/services/httpClient.js.map +1 -0
  18. package/dist/services/plr.d.ts +26 -0
  19. package/dist/services/plr.js +75 -0
  20. package/dist/services/plr.js.map +1 -0
  21. package/dist/services/sfr.d.ts +33 -0
  22. package/dist/services/sfr.js +124 -0
  23. package/dist/services/sfr.js.map +1 -0
  24. package/dist/services/snowflake.d.ts +12 -0
  25. package/dist/services/snowflake.js +71 -0
  26. package/dist/services/snowflake.js.map +1 -0
  27. package/dist/tools/dateHelper.d.ts +26 -0
  28. package/dist/tools/dateHelper.js +86 -0
  29. package/dist/tools/dateHelper.js.map +1 -0
  30. package/dist/tools/formatters.d.ts +66 -0
  31. package/dist/tools/formatters.js +219 -0
  32. package/dist/tools/formatters.js.map +1 -0
  33. package/dist/tools/health.d.ts +5 -0
  34. package/dist/tools/health.js +38 -0
  35. package/dist/tools/health.js.map +1 -0
  36. package/dist/tools/nextActions.d.ts +10 -0
  37. package/dist/tools/nextActions.js +23 -0
  38. package/dist/tools/nextActions.js.map +1 -0
  39. package/dist/tools/plr/borrowerContacts.d.ts +3 -0
  40. package/dist/tools/plr/borrowerContacts.js +73 -0
  41. package/dist/tools/plr/borrowerContacts.js.map +1 -0
  42. package/dist/tools/plr/borrowerLoans.d.ts +3 -0
  43. package/dist/tools/plr/borrowerLoans.js +115 -0
  44. package/dist/tools/plr/borrowerLoans.js.map +1 -0
  45. package/dist/tools/plr/borrowerProfile.d.ts +3 -0
  46. package/dist/tools/plr/borrowerProfile.js +201 -0
  47. package/dist/tools/plr/borrowerProfile.js.map +1 -0
  48. package/dist/tools/plr/borrowerRankings.d.ts +3 -0
  49. package/dist/tools/plr/borrowerRankings.js +123 -0
  50. package/dist/tools/plr/borrowerRankings.js.map +1 -0
  51. package/dist/tools/plr/borrowerSearch.d.ts +3 -0
  52. package/dist/tools/plr/borrowerSearch.js +128 -0
  53. package/dist/tools/plr/borrowerSearch.js.map +1 -0
  54. package/dist/tools/plr/churnedBorrowers.d.ts +3 -0
  55. package/dist/tools/plr/churnedBorrowers.js +86 -0
  56. package/dist/tools/plr/churnedBorrowers.js.map +1 -0
  57. package/dist/tools/plr/lenderBorrowers.d.ts +3 -0
  58. package/dist/tools/plr/lenderBorrowers.js +71 -0
  59. package/dist/tools/plr/lenderBorrowers.js.map +1 -0
  60. package/dist/tools/plr/lenderRankings.d.ts +3 -0
  61. package/dist/tools/plr/lenderRankings.js +74 -0
  62. package/dist/tools/plr/lenderRankings.js.map +1 -0
  63. package/dist/tools/plr/loansNearby.d.ts +3 -0
  64. package/dist/tools/plr/loansNearby.js +113 -0
  65. package/dist/tools/plr/loansNearby.js.map +1 -0
  66. package/dist/tools/plr/marketTrends.d.ts +3 -0
  67. package/dist/tools/plr/marketTrends.js +95 -0
  68. package/dist/tools/plr/marketTrends.js.map +1 -0
  69. package/dist/tools/plr/msaRankings.d.ts +3 -0
  70. package/dist/tools/plr/msaRankings.js +74 -0
  71. package/dist/tools/plr/msaRankings.js.map +1 -0
  72. package/dist/tools/plr/negativeRemarks.d.ts +3 -0
  73. package/dist/tools/plr/negativeRemarks.js +94 -0
  74. package/dist/tools/plr/negativeRemarks.js.map +1 -0
  75. package/dist/tools/plr/ownerSearch.d.ts +3 -0
  76. package/dist/tools/plr/ownerSearch.js +57 -0
  77. package/dist/tools/plr/ownerSearch.js.map +1 -0
  78. package/dist/tools/plr/portfolioSummary.d.ts +3 -0
  79. package/dist/tools/plr/portfolioSummary.js +99 -0
  80. package/dist/tools/plr/portfolioSummary.js.map +1 -0
  81. package/dist/tools/plr/topBorrowers.d.ts +3 -0
  82. package/dist/tools/plr/topBorrowers.js +69 -0
  83. package/dist/tools/plr/topBorrowers.js.map +1 -0
  84. package/dist/tools/plr/topLenders.d.ts +3 -0
  85. package/dist/tools/plr/topLenders.js +75 -0
  86. package/dist/tools/plr/topLenders.js.map +1 -0
  87. package/dist/tools/plr/transactionHistory.d.ts +3 -0
  88. package/dist/tools/plr/transactionHistory.js +74 -0
  89. package/dist/tools/plr/transactionHistory.js.map +1 -0
  90. package/dist/tools/prompts.d.ts +7 -0
  91. package/dist/tools/prompts.js +157 -0
  92. package/dist/tools/prompts.js.map +1 -0
  93. package/dist/tools/registerToolSafe.d.ts +29 -0
  94. package/dist/tools/registerToolSafe.js +36 -0
  95. package/dist/tools/registerToolSafe.js.map +1 -0
  96. package/dist/tools/sfr/activityHighlights.d.ts +3 -0
  97. package/dist/tools/sfr/activityHighlights.js +70 -0
  98. package/dist/tools/sfr/activityHighlights.js.map +1 -0
  99. package/dist/tools/sfr/bestBuyers.d.ts +3 -0
  100. package/dist/tools/sfr/bestBuyers.js +60 -0
  101. package/dist/tools/sfr/bestBuyers.js.map +1 -0
  102. package/dist/tools/sfr/buyerGrowth.d.ts +3 -0
  103. package/dist/tools/sfr/buyerGrowth.js +68 -0
  104. package/dist/tools/sfr/buyerGrowth.js.map +1 -0
  105. package/dist/tools/sfr/buyerProfile.d.ts +3 -0
  106. package/dist/tools/sfr/buyerProfile.js +162 -0
  107. package/dist/tools/sfr/buyerProfile.js.map +1 -0
  108. package/dist/tools/sfr/distressSearch.d.ts +3 -0
  109. package/dist/tools/sfr/distressSearch.js +173 -0
  110. package/dist/tools/sfr/distressSearch.js.map +1 -0
  111. package/dist/tools/sfr/flipActivity.d.ts +3 -0
  112. package/dist/tools/sfr/flipActivity.js +110 -0
  113. package/dist/tools/sfr/flipActivity.js.map +1 -0
  114. package/dist/tools/sfr/flipStats.d.ts +3 -0
  115. package/dist/tools/sfr/flipStats.js +98 -0
  116. package/dist/tools/sfr/flipStats.js.map +1 -0
  117. package/dist/tools/sfr/getProperty.d.ts +3 -0
  118. package/dist/tools/sfr/getProperty.js +142 -0
  119. package/dist/tools/sfr/getProperty.js.map +1 -0
  120. package/dist/tools/sfr/institutionalOwners.d.ts +3 -0
  121. package/dist/tools/sfr/institutionalOwners.js +88 -0
  122. package/dist/tools/sfr/institutionalOwners.js.map +1 -0
  123. package/dist/tools/sfr/investorActivity.d.ts +3 -0
  124. package/dist/tools/sfr/investorActivity.js +130 -0
  125. package/dist/tools/sfr/investorActivity.js.map +1 -0
  126. package/dist/tools/sfr/marketHighlights.d.ts +3 -0
  127. package/dist/tools/sfr/marketHighlights.js +100 -0
  128. package/dist/tools/sfr/marketHighlights.js.map +1 -0
  129. package/dist/tools/sfr/msaResolver.d.ts +15 -0
  130. package/dist/tools/sfr/msaResolver.js +109 -0
  131. package/dist/tools/sfr/msaResolver.js.map +1 -0
  132. package/dist/tools/sfr/propertyBatch.d.ts +3 -0
  133. package/dist/tools/sfr/propertyBatch.js +73 -0
  134. package/dist/tools/sfr/propertyBatch.js.map +1 -0
  135. package/dist/tools/sfr/propertyComps.d.ts +3 -0
  136. package/dist/tools/sfr/propertyComps.js +56 -0
  137. package/dist/tools/sfr/propertyComps.js.map +1 -0
  138. package/dist/tools/sfr/propertyTransactions.d.ts +3 -0
  139. package/dist/tools/sfr/propertyTransactions.js +50 -0
  140. package/dist/tools/sfr/propertyTransactions.js.map +1 -0
  141. package/dist/tools/sfr/rentalComparables.d.ts +3 -0
  142. package/dist/tools/sfr/rentalComparables.js +91 -0
  143. package/dist/tools/sfr/rentalComparables.js.map +1 -0
  144. package/dist/tools/sfr/rentalMarketAnalysis.d.ts +3 -0
  145. package/dist/tools/sfr/rentalMarketAnalysis.js +134 -0
  146. package/dist/tools/sfr/rentalMarketAnalysis.js.map +1 -0
  147. package/dist/tools/sfr/rentalStats.d.ts +3 -0
  148. package/dist/tools/sfr/rentalStats.js +118 -0
  149. package/dist/tools/sfr/rentalStats.js.map +1 -0
  150. package/dist/tools/sfr/searchProperties.d.ts +3 -0
  151. package/dist/tools/sfr/searchProperties.js +157 -0
  152. package/dist/tools/sfr/searchProperties.js.map +1 -0
  153. package/dist/tools/sfr/topBuyers.d.ts +3 -0
  154. package/dist/tools/sfr/topBuyers.js +91 -0
  155. package/dist/tools/sfr/topBuyers.js.map +1 -0
  156. package/dist/tools/sfr/zipDetail.d.ts +3 -0
  157. package/dist/tools/sfr/zipDetail.js +85 -0
  158. package/dist/tools/sfr/zipDetail.js.map +1 -0
  159. package/dist/tools/sfr/zipFinder.d.ts +3 -0
  160. package/dist/tools/sfr/zipFinder.js +79 -0
  161. package/dist/tools/sfr/zipFinder.js.map +1 -0
  162. package/dist/tools/slugHelper.d.ts +2 -0
  163. package/dist/tools/slugHelper.js +5 -0
  164. package/dist/tools/slugHelper.js.map +1 -0
  165. package/dist/tools/snowflake/compareMarkets.d.ts +2 -0
  166. package/dist/tools/snowflake/compareMarkets.js +95 -0
  167. package/dist/tools/snowflake/compareMarkets.js.map +1 -0
  168. package/dist/tools/snowflake/hviTrend.d.ts +2 -0
  169. package/dist/tools/snowflake/hviTrend.js +77 -0
  170. package/dist/tools/snowflake/hviTrend.js.map +1 -0
  171. package/dist/tools/snowflake/investorActivity.d.ts +2 -0
  172. package/dist/tools/snowflake/investorActivity.js +138 -0
  173. package/dist/tools/snowflake/investorActivity.js.map +1 -0
  174. package/dist/tools/snowflake/marketSnapshot.d.ts +2 -0
  175. package/dist/tools/snowflake/marketSnapshot.js +185 -0
  176. package/dist/tools/snowflake/marketSnapshot.js.map +1 -0
  177. package/dist/tools/snowflake/marketTrends.d.ts +2 -0
  178. package/dist/tools/snowflake/marketTrends.js +81 -0
  179. package/dist/tools/snowflake/marketTrends.js.map +1 -0
  180. package/dist/tools/snowflake/rankRentalZips.d.ts +2 -0
  181. package/dist/tools/snowflake/rankRentalZips.js +94 -0
  182. package/dist/tools/snowflake/rankRentalZips.js.map +1 -0
  183. package/dist/tools/snowflake/rankZips.d.ts +2 -0
  184. package/dist/tools/snowflake/rankZips.js +86 -0
  185. package/dist/tools/snowflake/rankZips.js.map +1 -0
  186. package/dist/tools/snowflake/rentalMarket.d.ts +2 -0
  187. package/dist/tools/snowflake/rentalMarket.js +111 -0
  188. package/dist/tools/snowflake/rentalMarket.js.map +1 -0
  189. package/dist/tools/snowflake/rentalYield.d.ts +2 -0
  190. package/dist/tools/snowflake/rentalYield.js +143 -0
  191. package/dist/tools/snowflake/rentalYield.js.map +1 -0
  192. package/dist/tools/snowflake/zipProfile.d.ts +2 -0
  193. package/dist/tools/snowflake/zipProfile.js +110 -0
  194. package/dist/tools/snowflake/zipProfile.js.map +1 -0
  195. package/dist/version.d.ts +1 -0
  196. package/dist/version.js +5 -0
  197. package/dist/version.js.map +1 -0
  198. package/package.json +53 -0
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2024-2026 SFR Analytics, Inc. All rights reserved.
2
+
3
+ PROPRIETARY LICENSE
4
+
5
+ This software and associated documentation files (the "Software") are the
6
+ proprietary property of SFR Analytics, Inc.
7
+
8
+ Use of this Software is governed by the SFR Analytics Terms of Service
9
+ available at: https://www.sfranalytics.com/terms
10
+
11
+ You may use this Software only in accordance with a valid SFR Analytics
12
+ subscription and the Terms of Service. You may not copy, modify, distribute,
13
+ sell, or sublicense this Software or any portion thereof without prior written
14
+ consent from SFR Analytics, Inc.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
+ SFR ANALYTICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
21
+ OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+
24
+ For licensing inquiries: support@sfranalytics.com
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # @sfranalytics/mcp
2
+
3
+ MCP server for [SFR Analytics](https://www.sfranalytics.com) — single-family rental property data, buyer intelligence, and private lending insights for AI assistants.
4
+
5
+ ## Quick Start
6
+
7
+ ### Claude Code
8
+
9
+ ```bash
10
+ claude mcp add sfra -- npx -y @sfranalytics/mcp \
11
+ --env SFR_API_TOKEN=your-sfr-key \
12
+ --env PLR_API_TOKEN=your-plr-key
13
+ ```
14
+
15
+ ### Claude Desktop
16
+
17
+ Add to your `claude_desktop_config.json`:
18
+
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "sfra": {
23
+ "command": "npx",
24
+ "args": ["-y", "@sfranalytics/mcp"],
25
+ "env": {
26
+ "SFR_API_TOKEN": "your-sfr-key",
27
+ "PLR_API_TOKEN": "your-plr-key"
28
+ }
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ ### Hosted HTTP Transport
35
+
36
+ Run the MCP server over HTTP:
37
+
38
+ ```bash
39
+ npm run dev:http
40
+ ```
41
+
42
+ Connect from Claude Code:
43
+
44
+ ```bash
45
+ claude mcp add sfra-http --transport http http://localhost:3000/mcp \
46
+ --header "SFR-Api-Token: your-sfr-key" \
47
+ --header "PLR-Api-Token: your-plr-key"
48
+ ```
49
+
50
+ Production endpoint example:
51
+
52
+ ```bash
53
+ claude mcp add sfra-http --transport http https://mcp.sfranalytics.com/mcp \
54
+ --header "SFR-Api-Token: your-sfr-key" \
55
+ --header "PLR-Api-Token: your-plr-key"
56
+ ```
57
+
58
+ Notes:
59
+
60
+ - Provide at least one token header (`SFR-Api-Token` or `PLR-Api-Token`).
61
+ - `MCP_ALLOWED_ORIGINS` controls allowed `Origin` headers.
62
+ - `MCP_ALLOWED_HOSTS` controls allowed `Host` headers (recommended for public deployments).
63
+ - `MCP_STRICT_TOKEN_VALIDATION=true` optionally validates token(s) on each MCP request via upstream auth probes.
64
+ - In strict mode, explicit auth failures return `401`; temporary probe outages return `503`.
65
+ - Raw HTTP clients should send `Accept: application/json, text/event-stream`.
66
+ - HTTP mode currently excludes Snowflake tools.
67
+
68
+ ## API Keys
69
+
70
+ You can configure one or both API keys. Tools are enabled based on which keys you provide.
71
+
72
+ | Key | Enables | Tools |
73
+ |-----|---------|-------|
74
+ | `SFR_API_TOKEN` | Property data, buyer intelligence, market analytics | 21 tools |
75
+ | `PLR_API_TOKEN` | Private lending, borrower/lender intelligence | 15 tools |
76
+
77
+ Get your API keys at [sfranalytics.com](https://www.sfranalytics.com).
78
+
79
+ ## Tool Categories
80
+
81
+ ### SFR API (21 tools)
82
+
83
+ | Category | Tools | Description |
84
+ |----------|-------|-------------|
85
+ | Property Research | 6 | Search transactions, property details, history, MLS listings, comparables, distress/foreclosure |
86
+ | Rental Analysis | 3 | Market-level rental stats, nearby rental comps, rent growth trends |
87
+ | Zip/Market Screening | 3 | Rank zips by yield/rent/value, zip deep-dives, institutional ownership |
88
+ | Buyer Intelligence | 5 | Top buyers, investor profiles, growth trends, best deals, market highlights |
89
+ | Market Activity | 4 | Investor transactions, flip stats, flip activity, activity snapshots |
90
+
91
+ ### PLR API (15 tools)
92
+
93
+ | Category | Tools | Description |
94
+ |----------|-------|-------------|
95
+ | Borrower Intelligence | 6 | Search, profiles, rankings, top borrowers, loan history, contacts |
96
+ | Lender Intelligence | 4 | Rankings, top lenders, lender clients, churned borrowers |
97
+ | Market & Portfolio | 5 | Nationwide trends, MSA rankings, nearby loans, transaction feed, portfolio totals |
98
+
99
+ ### Shared
100
+
101
+ | Tool | Description |
102
+ |------|-------------|
103
+ | `sfra_health` | Connectivity check for configured APIs |
104
+
105
+ ## Configuration
106
+
107
+ | Variable | Required | Default | Description |
108
+ |----------|----------|---------|-------------|
109
+ | `SFR_API_TOKEN` | One or both | — | SFR Analytics API token |
110
+ | `PLR_API_TOKEN` | One or both | — | Private Lender Radar API token |
111
+ | `SFR_BASE_URL` | No | `https://api.sfranalytics.com` | SFR API base URL |
112
+ | `PLR_BASE_URL` | No | `https://radar-api.sfranalytics.com` | PLR API base URL |
113
+ | `HTTP_TIMEOUT_MS` | No | `20000` | HTTP request timeout (ms) |
114
+ | `PORT` | No | `3000` | HTTP server port (`dev:http` / `start:http`) |
115
+ | `MCP_BIND_HOST` | No | `127.0.0.1` | HTTP bind host |
116
+ | `MCP_ALLOWED_ORIGINS` | No | — | Comma-separated allowed `Origin` values for `/mcp` |
117
+ | `MCP_ALLOWED_HOSTS` | No | — | Comma-separated allowed `Host` values for DNS rebinding protection |
118
+ | `MCP_STRICT_TOKEN_VALIDATION` | No | `false` | If `true`, validate provided HTTP token(s) on each request using upstream auth probes |
119
+ | `MCP_SFR_TOKEN_PROBE_ADDRESS` | No | `123 Main St, Phoenix, AZ 85004` | Address used for SFR strict-token auth probe |
120
+
121
+ At least one API token must be provided.
122
+
123
+ ## Example Prompts
124
+
125
+ The server includes built-in prompts for common workflows:
126
+
127
+ - **market-screen** — Find top zip codes by investment criteria
128
+ - **buyer-research** — Research a property investor's portfolio and strategy
129
+ - **property-analysis** — Evaluate a property for SFR investment
130
+ - **lending-overview** — Private lending market overview
131
+ - **borrower-outreach** — Build a targeted borrower outreach list
132
+
133
+ Prompts are scoped to your configured APIs.
134
+
135
+ ## Requirements
136
+
137
+ - Node.js >= 18.0.0
138
+ - An MCP-compatible client (Claude Code, Claude Desktop, etc.)
139
+
140
+ ## Support
141
+
142
+ - Email: support@sfranalytics.com
143
+ - Website: [sfranalytics.com](https://www.sfranalytics.com)
144
+
145
+ ## License
146
+
147
+ Proprietary. See [LICENSE](./LICENSE) and [Terms of Service](https://www.sfranalytics.com/terms).
@@ -0,0 +1,28 @@
1
+ export type ServiceConfig = {
2
+ baseUrl: string;
3
+ apiToken: string;
4
+ apiTokenHeader: string;
5
+ };
6
+ export type SnowflakeConfig = {
7
+ account: string;
8
+ username: string;
9
+ password: string;
10
+ database: string;
11
+ schema: string;
12
+ warehouse: string;
13
+ };
14
+ export type AppConfig = {
15
+ sfr: ServiceConfig | null;
16
+ plr: ServiceConfig | null;
17
+ snowflake: SnowflakeConfig | null;
18
+ http: {
19
+ timeoutMs: number;
20
+ };
21
+ };
22
+ export type HeaderConfigInput = {
23
+ sfrToken?: string;
24
+ plrToken?: string;
25
+ timeoutMs?: number;
26
+ };
27
+ export declare function loadConfig(): AppConfig;
28
+ export declare function configFromHeaders(input: HeaderConfigInput): AppConfig;
package/dist/config.js ADDED
@@ -0,0 +1,101 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ function env(name) {
5
+ const v = process.env[name];
6
+ return v && v.trim() ? v.trim() : undefined;
7
+ }
8
+ /** Optional local secrets file for dev convenience. Keep it out of git. */
9
+ function loadLocalSecrets() {
10
+ const filename = env("MCP_LOCAL_SECRETS") ??
11
+ path.join(os.homedir(), ".sfra-mcp.secrets.json");
12
+ try {
13
+ if (!fs.existsSync(filename))
14
+ return {};
15
+ const raw = fs.readFileSync(filename, "utf8");
16
+ const json = JSON.parse(raw);
17
+ return typeof json === "object" && json ? json : {};
18
+ }
19
+ catch {
20
+ return {};
21
+ }
22
+ }
23
+ export function loadConfig() {
24
+ const secrets = loadLocalSecrets();
25
+ const get = (name) => env(name) ?? secrets[name];
26
+ const sfrToken = get("SFR_API_TOKEN");
27
+ const plrToken = get("PLR_API_TOKEN");
28
+ const sfAccount = get("SNOWFLAKE_ACCOUNT");
29
+ const sfUser = get("SNOWFLAKE_USERNAME");
30
+ const sfPass = get("SNOWFLAKE_PASSWORD");
31
+ if (!sfrToken && !plrToken && !sfAccount) {
32
+ throw new Error([
33
+ "No API tokens configured. Set at least one:",
34
+ "",
35
+ " SFR_API_TOKEN — property data, buyer intelligence, market analytics (21 tools)",
36
+ " PLR_API_TOKEN — private lending, borrower/lender intelligence (15 tools)",
37
+ " SNOWFLAKE_ACCOUNT — Redfin market data, census, rental yield, investor activity (10 tools)",
38
+ "",
39
+ "Get your API keys at https://www.sfranalytics.com",
40
+ "Questions? support@sfranalytics.com",
41
+ ].join("\n"));
42
+ }
43
+ const snowflake = sfAccount && sfUser && sfPass
44
+ ? {
45
+ account: sfAccount,
46
+ username: sfUser,
47
+ password: sfPass,
48
+ database: get("SNOWFLAKE_DATABASE") ?? "ANALYTICS",
49
+ schema: get("SNOWFLAKE_SCHEMA") ?? "PUBLIC",
50
+ warehouse: get("SNOWFLAKE_WAREHOUSE") ?? "COMPUTE_WH",
51
+ }
52
+ : null;
53
+ return {
54
+ sfr: sfrToken
55
+ ? {
56
+ baseUrl: (get("SFR_BASE_URL") ?? "https://api.sfranalytics.com").replace(/\/$/, ""),
57
+ apiToken: sfrToken,
58
+ apiTokenHeader: get("SFR_API_TOKEN_HEADER") ?? "X-API-TOKEN",
59
+ }
60
+ : null,
61
+ plr: plrToken
62
+ ? {
63
+ baseUrl: (get("PLR_BASE_URL") ?? "https://radar-api.sfranalytics.com").replace(/\/$/, ""),
64
+ apiToken: plrToken,
65
+ apiTokenHeader: get("PLR_API_TOKEN_HEADER") ?? "X-API-Key",
66
+ }
67
+ : null,
68
+ snowflake,
69
+ http: {
70
+ timeoutMs: Number(get("HTTP_TIMEOUT_MS") ?? 20000),
71
+ },
72
+ };
73
+ }
74
+ export function configFromHeaders(input) {
75
+ const get = (name) => env(name);
76
+ const timeoutFromEnv = Number(get("HTTP_TIMEOUT_MS") ?? 20000);
77
+ const timeoutMs = typeof input.timeoutMs === "number" && Number.isFinite(input.timeoutMs)
78
+ ? input.timeoutMs
79
+ : timeoutFromEnv;
80
+ return {
81
+ sfr: input.sfrToken
82
+ ? {
83
+ baseUrl: (get("SFR_BASE_URL") ?? "https://api.sfranalytics.com").replace(/\/$/, ""),
84
+ apiToken: input.sfrToken,
85
+ apiTokenHeader: get("SFR_API_TOKEN_HEADER") ?? "X-API-TOKEN",
86
+ }
87
+ : null,
88
+ plr: input.plrToken
89
+ ? {
90
+ baseUrl: (get("PLR_BASE_URL") ?? "https://radar-api.sfranalytics.com").replace(/\/$/, ""),
91
+ apiToken: input.plrToken,
92
+ apiTokenHeader: get("PLR_API_TOKEN_HEADER") ?? "X-API-Key",
93
+ }
94
+ : null,
95
+ snowflake: null,
96
+ http: {
97
+ timeoutMs,
98
+ },
99
+ };
100
+ }
101
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/http.js ADDED
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ import cors from "cors";
3
+ import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
4
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
5
+ import { configFromHeaders } from "./config.js";
6
+ import { createServer } from "./server.js";
7
+ import { ApiError, HttpClient } from "./services/httpClient.js";
8
+ import { VERSION } from "./version.js";
9
+ function env(name) {
10
+ const value = process.env[name];
11
+ return value && value.trim().length > 0 ? value.trim() : undefined;
12
+ }
13
+ function parseCsv(input) {
14
+ if (!input)
15
+ return [];
16
+ return input
17
+ .split(",")
18
+ .map((part) => part.trim())
19
+ .filter((part) => part.length > 0);
20
+ }
21
+ function parseBoolean(input) {
22
+ if (!input)
23
+ return false;
24
+ const normalized = input.trim().toLowerCase();
25
+ return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
26
+ }
27
+ function headerValue(req, name) {
28
+ const value = req.get(name);
29
+ if (!value)
30
+ return undefined;
31
+ const trimmed = value.trim();
32
+ return trimmed.length > 0 ? trimmed : undefined;
33
+ }
34
+ function jsonRpcError(code, message, id = null) {
35
+ return {
36
+ jsonrpc: "2.0",
37
+ error: { code, message },
38
+ id,
39
+ };
40
+ }
41
+ function safeErrorMessage(error) {
42
+ if (error instanceof Error && error.message)
43
+ return error.message;
44
+ return "unknown error";
45
+ }
46
+ const host = env("MCP_BIND_HOST") ?? env("HOST") ?? "127.0.0.1";
47
+ const parsedPort = Number.parseInt(env("PORT") ?? "3000", 10);
48
+ const port = Number.isFinite(parsedPort) && parsedPort > 0 ? parsedPort : 3000;
49
+ const allowedHosts = parseCsv(env("MCP_ALLOWED_HOSTS"));
50
+ const allowedOrigins = parseCsv(env("MCP_ALLOWED_ORIGINS"));
51
+ const allowAnyOrigin = allowedOrigins.includes("*");
52
+ const allowedOriginSet = new Set(allowedOrigins.filter((origin) => origin !== "*"));
53
+ const strictTokenValidation = parseBoolean(env("MCP_STRICT_TOKEN_VALIDATION"));
54
+ const app = createMcpExpressApp({
55
+ host,
56
+ allowedHosts: allowedHosts.length > 0 ? allowedHosts : undefined,
57
+ });
58
+ function isOriginAllowed(origin) {
59
+ return allowAnyOrigin || allowedOriginSet.has(origin);
60
+ }
61
+ function validateOrigin(req, res, next) {
62
+ const origin = headerValue(req, "Origin");
63
+ if (!origin || isOriginAllowed(origin)) {
64
+ next();
65
+ return;
66
+ }
67
+ res.status(403).json(jsonRpcError(-32001, "Origin not allowed"));
68
+ }
69
+ const corsOptions = {
70
+ origin(origin, callback) {
71
+ if (!origin || isOriginAllowed(origin)) {
72
+ callback(null, true);
73
+ return;
74
+ }
75
+ callback(null, false);
76
+ },
77
+ methods: ["POST", "GET", "DELETE", "OPTIONS"],
78
+ allowedHeaders: [
79
+ "Content-Type",
80
+ "Accept",
81
+ "Mcp-Protocol-Version",
82
+ "Mcp-Session-Id",
83
+ "SFR-Api-Token",
84
+ "PLR-Api-Token",
85
+ ],
86
+ exposedHeaders: [
87
+ "Mcp-Session-Id",
88
+ "Mcp-Protocol-Version",
89
+ "mcp-session-id",
90
+ "mcp-protocol-version",
91
+ ],
92
+ };
93
+ // Security + protocol hygiene
94
+ app.use("/mcp", validateOrigin);
95
+ app.use(cors(corsOptions));
96
+ app.use((_req, res, next) => {
97
+ res.setHeader("Cache-Control", "no-store");
98
+ next();
99
+ });
100
+ async function quickTokenCheck(config) {
101
+ const checks = [];
102
+ if (config.sfr) {
103
+ const sfrHttp = new HttpClient({
104
+ baseUrl: config.sfr.baseUrl,
105
+ defaultHeaders: { [config.sfr.apiTokenHeader]: config.sfr.apiToken },
106
+ timeoutMs: config.http.timeoutMs,
107
+ });
108
+ checks.push((async () => {
109
+ try {
110
+ const probeAddress = env("MCP_SFR_TOKEN_PROBE_ADDRESS") ?? "123 Main St, Phoenix, AZ 85004";
111
+ await sfrHttp.request("GET", "/properties/by-address", { query: { address: probeAddress } });
112
+ return "valid";
113
+ }
114
+ catch (error) {
115
+ if (error instanceof ApiError && (error.status === 401 || error.status === 403))
116
+ return "invalid";
117
+ if (error instanceof ApiError && (error.status === 408 || error.status === 429 || error.status >= 500)) {
118
+ return "unavailable";
119
+ }
120
+ if (error instanceof ApiError) {
121
+ // Non-auth client errors (400, 404, 422) mean the server authenticated the request
122
+ // but the probe query was invalid — token is valid, probe params are just wrong
123
+ if (error.status !== 200) {
124
+ console.error(`SFR token probe returned unexpected status ${error.status}`);
125
+ }
126
+ return "valid";
127
+ }
128
+ return "unavailable";
129
+ }
130
+ })());
131
+ }
132
+ if (config.plr) {
133
+ const plrHttp = new HttpClient({
134
+ baseUrl: config.plr.baseUrl,
135
+ defaultHeaders: { [config.plr.apiTokenHeader]: config.plr.apiToken },
136
+ timeoutMs: config.http.timeoutMs,
137
+ });
138
+ checks.push((async () => {
139
+ try {
140
+ await plrHttp.request("GET", "/api/v1/analytics/portfolio/portfolio_summary/", {
141
+ query: { start_date: "2024-01-01", end_date: "2024-01-31" },
142
+ });
143
+ return "valid";
144
+ }
145
+ catch (error) {
146
+ if (error instanceof ApiError && (error.status === 401 || error.status === 403))
147
+ return "invalid";
148
+ if (error instanceof ApiError && (error.status === 408 || error.status === 429 || error.status >= 500)) {
149
+ return "unavailable";
150
+ }
151
+ if (error instanceof ApiError) {
152
+ if (error.status !== 200) {
153
+ console.error(`PLR token probe returned unexpected status ${error.status}`);
154
+ }
155
+ return "valid";
156
+ }
157
+ return "unavailable";
158
+ }
159
+ })());
160
+ }
161
+ if (checks.length === 0)
162
+ return "invalid";
163
+ const results = await Promise.all(checks);
164
+ if (results.includes("invalid"))
165
+ return "invalid";
166
+ if (results.includes("unavailable"))
167
+ return "unavailable";
168
+ return "valid";
169
+ }
170
+ app.post("/mcp", async (req, res) => {
171
+ const sfrToken = headerValue(req, "SFR-Api-Token");
172
+ const plrToken = headerValue(req, "PLR-Api-Token");
173
+ if (!sfrToken && !plrToken) {
174
+ res
175
+ .status(401)
176
+ .json(jsonRpcError(-32001, "Missing API token. Provide SFR-Api-Token and/or PLR-Api-Token."));
177
+ return;
178
+ }
179
+ const config = configFromHeaders({ sfrToken, plrToken });
180
+ if (strictTokenValidation) {
181
+ try {
182
+ const validationResult = await quickTokenCheck(config);
183
+ if (validationResult === "invalid") {
184
+ res.status(401).json(jsonRpcError(-32001, "Invalid API token."));
185
+ return;
186
+ }
187
+ if (validationResult === "unavailable") {
188
+ res.status(503).json(jsonRpcError(-32603, "Token validation unavailable. Try again later."));
189
+ return;
190
+ }
191
+ }
192
+ catch (error) {
193
+ console.error(`Token validation error: ${safeErrorMessage(error)}`);
194
+ res.status(503).json(jsonRpcError(-32603, "Token validation unavailable. Try again later."));
195
+ return;
196
+ }
197
+ }
198
+ const server = createServer(config);
199
+ const transport = new StreamableHTTPServerTransport({
200
+ sessionIdGenerator: undefined,
201
+ enableJsonResponse: true,
202
+ });
203
+ const cleanup = () => {
204
+ res.off("close", cleanup);
205
+ void transport.close().catch(() => { });
206
+ void server.close().catch(() => { });
207
+ };
208
+ res.on("close", cleanup);
209
+ try {
210
+ await server.connect(transport);
211
+ await transport.handleRequest(req, res, req.body);
212
+ }
213
+ catch (error) {
214
+ console.error(`Error handling MCP request: ${safeErrorMessage(error)}`);
215
+ if (!res.headersSent) {
216
+ res.status(500).json(jsonRpcError(-32603, "Internal server error"));
217
+ }
218
+ cleanup();
219
+ }
220
+ });
221
+ const methodNotAllowed = (_req, res) => {
222
+ res.status(405).json(jsonRpcError(-32000, "Method not allowed."));
223
+ };
224
+ app.get("/mcp", methodNotAllowed);
225
+ app.delete("/mcp", methodNotAllowed);
226
+ app.options("/mcp", cors(corsOptions));
227
+ app.get("/health", (_req, res) => {
228
+ res.status(200).json({ ok: true, version: VERSION });
229
+ });
230
+ const httpServer = app.listen(port, host, () => {
231
+ if (allowedOriginSet.size === 0 && !allowAnyOrigin) {
232
+ console.error("MCP_ALLOWED_ORIGINS not set. Requests with an Origin header will be rejected.");
233
+ }
234
+ console.error(`@sfranalytics/mcp-http v${VERSION} listening on http://${host}:${port}/mcp`);
235
+ });
236
+ httpServer.on("error", (error) => {
237
+ console.error("Failed to start HTTP server:", error);
238
+ process.exit(1);
239
+ });
240
+ function shutdown(signal) {
241
+ console.error(`Received ${signal}; shutting down HTTP server...`);
242
+ httpServer.close((error) => {
243
+ if (error) {
244
+ console.error("Error during shutdown:", error);
245
+ process.exit(1);
246
+ }
247
+ process.exit(0);
248
+ });
249
+ }
250
+ process.on("SIGINT", () => shutdown("SIGINT"));
251
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
252
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { loadConfig } from "./config.js";
4
+ import { createServer } from "./server.js";
5
+ import { VERSION } from "./version.js";
6
+ import { disconnectSnowflake, isSnowflakeConfigured } from "./services/snowflake.js";
7
+ async function main() {
8
+ const config = loadConfig();
9
+ const sfrCount = config.sfr ? 21 : 0;
10
+ const plrCount = config.plr ? 15 : 0;
11
+ const sfCount = config.snowflake ? 10 : 0;
12
+ const parts = [];
13
+ if (config.sfr)
14
+ parts.push(`SFR (${sfrCount} tools)`);
15
+ if (config.plr)
16
+ parts.push(`PLR (${plrCount} tools)`);
17
+ if (config.snowflake)
18
+ parts.push(`Snowflake (${sfCount} tools)`);
19
+ const server = createServer(config);
20
+ const transport = new StdioServerTransport();
21
+ await server.connect(transport);
22
+ // stdout is reserved for MCP messages — log to stderr only
23
+ console.error(`@sfranalytics/mcp v${VERSION} — ${parts.join(" + ")} + health`);
24
+ }
25
+ // Graceful shutdown
26
+ async function cleanup() {
27
+ if (isSnowflakeConfigured()) {
28
+ try {
29
+ await disconnectSnowflake();
30
+ }
31
+ catch { /* ignore */ }
32
+ }
33
+ process.exit(0);
34
+ }
35
+ process.on("SIGINT", cleanup);
36
+ process.on("SIGTERM", cleanup);
37
+ main().catch((err) => {
38
+ console.error("Fatal:", err);
39
+ process.exit(1);
40
+ });
41
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErF,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,MAAM,CAAC,GAAG;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,QAAQ,SAAS,CAAC,CAAC;IACtD,IAAI,MAAM,CAAC,GAAG;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,QAAQ,SAAS,CAAC,CAAC;IACtD,IAAI,MAAM,CAAC,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,cAAc,OAAO,SAAS,CAAC,CAAC;IAEjE,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,2DAA2D;IAC3D,OAAO,CAAC,KAAK,CAAC,sBAAsB,OAAO,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AACjF,CAAC;AAED,oBAAoB;AACpB,KAAK,UAAU,OAAO;IACpB,IAAI,qBAAqB,EAAE,EAAE,CAAC;QAC5B,IAAI,CAAC;YAAC,MAAM,mBAAmB,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AACD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC9B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAE/B,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { AppConfig } from "./config.js";
3
+ export declare function createServer(config: AppConfig): McpServer;