canli-markets-mcp 0.7.2 → 0.7.4

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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.4
4
+
5
+ - `treasury_yields` over several years lines each maturity up by name. The Treasury added the 4-month
6
+ bill in 2022 and the 6-week in 2025, so a range across those years had shorter rows under the newest
7
+ year's headings (a 2021 row's 10-year yield sat under "7 Yr"). Columns are now every maturity in any
8
+ year asked for, in maturity order, with null where a year did not publish one.
9
+
10
+ ## 0.7.3
11
+
12
+ - `news` and `explain_move` search for a company by its plain name when its SEC name ends in "/NEW"
13
+ (Costco Wholesale was searched as "Costco Wholesale CORP /NEW" and found nothing).
14
+
3
15
  ## 0.7.2
4
16
 
5
17
  - `market_overview`: the market at a glance in one call: US indices and the VIX, the eleven S&P sectors
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canli-markets-mcp",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
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/news.mjs CHANGED
@@ -22,7 +22,7 @@ export const newsInput = z.object({
22
22
 
23
23
  // A company's legal name as people write it: "NVIDIA CORP" -> "NVIDIA", "Apple Inc." -> "Apple".
24
24
  export function plainName(name) {
25
- return String(name).replace(/\s*\/[A-Z]{2}\/?\s*$/, "").replace(/\s*&\s*co\.?$/i, "").replace(/[,.]?\s+(inc|corp|corporation|co|company|ltd|plc|holdings|group|incorporated|lp|llc|sa|nv|ag|se)\.?$/i, "").replace(/[,.]?\s+(inc|corp|corporation|co|ltd|plc)\.?$/i, "").trim()
25
+ return String(name).replace(/\s*\/[A-Z]{2,3}\/?\s*$/, "").replace(/\s*&\s*co\.?$/i, "").replace(/[,.]?\s+(inc|corp|corporation|co|company|ltd|plc|holdings|group|incorporated|lp|llc|sa|nv|ag|se)\.?$/i, "").replace(/[,.]?\s+(inc|corp|corporation|co|ltd|plc)\.?$/i, "").trim()
26
26
  .replace(/\b([A-Z])([A-Z]+)\b/g, (w) => (w.length > 4 ? w[0] + w.slice(1).toLowerCase() : w));
27
27
  }
28
28
 
package/src/tools.mjs CHANGED
@@ -370,6 +370,30 @@ export const treasuryInput = z.object({
370
370
  start: DATE.optional().describe("A range instead of one day: first date."),
371
371
  end: DATE.optional().describe("Last date of the range; default today."),
372
372
  }).strict();
373
+ // A maturity in months ("1 Mo", "1.5 Month", "4 WEEKS BANK DISCOUNT", "10 Yr"), or null.
374
+ const tenorMonths = (h) => {
375
+ const m = /^(\d+(?:\.\d+)?)\s*(wk|weeks?|mo|months?|yr|years?)\b/i.exec(h);
376
+ if (!m) return null;
377
+ const u = m[2].toLowerCase();
378
+ return Number(m[1]) * (u.startsWith("w") ? 12 / 52 : u.startsWith("m") ? 1 : 12);
379
+ };
380
+ // Years of rates as one table. The Treasury adds and drops maturities (the 4-month bill in 2022,
381
+ // the 6-week in 2025), so each year's columns are matched by name; the newest year's order is kept
382
+ // and a maturity only older years have is placed by its length.
383
+ export function joinYears(parts) {
384
+ const names = parts[0].header.slice(1);
385
+ for (const p of parts.slice(1)) for (const h of p.header.slice(1)) {
386
+ if (names.includes(h)) continue;
387
+ const m = tenorMonths(h), at = m === null ? -1 : names.findIndex((x) => (tenorMonths(x) ?? Infinity) > m);
388
+ names.splice(at < 0 ? names.length : at, 0, h);
389
+ }
390
+ const rows = parts.flatMap((p) => {
391
+ const ix = names.map((h) => p.header.indexOf(h));
392
+ return p.rows.map((r) => [r[0], ...ix.map((i) => (i < 1 ? null : r[i] ?? null))]);
393
+ });
394
+ rows.sort((x, y) => x[0].localeCompare(y[0]));
395
+ return { names, rows };
396
+ }
373
397
  export async function treasuryYields(session, args) {
374
398
  const a = treasuryInput.parse(args);
375
399
  const curve = a.curve ?? "nominal";
@@ -379,17 +403,17 @@ export async function treasuryYields(session, args) {
379
403
  const years = [];
380
404
  for (let y = Number(last.slice(0, 4)); y >= Number(first.slice(0, 4)); y--) years.push(y);
381
405
  if (years.length > 40) throw new Error("treasury_yields reads at most 40 years at once.");
382
- let header = null, rows = [], url = null;
406
+ const parts = [];
383
407
  for (const y of years) {
384
408
  let t;
385
409
  try { t = await treasuryYear(session, y, curve); } catch (err) { if (err instanceof NotFound) continue; throw err; }
386
- header = header ?? t.header; url = url ?? t.url;
387
- rows = rows.concat(t.rows.filter((r) => r[0] >= first && r[0] <= last));
388
- if (!ranged && rows.length) break;
410
+ const kept = t.rows.filter((r) => r[0] >= first && r[0] <= last);
411
+ if (kept.length) parts.push({ ...t, rows: kept });
412
+ if (!ranged && kept.length) break;
389
413
  }
390
- if (!rows.length) throw new NotFound(`No ${curve} Treasury rates between ${first} and ${last}.`);
391
- rows.sort((x, y) => x[0].localeCompare(y[0]));
392
- const cols = ["date", ...header.slice(1)];
414
+ if (!parts.length) throw new NotFound(`No ${curve} Treasury rates between ${first} and ${last}.`);
415
+ const { names, rows } = joinYears(parts), url = parts[0].url;
416
+ const cols = ["date", ...names];
393
417
  const base = { curve, units: "percent a year", columns: cols, source: url, limits: ["U.S. Department of the Treasury daily rates, as published; par yields on a bond-equivalent basis.", "Not investment advice."] };
394
418
  if (!ranged) {
395
419
  const day = rows[rows.length - 1];