@tensorfeed/mcp-server 1.28.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 +15 -0
- package/dist/index.js +155 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
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
|
+
|
|
5
20
|
## 1.28.0 - 2026-05-13
|
|
6
21
|
|
|
7
22
|
### 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")'),
|
|
@@ -323,6 +368,114 @@ registerTool('get_ai_supply_chain_iocs', 'Get the latest AI/ML supply-chain indi
|
|
|
323
368
|
const footer = `\n\nPosture: ${data.posture}`;
|
|
324
369
|
return { content: [{ type: 'text', text: `${header}\n\n${body}${footer}` }] };
|
|
325
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
|
+
});
|
|
326
479
|
// ════════════════════════════════════════════════════════════════════
|
|
327
480
|
// PREMIUM TOOLS (require TENSORFEED_TOKEN env var, paid in USDC on Base)
|
|
328
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",
|