freshcontext-mcp 0.3.3 → 0.3.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.
package/USAGE.md ADDED
@@ -0,0 +1,294 @@
1
+ # FreshContext — Usage Guide
2
+ > *13 tools. No API keys. Every result timestamped.*
3
+
4
+ ---
5
+
6
+ ## Quick Start
7
+
8
+ Add FreshContext to Claude Desktop (no install required):
9
+
10
+ ```json
11
+ {
12
+ "mcpServers": {
13
+ "freshcontext": {
14
+ "command": "npx",
15
+ "args": ["-y", "mcp-remote", "https://freshcontext-mcp.gimmanuel73.workers.dev/mcp"]
16
+ }
17
+ }
18
+ }
19
+ ```
20
+
21
+ Restart Claude. Done. All 13 tools are now available.
22
+
23
+ ---
24
+
25
+ ## The 13 Tools
26
+
27
+ | Tool | What it does | Input |
28
+ |---|---|---|
29
+ | `extract_github` | README, stars, forks, last commit | GitHub URL |
30
+ | `extract_hackernews` | Stories + scores with timestamps | HN URL or search query |
31
+ | `extract_scholar` | Research papers ranked by year | Google Scholar URL |
32
+ | `extract_arxiv` | arXiv papers via official API | Search query or arXiv URL |
33
+ | `extract_reddit` | Subreddit posts + community sentiment | Subreddit URL |
34
+ | `extract_yc` | YC company listings by keyword | Topic keyword |
35
+ | `extract_producthunt` | Recent launches by topic | Topic or PH URL |
36
+ | `search_repos` | GitHub repos ranked by stars + recency | Search query |
37
+ | `package_trends` | npm/PyPI metadata + download stats | Package name(s) |
38
+ | `extract_finance` | Live stock data — price, P/E, 52w range | Ticker symbol(s) |
39
+ | `extract_landscape` | 6 sources in one call | Topic keyword |
40
+ | `extract_changelog` | Update history from any product/repo | GitHub URL, npm name, or website URL |
41
+ | `extract_govcontracts` | US federal contract awards | Company name, keyword, or NAICS code |
42
+
43
+ ---
44
+
45
+ ## Command Reference
46
+
47
+ Copy and paste these directly into Claude.
48
+
49
+ ---
50
+
51
+ ### Competitive Research
52
+
53
+ ```
54
+ Use extract_landscape with topic "AI data freshness"
55
+ ```
56
+ *Full picture: who's funded, what's trending, what repos exist, what packages are moving.*
57
+
58
+ ```
59
+ Use search_repos with query "mcp server typescript"
60
+ ```
61
+ *GitHub repos ranked by stars. See who's already building in your space.*
62
+
63
+ ```
64
+ Use extract_yc with query "developer infrastructure"
65
+ ```
66
+ *Which YC companies are funded in your category.*
67
+
68
+ ```
69
+ Use extract_producthunt with url "developer tools"
70
+ ```
71
+ *What launched this week in your category.*
72
+
73
+ ---
74
+
75
+ ### Market Intelligence
76
+
77
+ ```
78
+ Use extract_hackernews with url "https://hn.algolia.com/?q=model+context+protocol+2026"
79
+ ```
80
+ *What the developer community is saying about a topic right now.*
81
+
82
+ ```
83
+ Use extract_reddit with url "https://www.reddit.com/r/MachineLearning/"
84
+ ```
85
+ *Subreddit sentiment — what problems people are complaining about or excited about.*
86
+
87
+ ```
88
+ Use extract_finance with url "NVDA,MSFT,GOOGL"
89
+ ```
90
+ *Live stock data for competitive set. Know the market before a pitch.*
91
+
92
+ ```
93
+ Use extract_govcontracts with url "AI infrastructure"
94
+ ```
95
+ *Which companies are winning federal AI contracts. High-signal GTM data.*
96
+
97
+ ```
98
+ Use extract_govcontracts with url "Palantir"
99
+ ```
100
+ *How much government business a specific company is doing — and when.*
101
+
102
+ ---
103
+
104
+ ### Job Market Intelligence
105
+
106
+ ```
107
+ Use search_jobs with query "AI engineer" location "remote" max_age_days 7
108
+ ```
109
+ *Fresh job listings only. Every result has a freshness badge — never apply to a closed role again.*
110
+
111
+ ```
112
+ Use search_jobs with query "typescript developer" remote_only true
113
+ ```
114
+ *Remote-only filtered results with publish dates.*
115
+
116
+ ```
117
+ Use extract_govcontracts with url "541511"
118
+ ```
119
+ *NAICS 541511 = Custom Computer Programming Services. See who's hiring via contract awards.*
120
+
121
+ ---
122
+
123
+ ### Developer Ecosystem Research
124
+
125
+ ```
126
+ Use package_trends with url "mcp,wrangler,anthropic"
127
+ ```
128
+ *npm download trends for multiple packages. See what's gaining traction.*
129
+
130
+ ```
131
+ Use extract_changelog with url "https://github.com/anthropics/anthropic-sdk-python"
132
+ ```
133
+ *Latest releases with dates. Know exactly when a dependency shipped a change.*
134
+
135
+ ```
136
+ Use extract_changelog with url "freshcontext-mcp"
137
+ ```
138
+ *npm package changelog — version history and release cadence.*
139
+
140
+ ```
141
+ Use extract_changelog with url "https://linear.app"
142
+ ```
143
+ *Auto-discovers the changelog page and extracts update history.*
144
+
145
+ ```
146
+ Use search_repos with query "cloudflare workers MCP 2026"
147
+ ```
148
+ *What's being built on a platform right now.*
149
+
150
+ ---
151
+
152
+ ### Academic & Research Intelligence
153
+
154
+ ```
155
+ Use extract_arxiv with url "https://arxiv.org/search/?query=model+context+protocol&searchtype=all"
156
+ ```
157
+ *Latest papers on a topic with submission dates.*
158
+
159
+ ```
160
+ Use extract_scholar with url "https://scholar.google.com/scholar?q=AI+agent+grounding"
161
+ ```
162
+ *Google Scholar results ranked by recency.*
163
+
164
+ ---
165
+
166
+ ### Full Research Workflows
167
+
168
+ #### Validate a startup idea
169
+ ```
170
+ 1. Use extract_landscape with topic "[your idea]"
171
+ 2. Use extract_yc with query "[your idea]"
172
+ 3. Use extract_govcontracts with url "[your idea keyword]"
173
+ 4. Use extract_hackernews with url "https://hn.algolia.com/?q=[your+idea]"
174
+ ```
175
+ *Tells you: who's funded, what's trending, is there government demand, what do developers think.*
176
+
177
+ #### Research a company before a meeting
178
+ ```
179
+ 1. Use extract_github with url "https://github.com/[company]"
180
+ 2. Use extract_changelog with url "https://github.com/[company]/[main-repo]"
181
+ 3. Use extract_govcontracts with url "[company name]"
182
+ 4. Use extract_finance with url "[ticker]"
183
+ ```
184
+ *Tells you: how active is their engineering team, what did they ship recently, do they have government contracts, what's the stock doing.*
185
+
186
+ #### Find your next job
187
+ ```
188
+ 1. Use search_jobs with query "[your role]" max_age_days 7
189
+ 2. Use extract_govcontracts with url "[target company]"
190
+ 3. Use extract_changelog with url "https://github.com/[target-company]/[repo]"
191
+ ```
192
+ *Fresh listings only + proof the company is growing + proof they're actively shipping.*
193
+
194
+ #### Track a competitor
195
+ ```
196
+ 1. Use extract_changelog with url "https://[competitor].com"
197
+ 2. Use extract_github with url "https://github.com/[competitor]/[repo]"
198
+ 3. Use package_trends with url "[competitor-npm-package]"
199
+ 4. Use extract_hackernews with url "https://hn.algolia.com/?q=[competitor+name]"
200
+ ```
201
+ *When did they last ship? Is their repo active? Are downloads growing? What is the community saying?*
202
+
203
+ ---
204
+
205
+ ## Who This Is For
206
+
207
+ ### Jobseekers
208
+ The tool FreshContext was built for. Stop applying to roles that closed months ago. Every job listing comes with a freshness badge — green means posted this week, red means posted 60+ days ago. Filter by `max_age_days` to only see roles posted recently.
209
+
210
+ **Key tools:** `search_jobs`, `extract_govcontracts`, `extract_changelog`
211
+
212
+ ---
213
+
214
+ ### Founders & Product Teams
215
+ Validate ideas, track competitors, monitor your ecosystem. `extract_landscape` gives you a full competitive picture in one call — YC funding, GitHub activity, HN sentiment, Product Hunt launches, npm traction, all timestamped.
216
+
217
+ **Key tools:** `extract_landscape`, `extract_yc`, `extract_changelog`, `extract_hackernews`
218
+
219
+ ---
220
+
221
+ ### GTM & Sales Teams
222
+ Government contracts are buying intent signals. A company that just won a $5M DoD contract is hiring, spending, and building. FreshContext is the only MCP server with access to USASpending.gov contract data.
223
+
224
+ **Key tools:** `extract_govcontracts`, `extract_landscape`, `extract_finance`
225
+
226
+ ---
227
+
228
+ ### Investors & Analysts
229
+ Track portfolio companies, find signals before they're obvious, monitor competitor funding. `extract_govcontracts` shows government contract pipeline. `extract_changelog` shows engineering velocity. `extract_finance` gives live market data.
230
+
231
+ **Key tools:** `extract_govcontracts`, `extract_landscape`, `extract_finance`, `extract_changelog`
232
+
233
+ ---
234
+
235
+ ### Researchers & Academics
236
+ Fresh papers from arXiv and Google Scholar, ranked by date. Never cite a paper without knowing exactly when it was published and retrieved.
237
+
238
+ **Key tools:** `extract_arxiv`, `extract_scholar`, `extract_hackernews`
239
+
240
+ ---
241
+
242
+ ### Developers & DevRel Teams
243
+ Monitor your dependencies, track ecosystem traction, see what's getting attention in the community. `extract_changelog` works on any GitHub repo, npm package, or website that has a changelog.
244
+
245
+ **Key tools:** `extract_changelog`, `package_trends`, `search_repos`, `extract_github`
246
+
247
+ ---
248
+
249
+ ## Understanding the FreshContext Envelope
250
+
251
+ Every result is wrapped like this:
252
+
253
+ ```
254
+ [FRESHCONTEXT]
255
+ Source: https://github.com/owner/repo
256
+ Published: 2026-03-10
257
+ Retrieved: 2026-03-17T09:19:00Z
258
+ Confidence: high
259
+ ---
260
+ ... content ...
261
+ [/FRESHCONTEXT]
262
+ ```
263
+
264
+ **Confidence levels:**
265
+ - `high` — date came from a structured API field. Reliable.
266
+ - `medium` — date inferred from page signals or URL patterns. Likely correct.
267
+ - `low` — no date signal found. Treat with caution.
268
+
269
+ The gap between `Published` and `Retrieved` is your staleness indicator. If a job was published 90 days ago and retrieved today, the role is likely filled. If a GitHub repo was last pushed 2 years ago, the project may be abandoned.
270
+
271
+ ---
272
+
273
+ ## Freshness Score (coming in v0.4.0)
274
+
275
+ A numeric score from 0–100 will be added to every result:
276
+
277
+ ```
278
+ freshness_score = max(0, 100 - (days_since_retrieved × decay_rate))
279
+ ```
280
+
281
+ Different data types decay at different rates — financial data goes stale faster than academic papers. You'll be able to filter results by `freshness_score > 70` directly in your prompts.
282
+
283
+ ---
284
+
285
+ ## The Standard
286
+
287
+ FreshContext implements the **FreshContext Specification v1.0** — an open standard for data freshness envelopes. See `FRESHCONTEXT_SPEC.md` for the full specification.
288
+
289
+ Any tool or agent that wraps its retrieved data in the FreshContext envelope is **FreshContext-compatible**.
290
+
291
+ ---
292
+
293
+ *Built by Prince Gabriel — Grootfontein, Namibia 🇳🇦*
294
+ *"The work isn't gone. It's just waiting to be continued."*
@@ -1,63 +1,84 @@
1
1
  /**
2
2
  * Government Contracts adapter — fetches awarded contract data from USASpending.gov
3
3
  *
4
- * Why this is unique:
5
- * No other MCP server exposes government contract data.
6
- * For GTM teams, VC investors, and competitive researchers, knowing when a
7
- * company wins a government contract is a high-signal buying intent indicator.
8
- * A company that just won a $2M DoD contract is hiring, spending, and building.
4
+ * No other MCP server has this. USASpending.gov is the official US Treasury
5
+ * database of all federal contract awards. Updated daily.
9
6
  *
10
7
  * Accepts:
11
- * - Company name: "Cloudflare" → finds contracts awarded to that company
12
- * - NAICS code: "541511" → software publishers contracts
13
- * - Agency name: "Department of Defense" → all DoD contracts
14
- * - Keyword: "AI infrastructure" contracts with that keyword
15
- * - A URL: https://api.usaspending.gov/... → direct API call
16
- *
17
- * Data source: USASpending.gov public API (no API key required)
18
- * Coverage: All US federal contracts, grants, and awards
19
- * Freshness: Updated daily by the US Treasury
20
- *
21
- * What it returns:
22
- * - Award recipient name and location
23
- * - Contract amount (obligated)
24
- * - Award date (high confidence timestamp)
25
- * - Awarding agency and sub-agency
26
- * - Contract description / award title
27
- * - NAICS code and description
28
- * - Period of performance dates
8
+ * - Company name: "Palantir" → contracts awarded to that company
9
+ * - Keyword: "AI infrastructure" → contracts with that keyword in description
10
+ * - NAICS code: "541511" → all software publisher contracts
11
+ * - Direct URL: https://api.usaspending.gov/...direct API call
29
12
  */
30
13
  function sanitize(s) {
31
14
  return s.replace(/[^\x20-\x7E]/g, "").trim();
32
15
  }
33
16
  function formatUSD(amount) {
34
- if (amount === null || isNaN(amount))
17
+ if (amount === null || amount === undefined || isNaN(amount))
35
18
  return "N/A";
36
- if (Math.abs(amount) >= 1_000_000)
19
+ const abs = Math.abs(amount);
20
+ if (abs >= 1_000_000_000)
21
+ return `$${(amount / 1_000_000_000).toFixed(2)}B`;
22
+ if (abs >= 1_000_000)
37
23
  return `$${(amount / 1_000_000).toFixed(2)}M`;
38
- if (Math.abs(amount) >= 1_000)
24
+ if (abs >= 1_000)
39
25
  return `$${(amount / 1_000).toFixed(1)}K`;
40
26
  return `$${amount.toFixed(0)}`;
41
27
  }
42
- // ─── Search by recipient (company name) ──────────────────────────────────────
43
- async function searchByRecipient(query, maxLength) {
28
+ const HEADERS = {
29
+ "Content-Type": "application/json",
30
+ "Accept": "application/json",
31
+ "User-Agent": "Mozilla/5.0 (compatible; freshcontext-mcp/1.0; +https://github.com/PrinceGabriel-lgtm/freshcontext-mcp)",
32
+ };
33
+ async function fetchJSON(url, body) {
34
+ const controller = new AbortController();
35
+ const timeout = setTimeout(() => controller.abort(), 20000);
36
+ try {
37
+ const res = await fetch(url, {
38
+ method: body ? "POST" : "GET",
39
+ headers: HEADERS,
40
+ body: body ? JSON.stringify(body) : undefined,
41
+ signal: controller.signal,
42
+ });
43
+ if (!res.ok) {
44
+ const text = await res.text().catch(() => "");
45
+ throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);
46
+ }
47
+ return await res.json();
48
+ }
49
+ finally {
50
+ clearTimeout(timeout);
51
+ }
52
+ }
53
+ // ─── Search by recipient name using autocomplete then awards ─────────────────
54
+ async function searchByRecipient(name, maxLength) {
55
+ // Step 1: Use autocomplete to get the exact recipient name USASpending knows
56
+ let recipientName = name;
57
+ try {
58
+ const autoRes = await fetchJSON("https://api.usaspending.gov/api/v2/autocomplete/recipient/", { search_text: name, limit: 1 });
59
+ if (autoRes.results?.length) {
60
+ recipientName = autoRes.results[0].recipient_name;
61
+ }
62
+ }
63
+ catch {
64
+ // Use original name if autocomplete fails
65
+ }
66
+ // Step 2: Search awards with the resolved recipient name
44
67
  const body = {
45
68
  filters: {
46
- recipient_search_text: [query],
47
- time_period: [
48
- {
69
+ recipient_search_text: [recipientName],
70
+ time_period: [{
49
71
  start_date: new Date(Date.now() - 365 * 2 * 86400000).toISOString().slice(0, 10),
50
72
  end_date: new Date().toISOString().slice(0, 10),
51
- },
52
- ],
53
- award_type_codes: ["A", "B", "C", "D"], // contracts only
73
+ }],
74
+ award_type_codes: ["A", "B", "C", "D"],
54
75
  },
55
76
  fields: [
56
- "Award_ID", "Recipient_Name", "Award_Amount", "Description",
77
+ "Award_ID", "Recipient_Name", "Award_Amount",
57
78
  "Award_Date", "Start_Date", "End_Date",
58
79
  "Awarding_Agency_Name", "Awarding_Sub_Agency_Name",
59
- "recipient_location_state_name", "recipient_location_city_name",
60
- "naics_code", "naics_description",
80
+ "Description", "recipient_location_state_name",
81
+ "recipient_location_city_name", "naics_code", "naics_description",
61
82
  ],
62
83
  page: 1,
63
84
  limit: 10,
@@ -65,41 +86,33 @@ async function searchByRecipient(query, maxLength) {
65
86
  order: "desc",
66
87
  subawards: false,
67
88
  };
68
- const res = await fetch("https://api.usaspending.gov/api/v2/search/spending_by_award/", {
69
- method: "POST",
70
- headers: { "Content-Type": "application/json", "User-Agent": "freshcontext-mcp" },
71
- body: JSON.stringify(body),
72
- });
73
- if (!res.ok)
74
- throw new Error(`USASpending API error: ${res.status}`);
75
- const data = await res.json();
89
+ const data = await fetchJSON("https://api.usaspending.gov/api/v2/search/spending_by_award/", body);
76
90
  if (!data.results?.length) {
77
91
  return {
78
- raw: `No federal contracts found for "${query}" in the last 2 years.\n\nThis could mean:\n- The company name differs from the registered recipient name\n- The company operates under a subsidiary name\n- No contracts awarded in this period\n\nTry searching by parent company name or NAICS code.`,
92
+ raw: `No federal contracts found for "${name}" (searched as "${recipientName}") in the last 2 years.\n\nTips:\n- Try the full legal company name (e.g. "Palantir Technologies Inc")\n- Try a keyword search instead (e.g. "AI data analytics")\n- Try a NAICS code (e.g. 541511 for software)`,
79
93
  content_date: null,
80
94
  freshness_confidence: "high",
81
95
  };
82
96
  }
83
- return formatResults(data.results, `Federal contracts — ${query}`, maxLength);
97
+ return formatResults(data.results, `Federal contracts — ${recipientName}`, maxLength);
84
98
  }
85
99
  // ─── Search by keyword ────────────────────────────────────────────────────────
86
100
  async function searchByKeyword(keyword, maxLength) {
87
101
  const body = {
88
102
  filters: {
89
103
  keywords: [keyword],
90
- time_period: [
91
- {
104
+ time_period: [{
92
105
  start_date: new Date(Date.now() - 365 * 86400000).toISOString().slice(0, 10),
93
106
  end_date: new Date().toISOString().slice(0, 10),
94
- },
95
- ],
107
+ }],
96
108
  award_type_codes: ["A", "B", "C", "D"],
97
109
  },
98
110
  fields: [
99
- "Award_ID", "Recipient_Name", "Award_Amount", "Description",
111
+ "Award_ID", "Recipient_Name", "Award_Amount",
100
112
  "Award_Date", "Start_Date", "End_Date",
101
113
  "Awarding_Agency_Name", "Awarding_Sub_Agency_Name",
102
- "recipient_location_state_name", "naics_code", "naics_description",
114
+ "Description", "recipient_location_state_name",
115
+ "naics_code", "naics_description",
103
116
  ],
104
117
  page: 1,
105
118
  limit: 10,
@@ -107,17 +120,10 @@ async function searchByKeyword(keyword, maxLength) {
107
120
  order: "desc",
108
121
  subawards: false,
109
122
  };
110
- const res = await fetch("https://api.usaspending.gov/api/v2/search/spending_by_award/", {
111
- method: "POST",
112
- headers: { "Content-Type": "application/json", "User-Agent": "freshcontext-mcp" },
113
- body: JSON.stringify(body),
114
- });
115
- if (!res.ok)
116
- throw new Error(`USASpending keyword search error: ${res.status}`);
117
- const data = await res.json();
123
+ const data = await fetchJSON("https://api.usaspending.gov/api/v2/search/spending_by_award/", body);
118
124
  if (!data.results?.length) {
119
125
  return {
120
- raw: `No federal contracts found matching keyword "${keyword}" in the last year.`,
126
+ raw: `No federal contracts found matching "${keyword}" in the last year.`,
121
127
  content_date: null,
122
128
  freshness_confidence: "high",
123
129
  };
@@ -128,35 +134,30 @@ async function searchByKeyword(keyword, maxLength) {
128
134
  function formatResults(results, title, maxLength) {
129
135
  const lines = [title, ""];
130
136
  results.forEach((award, i) => {
131
- const desc = sanitize(award.Description ?? "No description");
137
+ const desc = sanitize(award.Description ?? "No description").slice(0, 300);
132
138
  const location = [award.recipient_location_city_name, award.recipient_location_state_name]
133
139
  .filter(Boolean).join(", ") || "N/A";
134
140
  lines.push(`[${i + 1}] ${sanitize(award.Recipient_Name ?? "Unknown")}`);
135
- lines.push(` Amount: ${formatUSD(award.Award_Amount)}`);
141
+ lines.push(` Amount: ${formatUSD(award.Award_Amount ?? null)}`);
136
142
  lines.push(` Awarded: ${award.Award_Date?.slice(0, 10) ?? "unknown"}`);
137
143
  lines.push(` Period: ${award.Start_Date?.slice(0, 10) ?? "?"} → ${award.End_Date?.slice(0, 10) ?? "?"}`);
138
144
  lines.push(` Agency: ${sanitize(award.Awarding_Agency_Name ?? "N/A")}`);
139
- if (award.Awarding_Sub_Agency_Name && award.Awarding_Sub_Agency_Name !== award.Awarding_Agency_Name) {
145
+ if (award.Awarding_Sub_Agency_Name !== award.Awarding_Agency_Name && award.Awarding_Sub_Agency_Name) {
140
146
  lines.push(` Sub-agency: ${sanitize(award.Awarding_Sub_Agency_Name)}`);
141
147
  }
142
148
  if (award.naics_code) {
143
149
  lines.push(` NAICS: ${award.naics_code} — ${sanitize(award.naics_description ?? "")}`);
144
150
  }
145
151
  lines.push(` Location: ${location}`);
146
- lines.push(` Description: ${desc.slice(0, 200)}`);
152
+ lines.push(` Description: ${desc}`);
147
153
  lines.push("");
148
154
  });
149
155
  const raw = lines.join("\n").slice(0, maxLength);
150
- // Newest award date for freshness
151
- const dates = results
152
- .map((r) => r.Award_Date)
153
- .filter(Boolean)
154
- .sort()
155
- .reverse();
156
+ const dates = results.map(r => r.Award_Date).filter(Boolean).sort().reverse();
156
157
  return {
157
158
  raw,
158
159
  content_date: dates[0] ?? null,
159
- freshness_confidence: "high", // USASpending dates are structured API fields
160
+ freshness_confidence: "high",
160
161
  };
161
162
  }
162
163
  // ─── Main export ──────────────────────────────────────────────────────────────
@@ -167,30 +168,30 @@ export async function govContractsAdapter(options) {
167
168
  throw new Error("Query required: company name, keyword, or NAICS code");
168
169
  // Direct API URL
169
170
  if (input.startsWith("https://api.usaspending.gov")) {
170
- const res = await fetch(input, { headers: { "User-Agent": "freshcontext-mcp" } });
171
- if (!res.ok)
172
- throw new Error(`USASpending direct fetch error: ${res.status}`);
173
- const data = await res.json();
174
- const raw = JSON.stringify(data, null, 2).slice(0, maxLength);
175
- return { raw, content_date: new Date().toISOString(), freshness_confidence: "high" };
171
+ const data = await fetchJSON(input);
172
+ return {
173
+ raw: JSON.stringify(data, null, 2).slice(0, maxLength),
174
+ content_date: new Date().toISOString(),
175
+ freshness_confidence: "high",
176
+ };
176
177
  }
177
- // NAICS code (6 digits)
178
+ // NAICS code (6 digits) — treat as keyword
178
179
  if (/^\d{6}$/.test(input)) {
179
180
  return searchByKeyword(input, maxLength);
180
181
  }
181
- // Default: try as recipient name first, fall back to keyword
182
+ // Multi-word input or known company name → try recipient first, fall back to keyword
182
183
  try {
183
184
  const result = await searchByRecipient(input, maxLength);
184
- // If no results found, try keyword search
185
- if (result.raw.includes("No federal contracts found")) {
186
- const kwResult = await searchByKeyword(input, maxLength);
187
- if (!kwResult.raw.includes("No federal contracts found")) {
188
- return kwResult;
189
- }
190
- }
191
- return result;
185
+ if (!result.raw.includes("No federal contracts found"))
186
+ return result;
187
+ // Fall back to keyword search
188
+ const kwResult = await searchByKeyword(input, maxLength);
189
+ if (!kwResult.raw.includes("No federal contracts found"))
190
+ return kwResult;
191
+ return result; // Return the "not found" message from recipient search
192
192
  }
193
- catch {
193
+ catch (err) {
194
+ // If recipient search fails entirely, try keyword
194
195
  return searchByKeyword(input, maxLength);
195
196
  }
196
197
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "freshcontext-mcp",
3
3
  "mcpName": "io.github.PrinceGabriel-lgtm/freshcontext",
4
- "version": "0.3.3",
4
+ "version": "0.3.5",
5
5
  "description": "Real-time web extraction MCP server with freshness timestamps for AI agents",
6
6
  "keywords": [
7
7
  "mcp",