xstock-mcp 1.3.0 → 1.4.0

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/README.md CHANGED
@@ -30,6 +30,7 @@
30
30
  | `get_dividend_history` | 分红历史、股息率、除息日 |
31
31
  | `get_institutional_holders` | 前10大机构持仓比例及变动 |
32
32
  | `get_similar_stocks` | 同行业可比公司估值对比 |
33
+ | `get_short_interest` | 做空比例、空头回补天数、与上月对比 |
33
34
  | `get_stock_full_overview` | 复合工具:行情 + 基本面 + 评级 + 新闻,一次返回 |
34
35
  | `search_stock` | 按名称或代码搜索 |
35
36
 
package/dist/index.js CHANGED
@@ -368,6 +368,28 @@ async function fetchInsiderActivity(symbol) {
368
368
  sharesAfter: tx.shareholderAfter ?? null
369
369
  }));
370
370
  }
371
+ async function fetchShortInterest(symbol) {
372
+ process.stderr.write(`[yahoo] fetchShortInterest symbol=${symbol}
373
+ `);
374
+ const summary = await yf.quoteSummary(symbol, {
375
+ modules: ["defaultKeyStatistics"]
376
+ });
377
+ const stats = summary.defaultKeyStatistics;
378
+ const sharesShort = stats?.sharesShort != null ? Number(stats.sharesShort) : null;
379
+ const priorMonth = stats?.sharesShortPriorMonth != null ? Number(stats.sharesShortPriorMonth) : null;
380
+ const change = sharesShort !== null && priorMonth !== null && priorMonth !== 0 ? Math.round((sharesShort - priorMonth) / priorMonth * 1e4) / 100 : null;
381
+ return {
382
+ symbol,
383
+ sharesShort,
384
+ sharesShortPriorMonth: priorMonth,
385
+ shortRatio: stats?.shortRatio != null ? Number(stats.shortRatio) : null,
386
+ shortPercentOfFloat: stats?.shortPercentOfFloat != null ? Math.round(Number(stats.shortPercentOfFloat) * 1e4) / 100 : null,
387
+ floatShares: stats?.floatShares != null ? Number(stats.floatShares) : null,
388
+ sharesOutstanding: stats?.sharesOutstanding != null ? Number(stats.sharesOutstanding) : null,
389
+ settlementDate: stats?.dateShortInterest instanceof Date ? stats.dateShortInterest.toISOString().slice(0, 10) : null,
390
+ changeFromPriorMonth: change
391
+ };
392
+ }
371
393
  function mapQuoteToMover(q) {
372
394
  return {
373
395
  symbol: String(q.symbol ?? ""),
@@ -1602,6 +1624,28 @@ var getSimilarStocksTool = {
1602
1624
  }
1603
1625
  }
1604
1626
  };
1627
+ var getShortInterestTool = {
1628
+ tool: {
1629
+ name: "get_short_interest",
1630
+ description: "\u83B7\u53D6\u7F8E\u80A1\u505A\u7A7A\u6570\u636E\uFF1A\u7A7A\u5934\u80A1\u6570\u3001\u505A\u7A7A\u5360\u6D41\u901A\u76D8\u6BD4\u4F8B\u3001\u7A7A\u5934\u56DE\u8865\u5929\u6570\uFF08Short Ratio\uFF09\u3001\u4E0E\u4E0A\u6708\u5BF9\u6BD4\u53D8\u5316\u3002\u7A7A\u5934\u56DE\u8865\u5929\u6570\u8D8A\u9AD8\u4EE3\u8868\u903C\u7A7A\u98CE\u9669\u8D8A\u5927\u3002",
1631
+ inputSchema: {
1632
+ type: "object",
1633
+ properties: {
1634
+ symbol: { type: "string", description: "\u7F8E\u80A1\u4EE3\u7801\uFF0C\u5982 GME\u3001TSLA\u3001NVDA" }
1635
+ },
1636
+ required: ["symbol"]
1637
+ }
1638
+ },
1639
+ handler: async (input) => {
1640
+ const parsed = SymbolInput3.safeParse(input);
1641
+ if (!parsed.success) return err(parsed.error.message);
1642
+ try {
1643
+ return ok(await fetchShortInterest(parsed.data.symbol));
1644
+ } catch (e) {
1645
+ return err(e.message);
1646
+ }
1647
+ }
1648
+ };
1605
1649
 
1606
1650
  // src/tools/index.ts
1607
1651
  var tools = [
@@ -1626,7 +1670,8 @@ var tools = [
1626
1670
  getMarketMoversTool,
1627
1671
  getDividendHistoryTool,
1628
1672
  getInstitutionalHoldersTool,
1629
- getSimilarStocksTool
1673
+ getSimilarStocksTool,
1674
+ getShortInterestTool
1630
1675
  ];
1631
1676
  var toolMap = new Map(
1632
1677
  tools.map((t) => [t.tool.name, t])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xstock-mcp",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Stock & Crypto analysis MCP Server",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/data/yahoo.ts CHANGED
@@ -340,6 +340,48 @@ export async function fetchInsiderActivity(symbol: string): Promise<InsiderTrans
340
340
  }));
341
341
  }
342
342
 
343
+ // ─── Short Interest ───────────────────────────────────────────────────────────
344
+
345
+ export interface ShortInterest {
346
+ symbol: string;
347
+ sharesShort: number | null;
348
+ sharesShortPriorMonth: number | null;
349
+ shortRatio: number | null;
350
+ shortPercentOfFloat: number | null;
351
+ floatShares: number | null;
352
+ sharesOutstanding: number | null;
353
+ settlementDate: string | null;
354
+ changeFromPriorMonth: number | null;
355
+ }
356
+
357
+ export async function fetchShortInterest(symbol: string): Promise<ShortInterest> {
358
+ process.stderr.write(`[yahoo] fetchShortInterest symbol=${symbol}\n`);
359
+ const summary = await yf.quoteSummary(symbol, {
360
+ modules: ["defaultKeyStatistics"] as never[],
361
+ });
362
+ const stats = summary.defaultKeyStatistics as Record<string, unknown> | undefined;
363
+
364
+ const sharesShort = stats?.sharesShort != null ? Number(stats.sharesShort) : null;
365
+ const priorMonth = stats?.sharesShortPriorMonth != null ? Number(stats.sharesShortPriorMonth) : null;
366
+ const change = sharesShort !== null && priorMonth !== null && priorMonth !== 0
367
+ ? Math.round(((sharesShort - priorMonth) / priorMonth) * 10000) / 100
368
+ : null;
369
+
370
+ return {
371
+ symbol,
372
+ sharesShort,
373
+ sharesShortPriorMonth: priorMonth,
374
+ shortRatio: stats?.shortRatio != null ? Number(stats.shortRatio) : null,
375
+ shortPercentOfFloat: stats?.shortPercentOfFloat != null
376
+ ? Math.round(Number(stats.shortPercentOfFloat) * 10000) / 100 : null,
377
+ floatShares: stats?.floatShares != null ? Number(stats.floatShares) : null,
378
+ sharesOutstanding: stats?.sharesOutstanding != null ? Number(stats.sharesOutstanding) : null,
379
+ settlementDate: stats?.dateShortInterest instanceof Date
380
+ ? stats.dateShortInterest.toISOString().slice(0, 10) : null,
381
+ changeFromPriorMonth: change,
382
+ };
383
+ }
384
+
343
385
  // ─── Market Movers ────────────────────────────────────────────────────────────
344
386
 
345
387
  export interface MoverItem {
@@ -5,7 +5,7 @@ import { searchStockTool } from "./search";
5
5
  import { getUsIndicesTool, getStockProfileTool, getEarningsCalendarTool, getUsSectorHeatmapTool } from "./us-market";
6
6
  import { getCryptoOverviewTool, getCryptoTopTool, getCryptoCatsTool, getCryptoFundingTool, getCryptoLiquidationTool } from "./crypto";
7
7
  import { getFinancialsTool, getAnalystRatingTool, getStockNewsTool, getInsiderActivityTool, getStockFullOverviewTool } from "./us-fundamentals";
8
- import { getMarketMoversTool, getDividendHistoryTool, getInstitutionalHoldersTool, getSimilarStocksTool } from "./us-market-b";
8
+ import { getMarketMoversTool, getDividendHistoryTool, getInstitutionalHoldersTool, getSimilarStocksTool, getShortInterestTool } from "./us-market-b";
9
9
 
10
10
  export * from "./types";
11
11
 
@@ -32,6 +32,7 @@ export const tools: ToolDef[] = [
32
32
  getDividendHistoryTool,
33
33
  getInstitutionalHoldersTool,
34
34
  getSimilarStocksTool,
35
+ getShortInterestTool,
35
36
  ];
36
37
 
37
38
  export const toolMap = new Map<string, ToolDef>(
@@ -4,6 +4,7 @@ import {
4
4
  fetchDividendHistory,
5
5
  fetchInstitutionalHolders,
6
6
  fetchSimilarStocks,
7
+ fetchShortInterest,
7
8
  } from "../data/yahoo";
8
9
  import type { ToolDef } from "./types";
9
10
  import { ok, err } from "./types";
@@ -126,3 +127,30 @@ export const getSimilarStocksTool: ToolDef = {
126
127
  }
127
128
  },
128
129
  };
130
+
131
+ // ─── get_short_interest ───────────────────────────────────────────────────────
132
+
133
+ export const getShortInterestTool: ToolDef = {
134
+ tool: {
135
+ name: "get_short_interest",
136
+ description:
137
+ "获取美股做空数据:空头股数、做空占流通盘比例、空头回补天数(Short Ratio)、与上月对比变化。" +
138
+ "空头回补天数越高代表逼空风险越大。",
139
+ inputSchema: {
140
+ type: "object",
141
+ properties: {
142
+ symbol: { type: "string", description: "美股代码,如 GME、TSLA、NVDA" },
143
+ },
144
+ required: ["symbol"],
145
+ },
146
+ },
147
+ handler: async (input) => {
148
+ const parsed = SymbolInput.safeParse(input);
149
+ if (!parsed.success) return err(parsed.error.message);
150
+ try {
151
+ return ok(await fetchShortInterest(parsed.data.symbol));
152
+ } catch (e) {
153
+ return err((e as Error).message);
154
+ }
155
+ },
156
+ };