freshcontext-mcp 0.3.9 → 0.3.11

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.
@@ -0,0 +1,14 @@
1
+ FROM apify/actor-node:18
2
+
3
+ # Copy package files first for better Docker layer caching
4
+ COPY package*.json ./
5
+
6
+ # Install all dependencies including apify SDK
7
+ RUN npm install --include=dev
8
+
9
+ # Copy source and build
10
+ COPY . ./
11
+ RUN npm run build
12
+
13
+ # Tell Apify to run the Actor entry point, not the MCP server
14
+ CMD ["node", "dist/apify.js"]
package/.actor/actor.json CHANGED
@@ -4,5 +4,6 @@
4
4
  "title": "FreshContext MCP",
5
5
  "version": "0.3.1",
6
6
  "input": "../input_schema.json",
7
- "output": "./output_schema.json"
7
+ "output": "./output_schema.json",
8
+ "dockerfile": "./Dockerfile"
8
9
  }
@@ -0,0 +1,32 @@
1
+ name: Build and Publish
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ build-and-publish:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: Checkout repository
14
+ uses: actions/checkout@v4
15
+
16
+ - name: Set up Node.js 18
17
+ uses: actions/setup-node@v4
18
+ with:
19
+ node-version: '18'
20
+ registry-url: 'https://registry.npmjs.org'
21
+
22
+ - name: Install dependencies
23
+ run: npm ci
24
+
25
+ - name: Build
26
+ run: npm run build
27
+
28
+ - name: Publish to npm
29
+ run: npm publish --access public
30
+ env:
31
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
32
+ continue-on-error: true
@@ -0,0 +1,88 @@
1
+ # FreshContext — Architecture Upgrade Checklist
2
+ **Date started:** 2026-03-19
3
+ **Author:** Prince Gabriel, Grootfontein, Namibia
4
+
5
+ ---
6
+
7
+ ## [ ] Upgrade 1 — freshness_score numeric field
8
+ Implement the 0-100 numeric score defined in FRESHCONTEXT_SPEC.md.
9
+ Formula: max(0, 100 - (days_since_retrieved * decay_rate))
10
+ Location: src/tools/freshnessStamp.ts
11
+ Decay rates by adapter: finance=5.0, jobs=3.0, hackernews=2.0, github=1.0, scholar=0.3, default=1.5
12
+ Adds the score to both the text envelope and the JSON form.
13
+ Makes FreshContext fully spec-compliant by your own standard.
14
+ Cost: zero.
15
+
16
+ ---
17
+
18
+ ## [x] Upgrade 2 — Cloudflare KV response caching ← DONE (already implemented in worker.ts)
19
+ Cache adapter results in KV with adapter-specific TTLs so the same query
20
+ hitting the Worker twice doesn't make two upstream API calls.
21
+ Cache key: sha256(tool + ":" + url)
22
+ TTLs: HN/Reddit = 1 hour, GitHub/YC = 6 hours, govcontracts/scholar = 24 hours
23
+ Location: worker/src/index.ts
24
+ Cost: zero. KV free tier is 100k reads/day, 1k writes/day.
25
+
26
+ ---
27
+
28
+ ## [x] Upgrade 3 — Apify Actor timeout increase ← DONE 2026-03-19
29
+ Change the Actor timeout from 300 seconds to 3600 seconds in the Apify UI.
30
+ Apify console → Actor → Settings → Timeout → 3600
31
+ Playwright-based tools (extract_reddit, extract_yc, extract_producthunt) need
32
+ more than 5 minutes to launch Chromium and scrape. They will keep timing out
33
+ until this is changed. This is a UI field change, not a code change.
34
+ Cost: zero.
35
+
36
+ ---
37
+
38
+ ## [x] Upgrade 4 — D1 deduplication in the cron job ← DONE (hash-based dedup already in runScheduledScrape)
39
+ Before inserting a new scrape result, check if the same source_url was already
40
+ stored in the last 24 hours. If yes, skip the insert.
41
+ Prevents the scrape_results table from filling with duplicate data across
42
+ consecutive cron runs, keeping the historical dataset clean for the intelligence
43
+ layer (Layer 7 in the roadmap).
44
+ Location: the cron job handler in the Worker code.
45
+ Cost: zero.
46
+
47
+ ---
48
+
49
+ ## [x] Upgrade 5 — Structured JSON response form ← DONE 2026-03-19
50
+ Add the optional JSON form defined in FRESHCONTEXT_SPEC.md alongside the text
51
+ envelope in every adapter response. The JSON form has: source_url, content_date,
52
+ retrieved_at, freshness_confidence, adapter, freshness_score.
53
+ When a request has Accept: application/json, serve the structured form.
54
+ Both forms can be returned together — text for agents, JSON for programmatic use.
55
+ Location: src/tools/freshnessStamp.ts (same file as Upgrade 1, do together)
56
+ Cost: zero.
57
+
58
+ ---
59
+
60
+ ## [x] Upgrade 6 — GitHub Actions CI/CD automation ← DONE 2026-03-19
61
+ .github/workflows/publish.yml created. Triggers on every push to main.
62
+ Runs npm ci → npm run build → npm publish using NPM_TOKEN secret.
63
+ continue-on-error on publish so doc-only pushes don't fail the workflow.
64
+ First run: green checkmark, 23 seconds.
65
+ Manual PowerShell build/publish commands no longer needed.
66
+
67
+ ---
68
+
69
+ ## [x] Upgrade 7 — server.json version sync ← DONE 2026-03-19
70
+ server.json (MCP Registry listing) shows version 0.3.1 while package.json
71
+ is at 0.3.10. Anyone discovering FreshContext via the MCP Registry sees an
72
+ outdated version number. Fix by updating server.json manually now, then
73
+ optionally add a workflow step that syncs the version automatically on each
74
+ GitHub Actions run.
75
+ Location: server.json — change "version" field to match package.json.
76
+ Cost: zero.
77
+
78
+ ---
79
+
80
+ ## Priority order for remaining six upgrades
81
+
82
+ Do Upgrade 3 first — it is one UI field change and immediately fixes the
83
+ broken Apify Actor runs. Do Upgrades 1 and 5 together second since they
84
+ both touch freshnessStamp.ts and completing them makes FreshContext fully
85
+ spec-compliant. Do Upgrade 2 third — KV caching makes the Worker resilient
86
+ against upstream API instability. Do Upgrade 4 fourth — D1 deduplication
87
+ prepares the dataset for the future intelligence layer. Do Upgrade 7 last —
88
+ a simple version number correction, low urgency but worth keeping clean.
@@ -0,0 +1,174 @@
1
+ # FreshContext — Architecture Upgrade Roadmap V1
2
+ **Date:** 2026-03-19
3
+ **Author:** Immanuel Gabriel (Prince Gabriel), Grootfontein, Namibia
4
+
5
+ This document describes every free structural upgrade available to FreshContext,
6
+ prioritised by impact, with implementation notes for each.
7
+
8
+ ---
9
+
10
+ ## Upgrade 1 — freshness_score numeric field (HIGHEST PRIORITY)
11
+
12
+ **What it is:** The FreshContext Specification v1.0 defines an optional freshness_score
13
+ field (0-100) calculated as: max(0, 100 - (days_since_retrieved * decay_rate)).
14
+ Right now every response carries the text envelope and the confidence level (high/medium/low)
15
+ but not the numeric score. This is the one remaining piece that makes FreshContext fully
16
+ spec-compliant by your own standard.
17
+
18
+ **Why it matters:** Once the score exists, agents can filter results programmatically —
19
+ "only use results with freshness_score > 70" rather than parsing the string confidence
20
+ level. This is the difference between a label and a query parameter. It also strengthens
21
+ the acquisition narrative: the spec is complete, the reference implementation is complete,
22
+ and the standard is fully self-consistent.
23
+
24
+ **Domain-specific decay rates from the spec:**
25
+ Financial data decays at 5.0 (half-life ~10 days). Job listings at 3.0 (~17 days).
26
+ News and HN at 2.0 (~25 days). GitHub repos at 1.0 (~50 days). Academic papers at 0.3
27
+ (~167 days). General web content defaults to 1.5.
28
+
29
+ **Where to implement:** In src/tools/freshnessStamp.ts — the function that wraps every
30
+ adapter result already has retrieved_at and content_date. Add a calculateFreshnessScore
31
+ function that takes content_date, decay_rate (looked up by adapter name), and returns
32
+ the numeric score. Add it to both the text envelope and the JSON form.
33
+
34
+ **Cost:** Zero. Pure TypeScript logic, no new services.
35
+
36
+ ---
37
+
38
+ ## Upgrade 2 — Cloudflare KV response caching
39
+
40
+ **What it is:** When the same query hits an adapter twice within a short window, the
41
+ Worker currently makes two full upstream API calls. KV caching stores the first result
42
+ with a TTL and serves subsequent identical requests from cache — meaning the upstream
43
+ API (USASpending, GitHub, HN, etc.) only gets called once per cache window.
44
+
45
+ **Why it matters:** This reduces the chance of hitting upstream rate limits, makes
46
+ repeated queries near-instant for users, and reduces Worker CPU time. For adapters like
47
+ extract_govcontracts that call a government API, caching also reduces the risk of
48
+ temporary blocks from aggressive polling.
49
+
50
+ **Implementation:** In the Worker code (worker/src/index.ts or equivalent), before calling
51
+ the adapter, compute a cache key as sha256(tool + ":" + url). Call env.KV.get(cacheKey).
52
+ If the result exists, return it immediately. If not, run the adapter, then call
53
+ env.KV.put(cacheKey, result, { expirationTtl: ttl }) before returning. Use adapter-specific
54
+ TTLs — 3600 seconds (1 hour) for HN and Reddit, 21600 (6 hours) for GitHub and YC,
55
+ 86400 (24 hours) for govcontracts and scholar.
56
+
57
+ **Cost:** Zero. KV reads are free up to 100,000 per day, writes free up to 1,000 per day
58
+ on Cloudflare's free tier. You are nowhere near those limits.
59
+
60
+ ---
61
+
62
+ ## Upgrade 3 — Apify Actor timeout increase
63
+
64
+ **What it is:** The Apify Actor timeout is currently set to 300 seconds (5 minutes). Tools
65
+ that use Playwright to launch a browser — extract_reddit, extract_yc, extract_producthunt —
66
+ need more time than this to launch Chromium, navigate, wait for the page to render, and
67
+ extract content. They will keep timing out until this setting is increased.
68
+
69
+ **Where to change it:** Apify console → your Actor → Settings → Timeout. Change from
70
+ 300 to 3600 (1 hour). This is a UI change, not a code change.
71
+
72
+ **Cost:** Zero. The timeout setting is just a number. You won't actually use anywhere
73
+ near 3600 seconds — most tools complete in 10-30 seconds. The setting just prevents Apify
74
+ from killing the process prematurely for the slower Playwright-based tools.
75
+
76
+ ---
77
+
78
+ ## Upgrade 4 — D1 deduplication in the cron job
79
+
80
+ **What it is:** Every 6 hours the cron job runs all 18 watched queries and stores results
81
+ in the scrape_results D1 table. Right now there is no deduplication — if the same article
82
+ or repo appears in two consecutive cron runs, it gets stored twice. Over time this creates
83
+ noise in the dataset and wastes storage.
84
+
85
+ **Implementation:** Before inserting a new result, run a SELECT to check whether a row
86
+ with the same source_url already exists within the last 24 hours. If it does, skip the
87
+ insert. This is a single SQL WHERE clause addition to the existing insert logic.
88
+
89
+ **Why it matters:** As you build the intelligence layer (Layer 7 in the roadmap), the
90
+ quality of the historical signal depends on clean, deduplicated data. Starting deduplication
91
+ now means the dataset is clean by the time you need it.
92
+
93
+ **Cost:** Zero. D1 reads are free up to 25 million rows per day. A deduplication check
94
+ adds one read per result per cron run — trivially within limits.
95
+
96
+ ---
97
+
98
+ ## Upgrade 5 — Structured JSON response form in every adapter
99
+
100
+ **What it is:** The FreshContext Specification defines two valid response formats — the
101
+ text envelope ([FRESHCONTEXT]...[/FRESHCONTEXT]) and an optional structured JSON form with
102
+ a freshcontext object containing source_url, content_date, retrieved_at,
103
+ freshness_confidence, adapter, and freshness_score fields. Right now only the text envelope
104
+ is returned. Adding the JSON form makes FreshContext usable programmatically without
105
+ parsing the text envelope.
106
+
107
+ **Implementation:** In src/tools/freshnessStamp.ts, after assembling the text envelope,
108
+ also return a structured object. When the Worker serves a response, detect whether the
109
+ request has Accept: application/json and serve the structured form instead of the text
110
+ form if so. Both formats can also be returned together — text for human/agent reading,
111
+ JSON for programmatic use.
112
+
113
+ **Cost:** Zero. This is a response format change, no new services.
114
+
115
+ ---
116
+
117
+ ## Upgrade 6 — GitHub Actions: version bump automation
118
+
119
+ **What it is:** The current GitHub Actions workflow (publish.yml) runs npm publish on every
120
+ push, but only succeeds if the version in package.json has changed. Right now you manually
121
+ bump the version before pushing. A small addition to the workflow can automate this by
122
+ running npm version patch automatically before the publish step — so every push to main
123
+ creates a new patch version and publishes it without any manual intervention.
124
+
125
+ **Tradeoff:** This means every push creates a new npm version, which may not always be
126
+ desirable for documentation-only changes. A better approach is to only auto-bump when
127
+ commits touch src/ or .actor/ — which can be detected in the workflow with a path filter.
128
+
129
+ **Implementation:** Add a paths filter to the workflow trigger so it only runs the publish
130
+ step when source files change. Then add an npm version patch --no-git-tag-version step
131
+ before the publish step. Push the bumped package.json back to the repo using a
132
+ git commit and git push within the workflow (requires GITHUB_TOKEN, which is automatically
133
+ available in all Actions workflows at no cost).
134
+
135
+ **Cost:** Zero.
136
+
137
+ ---
138
+
139
+ ## Upgrade 7 — server.json version sync check
140
+
141
+ **What it is:** The server.json file (used by the MCP Registry listing) still shows version
142
+ 0.3.1 while package.json is at 0.3.10. This discrepancy means anyone who discovers
143
+ FreshContext via the MCP Registry sees an outdated version number. It is a cosmetic issue
144
+ but it affects credibility in a space where people are evaluating tools carefully.
145
+
146
+ **Implementation:** Add a step to the GitHub Actions workflow that reads the version from
147
+ package.json and uses sed or node -e to update the version field in server.json to match
148
+ before committing. Alternatively, update server.json manually now and keep it in sync
149
+ going forward.
150
+
151
+ **Cost:** Zero.
152
+
153
+ ---
154
+
155
+ ## Priority Order for Implementation
156
+
157
+ The order that maximises impact relative to effort is as follows. Implement the Apify
158
+ timeout increase first because it is a one-field UI change that immediately fixes the
159
+ broken Actor runs. Implement KV caching second because it makes the Worker more robust
160
+ against upstream API instability and improves response times for repeat queries. Implement
161
+ the freshness_score calculation third because it completes the spec and strengthens every
162
+ conversation about acquisition or partnership. Implement D1 deduplication fourth because
163
+ it improves data quality for the intelligence layer you will eventually build. Implement
164
+ the structured JSON response form fifth as part of the same PR as freshness_score since
165
+ they touch the same file. Implement the GitHub Actions version sync last as a quality-of-life
166
+ automation.
167
+
168
+ The total engineering cost of all six remaining upgrades is approximately 4-6 hours of
169
+ focused work. All run entirely within free tiers.
170
+
171
+ ---
172
+
173
+ *"The work isn't gone. It's just waiting to be continued."*
174
+ *— Prince Gabriel, Grootfontein, Namibia*
package/README.md CHANGED
@@ -1,212 +1,239 @@
1
- # freshcontext-mcp
2
-
3
- I asked Claude to help me find a job. It gave me a list of openings. I applied to three of them. Two didn't exist anymore. One had been closed for two years.
4
-
5
- Claude had no idea. It presented everything with the same confidence.
6
-
7
- That's the problem freshcontext fixes.
8
-
9
- [![npm version](https://img.shields.io/npm/v/freshcontext-mcp)](https://www.npmjs.com/package/freshcontext-mcp)
10
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
-
12
- ---
13
-
14
- ## What it does
15
-
16
- Every MCP server returns data. freshcontext returns data **plus when it was retrieved and how confident that date is** — wrapped in a FreshContext envelope:
17
-
18
- ```
19
- [FRESHCONTEXT]
20
- Source: https://github.com/owner/repo
21
- Published: 2024-11-03
22
- Retrieved: 2026-03-05T09:19:00Z
23
- Confidence: high
24
- ---
25
- ... content ...
26
- [/FRESHCONTEXT]
27
- ```
28
-
29
- Claude now knows the difference between something from this morning and something from two years ago. You do too.
30
-
31
- ---
32
-
33
- ## 11 tools. No API keys.
34
-
35
- ### Intelligence
36
- | Tool | What it gets you |
37
- |---|---|
38
- | `extract_github` | README, stars, forks, language, topics, last commit |
39
- | `extract_hackernews` | Top stories or search results with scores and timestamps |
40
- | `extract_scholar` | Research papers — titles, authors, years, snippets |
41
- | `extract_arxiv` | arXiv papers via official API — more reliable than Scholar |
42
- | `extract_reddit` | Posts and community sentiment from any subreddit |
43
-
44
- ### Competitive research
45
- | Tool | What it gets you |
46
- |---|---|
47
- | `extract_yc` | YC company listings by keyword — who's funded in your space |
48
- | `extract_producthunt` | Recent launches by topic |
49
- | `search_repos` | GitHub repos ranked by stars with activity signals |
50
- | `package_trends` | npm and PyPI metadata — version history, release cadence |
51
-
52
- ### Market data
53
- | Tool | What it gets you |
54
- |---|---|
55
- | `extract_finance` | Live stock data — price, market cap, P/E, 52w range |
56
-
57
- ### Composite
58
- | Tool | What it gets you |
59
- |---|---|
60
- | `extract_landscape` | One call. YC + GitHub + HN + Reddit + Product Hunt + npm in parallel. Full timestamped picture. |
61
-
62
- ---
63
-
64
- ## Quick Start
65
-
66
- ### Option A — Cloud (no install)
67
-
68
- Add to your Claude Desktop config and restart:
69
-
70
- **Mac:** `~/Library/Application Support/Claude/claude_desktop_config.json`
71
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
72
-
73
- ```json
74
- {
75
- "mcpServers": {
76
- "freshcontext": {
77
- "command": "npx",
78
- "args": ["-y", "mcp-remote", "https://freshcontext-mcp.gimmanuel73.workers.dev/mcp"]
79
- }
80
- }
81
- }
82
- ```
83
-
84
- Restart Claude. Done.
85
-
86
- > Prefer a guided setup? Visit **[freshcontext-site.pages.dev](https://freshcontext-site.pages.dev)** — 3 steps, no terminal.
87
-
88
- ---
89
-
90
- ### Option B — Local (full Playwright)
91
-
92
- **Requires:** Node.js 18+ ([nodejs.org](https://nodejs.org))
93
-
94
- ```bash
95
- git clone https://github.com/PrinceGabriel-lgtm/freshcontext-mcp
96
- cd freshcontext-mcp
97
- npm install
98
- npx playwright install chromium
99
- npm run build
100
- ```
101
-
102
- Add to Claude Desktop config:
103
-
104
- **Mac:**
105
- ```json
106
- {
107
- "mcpServers": {
108
- "freshcontext": {
109
- "command": "node",
110
- "args": ["/Users/YOUR_USERNAME/path/to/freshcontext-mcp/dist/server.js"]
111
- }
112
- }
113
- }
114
- ```
115
-
116
- **Windows:**
117
- ```json
118
- {
119
- "mcpServers": {
120
- "freshcontext": {
121
- "command": "node",
122
- "args": ["C:\\Users\\YOUR_USERNAME\\path\\to\\freshcontext-mcp\\dist\\server.js"]
123
- }
124
- }
125
- }
126
- ```
127
-
128
- ---
129
-
130
- ### Troubleshooting (Mac)
131
-
132
- **"command not found: node"** — Use the full path:
133
- ```bash
134
- which node # copy this output, replace "node" in config
135
- ```
136
-
137
- **Config file doesn't exist** — Create it:
138
- ```bash
139
- mkdir -p ~/Library/Application\ Support/Claude
140
- touch ~/Library/Application\ Support/Claude/claude_desktop_config.json
141
- ```
142
-
143
- ---
144
-
145
- ## Usage examples
146
-
147
- **Is anyone already building what you're building?**
148
- ```
149
- Use extract_landscape with topic "cashflow prediction saas"
150
- ```
151
- Returns who's funded, what's trending, what repos exist, what packages are moving — all timestamped.
152
-
153
- **What's the community actually saying right now?**
154
- ```
155
- Use extract_reddit on r/MachineLearning
156
- Use extract_hackernews to search "mcp server 2026"
157
- ```
158
-
159
- **Did that company actually ship recently?**
160
- ```
161
- Use extract_github on https://github.com/some-org/some-repo
162
- ```
163
- Check `Published` vs `Retrieved`. If the gap is 18 months, Claude will tell you.
164
-
165
- ---
166
-
167
- ## How freshness works
168
-
169
- Most AI tools retrieve data silently. No timestamp, no signal, no way for the agent to know how old it is.
170
-
171
- freshcontext treats **retrieval time as first-class metadata**. Every adapter returns:
172
-
173
- - `retrieved_at` exact ISO timestamp of the fetch
174
- - `content_date` — best estimate of when the content was originally published
175
- - `freshness_confidence` `high`, `medium`, or `low` based on signal quality
176
- - `adapter` — which source the data came from
177
-
178
- When confidence is `high`, the date came from a structured field (API, metadata). When it's `medium` or `low`, freshcontext tells you why.
179
-
180
- ---
181
-
182
- ## Security
183
-
184
- - Input sanitization and domain allowlists on all adapters
185
- - SSRF prevention (blocked private IP ranges)
186
- - KV-backed global rate limiting: 60 req/min per IP across all edge nodes
187
- - No credentials required — all public data sources
188
-
189
- ---
190
-
191
- ## Roadmap
192
-
193
- - [x] GitHub, HN, Scholar, YC, Reddit, Product Hunt, Finance, arXiv adapters
194
- - [x] `extract_landscape` — 6-source composite tool
195
- - [x] Cloudflare Workers deployment
196
- - [x] KV-backed global rate limiting
197
- - [x] Listed on official MCP Registry
198
- - [ ] TTL-based caching layer
199
- - [ ] `freshness_score` numeric metric
200
- - [ ] Job search adapter (the tool that started all this)
201
-
202
- ---
203
-
204
- ## Contributing
205
-
206
- PRs welcome. New adapters are the highest-value contribution — see `src/adapters/` for the pattern.
207
-
208
- ---
209
-
210
- ## License
211
-
212
- MIT
1
+ # freshcontext-mcp
2
+
3
+ I asked Claude to help me find a job. It gave me a list of openings. I applied to three of them. Two didn't exist anymore. One had been closed for two years.
4
+
5
+ Claude had no idea. It presented everything with the same confidence.
6
+
7
+ That's the problem freshcontext fixes.
8
+
9
+ [![npm version](https://img.shields.io/npm/v/freshcontext-mcp)](https://www.npmjs.com/package/freshcontext-mcp)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
+
12
+ ---
13
+
14
+ ## What it does
15
+
16
+ Every MCP server returns data. freshcontext returns data **plus when it was retrieved and how confident that date is** — wrapped in a FreshContext envelope:
17
+
18
+ ```
19
+ [FRESHCONTEXT]
20
+ Source: https://github.com/owner/repo
21
+ Published: 2024-11-03
22
+ Retrieved: 2026-03-05T09:19:00Z
23
+ Confidence: high
24
+ ---
25
+ ... content ...
26
+ [/FRESHCONTEXT]
27
+ ```
28
+
29
+ Claude now knows the difference between something from this morning and something from two years ago. You do too.
30
+
31
+ ---
32
+
33
+ ## 13 tools. No API keys.
34
+
35
+ ### Intelligence
36
+ | Tool | What it gets you |
37
+ |---|---|
38
+ | `extract_github` | README, stars, forks, language, topics, last commit |
39
+ | `extract_hackernews` | Top stories or search results with scores and timestamps |
40
+ | `extract_scholar` | Research papers — titles, authors, years, snippets |
41
+ | `extract_arxiv` | arXiv papers via official API — more reliable than Scholar |
42
+ | `extract_reddit` | Posts and community sentiment from any subreddit |
43
+
44
+ ### Competitive research
45
+ | Tool | What it gets you |
46
+ |---|---|
47
+ | `extract_yc` | YC company listings by keyword — who's funded in your space |
48
+ | `extract_producthunt` | Recent launches by topic |
49
+ | `search_repos` | GitHub repos ranked by stars with activity signals |
50
+ | `package_trends` | npm and PyPI metadata — version history, release cadence |
51
+
52
+ ### Market data
53
+ | Tool | What it gets you |
54
+ |---|---|
55
+ | `extract_finance` | Live stock data — price, market cap, P/E, 52w range |
56
+
57
+ ### Composite
58
+ | Tool | What it gets you |
59
+ |---|---|
60
+ | `extract_landscape` | One call. YC + GitHub + HN + Reddit + Product Hunt + npm in parallel. Full timestamped picture. |
61
+
62
+ ### Update intelligence — unique to FreshContext
63
+ | Tool | What it gets you |
64
+ |---|---|
65
+ | `extract_changelog` | Update history from any GitHub repo, npm package, or website. Accepts a GitHub URL (uses the Releases API), an npm package name, or any website URL — auto-discovers `/changelog`, `/releases`, and `CHANGELOG.md`. Returns version numbers, release dates, and entry content, all timestamped. Use this to check if a dependency is still actively maintained, or to find out exactly when a feature shipped before referencing it. |
66
+
67
+ ### Government intelligence — unique to FreshContext
68
+ | Tool | What it gets you |
69
+ |---|---|
70
+ | `extract_govcontracts` | US federal contract awards pulled live from USASpending.gov — the official US Treasury database, updated daily. Search by company name, keyword, or NAICS code. Returns award amounts, awarding agency, period of performance, and contract description, all timestamped. A company that just won a $10M DoD contract is actively hiring and spending — that is a buying intent signal no other MCP server surfaces. |
71
+
72
+ ---
73
+
74
+ ## Quick Start
75
+
76
+ ### Option A — Cloud (no install)
77
+
78
+ Add to your Claude Desktop config and restart:
79
+
80
+ **Mac:** `~/Library/Application Support/Claude/claude_desktop_config.json`
81
+ **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
82
+
83
+ ```json
84
+ {
85
+ "mcpServers": {
86
+ "freshcontext": {
87
+ "command": "npx",
88
+ "args": ["-y", "mcp-remote", "https://freshcontext-mcp.gimmanuel73.workers.dev/mcp"]
89
+ }
90
+ }
91
+ }
92
+ ```
93
+
94
+ Restart Claude. Done.
95
+
96
+ > Prefer a guided setup? Visit **[freshcontext-site.pages.dev](https://freshcontext-site.pages.dev)** — 3 steps, no terminal.
97
+
98
+ ---
99
+
100
+ ### Option B — Local (full Playwright)
101
+
102
+ **Requires:** Node.js 18+ ([nodejs.org](https://nodejs.org))
103
+
104
+ ```bash
105
+ git clone https://github.com/PrinceGabriel-lgtm/freshcontext-mcp
106
+ cd freshcontext-mcp
107
+ npm install
108
+ npx playwright install chromium
109
+ npm run build
110
+ ```
111
+
112
+ Add to Claude Desktop config:
113
+
114
+ **Mac:**
115
+ ```json
116
+ {
117
+ "mcpServers": {
118
+ "freshcontext": {
119
+ "command": "node",
120
+ "args": ["/Users/YOUR_USERNAME/path/to/freshcontext-mcp/dist/server.js"]
121
+ }
122
+ }
123
+ }
124
+ ```
125
+
126
+ **Windows:**
127
+ ```json
128
+ {
129
+ "mcpServers": {
130
+ "freshcontext": {
131
+ "command": "node",
132
+ "args": ["C:\\Users\\YOUR_USERNAME\\path\\to\\freshcontext-mcp\\dist\\server.js"]
133
+ }
134
+ }
135
+ }
136
+ ```
137
+
138
+ ---
139
+
140
+ ### Troubleshooting (Mac)
141
+
142
+ **"command not found: node"** — Use the full path:
143
+ ```bash
144
+ which node # copy this output, replace "node" in config
145
+ ```
146
+
147
+ **Config file doesn't exist** Create it:
148
+ ```bash
149
+ mkdir -p ~/Library/Application\ Support/Claude
150
+ touch ~/Library/Application\ Support/Claude/claude_desktop_config.json
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Usage examples
156
+
157
+ **Is anyone already building what you're building?**
158
+ ```
159
+ Use extract_landscape with topic "cashflow prediction saas"
160
+ ```
161
+ Returns who's funded, what's trending, what repos exist, what packages are moving — all timestamped.
162
+
163
+ **What's the community actually saying right now?**
164
+ ```
165
+ Use extract_reddit on r/MachineLearning
166
+ Use extract_hackernews to search "mcp server 2026"
167
+ ```
168
+
169
+ **Did that company actually ship recently?**
170
+ ```
171
+ Use extract_github on https://github.com/some-org/some-repo
172
+ ```
173
+ Check `Published` vs `Retrieved`. If the gap is 18 months, Claude will tell you.
174
+
175
+ **Is this dependency still actively maintained?**
176
+ ```
177
+ Use extract_changelog with url "https://github.com/org/repo"
178
+ ```
179
+ Returns the last 8 releases with exact dates. If the last release was 18 months ago, you'll know before you pin the version.
180
+
181
+ **Which companies just won government contracts in AI?**
182
+ ```
183
+ Use extract_govcontracts with url "artificial intelligence"
184
+ ```
185
+ Returns the largest recent federal contract awards matching that keyword — company name, amount, agency, and award date. Pure buying intent signal.
186
+
187
+ ---
188
+
189
+ ## How freshness works
190
+
191
+ Most AI tools retrieve data silently. No timestamp, no signal, no way for the agent to know how old it is.
192
+
193
+ freshcontext treats **retrieval time as first-class metadata**. Every adapter returns:
194
+
195
+ - `retrieved_at` exact ISO timestamp of the fetch
196
+ - `content_date` best estimate of when the content was originally published
197
+ - `freshness_confidence` `high`, `medium`, or `low` based on signal quality
198
+ - `adapter` which source the data came from
199
+
200
+ When confidence is `high`, the date came from a structured field (API, metadata). When it's `medium` or `low`, freshcontext tells you why.
201
+
202
+ ---
203
+
204
+ ## Security
205
+
206
+ - Input sanitization and domain allowlists on all adapters
207
+ - SSRF prevention (blocked private IP ranges)
208
+ - KV-backed global rate limiting: 60 req/min per IP across all edge nodes
209
+ - No credentials required — all public data sources
210
+
211
+ ---
212
+
213
+ ## Roadmap
214
+
215
+ - [x] GitHub, HN, Scholar, YC, Reddit, Product Hunt, Finance, arXiv adapters
216
+ - [x] `extract_landscape` — 6-source composite tool
217
+ - [x] Cloudflare Workers deployment
218
+ - [x] KV-backed global rate limiting
219
+ - [x] Listed on official MCP Registry
220
+ - [x] `extract_changelog` — update cadence from any repo, package, or website
221
+ - [x] `extract_govcontracts` — US federal contract intelligence via USASpending.gov
222
+ - [x] Listed on Apify Store
223
+ - [x] FreshContext Specification v1.0 published
224
+ - [ ] TTL-based caching layer
225
+ - [ ] `freshness_score` numeric metric (0–100)
226
+ - [ ] `extract_devto` — developer article sentiment
227
+ - [ ] `extract_npm_releases` — package release velocity
228
+
229
+ ---
230
+
231
+ ## Contributing
232
+
233
+ PRs welcome. New adapters are the highest-value contribution — see `src/adapters/` for the pattern.
234
+
235
+ ---
236
+
237
+ ## License
238
+
239
+ MIT
@@ -0,0 +1,67 @@
1
+ # FreshContext — Architecture Upgrade Session V1
2
+ **Date:** 2026-03-19
3
+ **Session type:** Infrastructure hardening + automation
4
+ **Status:** 13 tools live. GitHub Actions CI/CD live. Apify Actor published. govcontracts fixed.
5
+
6
+ ---
7
+
8
+ ## What Was Accomplished This Session
9
+
10
+ ### 1. extract_govcontracts — fully repaired
11
+ The adapter was sending wrong field names to USASpending.gov on every call. Root cause:
12
+ every field used underscores ("Award_ID", "Recipient_Name") while the API requires
13
+ space-separated names ("Award ID", "Recipient Name"). After three rounds of diagnosis,
14
+ the correct fix was a full rewrite of govcontracts.ts. Confirmed working — Palantir
15
+ query returned $1.29B across 10 contracts live from the US Treasury.
16
+
17
+ ### 2. README updated to 13 tools
18
+ Updated from "11 tools" to "13 tools". extract_changelog and extract_govcontracts given
19
+ their own dedicated sections with separate headings. Two new usage examples added.
20
+ Roadmap updated to mark both adapters complete and add extract_devto + extract_npm_releases
21
+ as next planned work.
22
+
23
+ ### 3. Apify Actor — Dockerfile added
24
+ Actor was timing out because .actor/actor.json had no Dockerfile reference, so Apify
25
+ launched dist/server.js — the MCP stdio server that waits forever and never exits.
26
+ Fixed by creating .actor/Dockerfile using apify/actor-node:18, installing all deps
27
+ including apify SDK, and running dist/apify.js as the CMD. apify added to dependencies
28
+ in package.json. The apify.ts file was already complete — it just had no path to get called.
29
+
30
+ ### 4. GitHub Actions CI/CD — live and confirmed
31
+ .github/workflows/publish.yml created, triggers on every push to main. Runs npm ci,
32
+ npm run build, npm publish. Uses NPM_TOKEN secret. First run: green checkmark, 23 seconds.
33
+ Manual PowerShell build/publish commands are no longer needed. Bump version, git push, done.
34
+
35
+ ### 5. Outreach — 3 new Gmail drafts
36
+ Apollo.io — government contracts as missing GTM signal.
37
+ Harmonic.ai — contracts + changelog velocity as VC signals.
38
+ GitHub Partnerships — Releases API use case via extract_changelog.
39
+
40
+ ---
41
+
42
+ ## Current Stack State
43
+
44
+ | Layer | Status | Notes |
45
+ |---|---|---|
46
+ | npm | freshcontext-mcp@0.3.10 | 13 tools, published |
47
+ | Cloudflare Worker | Live | Global edge, KV rate limiting |
48
+ | D1 Database | Live | 18 watched queries, 6h cron |
49
+ | Synthesis | PAUSED | Needs $5 Anthropic credits |
50
+ | Apify Actor | Published | Dockerfile pushed, needs rebuild + test |
51
+ | GitHub Actions | LIVE | Green, 23s |
52
+ | MCP Registry | Listed | server.json version may need update |
53
+ | Payoneer | Approved | Customer ID 102746504 |
54
+
55
+ ---
56
+
57
+ ## Pending Action Items
58
+
59
+ Apify: Source tab → Build now → test with {"tool":"extract_hackernews","query":"mcp server 2026"}.
60
+ Then test extract_govcontracts with "Palantir" to confirm full end-to-end on Apify.
61
+ Increase Apify Actor timeout from 300s to 3600s in Settings (free, needed for Playwright tools).
62
+ Send three Gmail drafts: Apollo.io, Harmonic.ai, GitHub Partnerships.
63
+ Top up $5 Anthropic credits to re-enable /briefing/now synthesis endpoint.
64
+
65
+ ---
66
+
67
+ ## Next Architecture Upgrades (see ARCHITECTURE_UPGRADE_ROADMAP_V1.md)
@@ -0,0 +1,142 @@
1
+ # FreshContext — Architecture Session V2 Save
2
+ **Date:** 2026-03-19
3
+ **Session type:** Architecture upgrades complete + new composite adapters planned
4
+ **npm version:** 0.3.10 (GitHub Actions publishes automatically on every push now)
5
+
6
+ ---
7
+
8
+ ## What Was Completed This Session
9
+
10
+ ### All 7 Architecture Upgrades — DONE
11
+
12
+ | # | Upgrade | Status | Notes |
13
+ |---|---|---|---|
14
+ | 1 | freshness_score numeric field | DONE | Implemented in src/tools/freshnessStamp.ts |
15
+ | 2 | Cloudflare KV response caching | DONE | Already in worker.ts — discovered not missing |
16
+ | 3 | Apify Actor timeout increase | DONE | Changed to 3600s in Apify UI |
17
+ | 4 | D1 deduplication in cron job | DONE | Already in runScheduledScrape as hash-based dedup |
18
+ | 5 | Structured JSON response form | DONE | FRESHCONTEXT_JSON block appended to every response |
19
+ | 6 | GitHub Actions CI/CD | DONE | .github/workflows/publish.yml live, 23s green run |
20
+ | 7 | server.json version sync | DONE | Updated to 0.3.10, description to 13 tools |
21
+
22
+ ### Key implementation details
23
+
24
+ Upgrade 1 — freshnessStamp.ts now calculates max(0, 100 - (days × decayRate)) per adapter.
25
+ Decay rates: finance=5.0, jobs=3.0, hackernews/reddit/producthunt=2.0, yc/govcontracts=1.5,
26
+ github/repoSearch/packageTrends/changelog=1.0, scholar/arxiv=0.3, default=1.5.
27
+ Returns null when content_date is unknown. Returns 0 when score would go negative.
28
+ Score label added: current (90+), reliable (70+), verify before acting (50+), use with caution (<50).
29
+
30
+ Upgrade 5 — Every response now emits both:
31
+ [FRESHCONTEXT]...[/FRESHCONTEXT] ← text envelope for AI agents
32
+ [FRESHCONTEXT_JSON]...{...}...[/FRESHCONTEXT_JSON] ← structured JSON for programmatic use
33
+ JSON form contains: source_url, content_date, retrieved_at, freshness_confidence,
34
+ freshness_score, adapter, content. toStructuredJSON() is exported from freshnessStamp.ts.
35
+
36
+ ### Command reference HTML cheat sheet
37
+ FRESHCONTEXT_COMMANDS.html was created and delivered as a downloadable file.
38
+ Dark theme, click-to-copy cards, organized by: Landscape, Intelligence, Competitive,
39
+ Market data, Unique adapters, Power combos. Open in any browser, click a card to copy
40
+ the command, paste into Claude.
41
+
42
+ ---
43
+
44
+ ## Next Session — Two New Composite Adapters
45
+
46
+ ### Adapter 1: extract_gov_landscape
47
+ "Gov contracts for developers"
48
+
49
+ The idea: a single call that gives a complete government intelligence picture on a
50
+ company or keyword. Not just the contract data, but whether the companies winning
51
+ those contracts are actually shipping code and whether the developer community knows
52
+ about them.
53
+
54
+ Sources to combine in parallel:
55
+ - extract_govcontracts — who won, how much, which agency, when
56
+ - extract_github — are the winning companies actually building (stars, last commit, language)
57
+ - extract_hackernews — does the dev community know about them
58
+ - extract_changelog — are they shipping product (release velocity as a health signal)
59
+
60
+ Input: company name, keyword, or NAICS code (same as extract_govcontracts)
61
+ Output: unified FreshContext envelope with sections for each source, all timestamped
62
+
63
+ Why it matters: A $50M DoD contract winner with no GitHub commits in 6 months and
64
+ zero HN mentions is a very different company from one that's been pushing code weekly
65
+ and has 3 HN front-page mentions. This composite surfaces that difference in one call.
66
+
67
+ Location: src/adapters/govLandscape.ts
68
+ Tool name: extract_gov_landscape
69
+
70
+ ### Adapter 2: extract_finance_landscape
71
+ "Finance for developers"
72
+
73
+ The idea: a stock price is a backward-looking lagging indicator. What a technical
74
+ investor or developer actually needs is price combined with the signals only FreshContext
75
+ can surface — developer community sentiment, engineering velocity, ecosystem activity.
76
+
77
+ Sources to combine in parallel:
78
+ - extract_finance — live price, market cap, P/E, 52w range (Yahoo Finance)
79
+ - extract_hackernews — what are developers saying about this company right now
80
+ - extract_reddit — investor and developer community sentiment (r/investing + tech subs)
81
+ - search_repos — how many GitHub repos orbit this company's ecosystem
82
+ - extract_changelog — is the company actually shipping product (release velocity)
83
+
84
+ Input: ticker symbol(s) e.g. "PLTR" or "PLTR,MSFT"
85
+ Output: unified FreshContext envelope with sections for each source, all timestamped
86
+
87
+ Why it matters: Bloomberg Terminal doesn't read GitHub commit history as a company
88
+ health signal. FreshContext does. This composite is something no existing financial
89
+ tool offers — developer-native market intelligence with freshness scores on every source.
90
+
91
+ Location: src/adapters/financeLandscape.ts
92
+ Tool name: extract_finance_landscape
93
+
94
+ ### Pattern to follow
95
+ Both adapters should follow the exact same structure as extract_landscape in
96
+ src/adapters/landscape.ts — use Promise.allSettled for parallel calls, handle
97
+ partial failures gracefully (if one source fails, the others still return),
98
+ wrap the combined output in a single FreshContext envelope with sections clearly
99
+ labelled per source.
100
+
101
+ ---
102
+
103
+ ## Current Stack State
104
+
105
+ | Layer | Status |
106
+ |---|---|
107
+ | npm | freshcontext-mcp@0.3.10, auto-publishes via GitHub Actions |
108
+ | Cloudflare Worker | Live, KV caching active, rate limiting active |
109
+ | D1 Database | 18 watched queries, 6h cron, hash-based dedup |
110
+ | Synthesis | PAUSED — needs $5 Anthropic credits at console.anthropic.com |
111
+ | Apify Actor | Published, Dockerfile fixed, timeout 3600s |
112
+ | GitHub Actions | Live — push to main = auto build + publish |
113
+ | MCP Registry | server.json v0.3.10, 13 tools description |
114
+ | Payoneer | Approved — Customer ID 102746504 |
115
+
116
+ ## Outreach Status
117
+
118
+ | Target | Email | Status |
119
+ |---|---|---|
120
+ | Apify | jan@apify.com | Sent |
121
+ | Clay | kareem@clay.com | Sent |
122
+ | n8n | jan@n8n.io | Sent |
123
+ | Cloudflare Startups | startups@cloudflare.com | Sent — awaiting reply (10 business days) |
124
+ | Anthropic | partnerships@anthropic.com | Sent |
125
+ | Apollo.io | hello@apollo.io | Sent this session |
126
+ | Harmonic.ai | hello@harmonic.ai | Sent this session |
127
+ | GitHub Partnerships | partnerships@github.com | Sent this session |
128
+
129
+ ---
130
+
131
+ ## Resume Prompt for Next Session
132
+
133
+ "I'm building freshcontext-mcp — 13-tool web intelligence MCP server, fully
134
+ spec-compliant, GitHub Actions CI/CD live, all 7 architecture upgrades complete.
135
+ Next task: build two new composite adapters — extract_gov_landscape (govcontracts +
136
+ github + hackernews + changelog in parallel) and extract_finance_landscape (finance
137
+ + hackernews + reddit + search_repos + changelog in parallel). Both follow the
138
+ extract_landscape pattern in src/adapters/landscape.ts. See SESSION_SAVE_ARCHITECTURE_V2.md
139
+ for full spec."
140
+
141
+ *"The work isn't gone. It's just waiting to be continued."*
142
+ *— Prince Gabriel, Grootfontein, Namibia*
package/dist/server.js CHANGED
@@ -8,6 +8,8 @@ import { hackerNewsAdapter } from "./adapters/hackernews.js";
8
8
  import { ycAdapter } from "./adapters/yc.js";
9
9
  import { repoSearchAdapter } from "./adapters/repoSearch.js";
10
10
  import { packageTrendsAdapter } from "./adapters/packageTrends.js";
11
+ import { redditAdapter } from "./adapters/reddit.js";
12
+ import { financeAdapter } from "./adapters/finance.js";
11
13
  import { jobsAdapter } from "./adapters/jobs.js";
12
14
  import { changelogAdapter } from "./adapters/changelog.js";
13
15
  import { govContractsAdapter } from "./adapters/govcontracts.js";
@@ -220,6 +222,101 @@ server.registerTool("extract_govcontracts", {
220
222
  return { content: [{ type: "text", text: formatSecurityError(err) }] };
221
223
  }
222
224
  });
225
+ // ─── Tool: extract_gov_landscape ───────────────────────────────────────────
226
+ // "Gov contracts for developers" — the full picture on any company or keyword
227
+ // in the US federal market. Contracts alone tell you who won money. This tool
228
+ // also tells you whether they're actually shipping, and whether the developer
229
+ // community knows they exist. Unique: no other MCP server has this.
230
+ server.registerTool("extract_gov_landscape", {
231
+ description: "Composite government intelligence tool. Given a company name, keyword, or NAICS code, simultaneously queries: (1) USASpending.gov for federal contract awards, (2) GitHub for the company's repo activity, (3) Hacker News for developer community awareness, and (4) their product changelog for release velocity. Answers: Who is winning government contracts in this space? Are they actually building? Does the dev community know about them? Returns a unified 4-source timestamped report. Unique — not available in any other MCP server.",
232
+ inputSchema: z.object({
233
+ query: z.string().describe("Company name (e.g. 'Palantir'), keyword (e.g. 'artificial intelligence'), or NAICS code (e.g. '541511'). For GitHub and changelog sections, also optionally provide a GitHub URL."),
234
+ github_url: z.string().optional().describe("Optional GitHub repo URL for the company (e.g. 'https://github.com/palantir/palantir-java-format'). If omitted, GitHub and changelog sections use the query as a search term."),
235
+ max_length: z.number().optional().default(12000),
236
+ }),
237
+ annotations: { readOnlyHint: true, openWorldHint: true },
238
+ }, async ({ query, github_url, max_length }) => {
239
+ const perSection = Math.floor((max_length ?? 12000) / 4);
240
+ // All four sources fire in parallel — if one fails the others still return
241
+ const [contractsResult, hnResult, repoResult, changelogResult] = await Promise.allSettled([
242
+ // 1. The anchor: who is actually winning federal money in this space
243
+ govContractsAdapter({ url: query, maxLength: perSection }),
244
+ // 2. Dev community signal: does anyone in tech know about these companies
245
+ hackerNewsAdapter({
246
+ url: `https://hn.algolia.com/api/v1/search?query=${encodeURIComponent(query)}&tags=story&hitsPerPage=10`,
247
+ maxLength: perSection,
248
+ }),
249
+ // 3. GitHub activity: are they actually building, or contract farmers
250
+ repoSearchAdapter({ url: github_url ?? query, maxLength: perSection }),
251
+ // 4. Release velocity: how fast are they shipping product
252
+ changelogAdapter({ url: github_url ?? query, maxLength: perSection }),
253
+ ]);
254
+ const section = (label, result) => result.status === "fulfilled"
255
+ ? `## ${label}\n${result.value.raw}`
256
+ : `## ${label}\n[Unavailable: ${result.reason}]`;
257
+ const combined = [
258
+ `# Government Intelligence Landscape: "${query}"`,
259
+ `Generated: ${new Date().toISOString()}`,
260
+ `Sources: USASpending.gov · Hacker News · GitHub · Changelog`,
261
+ "",
262
+ section("🏛️ Federal Contract Awards (USASpending.gov)", contractsResult),
263
+ section("💬 Developer Community Awareness (Hacker News)", hnResult),
264
+ section("📦 GitHub Repository Activity", repoResult),
265
+ section("🔄 Product Release Velocity (Changelog)", changelogResult),
266
+ ].join("\n\n");
267
+ return { content: [{ type: "text", text: combined }] };
268
+ });
269
+ // ─── Tool: extract_finance_landscape ─────────────────────────────────────────
270
+ // "Finance for developers" — a stock price is a lagging indicator. This tool
271
+ // combines price data with the signals only developers can read: GitHub activity,
272
+ // community sentiment, repo ecosystem size, and product release velocity.
273
+ // Unique: Bloomberg Terminal doesn't read commit history as a company health signal.
274
+ server.registerTool("extract_finance_landscape", {
275
+ description: "Composite financial intelligence tool for developers. Given one or more ticker symbols, simultaneously queries: (1) Yahoo Finance for live price/market data, (2) Hacker News for developer community sentiment, (3) Reddit for investor and tech community discussion, (4) GitHub for repo ecosystem activity around the company's tech, and (5) their product changelog for release velocity as a company health signal. Answers: What's the price? What are developers saying? Is the company actually shipping? Returns a unified 5-source timestamped report. Bloomberg Terminal doesn't give you this.",
276
+ inputSchema: z.object({
277
+ tickers: z.string().describe("One or more ticker symbols e.g. 'PLTR' or 'PLTR,MSFT,GOOG'. Up to 5 tickers."),
278
+ company_name: z.string().optional().describe("Company name for HN/Reddit/GitHub searches e.g. 'Palantir'. If omitted, derived from the ticker."),
279
+ github_query: z.string().optional().describe("GitHub search query or repo URL for the company's tech ecosystem e.g. 'palantir' or 'https://github.com/palantir/foundry'. If omitted, uses company_name."),
280
+ max_length: z.number().optional().default(12000),
281
+ }),
282
+ annotations: { readOnlyHint: true, openWorldHint: true },
283
+ }, async ({ tickers, company_name, github_query, max_length }) => {
284
+ const perSection = Math.floor((max_length ?? 12000) / 5);
285
+ // Derive a search term: prefer explicit company_name, fall back to first ticker
286
+ const searchTerm = company_name ?? tickers.split(",")[0].trim();
287
+ const repoQuery = github_query ?? searchTerm;
288
+ // All five sources fire in parallel
289
+ const [priceResult, hnResult, redditResult, repoResult, changelogResult] = await Promise.allSettled([
290
+ // 1. The anchor: live price, market cap, P/E, 52w range
291
+ financeAdapter({ url: tickers, maxLength: perSection }),
292
+ // 2. Developer sentiment: what engineers think of this company's tech
293
+ hackerNewsAdapter({
294
+ url: `https://hn.algolia.com/api/v1/search?query=${encodeURIComponent(searchTerm)}&tags=story&hitsPerPage=10`,
295
+ maxLength: perSection,
296
+ }),
297
+ // 3. Broader community: investor and tech community discussion on Reddit
298
+ redditAdapter({ url: `https://www.reddit.com/search.json?q=${encodeURIComponent(searchTerm)}&sort=new&limit=15`, maxLength: perSection }),
299
+ // 4. Repo ecosystem: how many GitHub projects orbit this company's technology
300
+ repoSearchAdapter({ url: repoQuery, maxLength: perSection }),
301
+ // 5. Release velocity: is the company actually shipping product right now
302
+ changelogAdapter({ url: repoQuery, maxLength: perSection }),
303
+ ]);
304
+ const section = (label, result) => result.status === "fulfilled"
305
+ ? `## ${label}\n${result.value.raw}`
306
+ : `## ${label}\n[Unavailable: ${result.reason}]`;
307
+ const combined = [
308
+ `# Finance + Developer Intelligence: "${tickers}"${company_name ? ` (${company_name})` : ""}`,
309
+ `Generated: ${new Date().toISOString()}`,
310
+ `Sources: Yahoo Finance · Hacker News · Reddit · GitHub · Changelog`,
311
+ "",
312
+ section("📈 Market Data (Yahoo Finance)", priceResult),
313
+ section("💬 Developer Sentiment (Hacker News)", hnResult),
314
+ section("🗣️ Community Discussion (Reddit)", redditResult),
315
+ section("📦 Repo Ecosystem (GitHub)", repoResult),
316
+ section("🔄 Product Release Velocity (Changelog)", changelogResult),
317
+ ].join("\n\n");
318
+ return { content: [{ type: "text", text: combined }] };
319
+ });
223
320
  // ─── Start ───────────────────────────────────────────────────────────────────
224
321
  async function main() {
225
322
  const transport = new StdioServerTransport();
@@ -1,25 +1,109 @@
1
+ // ─── Decay rates per adapter ──────────────────────────────────────────────────
2
+ // From FreshContext Specification v1.0.
3
+ // Higher decay = data goes stale faster. Half-life = 100 / (2 * decayRate) days.
4
+ // finance=5.0 (half-life ~10d), jobs=3.0 (~17d), news/hn=2.0 (~25d),
5
+ // github=1.0 (~50d), scholar/arxiv=0.3 (~167d), default=1.5 (~33d)
6
+ const DECAY_RATES = {
7
+ finance: 5.0,
8
+ search_jobs: 3.0,
9
+ hackernews: 2.0,
10
+ reddit: 2.0,
11
+ producthunt: 2.0,
12
+ yc: 1.5,
13
+ govcontracts: 1.5,
14
+ github: 1.0,
15
+ repoSearch: 1.0,
16
+ packageTrends: 1.0,
17
+ changelog: 1.0,
18
+ scholar: 0.3,
19
+ arxiv: 0.3,
20
+ };
21
+ // ─── Score calculation ────────────────────────────────────────────────────────
22
+ // Returns null when content_date is unknown — we can't calculate age without a date.
23
+ // Returns 0 when the score would go negative (content is very old).
24
+ function calculateFreshnessScore(content_date, retrieved_at, adapter) {
25
+ if (!content_date)
26
+ return null;
27
+ const published = new Date(content_date).getTime();
28
+ const retrieved = new Date(retrieved_at).getTime();
29
+ // Guard against unparseable dates
30
+ if (isNaN(published) || isNaN(retrieved))
31
+ return null;
32
+ const daysSinceRetrieved = (retrieved - published) / (1000 * 60 * 60 * 24);
33
+ const decayRate = DECAY_RATES[adapter] ?? 1.5;
34
+ return Math.max(0, Math.round(100 - daysSinceRetrieved * decayRate));
35
+ }
36
+ // ─── Score label ──────────────────────────────────────────────────────────────
37
+ // Human-readable interpretation alongside the number, per the spec.
38
+ function scoreLabel(score) {
39
+ if (score === null)
40
+ return "unknown";
41
+ if (score >= 90)
42
+ return "current";
43
+ if (score >= 70)
44
+ return "reliable";
45
+ if (score >= 50)
46
+ return "verify before acting";
47
+ return "use with caution";
48
+ }
49
+ // ─── Main stamp function ──────────────────────────────────────────────────────
1
50
  export function stampFreshness(result, options, adapter) {
51
+ const retrieved_at = new Date().toISOString();
52
+ const freshness_score = calculateFreshnessScore(result.content_date, retrieved_at, adapter);
2
53
  return {
3
54
  content: result.raw.slice(0, options.maxLength ?? 8000),
4
55
  source_url: options.url,
5
56
  content_date: result.content_date,
6
- retrieved_at: new Date().toISOString(),
57
+ retrieved_at,
7
58
  freshness_confidence: result.freshness_confidence,
59
+ freshness_score,
8
60
  adapter,
9
61
  };
10
62
  }
63
+ // ─── Structured JSON form ─────────────────────────────────────────────────────
64
+ // Returns the spec-compliant JSON object defined in FRESHCONTEXT_SPEC.md.
65
+ // Programmatic consumers can parse this without touching the text envelope.
66
+ export function toStructuredJSON(ctx) {
67
+ return {
68
+ freshcontext: {
69
+ source_url: ctx.source_url,
70
+ content_date: ctx.content_date,
71
+ retrieved_at: ctx.retrieved_at,
72
+ freshness_confidence: ctx.freshness_confidence,
73
+ freshness_score: ctx.freshness_score,
74
+ adapter: ctx.adapter,
75
+ },
76
+ content: ctx.content,
77
+ };
78
+ }
79
+ // ─── Text envelope formatter ──────────────────────────────────────────────────
80
+ // Produces the [FRESHCONTEXT]...[/FRESHCONTEXT] envelope defined in the spec,
81
+ // followed by a [FRESHCONTEXT_JSON]...[/FRESHCONTEXT_JSON] block so both the
82
+ // human-readable envelope and the machine-parseable JSON travel together.
11
83
  export function formatForLLM(ctx) {
12
84
  const dateInfo = ctx.content_date
13
85
  ? `Published: ${ctx.content_date}`
14
86
  : "Publish date: unknown";
15
- return [
87
+ const scoreLine = ctx.freshness_score !== null
88
+ ? `Score: ${ctx.freshness_score}/100 (${scoreLabel(ctx.freshness_score)})`
89
+ : `Score: unknown`;
90
+ const textEnvelope = [
16
91
  `[FRESHCONTEXT]`,
17
92
  `Source: ${ctx.source_url}`,
18
93
  `${dateInfo}`,
19
94
  `Retrieved: ${ctx.retrieved_at}`,
20
95
  `Confidence: ${ctx.freshness_confidence}`,
96
+ `${scoreLine}`,
21
97
  `---`,
22
98
  ctx.content,
23
99
  `[/FRESHCONTEXT]`,
24
100
  ].join("\n");
101
+ // Append the structured JSON block so programmatic consumers
102
+ // can extract metadata without parsing the text envelope.
103
+ const jsonBlock = [
104
+ `[FRESHCONTEXT_JSON]`,
105
+ JSON.stringify(toStructuredJSON(ctx), null, 2),
106
+ `[/FRESHCONTEXT_JSON]`,
107
+ ].join("\n");
108
+ return `${textEnvelope}\n\n${jsonBlock}`;
25
109
  }
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.9",
4
+ "version": "0.3.11",
5
5
  "description": "Real-time web extraction MCP server with freshness timestamps for AI agents",
6
6
  "keywords": [
7
7
  "mcp",
@@ -37,6 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@modelcontextprotocol/sdk": "^1.0.0",
40
+ "apify": "^3.0.0",
40
41
  "playwright": "^1.44.0",
41
42
  "zod": "^3.23.0",
42
43
  "dotenv": "^16.4.0"
package/server.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
3
3
  "name": "io.github.PrinceGabriel-lgtm/freshcontext",
4
- "description": "Real-time web intelligence for AI agents. 11 tools, no API keys. GitHub, HN, Reddit, arXiv & more.",
4
+ "description": "Real-time web intelligence for AI agents. 15 tools, no API keys. GitHub, HN, Reddit, arXiv, govcontracts, changelog, gov landscape & finance landscape — every result timestamped with a freshness score.",
5
5
  "repository": {
6
6
  "url": "https://github.com/PrinceGabriel-lgtm/freshcontext-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.3.1",
9
+ "version": "0.3.11",
10
10
  "website_url": "https://freshcontext-site.pages.dev",
11
11
  "packages": [
12
12
  {
13
13
  "registry_type": "npm",
14
14
  "registry_base_url": "https://registry.npmjs.org",
15
15
  "identifier": "freshcontext-mcp",
16
- "version": "0.3.1",
16
+ "version": "0.3.11",
17
17
  "transport": {
18
18
  "type": "stdio"
19
19
  }