canli-markets-mcp 0.7.0 → 0.7.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.2
4
+
5
+ - `market_overview`: the market at a glance in one call: US indices and the VIX, the eleven S&P sectors
6
+ ranked by the month, Treasury yields, the dollar and major currencies, oil, gas, gold, silver and copper,
7
+ bitcoin and ether, and the main markets in Europe and Asia, each over a day, a week, a month, three
8
+ months, the year to date and a year (yields in percentage points). A market that fails is listed as
9
+ missing. 64 tools.
10
+ - `shareholder_returns`: what a company has paid its shareholders, year by year from its annual XBRL:
11
+ dividends per share and in total, buybacks and shares issued for cash, the diluted share count, the
12
+ payout ratio and the share of free cash flow paid out; the dividend's record on split-adjusted figures
13
+ (years raised in a row, 5- and 10-year growth; a split shows in older 10-Ks as a cut otherwise); and the
14
+ dividend and shareholder yields at the last close.
15
+ - `ownership_breadth` finds the CUSIP from a company's name in many more cases: 13F tables shorten
16
+ issuer names ("COSTCO WHSL CORP NEW", "BANK OF AMER CORP"), write "&" as "&", and some filers
17
+ slip, so names now match word by word with abbreviations, the CUSIP check digit is checked, the
18
+ company's own 13F and old text filings are skipped, three tables vote, and among several CUSIPs
19
+ (share classes, or a CUSIP that changed) the one most filers list is counted, the others shown.
20
+ A CUSIP new in recent quarters is flagged rather than read as a jump in ownership.
21
+ - `news` tries GDELT three times, each after a longer wait, before reporting its rate limit.
22
+
23
+ ## 0.7.1
24
+
25
+ - `compute_prices`: compute as a market. The on-demand hourly price of each data-centre GPU (B200,
26
+ H200, H100, A100, L40S...) across the clouds and GPU marketplaces, every provider's offer for one
27
+ model, recent list-price changes, and the Cirrun GPU Index, a daily chained index of those prices
28
+ (cirrun.com, no key, CC BY 4.0). 62 tools.
29
+
3
30
  ## 0.7.0
4
31
 
5
32
  - `ipo_pipeline`: S-1/F-1 registrations (IPO candidates, SPACs, funds, listed filers) and priced IPOs
package/README.md CHANGED
@@ -87,9 +87,11 @@ against the BIS (21 of 21 agreed on 2026-10-15).
87
87
  | `ownership_breadth` | How many institutions hold a stock, quarter by quarter: 13F filers whose holdings tables list its CUSIP, the change, and a sample of holders (Apple, Q2 2026: 6,776, up 1.9% on the quarter) | EDGAR full-text search over 13F information tables |
88
88
  | `forensic_scores` | Beneish's M-score, Piotroski's F-score and Altman's Z-score in one call, every input read from the company's annual XBRL with its tag, and missing inputs named rather than guessed | SEC XBRL company facts, price at the fiscal year end |
89
89
  | `funding_rates` | Crypto perpetual funding: each coin's latest eight-hour rate, the window's mean and annualized carry, the last week's, and the share of positive periods (BTC, 90 days to 2026-10-24: 5.6% a year) | OKX public data (no key) |
90
+ | `compute_prices` | Compute as a market: the on-demand hourly price of each data-centre GPU across the clouds (cheapest, median and dearest, and who offers them), every provider's offer for one model, recent list-price changes, and the Cirrun GPU Index (H100 SXM, 2026-10-24: $1.99 to $12.29 an hour, median $3.95) | Cirrun's GPU price index (no key, CC BY 4.0) |
90
91
  | `federal_contracts` | What the US government obligated to a company in a fiscal year: the total, by agency and industry code, the entities it contracts under, and its largest contracts with links (Lockheed Martin, fiscal 2026: $53.2 billion from Defense) | USAspending.gov (no key) |
91
92
  | `drug_pipeline` | A drug company's active clinical trials by phase, the primary completions due in the coming months (the next readouts), its late-stage trials and its FDA approvals (Vertex: 31 active trials, 16 in phase 3) | ClinicalTrials.gov and Drugs@FDA (no key) |
92
93
  | `comment_letters` | What the SEC's staff questioned in a company's filings and how it answered, grouped into reviews, with the topics of the newest letters (Apple, 2024: revenue recognition, segments, MD&A) | SEC EDGAR forms UPLOAD and CORRESP |
94
+ | `shareholder_returns` | What a company has paid its owners each year: dividends per share (with its split-adjusted record: years raised in a row, 5- and 10-year growth) and in total, buybacks and shares issued, the diluted share count, the payout ratio and the share of free cash flow paid out, and the dividend and shareholder yields at the last close | SEC XBRL company facts, the last close |
93
95
  | `reverse_dcf` | The free-cash-flow growth a share price implies: a DCF solved for the growth that reaches the market's enterprise value, every input from the annual XBRL with its tag, a sensitivity table and the company's own growth beside it | SEC XBRL company facts, the last close |
94
96
  | `filing_drift` | How a 10-K section's language drifts over several years: each year's change from the last (the measure Cohen, Malloy and Nguyen's "Lazy Prices" found preceded weaker returns), hedging and adverse words, readability, and a theme you name, with flags (Nvidia's risk factors: export-control sentences 8, 14, 37, 48, 51 over five reports) | SEC 10-K filings, computed on your machine |
95
97
  | `bank_financials` | A US bank's condition quarter by quarter from its call reports: assets, deposits and the share uninsured, loans and the share noncurrent, earnings, margin, returns and capital ratios; with no bank, the latest failures and their cost (Western Alliance, June 2026: 38% of deposits uninsured, tier 1 leverage 8.1%) | FDIC BankFind (no key) |
@@ -116,6 +118,7 @@ against the BIS (21 of 21 agreed on 2026-10-15).
116
118
  | `options_chain` | An option chain with IV, greeks, open interest, put/call ratios, max pain and the expected move | Cboe delayed quotes |
117
119
  | `volatility_surface` | Implied volatility term structure, 25-delta skew and the smile | Cboe delayed quotes |
118
120
  | `futures_curve` | The VIX futures curve (and other Cboe futures) against the index: contango or backwardation | Cboe settlements |
121
+ | `market_overview` | The market at a glance: US indices and the VIX, the eleven sectors ranked by the month, Treasury yields, the dollar and major currencies, oil, gas, gold, silver, copper, bitcoin, ether and the main markets abroad, each over a day, a week, a month, three months, the year to date and a year | Yahoo Finance chart data (no key) |
119
122
  | `price_history` | Daily to monthly bars for stocks in the US and abroad, currencies, futures, indices and crypto, adjusted, with dividends and splits, as columns | Yahoo Finance (no key), or Alpaca or Tiingo (your key) for US stocks |
120
123
  | `treasury_auctions` | How each Treasury auction went (yield, bid-to-cover, who bought) and which auctions come next | US Treasury Fiscal Data |
121
124
  | `federal_debt` | The US debt each business day, and the average rate the Treasury pays on it | US Treasury Fiscal Data |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canli-markets-mcp",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "MCP server for markets, funds and the economy from the sources themselves, no key: European companies' ESEF annual reports, central-bank rates (SOFR, €STR, SONIA, 40 policy rates), Polymarket and Kalshi odds, DeFi value locked and fees, financial statements as filed, the tables in filings, earnings guidance as ranges and a draft's figures checked against the filings, options chains and volatility, short interest, dark pools, futures positioning, fund and ETF holdings, 13D/13G, earnings dates and 8-K press-release headlines, segment revenue, executive pay, Form D, the Fed, Treasury auctions, World Bank and OECD macro, screens, filings and prices. Every result cites its sources.",
5
5
  "private": false,
6
6
  "type": "module",
package/src/breadth.mjs CHANGED
@@ -15,7 +15,7 @@ export const ownershipInput = z.object({
15
15
  }).strict();
16
16
 
17
17
  const EFTS = "https://efts.sec.gov/LATEST/search-index";
18
- const key = (s) => String(s ?? "").toUpperCase().replace(/&/g, " AND ").replace(/[^A-Z0-9 ]+/g, " ").replace(/\b(INC|CORP|CORPORATION|CO|COMPANY|LTD|PLC|HOLDINGS?|GROUP|THE|CL [A-C]|CLASS [A-C]|COM|NEW|DEL)\b/g, " ").replace(/\s+/g, " ").trim();
18
+ const key = (s) => String(s ?? "").replace(/&(amp|apos|#39);/gi, (_, e) => (e.toLowerCase() === "amp" ? "&" : "'")).toUpperCase().replace(/\/[A-Z]{2,3}\/?/g, " ").replace(/&/g, " AND ").replace(/[^A-Z0-9 ]+/g, " ").replace(/\b(INC|CORP|CORPORATION|CO|COMPANY|LTD|PLC|HOLDINGS?|GROUP|THE|CL [A-C]|CLASS [A-C]|COM|NEW|DEL)\b/g, " ").replace(/\s+/g, " ").trim();
19
19
 
20
20
  // 13F reports are due 45 days after a quarter ends: filings made in a calendar quarter report the
21
21
  // quarter before it. Each window is a calendar quarter of filing dates.
@@ -33,35 +33,90 @@ export function filingQuarters(today, n) {
33
33
  return out;
34
34
  }
35
35
 
36
- // The CUSIP 13F filers give a company: from information tables that name it, the CUSIP most often
37
- // beside a matching issuer name.
36
+ // 13F tables shorten issuer names ("COSTCO WHSL CORP NEW" for Costco Wholesale): two names match when
37
+ // they have the same words, each the same or one an abbreviation of the other (its letters in order,
38
+ // starting with the same letter), and the same first word.
39
+ const abbreviates = (a, b) => { if (a.length > b.length) [a, b] = [b, a]; if (a.length < 2 || a[0] !== b[0]) return false; let i = 0; for (const ch of b) if (ch === a[i]) i += 1; return i === a.length; };
40
+ export function sameIssuer(a, b) {
41
+ const x = key(a).split(" ").filter(Boolean), y = key(b).split(" ").filter(Boolean);
42
+ return x.length > 0 && x.length === y.length && x[0] === y[0] && x.every((w, i) => w === y[i] || abbreviates(w, y[i]));
43
+ }
44
+ // A CUSIP's ninth character is a check digit (the "modulus 10 double add double" of the first eight).
45
+ export function cusipValid(c) {
46
+ if (!/^[0-9A-Z*@#]{8}[0-9]$/.test(c)) return false;
47
+ let sum = 0;
48
+ for (let i = 0; i < 8; i += 1) {
49
+ const ch = c[i], v = /\d/.test(ch) ? Number(ch) : /[A-Z]/.test(ch) ? ch.charCodeAt(0) - 55 : { "*": 36, "@": 37, "#": 38 }[ch];
50
+ const d = i % 2 ? v * 2 : v;
51
+ sum += Math.floor(d / 10) + (d % 10);
52
+ }
53
+ return (10 - (sum % 10)) % 10 === Number(c[8]);
54
+ }
55
+
56
+ // The CUSIP 13F filers give a company: from information tables that name it, the valid CUSIP most
57
+ // often beside a matching issuer name, and the other classes seen (Alphabet's A and C shares share a
58
+ // name). The search is by the name's first word, since the tables abbreviate the rest; the company's
59
+ // own 13Fs are skipped (a bank's or insurer's can be too large to read).
38
60
  async function cusipOf(session, name) {
39
- const p = new URLSearchParams({ q: `"${name}"`, forms: "13F-HR" });
40
- const r = await getJson(session, `${EFTS}?${p}`);
41
- const want = key(name), votes = new Map();
42
- for (const h of (r?.hits?.hits ?? []).slice(0, 4)) {
61
+ const words = key(name).split(" ").filter(Boolean);
62
+ // The first two words as a phrase when there are two ("JPMORGAN CHASE", not every JPMorgan ETF), then
63
+ // the first alone ("COSTCO", whose tables say "COSTCO WHSL").
64
+ const queries = [...new Set([words.slice(0, 2).join(" "), words[0]].filter(Boolean))];
65
+ // Last year's filings only: their information tables are XML, and the CUSIP is the current one.
66
+ const day = (t) => new Date(t).toISOString().slice(0, 10), now = session.now();
67
+ const hits = [];
68
+ for (const q of queries) {
69
+ const p = new URLSearchParams({ q: `"${q}"`, forms: "13F-HR", dateRange: "custom", startdt: day(now - 400 * 86400000), enddt: day(now) });
70
+ hits.push(...((await getJson(session, `${EFTS}?${p}`))?.hits?.hits ?? []).filter((h) => /\.xml$/i.test(String(h._id))));
71
+ }
72
+ const votes = new Map(), seen = new Set();
73
+ let read = 0, matched = 0;
74
+ for (const h of hits) {
75
+ if (read >= 8) break;
76
+ if (seen.has(h._id)) continue;
77
+ seen.add(h._id);
78
+ const filer = (h._source.display_names ?? [])[0]?.replace(/\s+\(CIK \d+\)$/, "") ?? "";
79
+ if (sameIssuer(filer, name)) continue;
43
80
  const [adsh, file] = String(h._id).split(":");
44
81
  const cik = Number((h._source.ciks ?? [])[0]);
45
82
  if (!adsh || !file || !cik) continue;
83
+ read += 1;
46
84
  const xml = await getText(session, `https://www.sec.gov/Archives/edgar/data/${cik}/${adsh.replace(/-/g, "")}/${file}`, { maxBytes: 8 * 1024 * 1024 }).catch(() => "");
85
+ const here = new Map();
47
86
  for (const m of xml.matchAll(/<(?:\w+:)?infoTable>([\s\S]*?)<\/(?:\w+:)?infoTable>/g)) {
48
87
  const issuer = /<(?:\w+:)?nameOfIssuer>([^<]*)</.exec(m[1])?.[1], cusip = /<(?:\w+:)?cusip>([^<]*)</.exec(m[1])?.[1]?.trim().toUpperCase();
49
- if (issuer && cusip && key(issuer) === want) votes.set(cusip, (votes.get(cusip) ?? 0) + 1);
88
+ const title = /<(?:\w+:)?titleOfClass>([^<]*)</.exec(m[1])?.[1]?.trim() ?? null;
89
+ if (!issuer || !cusip || !cusipValid(cusip) || !sameIssuer(issuer, name)) continue;
90
+ if (!here.has(cusip)) here.set(cusip, title);
91
+ }
92
+ // One vote per table, and three tables that name it: one filer's slip does not decide it.
93
+ for (const [cusip, title] of here) { const v = votes.get(cusip) ?? { cusip, title_of_class: title, tables: 0 }; v.tables += 1; votes.set(cusip, v); }
94
+ if (here.size) matched += 1;
95
+ if (matched >= 3) break;
96
+ }
97
+ let classes = [...votes.values()].sort((a, b) => b.tables - a.tables).slice(0, 4);
98
+ // More than one: the one most filers list in the latest quarter (a class most hold, or the CUSIP
99
+ // most still use after a change), each with its count.
100
+ if (classes.length > 1) {
101
+ const w = filingQuarters(now, 1)[0];
102
+ for (const c of classes) {
103
+ const p = new URLSearchParams({ q: `"${c.cusip}"`, forms: "13F-HR", dateRange: "custom", startdt: w.filed_from, enddt: w.filed_to });
104
+ c.filers = (await getJson(session, `${EFTS}?${p}`))?.hits?.total?.value ?? 0;
50
105
  }
51
- if (votes.size) break;
106
+ classes = classes.sort((a, b) => b.filers - a.filers);
52
107
  }
53
- return [...votes].sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
108
+ return { cusip: classes[0]?.cusip ?? null, classes };
54
109
  }
55
110
 
56
111
  export async function ownershipBreadth(session, args) {
57
112
  const a = ownershipInput.parse(args);
58
113
  if (!a.company && !a.cusip) throw new Error("ownership_breadth needs company or cusip.");
59
- let who = null, cusip = a.cusip?.toUpperCase() ?? null;
114
+ let who = null, cusip = a.cusip?.toUpperCase() ?? null, classes = null;
60
115
  if (a.company) {
61
116
  const e = await resolveEntity(session, a.company);
62
117
  who = { cik: e.cik, name: e.name, ticker: e.ticker ?? null };
63
118
  if (!cusip) {
64
- cusip = await cusipOf(session, e.name ?? a.company);
119
+ ({ cusip, classes } = await cusipOf(session, e.name ?? a.company));
65
120
  if (!cusip) throw new Error(`No 13F information table names ${e.name ?? a.company} in a way that gives its CUSIP; pass cusip (the share class's 9-character CUSIP).`);
66
121
  }
67
122
  }
@@ -76,13 +131,18 @@ export async function ownershipBreadth(session, args) {
76
131
  latest ??= r;
77
132
  }
78
133
  const counts = rows.map((x) => x[3]);
134
+ // Filers in recent quarters and none before: a CUSIP new in that time (a company's CUSIP changes in a
135
+ // reorganisation or redomicile), not a jump in ownership.
136
+ const firstSeen = counts.findIndex((n, i) => n > 0 && counts.slice(i + 1).length > 0 && counts.slice(i + 1).every((m) => m === 0));
79
137
  const change = counts.length > 1 && counts[1] ? Math.round((counts[0] / counts[1] - 1) * 10000) / 10000 : null;
80
138
  const holders = (latest?.hits?.hits ?? []).slice(0, a.holders ?? 20).map((h) => [(h._source.display_names ?? [])[0]?.replace(/\s+\(CIK \d+\)$/, "").replace(/\s+/g, " ") ?? null, Number((h._source.ciks ?? [])[0]) || null, h._source.file_date, String(h._id).split(":")[0]]);
81
139
  return {
82
140
  ...(who ? { company: who } : {}), cusip,
141
+ ...(classes?.length > 1 ? { other_cusips: { note: "CUSIPs 13F tables list under this name (share classes, or a CUSIP that changed); the one most filers list in the latest quarter is counted. Pass cusip to count another.", columns: ["cusip", "title_of_class", "filers_latest_quarter"], rows: classes.map((c) => [c.cusip, c.title_of_class, c.filers]) } } : {}),
83
142
  columns: ["holdings_quarter", "filed_from", "filed_to", "filers", "at_least"],
84
143
  rows,
85
144
  change_latest_vs_prior: change,
145
+ ...(firstSeen >= 0 ? { warning: `No 13F lists ${cusip} before ${rows[firstSeen][0]}: a CUSIP new then, as when a company's CUSIP changes in a reorganisation; earlier quarters were filed under the old one (pass it as cusip to count them).` } : {}),
86
146
  holders_sample: { note: "13F filers whose latest-quarter information table lists the CUSIP, in search order (not by size).", columns: ["manager", "cik", "filed", "accession"], rows: holders },
87
147
  meaning: "filers counts the 13F information tables filed in each window that list the CUSIP: the number of institutions (managers with over $100 million in US equities) reporting the stock. The newest window may still be filling: 13Fs are due 45 days after the quarter. Chen, Hong and Stein (2002): a fall in breadth predicted lower returns.",
88
148
  source: `${EFTS} (EDGAR full-text search over 13F information tables)`,
@@ -0,0 +1,85 @@
1
+ // Compute as a market: what an hour of each data-centre GPU costs on demand across the clouds, and
2
+ // the Cirrun GPU Index, a daily chained index of those prices (public data, no key, CC BY 4.0).
3
+ // Every price is the provider's own list price, confirmed on its pricing page on the date shown.
4
+ import { z } from "zod";
5
+
6
+ import { getJson, getText } from "./sources.mjs";
7
+
8
+ const BASE = "https://cirrun.com/api/v1";
9
+
10
+ export const computeInput = z.object({
11
+ gpu: z.string().min(2).max(40).regex(/^[A-Za-z0-9 .\-]+$/, "a GPU model such as H100 or B200").optional().describe("A GPU model (H100, H200, B200, A100, L40S, RTX 4090...): every provider's offer for it. Default: one row per model."),
12
+ provider: z.string().min(2).max(40).regex(/^[A-Za-z0-9 .\-]+$/, "a provider's name").optional().describe("Only this provider's offers, e.g. RunPod or AWS."),
13
+ changes: z.number().int().min(0).max(200).optional().describe("Latest list-price changes to return; default 20."),
14
+ }).strict();
15
+
16
+ const norm = (s) => String(s ?? "").toLowerCase().replace(/[^a-z0-9]/g, "");
17
+ const pct = (a, b) => (Number.isFinite(a) && Number.isFinite(b) && b ? Math.round((a / b - 1) * 1e4) / 1e4 : null);
18
+
19
+ function parseCsv(text) {
20
+ const rows = [];
21
+ for (const line of String(text).split(/\r?\n/).filter(Boolean)) {
22
+ const out = [];
23
+ let cur = "", quoted = false;
24
+ for (let i = 0; i < line.length; i += 1) {
25
+ const ch = line[i];
26
+ if (quoted) { if (ch === "\"" && line[i + 1] === "\"") { cur += "\""; i += 1; } else if (ch === "\"") quoted = false; else cur += ch; }
27
+ else if (ch === "\"") quoted = true;
28
+ else if (ch === ",") { out.push(cur); cur = ""; }
29
+ else cur += ch;
30
+ }
31
+ out.push(cur);
32
+ rows.push(out);
33
+ }
34
+ return rows;
35
+ }
36
+
37
+ export async function computePrices(session, args) {
38
+ const a = computeInput.parse(args);
39
+ const [ref, prices] = await Promise.all([getJson(session, `${BASE}/reference`), getJson(session, `${BASE}/prices`)]);
40
+ const byProvider = (p) => !a.provider || norm(p).includes(norm(a.provider));
41
+ const index = ref.index ?? {};
42
+ const out = {
43
+ date: ref.date ?? null, currency: ref.currency ?? "USD", unit: ref.unit ?? "per GPU-hour, on-demand list price",
44
+ index: { name: index.name ?? "Cirrun GPU Index", level: index.level ?? null, started: index.started ?? null, start_level: index.start_level ?? null, gpu_models: index.gpu_models ?? null, offers: index.offers ?? null },
45
+ };
46
+ if (a.gpu) {
47
+ const partial = (prices.gpus ?? []).filter((x) => norm(x.name).includes(norm(a.gpu)));
48
+ const g = (prices.gpus ?? []).find((x) => norm(x.name) === norm(a.gpu) || norm(x.id) === norm(a.gpu)) ?? partial[0];
49
+ if (g && partial.length > 1 && norm(g.name) !== norm(a.gpu)) out.also_matched = partial.filter((x) => x !== g).map((x) => x.name);
50
+ if (!g) throw new Error(`No GPU model ${a.gpu} in the index; models: ${(prices.gpus ?? []).map((x) => x.name).join(", ")}.`);
51
+ const summary = (ref.gpus ?? []).find((x) => norm(x.gpu) === norm(g.name)) ?? {};
52
+ const rows = (g.rows ?? []).filter((r) => byProvider(r[0])).map(([provider, tier, price, billing, confirmed]) => [provider, tier, price, prices.billing?.[billing] ?? billing, confirmed]).sort((x, y) => x[2] - y[2]);
53
+ Object.assign(out, {
54
+ gpu: g.name, vram_gb: g.vram ?? summary.vram_gb ?? null,
55
+ summary: { offers: summary.offers ?? rows.length, low: summary.low ?? null, low_from: summary.low_from ?? null, median: summary.median ?? null, high: summary.high ?? null, high_from: summary.high_from ?? null, low_datacentre: summary.low_datacentre ?? null, low_datacentre_from: summary.low_datacentre_from ?? null, median_change_since_first_day: pct(summary.median, summary.first_day_median), first_day: summary.first_day ?? null },
56
+ columns: ["provider", "tier", "price", "billing", "confirmed"], rows,
57
+ sources: Object.fromEntries(Object.entries(prices.sources ?? {}).filter(([p]) => rows.some((r) => r[0] === p))),
58
+ });
59
+ } else {
60
+ const rows = (ref.gpus ?? []).map((x) => [x.gpu, x.vram_gb ?? null, x.offers ?? null, x.low ?? null, x.low_from ?? null, x.median ?? null, x.high ?? null, x.high_from ?? null, x.low_datacentre ?? null, pct(x.median, x.first_day_median)]);
61
+ Object.assign(out, { columns: ["gpu", "vram_gb", "offers", "low", "low_from", "median", "high", "high_from", "low_datacentre", "median_change_since_first_day"], rows });
62
+ if (a.provider) {
63
+ const offers = [];
64
+ for (const g of prices.gpus ?? []) for (const [provider, tier, price, billing, confirmed] of g.rows ?? []) if (byProvider(provider)) offers.push([g.name, provider, tier, price, prices.billing?.[billing] ?? billing, confirmed]);
65
+ if (!offers.length) throw new Error(`No offers from ${a.provider}; providers: ${Object.keys(prices.sources ?? {}).join(", ")}.`);
66
+ out.provider_offers = { columns: ["gpu", "provider", "tier", "price", "billing", "confirmed"], rows: offers.sort((x, y) => x[3] - y[3]) };
67
+ }
68
+ }
69
+ const n = a.changes ?? 20;
70
+ if (n) {
71
+ try {
72
+ const [head, ...body] = parseCsv(await getText(session, `${BASE}/changes.csv`, { maxBytes: 4 * 1024 * 1024 }));
73
+ const at = (k) => head.indexOf(k);
74
+ const rows = body.filter((r) => (!a.gpu || norm(r[at("gpu")]) === norm(out.gpu)) && byProvider(r[at("provider")])).map((r) => [r[at("date")], r[at("gpu")], r[at("provider")], r[at("tier")], Number(r[at("old")]) || null, Number(r[at("new")]) || null, pct(Number(r[at("new")]), Number(r[at("old")]))]);
75
+ out.changes = { note: "List-price changes the daily check found, newest first.", columns: ["date", "gpu", "provider", "tier", "old", "new", "change"], rows: rows.reverse().slice(0, n) };
76
+ } catch (err) { out.changes = { not_read: String(err.message).slice(0, 160) }; }
77
+ }
78
+ return {
79
+ ...out,
80
+ method: ref.method ?? null,
81
+ source: `${BASE}/reference and ${BASE}/prices (Cirrun GPU price index)`,
82
+ cite: ref.cite ?? "Cirrun GPU price index, https://cirrun.com/prices.html, CC BY 4.0",
83
+ limits: ["On-demand list prices from each provider's pricing page; spot, reserved and negotiated prices are lower, and availability is not shown.", "The index began on 2026-10-23, so its history is short.", "Not investment advice."],
84
+ };
85
+ }
package/src/news.mjs CHANGED
@@ -51,8 +51,9 @@ function refused(err) {
51
51
  return /429/.test(String(err?.message)) ? new Error("GDELT is limiting requests from this address (HTTP 429); it allows about one every five seconds. Ask again in a minute.") : err;
52
52
  }
53
53
 
54
- // attempts: GDELT requests to try before giving up (explain_move asks once, to stay quick).
55
- export async function news(session, args, { attempts = 2 } = {}) {
54
+ // attempts: GDELT requests to try before giving up, each after a longer wait (GDELT counts requests
55
+ // per address and often refuses one in a burst); explain_move asks once, to stay quick.
56
+ export async function news(session, args, { attempts = 3 } = {}) {
56
57
  const a = newsInput.parse(args);
57
58
  if (!a.query && !a.company) throw new Error("news needs query or company.");
58
59
  let who = null;
@@ -0,0 +1,62 @@
1
+ // The market at a glance: the main US indices and volatility, the eleven S&P sectors (by their SPDR
2
+ // funds), Treasury yields, the dollar, oil, gold and copper, bitcoin and ether, and the main markets
3
+ // abroad, each with its last close and its change over a day, a week, a month, three months, the
4
+ // year to date and a year. Prices are daily closes from price_history's sources; nothing is a quote.
5
+ import { z } from "zod";
6
+
7
+ import { priceHistory } from "./tools.mjs";
8
+
9
+ export const GROUPS = Object.freeze({
10
+ us: [["^GSPC", "S&P 500"], ["^IXIC", "Nasdaq Composite"], ["^NDX", "Nasdaq 100"], ["^DJI", "Dow Jones Industrial Average"], ["^RUT", "Russell 2000"], ["^VIX", "VIX (S&P 500 volatility)"]],
11
+ sectors: [["XLK", "Technology"], ["XLC", "Communication services"], ["XLY", "Consumer discretionary"], ["XLP", "Consumer staples"], ["XLF", "Financials"], ["XLV", "Health care"], ["XLI", "Industrials"], ["XLE", "Energy"], ["XLB", "Materials"], ["XLU", "Utilities"], ["XLRE", "Real estate"]],
12
+ rates: [["^IRX", "13-week Treasury bill yield"], ["^FVX", "5-year Treasury yield"], ["^TNX", "10-year Treasury yield"], ["^TYX", "30-year Treasury yield"]],
13
+ currencies: [["DX-Y.NYB", "US dollar index"], ["EURUSD=X", "EUR/USD"], ["USDJPY=X", "USD/JPY"], ["GBPUSD=X", "GBP/USD"]],
14
+ commodities: [["CL=F", "WTI crude oil"], ["BZ=F", "Brent crude oil"], ["NG=F", "US natural gas"], ["GC=F", "Gold"], ["SI=F", "Silver"], ["HG=F", "Copper"]],
15
+ crypto: [["BTC-USD", "Bitcoin"], ["ETH-USD", "Ether"]],
16
+ world: [["^STOXX50E", "Euro Stoxx 50"], ["^FTSE", "FTSE 100"], ["^GDAXI", "DAX"], ["^N225", "Nikkei 225"], ["^HSI", "Hang Seng"], ["000001.SS", "Shanghai Composite"], ["^BSESN", "Sensex"]],
17
+ });
18
+ // Yields are quoted in percent, so their changes are in percentage points, not percent of the level.
19
+ const YIELDS = new Set(GROUPS.rates.map(([s]) => s));
20
+
21
+ export const overviewInput = z.object({
22
+ groups: z.array(z.enum(Object.keys(GROUPS))).min(1).max(7).optional().describe(`Which groups to show (${Object.keys(GROUPS).join(", ")}); default all.`),
23
+ }).strict();
24
+
25
+ const round = (x, d = 4) => (Number.isFinite(x) ? Math.round(x * 10 ** d) / 10 ** d : null);
26
+
27
+ // Changes from closes (oldest first) to the last: n sessions back, and from the last close of the
28
+ // year before the last date's.
29
+ export function changes(dates, closes, { points = false } = {}) {
30
+ const last = closes.at(-1), at = (n) => closes.at(-1 - n);
31
+ const year = dates.at(-1).slice(0, 4), before = dates.findLastIndex((d) => d.slice(0, 4) < year);
32
+ const ch = (base) => (base == null || !Number.isFinite(base) ? null : points ? round(last - base, 3) : base ? round(last / base - 1) : null);
33
+ return { day: ch(at(1)), week: ch(at(5)), month: ch(at(21)), three_months: ch(at(63)), year_to_date: ch(before >= 0 ? closes[before] : null), year: ch(at(252)) };
34
+ }
35
+
36
+ export async function marketOverview(session, args) {
37
+ const a = overviewInput.parse(args ?? {});
38
+ const groups = a.groups ?? Object.keys(GROUPS);
39
+ const start = new Date(session.now() - 400 * 86400000).toISOString().slice(0, 10);
40
+ const sources = new Set(), missing = [];
41
+ const out = {};
42
+ await Promise.all(groups.map(async (g) => {
43
+ const rows = await Promise.all(GROUPS[g].map(async ([symbol, name]) => {
44
+ const p = await priceHistory(session, { symbol, start, adjusted: false }).catch((err) => { missing.push(`${symbol}: ${String(err.message).slice(0, 120)}`); return null; });
45
+ if (!p?.close?.length) return null;
46
+ if (p.source) sources.add(p.source);
47
+ const c = changes(p.dates, p.close, { points: YIELDS.has(symbol) });
48
+ return [symbol, name, p.dates.at(-1), round(p.close.at(-1), 4), c.day, c.week, c.month, c.three_months, c.year_to_date, c.year];
49
+ }));
50
+ out[g] = rows.filter(Boolean);
51
+ // Sectors are easiest read best to worst over the month.
52
+ if (g === "sectors") out[g].sort((x, y) => (y[6] ?? -Infinity) - (x[6] ?? -Infinity));
53
+ }));
54
+ return {
55
+ columns: ["symbol", "name", "date", "close", "day", "week", "month", "three_months", "year_to_date", "year"],
56
+ ...Object.fromEntries(groups.map((g) => [g, out[g]])),
57
+ ...(missing.length ? { missing } : {}),
58
+ meaning: "Changes are close to close over 1, 5, 21, 63 and 252 sessions and from the last close of the year before, as fractions (0.012 is 1.2%); for Treasury yields (quoted in percent) they are percentage points. Sectors are ranked by the month. Dates are each market's last close, so markets abroad and crypto can be a day apart.",
59
+ sources: [...sources],
60
+ limits: ["Daily closes, not live quotes; intraday moves are not shown.", "Index levels are price indices, without dividends; sector funds are SPDR ETFs.", "Not investment advice."],
61
+ };
62
+ }
@@ -0,0 +1,140 @@
1
+ // Shareholder returns: what a company has paid its owners, year by year, from its own annual XBRL.
2
+ // Dividends per share and in total, buybacks and new shares issued, the share count, and those set
3
+ // against earnings and free cash flow; with the last close, the dividend yield and the shareholder
4
+ // yield (dividends plus net buybacks over the market value). Each figure names its tag.
5
+ import { z } from "zod";
6
+
7
+ import { getJson, resolveEntity, tickerTable } from "./sources.mjs";
8
+ import { priceHistory } from "./tools.mjs";
9
+
10
+ export const payoutsInput = z.object({
11
+ company: z.string().min(1).max(120).describe("Ticker, CIK or name."),
12
+ years: z.number().int().min(2).max(20).optional().describe("Fiscal years to show, newest first; default 10."),
13
+ price: z.boolean().optional().describe("Add the dividend and shareholder yields at the last close; default true."),
14
+ }).strict();
15
+
16
+ const ANNUAL = new Set(["10-K", "10-K/A", "20-F", "20-F/A", "40-F", "40-F/A"]);
17
+ const days = (a, b) => (Date.parse(a) - Date.parse(b)) / 86400000;
18
+ // [tags, unit, flow]: a flow is a full fiscal year (a duration of about a year), the rest are balances.
19
+ export const MEASURES = {
20
+ dps_declared: [["CommonStockDividendsPerShareDeclared"], "USD/shares", true],
21
+ dps_paid: [["CommonStockDividendsPerShareCashPaid"], "USD/shares", true],
22
+ dividends_paid: [["PaymentsOfDividendsCommonStock", "PaymentsOfDividends", "PaymentsOfOrdinaryDividends"], "USD", true],
23
+ buybacks: [["PaymentsForRepurchaseOfCommonStock", "PaymentsForRepurchaseOfEquity"], "USD", true],
24
+ issued: [["ProceedsFromIssuanceOfCommonStock", "ProceedsFromStockOptionsExercised", "ProceedsFromIssuanceOfSharesUnderIncentiveAndShareBasedCompensationPlans"], "USD", true],
25
+ net_income: [["NetIncomeLoss", "ProfitLoss", "NetIncomeLossAvailableToCommonStockholdersBasic"], "USD", true],
26
+ cfo: [["NetCashProvidedByUsedInOperatingActivities", "NetCashProvidedByUsedInOperatingActivitiesContinuingOperations"], "USD", true],
27
+ capex: [["PaymentsToAcquirePropertyPlantAndEquipment", "PaymentsToAcquireProductiveAssets"], "USD", true],
28
+ diluted_shares: [["WeightedAverageNumberOfDilutedSharesOutstanding"], "shares", true],
29
+ };
30
+ const round = (x, d = 4) => (Number.isFinite(x) ? Math.round(x * 10 ** d) / 10 ** d : null);
31
+
32
+ // One measure's annual values by fiscal-year end (the latest filing's figure for each), from the tag
33
+ // the company reports most recently.
34
+ export function annualValues(facts, name) {
35
+ const [tags, unit, flow] = MEASURES[name];
36
+ let best = null;
37
+ for (const tag of tags) {
38
+ const rows = (facts[tag]?.units?.[unit] ?? []).filter((x) => ANNUAL.has(x.form) && (flow ? x.start && days(x.end, x.start) >= 350 && days(x.end, x.start) <= 380 : !x.start));
39
+ if (!rows.length) continue;
40
+ const by = new Map();
41
+ for (const r of rows) if (!by.has(r.end) || r.filed > by.get(r.end).filed) by.set(r.end, r);
42
+ const values = new Map([...by].sort((x, y) => x[0].localeCompare(y[0])).map(([end, r]) => [end, r.val]));
43
+ const last = [...values.keys()].at(-1);
44
+ if (!best || last > best.last || (last === best.last && values.size > best.values.size)) best = { tag, values, last };
45
+ }
46
+ return best && { tag: best.tag, values: best.values };
47
+ }
48
+
49
+ // Years in a row, back from the latest, that the yearly dividend per share rose (a cut or a flat year
50
+ // ends the run).
51
+ export function raisedYears(dps) {
52
+ const v = [...dps.values()];
53
+ let n = 0;
54
+ for (let i = v.length - 1; i > 0 && v[i] > v[i - 1]; i -= 1) n += 1;
55
+ return n;
56
+ }
57
+
58
+ // Dividends per share on today's shares: a year whose diluted share count jumps by a whole factor
59
+ // (2, 3, 4...) while the dividend per share falls by about the same is a split, and the years before
60
+ // it are divided by the factor. Older 10-Ks are not restated, so without this a split reads as a cut.
61
+ export function splitAdjusted(dps, shares) {
62
+ const ends = [...dps.keys()], out = new Map(dps), splits = [];
63
+ for (let i = ends.length - 1; i > 0; i -= 1) {
64
+ const s1 = shares?.get(ends[i]), s0 = shares?.get(ends[i - 1]), d1 = dps.get(ends[i]), d0 = dps.get(ends[i - 1]);
65
+ if (!s1 || !s0 || !d1 || !d0) continue;
66
+ const factor = Math.round(s1 / s0);
67
+ if (factor >= 2 && Math.abs(s1 / s0 / factor - 1) < 0.1 && d0 / d1 > factor * 0.6) {
68
+ splits.push({ fiscal_year_end: ends[i], factor });
69
+ for (const k of ends.slice(0, i)) out.set(k, out.get(k) / factor);
70
+ }
71
+ }
72
+ return { values: out, splits };
73
+ }
74
+
75
+ const cagr = (values, n) => {
76
+ const v = [...values.values()];
77
+ if (v.length < n + 1) return null;
78
+ const a = v.at(-1 - n), b = v.at(-1);
79
+ return a > 0 && b > 0 ? (b / a) ** (1 / n) - 1 : null;
80
+ };
81
+
82
+ // The dividend's record on split-adjusted figures: years raised in a row (within the years tagged in
83
+ // XBRL, which began in 2009-2011) and its growth.
84
+ function dividendRecord(dps, shares) {
85
+ const adj = splitAdjusted(dps, shares);
86
+ return {
87
+ raised_years_in_a_row: raisedYears(adj.values), years_tagged: adj.values.size,
88
+ growth_5y: round(cagr(adj.values, 5)), growth_10y: round(cagr(adj.values, 10)),
89
+ ...(adj.splits.length ? { splits_adjusted: adj.splits } : {}),
90
+ };
91
+ }
92
+
93
+ export async function shareholderReturns(session, args) {
94
+ const a = payoutsInput.parse(args);
95
+ const e = await resolveEntity(session, a.company);
96
+ const url = `https://data.sec.gov/api/xbrl/companyfacts/CIK${String(e.cik).padStart(10, "0")}.json`;
97
+ const cf = await getJson(session, url, { maxBytes: 96 * 1024 * 1024 });
98
+ const f = cf.facts?.["us-gaap"] ?? {};
99
+ const m = Object.fromEntries(Object.keys(MEASURES).map((k) => [k, annualValues(f, k)]));
100
+ const dps = m.dps_declared ?? m.dps_paid;
101
+ const ends = [...new Set(Object.values(m).filter(Boolean).flatMap((s) => [...s.values.keys()]))].sort();
102
+ if (!ends.length || !(m.dividends_paid || m.buybacks || dps)) throw new Error(`${cf.entityName ?? a.company} (CIK ${e.cik}) tags no annual dividends or buybacks in its XBRL.`);
103
+ const v = (k, end) => m[k]?.values.get(end) ?? null;
104
+ const rows = ends.slice(-(a.years ?? 10)).reverse().map((end, i, list) => {
105
+ const div = v("dividends_paid", end), back = v("buybacks", end), iss = v("issued", end), ni = v("net_income", end), cfo = v("cfo", end), capex = v("capex", end);
106
+ const fcf = cfo != null ? cfo - (capex ?? 0) : null, paid = (div ?? 0) + (back ?? 0);
107
+ const sh = v("diluted_shares", end), prior = list[i + 1] ? v("diluted_shares", list[i + 1]) : null;
108
+ return [end, dps?.values.get(end) ?? null, div, back, iss, round(ni ? (div ?? 0) / ni : null), round(fcf ? paid / fcf : null), sh, round(sh && prior ? sh / prior - 1 : null)];
109
+ });
110
+ let yields = null;
111
+ if (a.price !== false) {
112
+ const symbol = e.ticker ?? Object.values(await tickerTable(session)).find((t) => t.cik_str === e.cik)?.ticker;
113
+ const shares = (cf.facts?.dei?.EntityCommonStockSharesOutstanding?.units?.shares ?? []).sort((x, y) => (x.end < y.end ? 1 : -1))[0]?.val ?? null;
114
+ if (symbol && shares) {
115
+ const today = new Date(session.now()).toISOString().slice(0, 10);
116
+ const px = await priceHistory(session, { symbol, start: new Date(session.now() - 12 * 86400000).toISOString().slice(0, 10), end: today, adjusted: false }).catch(() => null);
117
+ const price = px?.close?.at(-1) ?? null, end = ends.at(-1);
118
+ if (price) {
119
+ const cap = price * shares, div = v("dividends_paid", end) ?? 0, net = (v("buybacks", end) ?? 0) - (v("issued", end) ?? 0);
120
+ yields = {
121
+ price, price_date: px?.dates?.at(-1) ?? px?.date?.at(-1) ?? null, market_value: round(cap, 0), fiscal_year_end: end,
122
+ dividend_yield: dps?.values.has(end) ? round(dps.values.get(end) / price) : round(div / cap),
123
+ net_buyback_yield: round(net / cap), shareholder_yield: round((div + net) / cap),
124
+ source: px?.source ?? null,
125
+ };
126
+ }
127
+ }
128
+ }
129
+ return {
130
+ company: { cik: e.cik, name: cf.entityName ?? e.name, ticker: e.ticker ?? null },
131
+ columns: ["fiscal_year_end", "dividend_per_share", "dividends_paid", "buybacks", "shares_issued_for_cash", "payout_ratio", "paid_out_of_free_cash_flow", "diluted_shares", "diluted_shares_change"],
132
+ rows,
133
+ dividends: dps ? { per_share_tag: dps.tag, ...dividendRecord(dps.values, m.diluted_shares?.values) } : { note: "No dividend per share tagged for full years." },
134
+ ...(yields ? { yields } : {}),
135
+ tags: Object.fromEntries(Object.entries(m).map(([k, s]) => [k, s?.tag ?? null])),
136
+ sources: [url, ...(yields?.source ? [yields.source] : [])],
137
+ meaning: "payout_ratio is common dividends paid over net income; paid_out_of_free_cash_flow is dividends plus buybacks over operating cash flow less capital spending (above 1 means more was paid out than the business made in cash). A falling diluted share count means buybacks outran new shares. shareholder_yield is the latest year's dividends plus buybacks less shares issued for cash, over the market value at the last close.",
138
+ limits: ["Figures are the full fiscal years in annual reports; the current year's payments are not in until its 10-K.", "Dividends per share are as declared (or paid, when only that is tagged) and are not adjusted for later splits; a split shows as a step in the share count.", "Buybacks are cash paid for repurchases, which can include shares withheld for taxes on stock awards.", "For banks, brokers and insurers, operating cash flow moves with loans, deposits and trading, so paid_out_of_free_cash_flow says little; use payout_ratio.", "raised_years_in_a_row counts only the years tagged in XBRL (filings since 2009-2011), so a longer record is cut at its start.", "Not investment advice."],
139
+ };
140
+ }
package/src/server.mjs CHANGED
@@ -27,6 +27,9 @@ import * as DB from "./credit.mjs";
27
27
  import * as OB from "./breadth.mjs";
28
28
  import * as FO from "./forensic.mjs";
29
29
  import * as FU from "./funding.mjs";
30
+ import * as CO from "./compute.mjs";
31
+ import * as PO from "./payouts.mjs";
32
+ import * as MO from "./overview.mjs";
30
33
  import * as FC from "./contracts.mjs";
31
34
  import * as PL from "./pipeline.mjs";
32
35
  import * as CL from "./letters.mjs";
@@ -56,7 +59,7 @@ import * as CR from "./crypto.mjs";
56
59
 
57
60
  export const SERVER_NAME = "canli-markets-mcp";
58
61
  export const SERVER_VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
59
- export const SERVER_INSTRUCTIONS = "Market data and research from the public sources themselves. Before answering with a company's reported figures, check them with verify_claims: each comes back confirmed, contradicted (with the filed figure and what the wrong one matches) or not reported. Across the market: screen_companies ranks every US-listed company on fundamentals, company_report gives one company's financials, segment_revenue its revenue by segment, product and region, valuation, risk, insiders and filings in one call, event_study measures price reactions to earnings or insider trades, mentions_trend counts a phrase in filings over time. Analyst work: financial_statements gives the income statement, balance sheet and cash flows exactly as filed (several years joined, restatements and splits shown, never mixed); filing_tables finds the table that holds a figure in a filing, or follows it through several years of filings (members, stores, ARPU, guidance tables); earnings_release reads a results press release: results, guidance as ranges, and last quarter's guidance against this quarter's results. Companies: company_profile, list_filings, then read_filing (section \"risk factors\", \"md&a\" and so on for a 10-K or 10-Q; document \"EX-99.1\" for an 8-K press release); search_filings finds words across all filings; executive_pay reads a proxy's pay-versus-performance table; company_events gives a company's 8-K news (or, by event, the whole market's: restatements, bankruptcies, cyber incidents); latest_filings is what has just been filed, across every company, by form and event; filing_diff shows what changed in a 10-K's risk factors (or any item) since the last one; filing_drift follows a section over several years (how much its wording moved, hedging, readability, and a theme such as \"supply chain\"), flagging the years and themes that moved most; news gives the world's press coverage of a company or topic, with its tone; explain_move puts a day's move beside the filings and headlines around it; portfolio_xray opens a portfolio's funds into the companies they hold; comps sets a company beside its industry peers; debt_profile gives its debt, when it falls due and what it costs; ownership_breadth counts the institutions holding it, quarter by quarter; forensic_scores runs the Beneish, Piotroski and Altman screens on its filings; reverse_dcf solves for the growth its share price implies; comment_letters gives what the SEC questioned in its filings and how it answered; federal_contracts what the US government paid it (USAspending); drug_pipeline a drug company's active trials by phase, its next readouts and its FDA approvals; bank_financials a US bank's quarterly call-report condition (uninsured deposits, bad loans, capital ratios) or the latest bank failures (FDIC). earnings_calendar gives a company's results dates and the next one estimated, or who reports in a window. Ownership: insider_trades (Form 4), planned_insider_sales (Form 144, filed before an insider sells) and fund_holdings (a manager's 13F); large_holders (13D/13G: 5 percent holders and activists), investment_advisers (Form ADV); funds: fund_portfolio (what a mutual fund or ETF holds, its flows and returns, from N-PORT); private markets: private_offerings (Form D raises by startups and funds) and ipo_pipeline (S-1/F-1 registrations and priced IPOs); positioning: short_interest, off_exchange_volume (FINRA dark pools), fails_to_deliver (SEC) and futures_positioning (CFTC Commitments of Traders). Rates and the economy: economic_calendar (when the data comes out), fomc (the Fed's decisions, votes and projections), treasury_yields, treasury_auctions, federal_debt, economic_series (FRED), world_economy (World Bank, every country) and economic_outlook (OECD projections). Beyond the US: european_filings (a company's ESEF annual report, the figures it tagged), reference_rates (SOFR, EFFR, €STR, ECB and Bank of England rates from the central banks, and about forty central banks' policy rates), prediction_markets (Polymarket and Kalshi prices for an outcome) and crypto_fundamentals (DefiLlama: a protocol's value locked, fees and revenue, a chain's value locked, a stablecoin's supply) and funding_rates (perpetual funding and its annualized carry, OKX). Options: options_chain, volatility_surface and futures_curve (Cboe delayed data). Prices: price_history for stocks in the US and abroad, currencies, futures, indices and crypto (no key needed; the user's Alpaca or Tiingo key is used for US stocks when set). Every result has its source URL; cite it.";
62
+ export const SERVER_INSTRUCTIONS = "Market data and research from the public sources themselves. Before answering with a company's reported figures, check them with verify_claims: each comes back confirmed, contradicted (with the filed figure and what the wrong one matches) or not reported. Across the market: screen_companies ranks every US-listed company on fundamentals, company_report gives one company's financials, segment_revenue its revenue by segment, product and region, valuation, risk, insiders and filings in one call, event_study measures price reactions to earnings or insider trades, mentions_trend counts a phrase in filings over time. Analyst work: financial_statements gives the income statement, balance sheet and cash flows exactly as filed (several years joined, restatements and splits shown, never mixed); filing_tables finds the table that holds a figure in a filing, or follows it through several years of filings (members, stores, ARPU, guidance tables); earnings_release reads a results press release: results, guidance as ranges, and last quarter's guidance against this quarter's results. Companies: company_profile, list_filings, then read_filing (section \"risk factors\", \"md&a\" and so on for a 10-K or 10-Q; document \"EX-99.1\" for an 8-K press release); search_filings finds words across all filings; executive_pay reads a proxy's pay-versus-performance table; company_events gives a company's 8-K news (or, by event, the whole market's: restatements, bankruptcies, cyber incidents); latest_filings is what has just been filed, across every company, by form and event; filing_diff shows what changed in a 10-K's risk factors (or any item) since the last one; filing_drift follows a section over several years (how much its wording moved, hedging, readability, and a theme such as \"supply chain\"), flagging the years and themes that moved most; news gives the world's press coverage of a company or topic, with its tone; explain_move puts a day's move beside the filings and headlines around it; portfolio_xray opens a portfolio's funds into the companies they hold; comps sets a company beside its industry peers; debt_profile gives its debt, when it falls due and what it costs; ownership_breadth counts the institutions holding it, quarter by quarter; forensic_scores runs the Beneish, Piotroski and Altman screens on its filings; reverse_dcf solves for the growth its share price implies; shareholder_returns what it has paid its owners each year (dividends, buybacks, the share count, payout ratios, its dividend record and yields); comment_letters gives what the SEC questioned in its filings and how it answered; federal_contracts what the US government paid it (USAspending); drug_pipeline a drug company's active trials by phase, its next readouts and its FDA approvals; bank_financials a US bank's quarterly call-report condition (uninsured deposits, bad loans, capital ratios) or the latest bank failures (FDIC). earnings_calendar gives a company's results dates and the next one estimated, or who reports in a window. Ownership: insider_trades (Form 4), planned_insider_sales (Form 144, filed before an insider sells) and fund_holdings (a manager's 13F); large_holders (13D/13G: 5 percent holders and activists), investment_advisers (Form ADV); funds: fund_portfolio (what a mutual fund or ETF holds, its flows and returns, from N-PORT); private markets: private_offerings (Form D raises by startups and funds) and ipo_pipeline (S-1/F-1 registrations and priced IPOs); positioning: short_interest, off_exchange_volume (FINRA dark pools), fails_to_deliver (SEC) and futures_positioning (CFTC Commitments of Traders). Rates and the economy: economic_calendar (when the data comes out), fomc (the Fed's decisions, votes and projections), treasury_yields, treasury_auctions, federal_debt, economic_series (FRED), world_economy (World Bank, every country) and economic_outlook (OECD projections). Beyond the US: european_filings (a company's ESEF annual report, the figures it tagged), reference_rates (SOFR, EFFR, €STR, ECB and Bank of England rates from the central banks, and about forty central banks' policy rates), prediction_markets (Polymarket and Kalshi prices for an outcome) and crypto_fundamentals (DefiLlama: a protocol's value locked, fees and revenue, a chain's value locked, a stablecoin's supply) and funding_rates (perpetual funding and its annualized carry, OKX). Compute: compute_prices (what an hour of each data-centre GPU costs on demand across the clouds, and the Cirrun GPU Index). Options: options_chain, volatility_surface and futures_curve (Cboe delayed data). The market at a glance: market_overview (US indices and the VIX, the eleven sectors ranked by the month, Treasury yields, the dollar, oil, gold, bitcoin and markets abroad, each over a day, a week, a month, three months, the year to date and a year). Prices: price_history for stocks in the US and abroad, currencies, futures, indices and crypto (no key needed; the user's Alpaca or Tiingo key is used for US stocks when set). Every result has its source URL; cite it.";
60
63
  export const SERVER_INFO = Object.freeze({
61
64
  name: SERVER_NAME, version: SERVER_VERSION, title: "Canli Markets",
62
65
  websiteUrl: "https://canlicapital.com/developers",
@@ -100,6 +103,9 @@ export const TOOL_DESCRIPTIONS = Object.freeze({
100
103
  filing_drift: "How a 10-K section's language drifts over several years: each year's change from the year before (the Lazy Prices measure), hedging and adverse words, readability, and how much it says about a theme you name and how that wording moves, with flags for the years and themes that moved most.",
101
104
  planned_insider_sales: "Insiders' planned sales from Form 144, filed before they sell: seller, role, shares, market value, share of the company, approximate sale date, how the shares were acquired and any 10b5-1 plan date, with totals by seller.",
102
105
  ipo_pipeline: "The IPO pipeline from EDGAR: companies that filed or amended a registration (S-1, F-1) in a window, labelled IPO candidate, SPAC, fund or already listed, and offerings priced (424B4) with price, shares and proceeds from the prospectus cover.",
106
+ shareholder_returns: "What a company has paid its shareholders, year by year from its annual XBRL: dividends per share (split-adjusted record: years raised in a row, 5- and 10-year growth) and in total, buybacks and shares issued for cash, the diluted share count and its change, the payout ratio and the share of free cash flow paid out, and at the last close the dividend yield and shareholder yield.",
107
+ market_overview: "The market at a glance, no key: the S&P 500, Nasdaq, Dow, Russell 2000 and VIX, the eleven S&P sectors (SPDR funds) ranked by the month, Treasury yields from 13 weeks to 30 years, the dollar and major currencies, oil, gas, gold, silver and copper, bitcoin and ether, and the main markets in Europe and Asia, each with its last close and its change over a day, a week, a month, three months, the year to date and a year.",
108
+ compute_prices: "Compute as a market, no key: the on-demand hourly price of each data-centre GPU (B200, H200, H100, A100, L40S...) across the clouds and GPU marketplaces, the cheapest, median and dearest offer with who offers it, every provider's offer for one model, recent list-price changes, and the Cirrun GPU Index (a daily chained index of those prices). Each price is the provider's own list price with the date it was confirmed.",
103
109
  funding_rates: "Crypto perpetual funding on OKX's USDT perpetuals, no key: each coin's latest eight-hour rate, the window's mean and its annualized carry (short the perpetual against long spot), the last week's, and the share of positive periods.",
104
110
  private_offerings: "Private markets from Form D, filed when startups and funds sell securities without registering: amount offered and sold, number of investors, minimum investment, exemption (Rule 506(b) or 506(c)), industry, fund type and the people behind it, for one issuer or across the market (newest first, filtered by industry or amount sold).",
105
111
  fails_to_deliver: "Shares that failed to settle, from the SEC's fails-to-deliver data (NSCC): a stock's outstanding fail balance on each settlement date over the latest half-month files, with prices and dollar values, or the securities with the largest fails across the market.",
@@ -166,6 +172,9 @@ export const TOOL_KEYWORDS = Object.freeze({
166
172
  filing_drift: "language drift text change lazy prices semantic drift wording change over years risk factor creep obfuscation hedging uncertainty readability fog theme trend supply chain tariff mentions over time",
167
173
  planned_insider_sales: "form 144 planned insider sales intends to sell insider selling notice proposed sale officer director executive selling 10b5-1 plan upcoming insider sales",
168
174
  ipo_pipeline: "ipo ipos initial public offering going public s-1 f-1 registration statement priced offering 424b4 spac blank check new listings ipo calendar pipeline upcoming ipos",
175
+ shareholder_returns: "dividend dividends yield payout ratio dividend history dividend growth aristocrat king raised increases buyback buybacks repurchase repurchases share count dilution shareholder yield capital return returned cash to shareholders per share",
176
+ market_overview: "market today how is the market doing stocks overview snapshot summary indices index s&p nasdaq dow russell vix sectors sector performance rotation best worst yields bonds treasury dollar oil gold bitcoin crypto world markets europe asia week month year to date ytd",
177
+ compute_prices: "gpu compute price prices cost per hour cloud rental rent h100 h200 b200 a100 l40s rtx 4090 nvidia ai training inference data centre center index commodity aws runpod lambda coreweave modal cheapest",
169
178
  funding_rates: "crypto funding rate perpetual perp swap carry basis cash and carry bitcoin ethereum longs pay shorts okx",
170
179
  private_offerings: "private placement placements form d raise raised fundraising funding round startup startups venture capital vc private equity hedge fund real estate syndication reg d 506b 506c accredited investors",
171
180
  fails_to_deliver: "fails to deliver ftd failed settlement naked short selling short squeeze reg sho threshold securities settlement fails nscc",
@@ -233,6 +242,9 @@ export const OUTPUT_SCHEMAS = Object.freeze({
233
242
  filing_drift: z.looseObject({ company: opt(z.looseObject({})), section: opt(z.looseObject({})), theme: opt(z.string()), flags: opt(z.array(z.string())), filings: opt(z.array(z.string())), not_read: opt(z.looseObject({})), ...table, ...common }).describe("rows are annual reports, newest first, in the order of columns; flags are plain sentences."),
234
243
  planned_insider_sales: z.looseObject({ company: opt(z.looseObject({})), notices: opt(z.number()), planned_value_total: opt(z.number()), by_seller: opt(z.looseObject({})), not_read: opt(z.array(z.unknown())), ...table, ...common }).describe("rows are Form 144 notices, newest first, in the order of columns."),
235
244
  ipo_pipeline: z.looseObject({ from: opt(z.string()), to: opt(z.string()), kind: opt(z.string()), registrations: opt(z.looseObject({})), counts: opt(z.looseObject({})), priced: opt(z.looseObject({})), ...common }).describe("registrations.rows and priced.rows, newest first, in the order of their columns."),
245
+ shareholder_returns: z.looseObject({ company: opt(z.looseObject({})), dividends: opt(z.looseObject({})), yields: opt(z.looseObject({})), tags: opt(z.looseObject({})), ...table, ...common }).describe("rows are fiscal years, newest first, in the order of columns; dividends is the split-adjusted record; yields use the last close."),
246
+ market_overview: z.looseObject({ columns: opt(z.array(z.string())), us: opt(z.array(z.unknown())), sectors: opt(z.array(z.unknown())), rates: opt(z.array(z.unknown())), currencies: opt(z.array(z.unknown())), commodities: opt(z.array(z.unknown())), crypto: opt(z.array(z.unknown())), world: opt(z.array(z.unknown())), ...common }).describe("each group's rows in the order of columns; changes are fractions, and percentage points for yields."),
247
+ compute_prices: z.looseObject({ date: opt(z.string()), index: opt(z.looseObject({})), gpu: opt(z.string()), summary: opt(z.looseObject({})), provider_offers: opt(z.looseObject({})), changes: opt(z.looseObject({})), cite: opt(z.string()), ...table, ...common }).describe("Without gpu, rows are GPU models with their cheapest, median and dearest offers; with gpu, rows are every provider's offer for it, cheapest first. Prices are USD per GPU-hour."),
236
248
  funding_rates: z.looseObject({ days: opt(z.number()), series: opt(z.looseObject({})), missing: opt(z.array(z.unknown())), ...table, ...common }).describe("rows are coins by annualized funding, in the order of columns."),
237
249
  private_offerings: z.looseObject({ issuer: opt(z.looseObject({})), people: opt(z.array(z.unknown())), ...table, ...common }).describe("rows are Form D offerings, newest first, in the order of columns; sold and offered are dollars (offered may be indefinite)."),
238
250
  fails_to_deliver: z.looseObject({ symbol: opt(z.string()), largest: opt(z.looseObject({})), files: opt(z.array(z.string())), ...table, ...common }).describe("rows are settlement dates with the outstanding fail balance (one stock) or securities ranked by their largest balance by value (the market)."),
@@ -260,7 +272,7 @@ export const OUTPUT_SCHEMAS = Object.freeze({
260
272
  price_history: z.looseObject({ symbol: opt(z.string()), change: opt(z.looseObject({})), dates: opt(z.array(z.string())), close: opt(z.array(z.number())), ...common }).describe("dates, open, high, low, close and volume are aligned columns, oldest first; change.total_return is the window's return with dividends reinvested (a fraction)."),
261
273
  });
262
274
 
263
- const TITLES = { european_filings: "European filings (ESEF)", reference_rates: "Reference and policy rates", prediction_markets: "Prediction markets", crypto_fundamentals: "Crypto fundamentals (DefiLlama)", verify_claims: "Verify claims against filings", financial_statements: "Financial statements as filed", filing_tables: "Tables in filings", earnings_release: "Earnings release and guidance", screen_companies: "Screen companies", company_report: "Company report", event_study: "Event study", mentions_trend: "Mentions over time", company_profile: "Company profile", list_filings: "List filings", read_filing: "Read a filing", search_filings: "Search filings", insider_trades: "Insider trades", fund_holdings: "Fund holdings (13F)", fund_portfolio: "Fund or ETF portfolio (N-PORT)", large_holders: "Large holders (13D/13G)", earnings_calendar: "Earnings calendar", investment_advisers: "Investment advisers (Form ADV)", company_events: "Company events (8-K)", latest_filings: "Latest filings (EDGAR feed)", filing_diff: "What changed in a filing", news: "News and tone (GDELT)", explain_move: "Explain a stock move", portfolio_xray: "Portfolio X-ray (look-through)", comps: "Comparable companies", debt_profile: "Debt profile", ownership_breadth: "Breadth of institutional ownership (13F)", forensic_scores: "Forensic accounting screens", funding_rates: "Crypto funding rates (OKX)", federal_contracts: "Federal contracts (USAspending)", drug_pipeline: "Drug pipeline and FDA approvals", comment_letters: "SEC comment letters", reverse_dcf: "Reverse DCF (implied growth)", bank_financials: "Bank financials (FDIC call reports)", filing_drift: "Language drift across filings", planned_insider_sales: "Planned insider sales (Form 144)", ipo_pipeline: "IPO pipeline (S-1, F-1, 424B4)", private_offerings: "Private offerings (Form D)", short_interest: "Short interest (FINRA)", off_exchange_volume: "Dark pool and OTC volume (FINRA)", fails_to_deliver: "Fails to deliver (SEC)", executive_pay: "Executive pay vs performance", fomc: "Fed rate decisions (FOMC)", economic_calendar: "Economic calendar", index_constituents: "Index constituents", options_chain: "Options chain (Cboe)", volatility_surface: "Volatility surface (Cboe)", futures_curve: "Futures curve (Cboe)", segment_revenue: "Revenue by segment", futures_positioning: "Futures positioning (CFTC COT)", treasury_yields: "Treasury yields", economic_series: "Economic series (FRED)", world_economy: "World economy (World Bank)", economic_outlook: "Economic outlook (OECD projections)", treasury_auctions: "Treasury auctions", federal_debt: "US federal debt", price_history: "Price history" };
275
+ const TITLES = { european_filings: "European filings (ESEF)", reference_rates: "Reference and policy rates", prediction_markets: "Prediction markets", crypto_fundamentals: "Crypto fundamentals (DefiLlama)", verify_claims: "Verify claims against filings", financial_statements: "Financial statements as filed", filing_tables: "Tables in filings", earnings_release: "Earnings release and guidance", screen_companies: "Screen companies", company_report: "Company report", event_study: "Event study", mentions_trend: "Mentions over time", company_profile: "Company profile", list_filings: "List filings", read_filing: "Read a filing", search_filings: "Search filings", insider_trades: "Insider trades", fund_holdings: "Fund holdings (13F)", fund_portfolio: "Fund or ETF portfolio (N-PORT)", large_holders: "Large holders (13D/13G)", earnings_calendar: "Earnings calendar", investment_advisers: "Investment advisers (Form ADV)", company_events: "Company events (8-K)", latest_filings: "Latest filings (EDGAR feed)", filing_diff: "What changed in a filing", news: "News and tone (GDELT)", explain_move: "Explain a stock move", portfolio_xray: "Portfolio X-ray (look-through)", comps: "Comparable companies", debt_profile: "Debt profile", ownership_breadth: "Breadth of institutional ownership (13F)", forensic_scores: "Forensic accounting screens", funding_rates: "Crypto funding rates (OKX)", compute_prices: "GPU compute prices (Cirrun index)", shareholder_returns: "Dividends and buybacks", market_overview: "Market overview", federal_contracts: "Federal contracts (USAspending)", drug_pipeline: "Drug pipeline and FDA approvals", comment_letters: "SEC comment letters", reverse_dcf: "Reverse DCF (implied growth)", bank_financials: "Bank financials (FDIC call reports)", filing_drift: "Language drift across filings", planned_insider_sales: "Planned insider sales (Form 144)", ipo_pipeline: "IPO pipeline (S-1, F-1, 424B4)", private_offerings: "Private offerings (Form D)", short_interest: "Short interest (FINRA)", off_exchange_volume: "Dark pool and OTC volume (FINRA)", fails_to_deliver: "Fails to deliver (SEC)", executive_pay: "Executive pay vs performance", fomc: "Fed rate decisions (FOMC)", economic_calendar: "Economic calendar", index_constituents: "Index constituents", options_chain: "Options chain (Cboe)", volatility_surface: "Volatility surface (Cboe)", futures_curve: "Futures curve (Cboe)", segment_revenue: "Revenue by segment", futures_positioning: "Futures positioning (CFTC COT)", treasury_yields: "Treasury yields", economic_series: "Economic series (FRED)", world_economy: "World economy (World Bank)", economic_outlook: "Economic outlook (OECD projections)", treasury_auctions: "Treasury auctions", federal_debt: "US federal debt", price_history: "Price history" };
264
276
  export const TOOLS = Object.freeze({
265
277
  verify_claims: [VC.verifyInput, VC.verifyClaims],
266
278
  european_filings: [EU.esefInput, EU.europeanFilings], reference_rates: [RT.ratesInput, RT.referenceRates], prediction_markets: [PM.predictionsInput, PM.predictionMarkets], crypto_fundamentals: [CR.cryptoInput, CR.cryptoFundamentals],
@@ -269,7 +281,7 @@ export const TOOLS = Object.freeze({
269
281
  company_profile: [T.profileInput, T.companyProfile], list_filings: [T.listInput, T.listFilings], read_filing: [T.readInput, T.readFiling],
270
282
  search_filings: [T.searchInput, T.searchFilings], insider_trades: [T.insiderInput, T.insiderTrades], fund_holdings: [T.holdingsInput, T.fundHoldings],
271
283
  treasury_yields: [T.treasuryInput, T.treasuryYields], economic_series: [T.fredInput, T.economicSeries], price_history: [T.priceInput, T.priceHistory],
272
- fund_portfolio: [F.portfolioInput, F.fundPortfolio], options_chain: [Opt.chainInput, Opt.optionsChain], volatility_surface: [Opt.surfaceInput, Opt.volatilitySurface], futures_curve: [Opt.curveInput, Opt.futuresCurve], segment_revenue: [Seg.segmentInput, Seg.segmentRevenue], index_constituents: [F.indexInput, F.indexConstituents], fomc: [CB.fomcInput, CB.fomc], economic_calendar: [Cal.calendarInput, Cal.economicCalendar], executive_pay: [Pay.payInput, Pay.executivePay], short_interest: [Pos.shortInput, Pos.shortInterest], off_exchange_volume: [Pos.offExchangeInput, Pos.offExchangeVolume], fails_to_deliver: [Pos.ftdInput, Pos.failsToDeliver], futures_positioning: [Pos.cotInput, Pos.futuresPositioning], private_offerings: [P.offeringsInput, P.privateOfferings], company_events: [E.eventsInput, E.companyEvents], latest_filings: [LF.latestInput, LF.latestFilings], filing_diff: [DF.diffInput, DF.filingDiff], news: [NW.newsInput, NW.news], explain_move: [EX.explainInput, EX.explainMove], portfolio_xray: [XR.xrayInput, XR.portfolioXray], comps: [CP.compsInput, CP.comps], debt_profile: [DB.debtInput, DB.debtProfile], ownership_breadth: [OB.ownershipInput, OB.ownershipBreadth], forensic_scores: [FO.forensicInput, FO.forensicScores], funding_rates: [FU.fundingInput, FU.fundingRates], federal_contracts: [FC.contractsInput, FC.federalContracts], drug_pipeline: [PL.pipelineInput, PL.drugPipeline], comment_letters: [CL.lettersInput, CL.commentLetters], reverse_dcf: [VA.reverseDcfInput, VA.reverseDcf], bank_financials: [BK.bankInput, BK.bankFinancials], filing_drift: [DR.driftInput, DR.filingDrift], planned_insider_sales: [PS.plannedInput, PS.plannedInsiderSales], ipo_pipeline: [IP.ipoInput, IP.ipoPipeline], large_holders: [O.holdersInput, O.largeHolders], earnings_calendar: [Earn.earningsInput, Earn.earningsCalendar], investment_advisers: [Adv.adviserInput, Adv.investmentAdvisers], world_economy: [G.worldInput, G.worldEconomy], economic_outlook: [G.outlookInput, G.economicOutlook], treasury_auctions: [G.auctionInput, G.treasuryAuctions], federal_debt: [G.debtInput, G.federalDebt],
284
+ fund_portfolio: [F.portfolioInput, F.fundPortfolio], options_chain: [Opt.chainInput, Opt.optionsChain], volatility_surface: [Opt.surfaceInput, Opt.volatilitySurface], futures_curve: [Opt.curveInput, Opt.futuresCurve], segment_revenue: [Seg.segmentInput, Seg.segmentRevenue], index_constituents: [F.indexInput, F.indexConstituents], fomc: [CB.fomcInput, CB.fomc], economic_calendar: [Cal.calendarInput, Cal.economicCalendar], executive_pay: [Pay.payInput, Pay.executivePay], short_interest: [Pos.shortInput, Pos.shortInterest], off_exchange_volume: [Pos.offExchangeInput, Pos.offExchangeVolume], fails_to_deliver: [Pos.ftdInput, Pos.failsToDeliver], futures_positioning: [Pos.cotInput, Pos.futuresPositioning], private_offerings: [P.offeringsInput, P.privateOfferings], company_events: [E.eventsInput, E.companyEvents], latest_filings: [LF.latestInput, LF.latestFilings], filing_diff: [DF.diffInput, DF.filingDiff], news: [NW.newsInput, NW.news], explain_move: [EX.explainInput, EX.explainMove], portfolio_xray: [XR.xrayInput, XR.portfolioXray], comps: [CP.compsInput, CP.comps], debt_profile: [DB.debtInput, DB.debtProfile], ownership_breadth: [OB.ownershipInput, OB.ownershipBreadth], forensic_scores: [FO.forensicInput, FO.forensicScores], funding_rates: [FU.fundingInput, FU.fundingRates], compute_prices: [CO.computeInput, CO.computePrices], shareholder_returns: [PO.payoutsInput, PO.shareholderReturns], market_overview: [MO.overviewInput, MO.marketOverview], federal_contracts: [FC.contractsInput, FC.federalContracts], drug_pipeline: [PL.pipelineInput, PL.drugPipeline], comment_letters: [CL.lettersInput, CL.commentLetters], reverse_dcf: [VA.reverseDcfInput, VA.reverseDcf], bank_financials: [BK.bankInput, BK.bankFinancials], filing_drift: [DR.driftInput, DR.filingDrift], planned_insider_sales: [PS.plannedInput, PS.plannedInsiderSales], ipo_pipeline: [IP.ipoInput, IP.ipoPipeline], large_holders: [O.holdersInput, O.largeHolders], earnings_calendar: [Earn.earningsInput, Earn.earningsCalendar], investment_advisers: [Adv.adviserInput, Adv.investmentAdvisers], world_economy: [G.worldInput, G.worldEconomy], economic_outlook: [G.outlookInput, G.economicOutlook], treasury_auctions: [G.auctionInput, G.treasuryAuctions], federal_debt: [G.debtInput, G.federalDebt],
273
285
  });
274
286
  const ANNOTATIONS = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true };
275
287