@tensorfeed/mcp-server 1.27.0 → 1.29.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/CHANGELOG.md +22 -0
- package/dist/index.js +211 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the [TensorFeed.ai MCP server](https://github.com/RipperMercs/tensorfeed-mcp). Free tools work without configuration; premium tools require a bearer token via the `TENSORFEED_TOKEN` env var. Buy credits at [tensorfeed.ai/developers/agent-payments](https://tensorfeed.ai/developers/agent-payments).
|
|
4
4
|
|
|
5
|
+
## 1.29.0 - 2026-05-13
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `get_honeypot_iocs` (free): wraps `/api/security/iocs.json`. First-party honeypot indicators-of-compromise observed against TensorFeed.ai trap endpoints over the last 30 days. Filter by `type` (ip / ua / path), cap with `limit` (default 50, max 500). License CC0; pure first-party observation, not a re-export. Pairs with `get_ai_supply_chain_iocs` (GHSA-derived) for a two-layer defender feed.
|
|
9
|
+
- `check_afta_certification` (free): wraps `/api/afta-certify/check`. Runs 6 deterministic AFTA probes (well-known endpoints, x402 manifest, pricing transparency, free trial, federation membership, signed receipts) against any domain and returns score, verdict, and per-check pass/fail. Useful for federation prospects, agent governance audits, and demand-side trust signals.
|
|
10
|
+
- `get_agent_reputation_card` (free): wraps `/api/agents/reputation/{wallet}` and `/api/agents/reputation/by-token/{prefix}`. Returns the Agent Reputation Bureau card for an EVM wallet or tf_live_ token prefix: reliability, activity, spend, streak, composite trust score, trust grade (S/A/B/C/D), flags, claim status, leaderboard ranks. Cards rebuild daily 04:50 UTC. Returns a helpful 404 hint when no card exists yet.
|
|
11
|
+
|
|
12
|
+
### Added (MCP primitive)
|
|
13
|
+
- `tensorfeed://news/latest` resource: latest 20 AI news articles as a JSON resource. First TF use of the MCP `resources` primitive, exposed alongside the existing `get_ai_news` tool. Hosts that prefer data-shaped surfaces (Claude Desktop, some agent frameworks) can attach the news feed directly without going through tool-call decision loops.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- `registerTool` now auto-infers `PREMIUM_READ_TOOL` annotations (`idempotentHint: false`) for any tool whose description includes "Costs N credit". Previously all read tools defaulted to `idempotentHint: true`, which meant agents that retried paid calls on transient failure could double-charge users. Affects every paid read tool (premium_routing, pricing_series, benchmark_series, status_uptime, status_leaderboard, premium_agents_directory, whats_new, compare_models, provider_deepdive, cost_projection, news_search, probe_series_premium). Write tools (create_*_watch / delete_watch) keep their explicit CREATE_TOOL / DELETE_TOOL annotations and bypass the inference.
|
|
17
|
+
|
|
18
|
+
Tool count: 46 -> 49. First MCP resource live. Annotations now spec-honest about retry semantics.
|
|
19
|
+
|
|
20
|
+
## 1.28.0 - 2026-05-13
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- `get_ai_supply_chain_iocs` (free): wraps `/api/security/ai-supply-chain-iocs.json`. Daily-refreshed list of GitHub Security Advisory entries filtered by AI/ML/LLM/MCP keyword vocabulary across npm, PyPI, Go, Maven, and other package ecosystems. Each row carries the package name, ecosystem, GHSA advisory ID, severity, summary, vulnerable version range, publication date, and the canonical GHSA URL. Optional `severity` and `ecosystem` filters; `limit` caps result count (default 25, max 100). Republish posture: TensorFeed re-publishes already-public advisories; the linked GHSA record is authoritative. Useful for MCP-server reviewers, AI-tool maintainers, and supply-chain monitors that want a single AI-relevant feed rather than parsing all of GHSA.
|
|
24
|
+
|
|
25
|
+
Tool count: 45 -> 46. Lands the same security feed that caught the @mistralai npm worm on day 1, now reachable as an MCP tool for defenders running agentic supply-chain audits.
|
|
26
|
+
|
|
5
27
|
## 1.27.0 - 2026-05-09
|
|
6
28
|
|
|
7
29
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -78,6 +78,16 @@ const READ_TOOL = {
|
|
|
78
78
|
idempotentHint: true,
|
|
79
79
|
openWorldHint: true,
|
|
80
80
|
};
|
|
81
|
+
// Paid read tools: same as READ_TOOL but idempotentHint: false because each
|
|
82
|
+
// call burns a credit. Agents that retry on transient failure under
|
|
83
|
+
// READ_TOOL semantics would double-charge users on flaky networks. Applied
|
|
84
|
+
// automatically below when a tool's description includes "Costs N credit".
|
|
85
|
+
const PREMIUM_READ_TOOL = {
|
|
86
|
+
readOnlyHint: true,
|
|
87
|
+
destructiveHint: false,
|
|
88
|
+
idempotentHint: false,
|
|
89
|
+
openWorldHint: true,
|
|
90
|
+
};
|
|
81
91
|
const CREATE_TOOL = {
|
|
82
92
|
readOnlyHint: false,
|
|
83
93
|
destructiveHint: false,
|
|
@@ -90,15 +100,50 @@ const DELETE_TOOL = {
|
|
|
90
100
|
idempotentHint: true,
|
|
91
101
|
openWorldHint: true,
|
|
92
102
|
};
|
|
93
|
-
|
|
103
|
+
const PAID_TOOL_RE = /Costs \d+ credit/i;
|
|
104
|
+
function registerTool(name, description, paramsSchema, cb, annotations) {
|
|
105
|
+
// When no explicit annotation is passed, infer from the description.
|
|
106
|
+
// Free reads: READ_TOOL (idempotent). Paid reads (description mentions
|
|
107
|
+
// "Costs N credit"): PREMIUM_READ_TOOL (NOT idempotent, because each
|
|
108
|
+
// call burns a credit and naive retry would double-charge). Tools that
|
|
109
|
+
// mutate (create_*_watch / delete_watch) pass CREATE_TOOL / DELETE_TOOL
|
|
110
|
+
// explicitly and bypass this inference.
|
|
111
|
+
const finalAnnotations = annotations ?? (PAID_TOOL_RE.test(description) ? PREMIUM_READ_TOOL : READ_TOOL);
|
|
94
112
|
const wrapped = (async (args, extra) => {
|
|
95
113
|
const result = await cb(args, extra);
|
|
96
114
|
return sanitizeToolResponse(result);
|
|
97
115
|
});
|
|
98
116
|
// Direct dispatch to the SDK's underlying registration. Do not call
|
|
99
117
|
// registerTool() here or it will recurse forever.
|
|
100
|
-
return server.tool(name, description, paramsSchema,
|
|
118
|
+
return server.tool(name, description, paramsSchema, finalAnnotations, wrapped);
|
|
101
119
|
}
|
|
120
|
+
// ── Resource: tensorfeed://news (latest 20 AI articles) ─────────────
|
|
121
|
+
// MCP's `resources` primitive is a parallel surface to tools. Hosts can
|
|
122
|
+
// list resources, subscribe to them, and fetch them by URI without going
|
|
123
|
+
// through the tool-call decision loop. We expose the latest news as a
|
|
124
|
+
// resource AS WELL AS the get_ai_news tool, so clients that prefer
|
|
125
|
+
// data-shaped surfaces (Claude Desktop, some agent frameworks) can attach
|
|
126
|
+
// the news feed directly. Same upstream endpoint; different MCP primitive.
|
|
127
|
+
server.registerResource('latest_news', 'tensorfeed://news/latest', {
|
|
128
|
+
description: 'Latest 20 AI news articles aggregated by TensorFeed.ai. Refreshed every ~10 minutes from 15+ sources (Anthropic, OpenAI, Google, TechCrunch, The Verge, arXiv, and more). Each row carries title, source, URL, snippet, categories, and publication timestamp. JSON payload. Refresh on `resources/read`; no subscription required.',
|
|
129
|
+
mimeType: 'application/json',
|
|
130
|
+
}, async (uri) => {
|
|
131
|
+
const data = (await fetchJSON('/news?limit=20'));
|
|
132
|
+
return {
|
|
133
|
+
contents: [
|
|
134
|
+
{
|
|
135
|
+
uri: uri.toString(),
|
|
136
|
+
mimeType: 'application/json',
|
|
137
|
+
text: JSON.stringify({
|
|
138
|
+
source: 'TensorFeed.ai',
|
|
139
|
+
fetched_at: new Date().toISOString(),
|
|
140
|
+
count: data.articles.length,
|
|
141
|
+
articles: data.articles,
|
|
142
|
+
}, null, 2),
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
});
|
|
102
147
|
// ── Tool: get_ai_news ───────────────────────────────────────────────
|
|
103
148
|
registerTool('get_ai_news', 'Get the latest AI news articles from TensorFeed.ai. Aggregates from 15+ sources including Anthropic, OpenAI, Google, TechCrunch, The Verge, arXiv, and more.', {
|
|
104
149
|
category: z.string().optional().describe('Filter by category (e.g. "anthropic", "openai", "research", "tools")'),
|
|
@@ -267,6 +312,170 @@ registerTool('get_agent_activity', 'Get live AI bot traffic on TensorFeed.ai. Re
|
|
|
267
312
|
],
|
|
268
313
|
};
|
|
269
314
|
});
|
|
315
|
+
// ── Tool: get_ai_supply_chain_iocs ──────────────────────────────────
|
|
316
|
+
registerTool('get_ai_supply_chain_iocs', 'Get the latest AI/ML supply-chain indicators-of-compromise from the TensorFeed defender feed. Daily-refreshed list of GitHub Security Advisory entries filtered by AI/ML/LLM/MCP keyword vocabulary across npm, PyPI, Go, Maven, and other ecosystems. Each entry includes the package name, ecosystem, GHSA advisory ID, severity, summary, vulnerable version range, publication date, and a link to the authoritative GHSA record. Pure data feed: TensorFeed re-publishes already-public advisories, does not detect malware, and does not attribute it. Always treat the linked GHSA record as authoritative. Useful for AI-tool maintainers, MCP-server reviewers, and supply-chain monitors that want a single feed of AI-relevant advisories rather than parsing all of GHSA.', {
|
|
317
|
+
severity: z
|
|
318
|
+
.enum(['critical', 'high', 'moderate', 'low'])
|
|
319
|
+
.optional()
|
|
320
|
+
.describe('Filter to a single GHSA severity tier'),
|
|
321
|
+
ecosystem: z
|
|
322
|
+
.string()
|
|
323
|
+
.optional()
|
|
324
|
+
.describe('Filter to a single package ecosystem (e.g. "npm", "pypi", "go", "maven")'),
|
|
325
|
+
limit: z
|
|
326
|
+
.number()
|
|
327
|
+
.min(1)
|
|
328
|
+
.max(100)
|
|
329
|
+
.optional()
|
|
330
|
+
.describe('Maximum number of advisories to return (default 25, max 100)'),
|
|
331
|
+
}, async ({ severity, ecosystem, limit }) => {
|
|
332
|
+
const data = (await fetchJSON('/security/ai-supply-chain-iocs.json'));
|
|
333
|
+
const sevWanted = severity?.toLowerCase();
|
|
334
|
+
const ecoWanted = ecosystem?.toLowerCase();
|
|
335
|
+
const filtered = data.entries.filter((e) => {
|
|
336
|
+
if (sevWanted && e.severity.toLowerCase() !== sevWanted)
|
|
337
|
+
return false;
|
|
338
|
+
if (ecoWanted && e.package.ecosystem.toLowerCase() !== ecoWanted)
|
|
339
|
+
return false;
|
|
340
|
+
return true;
|
|
341
|
+
});
|
|
342
|
+
const cap = Math.min(limit ?? 25, 100);
|
|
343
|
+
const rows = filtered.slice(0, cap);
|
|
344
|
+
if (rows.length === 0) {
|
|
345
|
+
return {
|
|
346
|
+
content: [
|
|
347
|
+
{
|
|
348
|
+
type: 'text',
|
|
349
|
+
text: `No AI supply-chain IOCs matched (total in feed: ${data.total}, generated ${data.generated_at}).`,
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
const body = rows
|
|
355
|
+
.map((e, i) => {
|
|
356
|
+
const kw = e.ai_relevance?.matched_keywords?.length
|
|
357
|
+
? ` [matched: ${e.ai_relevance.matched_keywords.join(', ')}]`
|
|
358
|
+
: '';
|
|
359
|
+
return [
|
|
360
|
+
`${i + 1}. ${e.package.ecosystem}: ${e.package.name} (${e.severity.toUpperCase()})`,
|
|
361
|
+
` ${e.summary}`,
|
|
362
|
+
` GHSA: ${e.advisory_id} ${e.url}`,
|
|
363
|
+
` Affected: ${e.vulnerable_version_range} Published: ${e.published_at}${kw}`,
|
|
364
|
+
].join('\n');
|
|
365
|
+
})
|
|
366
|
+
.join('\n\n');
|
|
367
|
+
const header = `AI supply-chain IOCs (${rows.length} of ${filtered.length} matched, ${data.total} in feed, generated ${data.generated_at}):`;
|
|
368
|
+
const footer = `\n\nPosture: ${data.posture}`;
|
|
369
|
+
return { content: [{ type: 'text', text: `${header}\n\n${body}${footer}` }] };
|
|
370
|
+
});
|
|
371
|
+
// ── Tool: get_honeypot_iocs ─────────────────────────────────────────
|
|
372
|
+
registerTool('get_honeypot_iocs', 'Get the TensorFeed honeypot indicators-of-compromise feed. Returns attacker IP addresses, user agents, and target paths observed against TensorFeed.ai trap endpoints over the last 30 days. Pure first-party observation; not a re-export of upstream threat intel. License CC0; downstream defenders may ingest and pre-block at their edge. Each row: ioc value, ioc type (ip / ua / path), first_seen, last_seen, hit_count. Useful as a low-noise honeypot signal layered on top of public IOC feeds.', {
|
|
373
|
+
type: z
|
|
374
|
+
.enum(['ip', 'ua', 'path'])
|
|
375
|
+
.optional()
|
|
376
|
+
.describe('Filter to one IOC type. Omit to return all types.'),
|
|
377
|
+
limit: z
|
|
378
|
+
.number()
|
|
379
|
+
.min(1)
|
|
380
|
+
.max(500)
|
|
381
|
+
.optional()
|
|
382
|
+
.describe('Maximum number of IOCs to return (default 50, max 500)'),
|
|
383
|
+
}, async ({ type, limit }) => {
|
|
384
|
+
const data = (await fetchJSON('/security/iocs.json'));
|
|
385
|
+
const typeWanted = type?.toLowerCase();
|
|
386
|
+
const filtered = data.iocs.filter((e) => !typeWanted || e.type.toLowerCase() === typeWanted);
|
|
387
|
+
const cap = Math.min(limit ?? 50, 500);
|
|
388
|
+
const rows = filtered.slice(0, cap);
|
|
389
|
+
if (rows.length === 0) {
|
|
390
|
+
return {
|
|
391
|
+
content: [
|
|
392
|
+
{
|
|
393
|
+
type: 'text',
|
|
394
|
+
text: `No honeypot IOCs matched (total in feed: ${data.total_iocs}, window ${data.window_hours}h, generated ${data.generated_at}).`,
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
const body = rows
|
|
400
|
+
.map((e, i) => `${i + 1}. [${e.type}] ${e.ioc}\n hits: ${e.hit_count} first: ${e.first_seen} last: ${e.last_seen}`)
|
|
401
|
+
.join('\n');
|
|
402
|
+
const header = `TensorFeed honeypot IOCs (${rows.length} of ${filtered.length} matched, ${data.total_iocs} in feed, ${data.window_hours}h window, generated ${data.generated_at}):`;
|
|
403
|
+
const footer = `\n\nLicense: ${data.policy.license}. ${data.policy.description}`;
|
|
404
|
+
return { content: [{ type: 'text', text: `${header}\n\n${body}${footer}` }] };
|
|
405
|
+
});
|
|
406
|
+
// ── Tool: check_afta_certification ──────────────────────────────────
|
|
407
|
+
registerTool('check_afta_certification', 'Check whether a domain is AFTA-certified (Agent Fair-Trade Agreement). AFTA is an open standard: cryptographically-signed receipts, transparent pricing, no-charge guarantees for failed responses, on-chain settlement, and federated trust between participating sites. The check runs 6 deterministic probes against the target domain (e.g. tensorfeed.ai, terminalfeed.io): well-known endpoint, receipts endpoint, x402 payment manifest, pricing transparency, free trial availability, federation membership. Returns score, verdict, and which checks passed/failed.', {
|
|
408
|
+
domain: z
|
|
409
|
+
.string()
|
|
410
|
+
.describe('Domain to certify (e.g. "tensorfeed.ai", "terminalfeed.io"). Bare host, no protocol.'),
|
|
411
|
+
}, async ({ domain }) => {
|
|
412
|
+
const data = (await fetchJSON(`/afta-certify/check?domain=${encodeURIComponent(domain)}`));
|
|
413
|
+
const lines = data.checks.map((c) => ` ${c.passed ? 'PASS' : 'FAIL'} ${c.name}${c.details ? ` (${c.details})` : ''}`);
|
|
414
|
+
const certified = data.afta_certified ? 'AFTA-CERTIFIED' : 'NOT certified';
|
|
415
|
+
const next = data.next_step ? `\nNext step: ${data.next_step}` : '';
|
|
416
|
+
return {
|
|
417
|
+
content: [
|
|
418
|
+
{
|
|
419
|
+
type: 'text',
|
|
420
|
+
text: `Domain: ${data.domain}\nScore: ${data.score}/${data.max}\nVerdict: ${data.verdict} (${certified})\nChecked: ${data.checked_at}\n\nChecks:\n${lines.join('\n')}${next}`,
|
|
421
|
+
},
|
|
422
|
+
],
|
|
423
|
+
};
|
|
424
|
+
});
|
|
425
|
+
// ── Tool: get_agent_reputation_card ─────────────────────────────────
|
|
426
|
+
registerTool('get_agent_reputation_card', 'Get a TensorFeed Agent Reputation Bureau card for an EVM wallet address or a tf_live_ token prefix. Cards rebuild daily at 04:50 UTC from on-chain receipts plus TF telemetry. Includes reliability score, paid-call activity, total USDC spent, longest paid streak, composite trust score (0-1), trust grade (S/A/B/C/D), flags (sybil_risk, abuse, paid_free_ratio_low), operator-claim status (wallet -> display name signed by EOA), and per-metric leaderboard ranks. Returns null/404 for unknown agents (TF only builds cards for wallets observed in production). Useful for assessing third-party agent trustworthiness before delegating high-value work.', {
|
|
427
|
+
wallet: z
|
|
428
|
+
.string()
|
|
429
|
+
.optional()
|
|
430
|
+
.describe('EVM wallet address (0x... 42 chars). Provide either this OR token_prefix.'),
|
|
431
|
+
token_prefix: z
|
|
432
|
+
.string()
|
|
433
|
+
.optional()
|
|
434
|
+
.describe('First N chars of an agent\'s tf_live_ bearer token. Useful when the agent has not signed an operator-claim yet.'),
|
|
435
|
+
}, async ({ wallet, token_prefix }) => {
|
|
436
|
+
if (!wallet && !token_prefix) {
|
|
437
|
+
return {
|
|
438
|
+
content: [
|
|
439
|
+
{
|
|
440
|
+
type: 'text',
|
|
441
|
+
text: 'Provide either `wallet` (0x... EVM address) or `token_prefix` (tf_live_... token prefix). Both are accepted, one is required.',
|
|
442
|
+
},
|
|
443
|
+
],
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
const path = wallet
|
|
447
|
+
? `/agents/reputation/${encodeURIComponent(wallet)}`
|
|
448
|
+
: `/agents/reputation/by-token/${encodeURIComponent(token_prefix)}`;
|
|
449
|
+
let data;
|
|
450
|
+
try {
|
|
451
|
+
data = await fetchJSON(path);
|
|
452
|
+
}
|
|
453
|
+
catch (e) {
|
|
454
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
455
|
+
if (msg.includes('404') || msg.toLowerCase().includes('not_found')) {
|
|
456
|
+
return {
|
|
457
|
+
content: [
|
|
458
|
+
{
|
|
459
|
+
type: 'text',
|
|
460
|
+
text: `No reputation card for ${wallet ?? token_prefix}. TF only builds cards for wallets observed in production; if this is a real agent that has paid TF before, it should appear in the next daily rebuild (04:50 UTC).`,
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
throw e;
|
|
466
|
+
}
|
|
467
|
+
// The card schema is rich; render the highlights deterministically and
|
|
468
|
+
// include the raw JSON so the agent can reason over any field it needs.
|
|
469
|
+
const card = data.card ?? data;
|
|
470
|
+
return {
|
|
471
|
+
content: [
|
|
472
|
+
{
|
|
473
|
+
type: 'text',
|
|
474
|
+
text: `Agent reputation card for ${wallet ?? `token_prefix=${token_prefix}`}:\n\n${JSON.stringify(card, null, 2)}`,
|
|
475
|
+
},
|
|
476
|
+
],
|
|
477
|
+
};
|
|
478
|
+
});
|
|
270
479
|
// ════════════════════════════════════════════════════════════════════
|
|
271
480
|
// PREMIUM TOOLS (require TENSORFEED_TOKEN env var, paid in USDC on Base)
|
|
272
481
|
// ════════════════════════════════════════════════════════════════════
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tensorfeed/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.29.0",
|
|
4
4
|
"mcpName": "ai.tensorfeed/mcp-server",
|
|
5
5
|
"description": "MCP server for TensorFeed.ai - AI news, status, model data, and premium endpoints (routing, cost projection, forecast, history series, news search, webhook watches with digest tier, enriched directory) for AI agents",
|
|
6
6
|
"main": "dist/index.js",
|