@finchagentic/mcp 4.6.0 → 4.6.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/README.md +7 -7
- package/package.json +5 -6
- package/dist/_http-cache.js +0 -96
- package/dist/agent-loop.js +0 -301
- package/dist/annotations.js +0 -122
- package/dist/cli.js +0 -1391
- package/dist/clink-input.js +0 -15
- package/dist/config.js +0 -132
- package/dist/convex.js +0 -175
- package/dist/dex-pair.js +0 -54
- package/dist/enrichment-router.js +0 -315
- package/dist/index.js +0 -258
- package/dist/llm.js +0 -298
- package/dist/local-memory-file.js +0 -147
- package/dist/local-memory.js +0 -135
- package/dist/local-vault.js +0 -454
- package/dist/output-schemas.js +0 -605
- package/dist/project.js +0 -36
- package/dist/prompts.js +0 -111
- package/dist/public-url.js +0 -107
- package/dist/resources.js +0 -111
- package/dist/server.js +0 -322
- package/dist/signal-gate.js +0 -57
- package/dist/token-decimals.js +0 -26
- package/dist/token-gate.js +0 -88
- package/dist/tool-filter.js +0 -53
- package/dist/tools/_solidity-scan.js +0 -313
- package/dist/tools/agents.js +0 -441
- package/dist/tools/automation.js +0 -354
- package/dist/tools/base-mcp.js +0 -466
- package/dist/tools/base.js +0 -283
- package/dist/tools/chronicle.js +0 -268
- package/dist/tools/coder.js +0 -94
- package/dist/tools/deep-research.js +0 -1421
- package/dist/tools/defi.js +0 -292
- package/dist/tools/equity.js +0 -372
- package/dist/tools/events.js +0 -182
- package/dist/tools/github.js +0 -564
- package/dist/tools/insider.js +0 -264
- package/dist/tools/insight.js +0 -630
- package/dist/tools/market.js +0 -555
- package/dist/tools/memory.js +0 -1044
- package/dist/tools/miroshark.js +0 -350
- package/dist/tools/monitor.js +0 -319
- package/dist/tools/os.js +0 -236
- package/dist/tools/packets.js +0 -296
- package/dist/tools/research-chain.js +0 -226
- package/dist/tools/research-compare.js +0 -280
- package/dist/tools/research.js +0 -188
- package/dist/tools/rh-bridge.js +0 -148
- package/dist/tools/rh-mcp.js +0 -1448
- package/dist/tools/rh-orders.js +0 -556
- package/dist/tools/scanner.js +0 -564
- package/dist/tools/stake.js +0 -369
- package/dist/tools/vault.js +0 -1020
- package/dist/tools/wallet.js +0 -200
- package/dist/types.js +0 -2
- package/dist/wallet.js +0 -372
package/dist/tools/insider.js
DELETED
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Insider transactions from SEC Form 4 — parsed from the filings themselves.
|
|
3
|
-
//
|
|
4
|
-
// Two accuracy traps make naive versions of this actively misleading, and both
|
|
5
|
-
// are handled here:
|
|
6
|
-
//
|
|
7
|
-
// 1. A company's filing feed contains Form 4s where that company is the
|
|
8
|
-
// REPORTING OWNER of a stake in some other issuer. Those are not insider
|
|
9
|
-
// trades in the ticker you asked about, so every filing is checked against
|
|
10
|
-
// `issuerTradingSymbol` before it counts.
|
|
11
|
-
//
|
|
12
|
-
// 2. Most "insider selling" is transaction code F — shares withheld to cover
|
|
13
|
-
// tax on vesting RSUs. It is automatic, not a decision, and carries no
|
|
14
|
-
// signal. Only P (open-market purchase) and S (open-market sale) reflect a
|
|
15
|
-
// choice, so they are reported separately from everything else.
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.INSIDER_TOOLS = void 0;
|
|
18
|
-
exports.buildInsiderSummary = buildInsiderSummary;
|
|
19
|
-
exports.handleInsiderTool = handleInsiderTool;
|
|
20
|
-
const zod_1 = require("zod");
|
|
21
|
-
const SEC_UA = "Finch MCP research (contact: support@finchagentic.com)";
|
|
22
|
-
/** SEC Form 4 transaction codes. Only P and S are discretionary market trades. */
|
|
23
|
-
const TX_CODES = {
|
|
24
|
-
P: { label: "Open-market purchase", discretionary: true },
|
|
25
|
-
S: { label: "Open-market sale", discretionary: true },
|
|
26
|
-
A: { label: "Grant / award", discretionary: false },
|
|
27
|
-
M: { label: "Option exercise", discretionary: false },
|
|
28
|
-
F: { label: "Tax withholding on vesting", discretionary: false },
|
|
29
|
-
G: { label: "Gift", discretionary: false },
|
|
30
|
-
C: { label: "Conversion", discretionary: false },
|
|
31
|
-
X: { label: "Option exercise (in/out of money)", discretionary: false },
|
|
32
|
-
D: { label: "Disposition to issuer", discretionary: false },
|
|
33
|
-
};
|
|
34
|
-
exports.INSIDER_TOOLS = [
|
|
35
|
-
{
|
|
36
|
-
name: "stock_insider",
|
|
37
|
-
description: "Parse recent SEC Form 4 insider transactions for a US-listed company, straight from EDGAR. " +
|
|
38
|
-
"Separates DISCRETIONARY trades (open-market buys/sells — the ones that carry signal) from " +
|
|
39
|
-
"automatic ones (RSU tax withholding, grants, option exercises) that are routinely misreported " +
|
|
40
|
-
"as 'insider selling'. Also verifies each filing is about the ticker you asked for, since a " +
|
|
41
|
-
"company's feed includes its own stakes in other issuers. No API key needed.",
|
|
42
|
-
inputSchema: {
|
|
43
|
-
type: "object",
|
|
44
|
-
properties: {
|
|
45
|
-
ticker: { type: "string", description: "US ticker symbol, e.g. 'HOOD', 'NVDA'" },
|
|
46
|
-
limit: { type: "number", description: "How many recent Form 4 filings to parse (default 15, max 40)" },
|
|
47
|
-
},
|
|
48
|
-
required: ["ticker"],
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
];
|
|
52
|
-
const Schema = zod_1.z.object({
|
|
53
|
-
ticker: zod_1.z.string().min(1).max(10),
|
|
54
|
-
limit: zod_1.z.number().int().min(1).max(40).optional(),
|
|
55
|
-
});
|
|
56
|
-
// Pure map to the stock_insider structuredContent payload - aggregates the
|
|
57
|
-
// same discretionary/automatic split the text report shows.
|
|
58
|
-
function buildInsiderSummary(ticker, companyName, filingsParsed, skippedOtherIssuer, buys, sells, automatic) {
|
|
59
|
-
const sumV = (arr) => arr.reduce((s, t) => s + t.value, 0);
|
|
60
|
-
const sumS = (arr) => arr.reduce((s, t) => s + t.shares, 0);
|
|
61
|
-
const discretionary = [...buys, ...sells].sort((a, b) => b.date.localeCompare(a.date));
|
|
62
|
-
return {
|
|
63
|
-
ticker,
|
|
64
|
-
companyName,
|
|
65
|
-
filingsParsed,
|
|
66
|
-
skippedOtherIssuer,
|
|
67
|
-
buys: { count: buys.length, shares: sumS(buys), valueUsd: sumV(buys) },
|
|
68
|
-
sells: { count: sells.length, shares: sumS(sells), valueUsd: sumV(sells) },
|
|
69
|
-
netBuyValueUsd: sumV(buys) - sumV(sells),
|
|
70
|
-
automaticCount: automatic.length,
|
|
71
|
-
transactions: discretionary.slice(0, 12).map((t) => ({
|
|
72
|
-
date: t.date,
|
|
73
|
-
owner: t.owner,
|
|
74
|
-
role: t.role,
|
|
75
|
-
acquired: t.acquired,
|
|
76
|
-
planned: t.planned,
|
|
77
|
-
shares: t.shares,
|
|
78
|
-
priceUsd: t.price,
|
|
79
|
-
valueUsd: t.value,
|
|
80
|
-
heldAfter: t.heldAfter,
|
|
81
|
-
})),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
async function secFetch(url) {
|
|
85
|
-
try {
|
|
86
|
-
const res = await fetch(url, { headers: { "User-Agent": SEC_UA }, signal: AbortSignal.timeout(20000) });
|
|
87
|
-
if (!res.ok)
|
|
88
|
-
return null;
|
|
89
|
-
return await res.text();
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
const tag = (xml, t) => {
|
|
96
|
-
const m = xml.match(new RegExp(`<${t}>([^<]*)</${t}>`));
|
|
97
|
-
return m ? m[1].trim() : null;
|
|
98
|
-
};
|
|
99
|
-
/** Most Form 4 fields wrap their content in <value>, some do not. */
|
|
100
|
-
const tagVal = (xml, t) => {
|
|
101
|
-
const m = xml.match(new RegExp(`<${t}>\\s*<value>([^<]*)</value>`, "s"));
|
|
102
|
-
return m ? m[1].trim() : tag(xml, t);
|
|
103
|
-
};
|
|
104
|
-
function fmtUsd(v) {
|
|
105
|
-
const a = Math.abs(v);
|
|
106
|
-
if (a >= 1e9)
|
|
107
|
-
return `$${(a / 1e9).toFixed(2)}B`;
|
|
108
|
-
if (a >= 1e6)
|
|
109
|
-
return `$${(a / 1e6).toFixed(2)}M`;
|
|
110
|
-
if (a >= 1e3)
|
|
111
|
-
return `$${(a / 1e3).toFixed(0)}K`;
|
|
112
|
-
return `$${a.toFixed(0)}`;
|
|
113
|
-
}
|
|
114
|
-
async function handleInsiderTool(name, args) {
|
|
115
|
-
if (name !== "stock_insider")
|
|
116
|
-
return null;
|
|
117
|
-
const parsed = Schema.safeParse(args);
|
|
118
|
-
if (!parsed.success) {
|
|
119
|
-
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
120
|
-
}
|
|
121
|
-
const ticker = parsed.data.ticker.trim().toUpperCase();
|
|
122
|
-
const limit = parsed.data.limit ?? 15;
|
|
123
|
-
const tickersRaw = await secFetch("https://www.sec.gov/files/company_tickers.json");
|
|
124
|
-
if (!tickersRaw)
|
|
125
|
-
return { content: [{ type: "text", text: "SEC ticker index unavailable — try again shortly." }], isError: true };
|
|
126
|
-
const hit = Object.values(JSON.parse(tickersRaw)).find((x) => String(x.ticker).toUpperCase() === ticker);
|
|
127
|
-
if (!hit) {
|
|
128
|
-
return {
|
|
129
|
-
content: [{ type: "text", text: `No SEC filer found for **${ticker}**. US-listed SEC filers only.` }],
|
|
130
|
-
isError: true,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
const cik = String(hit.cik_str).padStart(10, "0");
|
|
134
|
-
const subsRaw = await secFetch(`https://data.sec.gov/submissions/CIK${cik}.json`);
|
|
135
|
-
if (!subsRaw)
|
|
136
|
-
return { content: [{ type: "text", text: `Could not load SEC filings for ${ticker}.` }], isError: true };
|
|
137
|
-
const subs = JSON.parse(subsRaw);
|
|
138
|
-
const r = subs.filings?.recent;
|
|
139
|
-
if (!r?.form)
|
|
140
|
-
return { content: [{ type: "text", text: `No filing index for ${ticker}.` }], isError: true };
|
|
141
|
-
const candidates = [];
|
|
142
|
-
for (let i = 0; i < r.form.length && candidates.length < limit; i++) {
|
|
143
|
-
if (r.form[i] !== "4")
|
|
144
|
-
continue;
|
|
145
|
-
candidates.push({
|
|
146
|
-
acc: String(r.accessionNumber[i]).replace(/-/g, ""),
|
|
147
|
-
// primaryDocument points at the XSL-rendered view; the raw XML sits
|
|
148
|
-
// beside it without that prefix.
|
|
149
|
-
doc: String(r.primaryDocument[i]).replace(/^xsl[^/]*\//, ""),
|
|
150
|
-
date: r.filingDate[i],
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
if (!candidates.length) {
|
|
154
|
-
return { content: [{ type: "text", text: `No Form 4 filings found for **${ticker}**.` }] };
|
|
155
|
-
}
|
|
156
|
-
const cikNum = String(Number(cik));
|
|
157
|
-
const txs = [];
|
|
158
|
-
let skippedOtherIssuer = 0;
|
|
159
|
-
const docs = await Promise.all(candidates.map((c) => secFetch(`https://www.sec.gov/Archives/edgar/data/${cikNum}/${c.acc}/${c.doc}`)));
|
|
160
|
-
docs.forEach((xml, idx) => {
|
|
161
|
-
if (!xml)
|
|
162
|
-
return;
|
|
163
|
-
// Guard 1: is this filing actually about the ticker we asked for?
|
|
164
|
-
const issuerSym = tag(xml, "issuerTradingSymbol")?.toUpperCase();
|
|
165
|
-
if (issuerSym && issuerSym !== ticker) {
|
|
166
|
-
skippedOtherIssuer++;
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
const owner = tag(xml, "rptOwnerName") ?? "(unnamed)";
|
|
170
|
-
// A 10b5-1 plan is scheduled in advance, so the sale date carries no view on
|
|
171
|
-
// the price — reporting it as a discretionary decision would misread it.
|
|
172
|
-
const planned = tag(xml, "aff10b5One") === "1" || /10b5-1/i.test(xml);
|
|
173
|
-
const officerTitle = tag(xml, "officerTitle");
|
|
174
|
-
const isDir = tag(xml, "isDirector") === "1";
|
|
175
|
-
const isTen = tag(xml, "isTenPercentOwner") === "1";
|
|
176
|
-
// `officerTitle` is present but empty for non-officers, and `??` only falls
|
|
177
|
-
// back on null/undefined — so directors and 10% owners rendered as "()",
|
|
178
|
-
// dropping the one field that says how much weight the trade carries.
|
|
179
|
-
const role = officerTitle?.trim() || (isDir ? "Director" : isTen ? "10% owner" : "Insider");
|
|
180
|
-
// A filing can carry several transactions; parse each block separately.
|
|
181
|
-
const blocks = xml.match(/<nonDerivativeTransaction>[\s\S]*?<\/nonDerivativeTransaction>/g) ?? [];
|
|
182
|
-
for (const b of blocks) {
|
|
183
|
-
const code = tag(b, "transactionCode") ?? "?";
|
|
184
|
-
const shares = Number(tagVal(b, "transactionShares") ?? 0);
|
|
185
|
-
const price = Number(tagVal(b, "transactionPricePerShare") ?? 0);
|
|
186
|
-
const acquired = (tagVal(b, "transactionAcquiredDisposedCode") ?? "") === "A";
|
|
187
|
-
const heldAfterRaw = tagVal(b, "sharesOwnedFollowingTransaction");
|
|
188
|
-
if (!shares)
|
|
189
|
-
continue;
|
|
190
|
-
txs.push({
|
|
191
|
-
owner,
|
|
192
|
-
role,
|
|
193
|
-
date: tagVal(b, "transactionDate") ?? candidates[idx].date,
|
|
194
|
-
code,
|
|
195
|
-
shares,
|
|
196
|
-
price,
|
|
197
|
-
value: shares * price,
|
|
198
|
-
heldAfter: heldAfterRaw ? Number(heldAfterRaw) : null,
|
|
199
|
-
acquired,
|
|
200
|
-
planned,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
if (!txs.length) {
|
|
205
|
-
return {
|
|
206
|
-
content: [{
|
|
207
|
-
type: "text",
|
|
208
|
-
text: `# ${subs.name} (${ticker}) — insider activity\n\n` +
|
|
209
|
-
`Parsed ${candidates.length} Form 4 filings but found no share transactions for ${ticker}` +
|
|
210
|
-
(skippedOtherIssuer ? `; ${skippedOtherIssuer} were about a different issuer.` : `.`),
|
|
211
|
-
}],
|
|
212
|
-
structuredContent: buildInsiderSummary(ticker, subs.name ?? null, candidates.length, skippedOtherIssuer, [], [], []),
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
txs.sort((a, b) => b.date.localeCompare(a.date));
|
|
216
|
-
const discretionary = txs.filter((t) => TX_CODES[t.code]?.discretionary);
|
|
217
|
-
const buys = discretionary.filter((t) => t.acquired);
|
|
218
|
-
const sells = discretionary.filter((t) => !t.acquired);
|
|
219
|
-
const automatic = txs.filter((t) => !TX_CODES[t.code]?.discretionary);
|
|
220
|
-
const sum = (arr) => arr.reduce((s, t) => s + t.value, 0);
|
|
221
|
-
const lines = [
|
|
222
|
-
`# ${subs.name} (${ticker}) — insider activity`,
|
|
223
|
-
``,
|
|
224
|
-
`Parsed **${candidates.length}** recent Form 4 filings from SEC EDGAR` +
|
|
225
|
-
(skippedOtherIssuer ? ` (${skippedOtherIssuer} skipped — filed about a different issuer)` : ""),
|
|
226
|
-
``,
|
|
227
|
-
`## Discretionary trades — the ones that carry signal`,
|
|
228
|
-
``,
|
|
229
|
-
`| | Count | Shares | Value |`,
|
|
230
|
-
`|---|---|---|---|`,
|
|
231
|
-
`| 🟢 Open-market **buys** | ${buys.length} | ${buys.reduce((s, t) => s + t.shares, 0).toLocaleString()} | ${fmtUsd(sum(buys))} |`,
|
|
232
|
-
`| 🔴 Open-market **sells** | ${sells.length} | ${sells.reduce((s, t) => s + t.shares, 0).toLocaleString()} | ${fmtUsd(sum(sells))} |`,
|
|
233
|
-
``,
|
|
234
|
-
];
|
|
235
|
-
if (discretionary.length) {
|
|
236
|
-
lines.push(`### Detail`, ``);
|
|
237
|
-
for (const t of discretionary.slice(0, 12)) {
|
|
238
|
-
lines.push(`- **${t.date}** · ${t.owner} _(${t.role})_ — ${t.acquired ? "🟢 bought" : "🔴 sold"} ` +
|
|
239
|
-
`${t.planned ? "[10b5-1 plan] " : ""}` +
|
|
240
|
-
`${t.shares.toLocaleString()} @ $${t.price.toFixed(2)} = **${fmtUsd(t.value)}**` +
|
|
241
|
-
(t.heldAfter ? ` · holds ${t.heldAfter.toLocaleString()} after` : ""));
|
|
242
|
-
}
|
|
243
|
-
lines.push(``);
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
lines.push(`_No open-market buys or sells in this window._`, ``);
|
|
247
|
-
}
|
|
248
|
-
if (automatic.length) {
|
|
249
|
-
const byCode = {};
|
|
250
|
-
for (const t of automatic) {
|
|
251
|
-
const k = t.code;
|
|
252
|
-
byCode[k] = { n: (byCode[k]?.n ?? 0) + 1, value: (byCode[k]?.value ?? 0) + t.value };
|
|
253
|
-
}
|
|
254
|
-
lines.push(`## Automatic / non-discretionary — **not** a trading signal`, ``, ...Object.entries(byCode).map(([code, v]) => `- **${code}** ${TX_CODES[code]?.label ?? "Other"} — ${v.n} transaction(s), ${fmtUsd(v.value)}`), ``, `These are grants vesting, options exercising, and shares withheld for tax. They happen on a ` +
|
|
255
|
-
`schedule, not on a view. Headlines that call code F "insider selling" are describing payroll.`, ``);
|
|
256
|
-
}
|
|
257
|
-
lines.push(`---`, ``, `## How to read this`, ``, `- **Open-market buys are the rarer, stronger signal.** Insiders sell for many reasons — ` +
|
|
258
|
-
`diversification, tax, a house. They buy for one.`, `- Weigh the trade against what the person still holds. Selling 5% of a stake differs from exiting.`, `- Check role: a CEO or CFO purchase reads differently from a director's.`, `- This window covers the last ${candidates.length} Form 4 filings only — it is not the full history, ` +
|
|
259
|
-
`and Form 4s are filed within two business days, so very recent activity may not appear yet.`, ``, `_Source: SEC EDGAR Form 4. Not investment advice._`);
|
|
260
|
-
return {
|
|
261
|
-
content: [{ type: "text", text: lines.join("\n") }],
|
|
262
|
-
structuredContent: buildInsiderSummary(ticker, subs.name ?? null, candidates.length, skippedOtherIssuer, buys, sells, automatic),
|
|
263
|
-
};
|
|
264
|
-
}
|