freshcontext-mcp 0.3.11 → 0.3.12

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/.actor/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM apify/actor-node:18
1
+ FROM apify/actor-node:20
2
2
 
3
3
  # Copy package files first for better Docker layer caching
4
4
  COPY package*.json ./
@@ -6,9 +6,11 @@ COPY package*.json ./
6
6
  # Install all dependencies including apify SDK
7
7
  RUN npm install --include=dev
8
8
 
9
- # Copy source and build
9
+ # Copy source and pre-built dist
10
10
  COPY . ./
11
- RUN npm run build
11
+
12
+ # Rebuild TypeScript (dist/ from repo is fallback if this fails)
13
+ RUN npm run build || echo "Build had warnings, using pre-compiled dist/"
12
14
 
13
15
  # Tell Apify to run the Actor entry point, not the MCP server
14
16
  CMD ["node", "dist/apify.js"]
package/HANDOFF.md ADDED
@@ -0,0 +1,184 @@
1
+ # FreshContext — Handoff Document
2
+ **Version:** 0.3.11
3
+ **Date:** 2026-03-20
4
+ **Author:** Immanuel Gabriel (Prince Gabriel), Grootfontein, Namibia
5
+ **Contact:** gimmanuel73@gmail.com
6
+
7
+ ---
8
+
9
+ ## What You Are Receiving
10
+
11
+ FreshContext is a web intelligence engine for AI agents. It wraps every piece of
12
+ retrieved web data in a structured freshness envelope — exact retrieval timestamp,
13
+ publication date estimate, freshness confidence (high/medium/low), and a 0-100
14
+ numeric score with domain-specific decay rates.
15
+
16
+ 15 tools. No API keys required. Deployed globally on Cloudflare's edge. Listed on
17
+ Anthropic's official MCP Registry, npm, and Apify Store.
18
+
19
+ The two tools that exist nowhere else: extract_govcontracts (US federal contract
20
+ intelligence via USASpending.gov) and extract_changelog (product release velocity
21
+ from any GitHub repo, npm package, or website).
22
+
23
+ ---
24
+
25
+ ## Services and Infrastructure
26
+
27
+ ### 1. GitHub Repository
28
+ URL: https://github.com/PrinceGabriel-lgtm/freshcontext-mcp
29
+ Branch: main
30
+ Transfer method: GitHub Settings > Transfer ownership
31
+ What it contains: All source code, Dockerfile, specs, session saves, roadmap
32
+
33
+ ### 2. npm Package
34
+ Package: freshcontext-mcp (v0.3.11)
35
+ URL: https://www.npmjs.com/package/freshcontext-mcp
36
+ Account: immanuel-gabriel on npmjs.com
37
+ Transfer method: npm owner add new-username freshcontext-mcp
38
+ Note: Published automatically via GitHub Actions on every push to main
39
+
40
+ ### 3. Cloudflare Account
41
+ What lives here:
42
+ Worker: freshcontext-mcp (the live MCP endpoint)
43
+ D1: freshcontext-db (ID: d9898d65-f67e-4dcb-abdc-7f7b53f2d444)
44
+ KV: RATE_LIMITER and CACHE (IDs in wrangler.jsonc)
45
+ Cron: 0 */6 * * * (every 6 hours, runs automatically)
46
+ Endpoint: https://freshcontext-mcp.gimmanuel73.workers.dev/mcp
47
+
48
+ Transfer method: Add new account as Super Administrator, remove original.
49
+ D1 export: wrangler d1 export freshcontext-db --output=dump.sql
50
+
51
+ ### 4. Apify Actor
52
+ Actor: prince_gabriel/freshcontext-mcp
53
+ URL: https://apify.com/prince_gabriel/freshcontext-mcp
54
+ Monetization: $50.00 per 1,000 results (Pay per event)
55
+ Transfer method: Re-publish under new Apify account
56
+
57
+ ### 5. MCP Registry Listing
58
+ Entry: io.github.PrinceGabriel-lgtm/freshcontext
59
+ Config: server.json in the GitHub repo
60
+ Transfer method: Update server.json with new repo URL and re-submit
61
+
62
+ ### 6. GitHub Actions CI/CD
63
+ File: .github/workflows/publish.yml
64
+ Action: On every push to main — npm ci > tsc > npm publish
65
+ Secret: NPM_TOKEN (granular access token from npmjs.com, renew annually)
66
+
67
+ ---
68
+
69
+ ## Credentials Map (categories only — not values)
70
+
71
+ | Credential | Where Used | Location |
72
+ |---|---|---|
73
+ | API_KEY | Worker auth header | Cloudflare env var |
74
+ | ANTHROPIC_KEY | Synthesis/briefing endpoint | Cloudflare env var |
75
+ | GITHUB_TOKEN | GitHub API rate limit bypass | Cloudflare env var |
76
+ | NPM_TOKEN | GitHub Actions auto-publish | GitHub secret |
77
+
78
+ ---
79
+
80
+ ## Codebase Map
81
+
82
+ src/server.ts — MCP stdio server, all 15 tools registered here
83
+ src/apify.ts — Apify Actor entry point (read input, call adapter, exit)
84
+ src/security.ts — URL validation, SSRF prevention
85
+ src/types.ts — FreshContext, AdapterResult, ExtractOptions interfaces
86
+ src/adapters/ — One file per data source (13 files)
87
+ changelog.ts — UNIQUE: GitHub Releases API + npm + auto-discover
88
+ govcontracts.ts — UNIQUE: USASpending.gov federal contract awards
89
+ src/tools/
90
+ freshnessStamp.ts — Score calculation, JSON form, text envelope
91
+ worker/src/worker.ts — Cloudflare Worker: 15 tools + KV cache + rate limit
92
+ + D1 cron scraper + briefing formatter
93
+ .actor/Dockerfile — Apify build config (Node 20, runs dist/apify.js)
94
+ .actor/actor.json — Apify Actor metadata
95
+ FRESHCONTEXT_SPEC.md — The open standard (MIT license)
96
+ ROADMAP.md — 10-layer product vision
97
+ server.json — MCP Registry listing
98
+ .github/workflows/
99
+ publish.yml — GitHub Actions CI/CD
100
+
101
+ ---
102
+
103
+ ## The 15 Tools
104
+
105
+ Standard (11):
106
+ extract_github, extract_hackernews, extract_scholar, extract_arxiv,
107
+ extract_reddit, extract_yc, extract_producthunt, search_repos,
108
+ package_trends, extract_finance, search_jobs
109
+
110
+ Composite landscapes (3):
111
+ extract_landscape — YC + GitHub + HN + Reddit + Product Hunt + npm
112
+ extract_gov_landscape — govcontracts + HN + GitHub + changelog
113
+ extract_finance_landscape — finance + HN + Reddit + GitHub + changelog
114
+
115
+ Unique — not in any other MCP server (2):
116
+ extract_changelog — release history from any repo, package, or website
117
+ extract_govcontracts — US federal contract awards from USASpending.gov
118
+
119
+ ---
120
+
121
+ ## D1 Database Schema
122
+
123
+ watched_queries — 18 active monitored topics
124
+ id, adapter, query, label, filters, enabled, last_run_at
125
+
126
+ scrape_results — raw results, deduplicated by content hash
127
+ id, watched_query_id, adapter, query, raw_content, result_hash, is_new, scraped_at
128
+
129
+ briefings — formatted intelligence reports per cron run
130
+ id, user_id, summary, new_results_count, adapters_run, created_at
131
+
132
+ user_profiles — personalization data for briefing synthesis
133
+ id, name, skills, certifications, targets, location, context
134
+
135
+ ---
136
+
137
+ ## The FreshContext Specification
138
+
139
+ FRESHCONTEXT_SPEC.md is the open standard, MIT license, authored March 2026.
140
+ Any implementation returning the [FRESHCONTEXT]...[/FRESHCONTEXT] envelope
141
+ or the structured JSON form with freshcontext.retrieved_at and
142
+ freshcontext.freshness_confidence is FreshContext-compatible.
143
+
144
+ The spec is the durable asset. The code is the reference implementation.
145
+
146
+ ---
147
+
148
+ ## What Keeps Running Without You
149
+
150
+ The Cloudflare cron fires every 6 hours automatically. Every run scrapes all 18
151
+ watched queries, deduplicates by content hash, stores new signals in D1, and
152
+ generates a briefing. The dataset accumulates indefinitely. No action required.
153
+
154
+ ---
155
+
156
+ ## Pending Items at Time of Handoff
157
+
158
+ Apify Playwright — extract_reddit, extract_yc, extract_producthunt may error on
159
+ Apify. Fix: add RUN npx playwright install chromium --with-deps to .actor/Dockerfile.
160
+
161
+ Synthesis endpoint — /briefing/now is paused. Needs ANTHROPIC_KEY in Worker env
162
+ and credits loaded at console.anthropic.com. Infrastructure is fully built.
163
+
164
+ Agnost AI analytics — free MCP analytics offered by Apify. Sign up at app.agnost.ai,
165
+ add one line to server.ts. Gives tool call tracking and usage dashboards.
166
+
167
+ Apify GitHub Actions trigger — npm publish is automated but Apify rebuild is
168
+ manual. Add a workflow step calling Apify's API to auto-rebuild on push.
169
+
170
+ ---
171
+
172
+ ## Outreach Status at Time of Handoff
173
+
174
+ 19 confirmed delivered emails across US, Singapore, China, and Europe.
175
+ Cloudflare for Startups — awaiting reply (10 business day window).
176
+ GovTech Singapore — auto-acknowledged, reply expected by March 25, 2026.
177
+ All others — first contact sent, no replies yet.
178
+
179
+ HN post live: https://news.ycombinator.com/user?id=Prince-Gabriel
180
+
181
+ ---
182
+
183
+ *"The work isn't gone. It's just waiting to be continued."*
184
+ *— Prince Gabriel, Grootfontein, Namibia*
@@ -0,0 +1,60 @@
1
+ # FreshContext — Session Save V4
2
+ **Date:** 2026-03-20
3
+ **npm:** freshcontext-mcp@0.3.11
4
+ **Tools:** 15 live → building to 18
5
+
6
+ ---
7
+
8
+ ## What Was Done This Session
9
+
10
+ - Apify Actor fixed: Node 20 base image, clean build confirmed
11
+ - HANDOFF.md created and pushed
12
+ - Command reference HTML updated with example output section
13
+ - Intelligence report (Anthropic/OpenAI/Palantir) confirmed as best product demo asset
14
+ - SESSION_SAVE_V3.md written
15
+ - Strategic discussion: keep the niche, deepen it — spec adoption > more tools
16
+ - Identified two new unique adapters: extract_sec_filings + extract_gdelt
17
+ - Composite: extract_company_landscape (5 sources: SEC + govcontracts + GDELT + changelog + finance)
18
+
19
+ ---
20
+
21
+ ## Next Build — NOW IN PROGRESS
22
+
23
+ ### extract_sec_filings
24
+ Source: SEC EDGAR full-text search (efts.sec.gov/LATEST/search-index)
25
+ No auth. Free. Real-time.
26
+ Returns: 8-K filings — legally mandated material event disclosures
27
+ CEO changes, major contracts, acquisitions, breaches, regulatory actions
28
+ File: src/adapters/secFilings.ts
29
+
30
+ ### extract_gdelt
31
+ Source: GDELT Project (api.gdeltproject.org/api/v2/doc/doc)
32
+ No auth. Free. Updated every 15 minutes.
33
+ Returns: Structured global news events with tone, goldstein scale, actor tags
34
+ File: src/adapters/gdelt.ts
35
+
36
+ ### extract_company_landscape (composite)
37
+ 5 sources in parallel:
38
+ 1. extract_sec_filings — legal disclosures
39
+ 2. extract_govcontracts — federal contract footprint
40
+ 3. extract_gdelt — global news events
41
+ 4. extract_changelog — shipping velocity
42
+ 5. extract_finance — market pricing
43
+ File: register in src/server.ts
44
+
45
+ ---
46
+
47
+ ## Moat Summary (all 4 unique adapters)
48
+ extract_changelog — release history from any repo/package/site
49
+ extract_govcontracts — US federal contract awards (USASpending.gov)
50
+ extract_sec_filings — 8-K material event disclosures (SEC EDGAR)
51
+ extract_gdelt — global structured news events (GDELT Project)
52
+
53
+ These 4 exist in no other MCP server. All free. All no-auth.
54
+
55
+ ---
56
+
57
+ ## Resume Prompt
58
+ "I'm building freshcontext-mcp. Just built extract_sec_filings and extract_gdelt.
59
+ Need to register them in server.ts and build extract_company_landscape composite.
60
+ See SESSION_SAVE_V4.md."
@@ -0,0 +1,104 @@
1
+ /**
2
+ * GDELT adapter — fetches structured global news intelligence from the GDELT Project
3
+ *
4
+ * No other MCP server has this. GDELT monitors broadcast, print, and web news
5
+ * from every country in 100+ languages, updated every 15 minutes. Free, no auth.
6
+ *
7
+ * Returns structured geopolitical intelligence — not just headlines, but event codes,
8
+ * actor tags, tone scores, goldstein scale (impact measure), location, timestamp.
9
+ *
10
+ * API: https://api.gdeltproject.org/api/v2/doc/doc
11
+ */
12
+ const HEADERS = {
13
+ "Accept": "application/json",
14
+ "User-Agent": "freshcontext-mcp/1.0 contact@freshcontext.dev",
15
+ };
16
+ function parseGdeltDate(raw) {
17
+ if (!raw)
18
+ return null;
19
+ // Format: 20260320T093000Z → 2026-03-20T09:30:00Z
20
+ const m = raw.match(/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z?$/);
21
+ if (!m)
22
+ return null;
23
+ return `${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}Z`;
24
+ }
25
+ async function fetchGdelt(query, maxRecords = 15) {
26
+ const params = new URLSearchParams({
27
+ query: query,
28
+ mode: "artlist",
29
+ maxrecords: String(maxRecords),
30
+ format: "json",
31
+ timespan: "1month",
32
+ sort: "DateDesc",
33
+ });
34
+ const url = `https://api.gdeltproject.org/api/v2/doc/doc?${params}`;
35
+ const controller = new AbortController();
36
+ const timeout = setTimeout(() => controller.abort(), 20000);
37
+ try {
38
+ const res = await fetch(url, {
39
+ headers: HEADERS,
40
+ signal: controller.signal,
41
+ });
42
+ if (!res.ok) {
43
+ const text = await res.text().catch(() => "");
44
+ throw new Error(`GDELT HTTP ${res.status}: ${text.slice(0, 200)}`);
45
+ }
46
+ const text = await res.text();
47
+ // GDELT sometimes returns empty body or non-JSON
48
+ if (!text.trim() || text.trim() === "null") {
49
+ return { articles: [] };
50
+ }
51
+ return JSON.parse(text);
52
+ }
53
+ finally {
54
+ clearTimeout(timeout);
55
+ }
56
+ }
57
+ function formatArticles(data, query, maxLength) {
58
+ const articles = data.articles ?? [];
59
+ if (!articles.length) {
60
+ return {
61
+ raw: `No GDELT news events found for "${query}" in the last month.\n\nTips:\n- Try a broader keyword: "artificial intelligence" instead of "Claude AI"\n- Try a company name: "Palantir"\n- GDELT covers global news — try in English for best results`,
62
+ content_date: null,
63
+ freshness_confidence: "high",
64
+ };
65
+ }
66
+ const lines = [
67
+ `GDELT Global News Intelligence — ${query}`,
68
+ `${articles.length} articles from global news sources (last 30 days)`,
69
+ "",
70
+ ];
71
+ let latestDate = null;
72
+ articles.forEach((article, i) => {
73
+ const date = parseGdeltDate(article.seendate);
74
+ const title = (article.title ?? "No title").slice(0, 200);
75
+ const domain = article.domain ?? "unknown";
76
+ const country = article.sourcecountry ?? "N/A";
77
+ const language = article.language ?? "N/A";
78
+ const url = article.url ?? "";
79
+ if (date && (!latestDate || date > latestDate))
80
+ latestDate = date;
81
+ lines.push(`[${i + 1}] ${title}`);
82
+ lines.push(` Source: ${domain} (${country})`);
83
+ lines.push(` Language: ${language}`);
84
+ lines.push(` Date: ${date ?? article.seendate ?? "unknown"}`);
85
+ if (url)
86
+ lines.push(` URL: ${url.slice(0, 200)}`);
87
+ lines.push("");
88
+ });
89
+ lines.push(`Source: GDELT Project — https://www.gdeltproject.org`);
90
+ lines.push(`Coverage: 100+ languages, every country, updated every 15 minutes`);
91
+ return {
92
+ raw: lines.join("\n").slice(0, maxLength),
93
+ content_date: latestDate,
94
+ freshness_confidence: "high",
95
+ };
96
+ }
97
+ export async function gdeltAdapter(options) {
98
+ const query = (options.url ?? "").trim();
99
+ const maxLength = options.maxLength ?? 6000;
100
+ if (!query)
101
+ throw new Error("Query required: company name, topic, or keyword");
102
+ const data = await fetchGdelt(query);
103
+ return formatArticles(data, query, maxLength);
104
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * SEC EDGAR adapter — fetches 8-K filings from the SEC's full-text search API
3
+ *
4
+ * No other MCP server has this. 8-K filings are legally mandated disclosures
5
+ * of material corporate events — CEO changes, acquisitions, data breaches,
6
+ * major contracts, regulatory actions. Filed within 4 business days of the event.
7
+ *
8
+ * This is the most reliable early-warning signal for corporate events in existence.
9
+ * Free, no auth, updated in real time.
10
+ *
11
+ * API: https://efts.sec.gov/LATEST/search-index
12
+ */
13
+ const HEADERS = {
14
+ "Accept": "application/json",
15
+ "User-Agent": "freshcontext-mcp/1.0 contact@freshcontext.dev",
16
+ };
17
+ async function fetchSecFilings(query, maxResults = 10) {
18
+ const today = new Date().toISOString().slice(0, 10);
19
+ const oneYearAgo = new Date(Date.now() - 365 * 86400000).toISOString().slice(0, 10);
20
+ const params = new URLSearchParams({
21
+ q: `"${query}"`,
22
+ forms: "8-K",
23
+ dateRange: "custom",
24
+ startdt: oneYearAgo,
25
+ enddt: today,
26
+ hits: String(maxResults),
27
+ });
28
+ const url = `https://efts.sec.gov/LATEST/search-index?${params}`;
29
+ const controller = new AbortController();
30
+ const timeout = setTimeout(() => controller.abort(), 20000);
31
+ try {
32
+ const res = await fetch(url, {
33
+ headers: HEADERS,
34
+ signal: controller.signal,
35
+ });
36
+ if (!res.ok) {
37
+ const text = await res.text().catch(() => "");
38
+ throw new Error(`SEC EDGAR HTTP ${res.status}: ${text.slice(0, 200)}`);
39
+ }
40
+ return await res.json();
41
+ }
42
+ finally {
43
+ clearTimeout(timeout);
44
+ }
45
+ }
46
+ function formatFilings(data, query, maxLength) {
47
+ const hits = data.hits?.hits ?? [];
48
+ const total = data.hits?.total?.value ?? 0;
49
+ if (!hits.length) {
50
+ return {
51
+ raw: `No 8-K filings found for "${query}" in the last year.\n\nTips:\n- Try the full legal company name: "Palantir Technologies"\n- Try a ticker-like keyword: "PLTR"\n- 8-K filings are only for public companies`,
52
+ content_date: null,
53
+ freshness_confidence: "high",
54
+ };
55
+ }
56
+ const lines = [
57
+ `SEC 8-K Filings — ${query}`,
58
+ `${total.toLocaleString()} total filings found (showing ${hits.length})`,
59
+ "",
60
+ ];
61
+ let latestDate = null;
62
+ hits.forEach((hit, i) => {
63
+ const src = hit._source ?? {};
64
+ const entityName = src.entity_name ?? hit.entity_name ?? "Unknown";
65
+ const fileDate = src.filed_at?.slice(0, 10) ?? hit.file_date ?? "unknown";
66
+ const period = src.period_of_report?.slice(0, 10) ?? hit.period_of_report ?? "unknown";
67
+ const formType = src.form_type ?? hit.form_type ?? "8-K";
68
+ const location = [src.biz_location, src.inc_states].filter(Boolean).join(" / ") || "N/A";
69
+ const filingId = hit._id ?? "N/A";
70
+ if (fileDate && fileDate !== "unknown") {
71
+ if (!latestDate || fileDate > latestDate)
72
+ latestDate = fileDate;
73
+ }
74
+ lines.push(`[${i + 1}] ${entityName}`);
75
+ lines.push(` Form: ${formType}`);
76
+ lines.push(` Filed: ${fileDate}`);
77
+ lines.push(` Period: ${period}`);
78
+ lines.push(` Location: ${location}`);
79
+ lines.push(` Filing ID: ${filingId}`);
80
+ lines.push(` View: https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&company=${encodeURIComponent(entityName)}&type=8-K&dateb=&owner=include&count=10`);
81
+ lines.push("");
82
+ });
83
+ lines.push(`Source: SEC EDGAR — https://efts.sec.gov/LATEST/search-index`);
84
+ lines.push(`Note: 8-K filings are legally mandated disclosures of material events filed within 4 business days.`);
85
+ return {
86
+ raw: lines.join("\n").slice(0, maxLength),
87
+ content_date: latestDate,
88
+ freshness_confidence: "high",
89
+ };
90
+ }
91
+ export async function secFilingsAdapter(options) {
92
+ const query = (options.url ?? "").trim();
93
+ const maxLength = options.maxLength ?? 6000;
94
+ if (!query)
95
+ throw new Error("Company name or keyword required");
96
+ const data = await fetchSecFilings(query);
97
+ return formatFilings(data, query, maxLength);
98
+ }
package/dist/server.js CHANGED
@@ -13,6 +13,8 @@ import { financeAdapter } from "./adapters/finance.js";
13
13
  import { jobsAdapter } from "./adapters/jobs.js";
14
14
  import { changelogAdapter } from "./adapters/changelog.js";
15
15
  import { govContractsAdapter } from "./adapters/govcontracts.js";
16
+ import { secFilingsAdapter } from "./adapters/secFilings.js";
17
+ import { gdeltAdapter } from "./adapters/gdelt.js";
16
18
  import { stampFreshness, formatForLLM } from "./tools/freshnessStamp.js";
17
19
  import { formatSecurityError } from "./security.js";
18
20
  const server = new McpServer({
@@ -317,6 +319,95 @@ server.registerTool("extract_finance_landscape", {
317
319
  ].join("\n\n");
318
320
  return { content: [{ type: "text", text: combined }] };
319
321
  });
322
+ // ─── Tool: extract_sec_filings ─────────────────────────────────────────────
323
+ // 8-K filings = legally mandated material event disclosures. CEO changes,
324
+ // acquisitions, breaches, major contracts, regulatory actions — all filed
325
+ // within 4 business days. Most reliable early-warning corporate signal in existence.
326
+ // Unique: no other MCP server has this.
327
+ server.registerTool("extract_sec_filings", {
328
+ description: "Fetch SEC 8-K filings for any public company from the SEC EDGAR full-text search API. 8-K filings are legally mandated disclosures of material corporate events — CEO changes, acquisitions, data breaches, major contracts, regulatory actions — filed within 4 business days. Free, no auth, real-time. Pass a company name, ticker, or keyword. Unique: not available in any other MCP server.",
329
+ inputSchema: z.object({
330
+ url: z.string().describe("Company name, ticker, or keyword e.g. 'Palantir', 'PLTR', 'artificial intelligence'"),
331
+ max_length: z.number().optional().default(6000),
332
+ }),
333
+ annotations: { readOnlyHint: true, openWorldHint: true },
334
+ }, async ({ url, max_length }) => {
335
+ try {
336
+ const result = await secFilingsAdapter({ url, maxLength: max_length });
337
+ const ctx = stampFreshness(result, { url, maxLength: max_length }, "sec_filings");
338
+ return { content: [{ type: "text", text: formatForLLM(ctx) }] };
339
+ }
340
+ catch (err) {
341
+ return { content: [{ type: "text", text: formatSecurityError(err) }] };
342
+ }
343
+ });
344
+ // ─── Tool: extract_gdelt ─────────────────────────────────────────────────────
345
+ // GDELT monitors news from every country in 100+ languages, updated every 15 min.
346
+ // Returns structured global news intelligence — not just headlines but event
347
+ // codes, actor tags, tone scores, goldstein scale (impact), location, timestamp.
348
+ // Unique: no other MCP server has this.
349
+ server.registerTool("extract_gdelt", {
350
+ description: "Fetch global news intelligence from the GDELT Project. GDELT monitors broadcast, print, and web news from every country in 100+ languages, updated every 15 minutes. Returns structured articles with source country, language, and publication date. Free, no auth. Pass any company name, topic, or keyword. Unique: not available in any other MCP server.",
351
+ inputSchema: z.object({
352
+ url: z.string().describe("Query: company name, topic, or keyword e.g. 'Palantir', 'artificial intelligence', 'MCP server'"),
353
+ max_length: z.number().optional().default(6000),
354
+ }),
355
+ annotations: { readOnlyHint: true, openWorldHint: true },
356
+ }, async ({ url, max_length }) => {
357
+ try {
358
+ const result = await gdeltAdapter({ url, maxLength: max_length });
359
+ const ctx = stampFreshness(result, { url, maxLength: max_length }, "gdelt");
360
+ return { content: [{ type: "text", text: formatForLLM(ctx) }] };
361
+ }
362
+ catch (err) {
363
+ return { content: [{ type: "text", text: formatSecurityError(err) }] };
364
+ }
365
+ });
366
+ // ─── Tool: extract_company_landscape ─────────────────────────────────────────
367
+ // The most complete single-call company intelligence available in any MCP server.
368
+ // 5 moat sources: SEC 8-K legal disclosures + federal contract footprint +
369
+ // global news intelligence + product release velocity + market pricing.
370
+ // Unique: this combination exists nowhere else.
371
+ server.registerTool("extract_company_landscape", {
372
+ description: "Composite company intelligence tool. The most complete single-call company analysis available. Simultaneously queries 5 unique sources: (1) SEC EDGAR for 8-K material event filings — what the company legally just disclosed, (2) USASpending.gov for federal contract footprint — who is giving them government money, (3) GDELT for global news intelligence — what the world is saying about them right now, (4) their product changelog — are they actually shipping, (5) Yahoo Finance — what the market is pricing in. Returns a unified 5-source timestamped report. Unique: this combination is not available in any other MCP server.",
373
+ inputSchema: z.object({
374
+ company: z.string().describe("Company name e.g. 'Palantir', 'Anthropic', 'OpenAI'"),
375
+ ticker: z.string().optional().describe("Stock ticker for finance data e.g. 'PLTR'. Leave blank for private companies."),
376
+ github_url: z.string().optional().describe("Optional GitHub repo or org URL e.g. 'https://github.com/palantir'. Improves changelog accuracy."),
377
+ max_length: z.number().optional().default(15000),
378
+ }),
379
+ annotations: { readOnlyHint: true, openWorldHint: true },
380
+ }, async ({ company, ticker, github_url, max_length }) => {
381
+ const perSection = Math.floor((max_length ?? 15000) / 5);
382
+ const repoQuery = github_url ?? company;
383
+ const [secResult, contractsResult, gdeltResult, changelogResult, financeResult] = await Promise.allSettled([
384
+ // 1. What did they legally just disclose
385
+ secFilingsAdapter({ url: company, maxLength: perSection }),
386
+ // 2. Who is giving them government money
387
+ govContractsAdapter({ url: company, maxLength: perSection }),
388
+ // 3. What is global news saying right now
389
+ gdeltAdapter({ url: company, maxLength: perSection }),
390
+ // 4. Are they actually shipping product
391
+ changelogAdapter({ url: repoQuery, maxLength: perSection }),
392
+ // 5. What is the market pricing in
393
+ financeAdapter({ url: ticker ?? company, maxLength: perSection }),
394
+ ]);
395
+ const section = (label, result) => result.status === "fulfilled"
396
+ ? `## ${label}\n${result.value.raw}`
397
+ : `## ${label}\n[Unavailable: ${result.reason}]`;
398
+ const combined = [
399
+ `# Company Intelligence Landscape: "${company}"${ticker ? ` (${ticker})` : ""}`,
400
+ `Generated: ${new Date().toISOString()}`,
401
+ `Sources: SEC EDGAR · USASpending.gov · GDELT · Changelog · Yahoo Finance`,
402
+ "",
403
+ section("📋 SEC 8-K Filings — Legal Disclosures", secResult),
404
+ section("🏛️ Federal Contract Awards (USASpending.gov)", contractsResult),
405
+ section("🌍 Global News Intelligence (GDELT)", gdeltResult),
406
+ section("🔄 Product Release Velocity (Changelog)", changelogResult),
407
+ section("📈 Market Data (Yahoo Finance)", financeResult),
408
+ ].join("\n\n");
409
+ return { content: [{ type: "text", text: combined }] };
410
+ });
320
411
  // ─── Start ───────────────────────────────────────────────────────────────────
321
412
  async function main() {
322
413
  const transport = new StdioServerTransport();
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.11",
4
+ "version": "0.3.12",
5
5
  "description": "Real-time web extraction MCP server with freshness timestamps for AI agents",
6
6
  "keywords": [
7
7
  "mcp",