@tensorfeed/mcp-server 1.28.0 → 1.30.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/index.js +172 -19
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
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.30.0 - 2026-05-14
6
+
7
+ ### Changed
8
+ - Every paid tool description now advertises the USD price alongside the credit cost. "Costs 1 credit." -> "Costs 1 credit ($0.02).". Lets agents budget calls without needing to look up credit-to-USD conversion separately. No behavior change; pure description metadata.
9
+
10
+ ### Changed (strict-premium tier)
11
+ - Six tools now flagged as "Strict premium, no free trial" in their descriptions: `pricing_series`, `benchmark_series`, `status_uptime`, `status_leaderboard`, `provider_deepdive`, `probe_series_premium`. These map to TF Worker paths in the strict-premium set (worker/src/strict-premium-endpoints.ts) which bypasses the per-IP 100-call/day free trial. Their free 7-day-capped siblings (`pricing_series_free`, `benchmark_series_free`, `status_uptime_free`, `status_leaderboard_free`) remain the discovery option. Agents that hit these without auth get a 402 immediately with a strict-premium-specific message, not the free-trial advert.
12
+ - Rationale: per the TF 70/30 premium-lock framework, full-window historical time-series and heavy aggregations are TF's moat and warrant strict gating during the demand-discovery phase. Pricing per call is unchanged at this version; repricing into a higher tier is a separate future decision.
13
+
14
+ ## 1.29.0 - 2026-05-13
15
+
16
+ ### Added
17
+ - `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.
18
+ - `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.
19
+ - `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.
20
+
21
+ ### Added (MCP primitive)
22
+ - `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.
23
+
24
+ ### Fixed
25
+ - `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.
26
+
27
+ Tool count: 46 -> 49. First MCP resource live. Annotations now spec-honest about retry semantics.
28
+
5
29
  ## 1.28.0 - 2026-05-13
6
30
 
7
31
  ### 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
- function registerTool(name, description, paramsSchema, cb, annotations = READ_TOOL) {
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, annotations, wrapped);
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
  // ════════════════════════════════════════════════════════════════════
@@ -358,7 +511,7 @@ registerTool('get_account_usage', 'Show per-endpoint usage for the configured Te
358
511
  };
359
512
  });
360
513
  // ── Tool: premium_routing (1 credit) ────────────────────────────────
361
- registerTool('premium_routing', 'Get a ranked list of recommended AI models for a task with full score breakdown (quality, availability, cost, latency). Costs 1 credit.', {
514
+ registerTool('premium_routing', 'Get a ranked list of recommended AI models for a task with full score breakdown (quality, availability, cost, latency). Costs 1 credit ($0.02).', {
362
515
  task: z.enum(['code', 'reasoning', 'creative', 'general']).optional().describe('Task type the model needs to be good at (default: general)'),
363
516
  budget: z.number().optional().describe('Max blended USD per 1M tokens'),
364
517
  min_quality: z.number().min(0).max(1).optional().describe('Minimum quality score in [0, 1]'),
@@ -475,7 +628,7 @@ registerTool('status_leaderboard_free', 'Cross-provider uptime leaderboard, rank
475
628
  };
476
629
  });
477
630
  // ── Tool: pricing_series (1 credit) ─────────────────────────────────
478
- registerTool('pricing_series', 'Daily price points for one AI model with min/max/delta summary. Default range = last 30 days, max 90 days. Costs 1 credit.', {
631
+ registerTool('pricing_series', 'Daily price points for one AI model with min/max/delta summary. Default range = last 30 days, max 90 days. Costs 1 credit ($0.02). Strict premium, no free trial; the 7-day-capped sibling pricing_series_free is the discovery option.', {
479
632
  model: z.string().describe('Model id or display name (e.g. "Claude Opus 4.7" or "claude-opus-4-7")'),
480
633
  from: z.string().optional().describe('Start date YYYY-MM-DD UTC (default: 30 days ago)'),
481
634
  to: z.string().optional().describe('End date YYYY-MM-DD UTC (default: today)'),
@@ -500,7 +653,7 @@ registerTool('pricing_series', 'Daily price points for one AI model with min/max
500
653
  };
501
654
  });
502
655
  // ── Tool: benchmark_series (1 credit) ───────────────────────────────
503
- registerTool('benchmark_series', 'Score evolution for a single benchmark on one AI model. Costs 1 credit. Benchmark keys: swe_bench, mmlu_pro, gpqa_diamond, math, human_eval.', {
656
+ registerTool('benchmark_series', 'Score evolution for a single benchmark on one AI model. Costs 1 credit ($0.02). Strict premium, no free trial; the 7-day-capped sibling benchmark_series_free is the discovery option. Benchmark keys: swe_bench, mmlu_pro, gpqa_diamond, math, human_eval.', {
504
657
  model: z.string().describe('Model id or display name'),
505
658
  benchmark: z.string().describe('Benchmark key (e.g. swe_bench, mmlu_pro, gpqa_diamond, math, human_eval)'),
506
659
  from: z.string().optional().describe('Start date YYYY-MM-DD UTC (default: 30 days ago)'),
@@ -526,7 +679,7 @@ registerTool('benchmark_series', 'Score evolution for a single benchmark on one
526
679
  };
527
680
  });
528
681
  // ── Tool: status_uptime (1 credit) ──────────────────────────────────
529
- registerTool('status_uptime', 'Daily uptime rollup for one provider with operational/degraded/down day counts and uptime % (degraded counts as half-credit). Costs 1 credit.', {
682
+ registerTool('status_uptime', 'Daily uptime rollup for one provider with operational/degraded/down day counts and uptime % (degraded counts as half-credit). Costs 1 credit ($0.02). Strict premium, no free trial; the 7-day-capped sibling status_uptime_free is the discovery option.', {
530
683
  provider: z.string().describe('Provider name (e.g. anthropic, openai, google)'),
531
684
  from: z.string().optional().describe('Start date YYYY-MM-DD UTC (default: 30 days ago)'),
532
685
  to: z.string().optional().describe('End date YYYY-MM-DD UTC (default: today)'),
@@ -553,7 +706,7 @@ registerTool('status_uptime', 'Daily uptime rollup for one provider with operati
553
706
  };
554
707
  });
555
708
  // ── Tool: status_leaderboard (1 credit) ─────────────────────────────
556
- registerTool('status_leaderboard', 'Cross-provider uptime leaderboard for a custom date range up to 90 days. Same minute-resolution counter source as status_leaderboard_free, but adds incident_count and mttr_minutes (mean time to recover) per provider. Sorted by uptime % DESC. Costs 1 credit.', {
709
+ registerTool('status_leaderboard', 'Cross-provider uptime leaderboard for a custom date range up to 90 days. Same minute-resolution counter source as status_leaderboard_free, but adds incident_count and mttr_minutes (mean time to recover) per provider. Sorted by uptime % DESC. Costs 1 credit ($0.02). Strict premium, no free trial; the 7-day-capped sibling status_leaderboard_free is the discovery option.', {
557
710
  from: z.string().optional().describe('Start date YYYY-MM-DD UTC (default: 30 days ago)'),
558
711
  to: z.string().optional().describe('End date YYYY-MM-DD UTC (default: today)'),
559
712
  }, async ({ from, to }) => {
@@ -579,7 +732,7 @@ registerTool('status_leaderboard', 'Cross-provider uptime leaderboard for a cust
579
732
  };
580
733
  });
581
734
  // ── Tool: premium_agents_directory (1 credit) ───────────────────────
582
- registerTool('premium_agents_directory', 'Enriched AI agents catalog joined with live status, recent news, agent traffic, flagship pricing, and a 0-100 trending_score. Costs 1 credit.', {
735
+ registerTool('premium_agents_directory', 'Enriched AI agents catalog joined with live status, recent news, agent traffic, flagship pricing, and a 0-100 trending_score. Costs 1 credit ($0.02).', {
583
736
  category: z.string().optional().describe('coding, research, general, creative, frameworks'),
584
737
  status: z.enum(['operational', 'degraded', 'down', 'unknown']).optional(),
585
738
  open_source: z.boolean().optional(),
@@ -612,7 +765,7 @@ registerTool('premium_agents_directory', 'Enriched AI agents catalog joined with
612
765
  };
613
766
  });
614
767
  // ── Tool: whats_new (1 credit) ──────────────────────────────────────
615
- registerTool('whats_new', 'Agent morning brief: pricing changes, new/removed models, status incidents, and top news from the last 1-7 days. The tool to call when your agent boots up. Costs 1 credit.', {
768
+ registerTool('whats_new', 'Agent morning brief: pricing changes, new/removed models, status incidents, and top news from the last 1-7 days. The tool to call when your agent boots up. Costs 1 credit ($0.02).', {
616
769
  days: z.number().min(1).max(7).optional().describe('Window length in days (default 1)'),
617
770
  news_limit: z.number().min(1).max(25).optional().describe('Max news headlines (default 10)'),
618
771
  }, async ({ days, news_limit }) => {
@@ -648,7 +801,7 @@ registerTool('whats_new', 'Agent morning brief: pricing changes, new/removed mod
648
801
  };
649
802
  });
650
803
  // ── Tool: compare_models (1 credit) ─────────────────────────────────
651
- registerTool('compare_models', 'Side-by-side comparison of 2-5 AI models. Returns pricing, benchmarks (normalized to a union of keys with null for missing scores), provider status, and recent news per model, plus rankings (cheapest blended, most context, per-benchmark leaderboard). Costs 1 credit.', {
804
+ registerTool('compare_models', 'Side-by-side comparison of 2-5 AI models. Returns pricing, benchmarks (normalized to a union of keys with null for missing scores), provider status, and recent news per model, plus rankings (cheapest blended, most context, per-benchmark leaderboard). Costs 1 credit ($0.02).', {
652
805
  ids: z.string().describe('Comma-separated list of 2-5 model ids or display names. Examples: "Claude Opus 4.7,GPT-5.5,Gemini 3" or "opus-4-7,gpt-5-5"'),
653
806
  }, async ({ ids }) => {
654
807
  const data = (await fetchJSON(`/premium/compare/models?ids=${encodeURIComponent(ids)}`, { auth: true }));
@@ -675,7 +828,7 @@ registerTool('compare_models', 'Side-by-side comparison of 2-5 AI models. Return
675
828
  };
676
829
  });
677
830
  // ── Tool: provider_deepdive (1 credit) ──────────────────────────────
678
- registerTool('provider_deepdive', 'Everything about an AI provider in one call: live status, all models with pricing + tier + benchmark scores joined in, recent news mentions, and agent traffic. Costs 1 credit. Aggregation IS the value; doing this from free endpoints would take 4 round-trips.', {
831
+ registerTool('provider_deepdive', 'Everything about an AI provider in one call: live status, all models with pricing + tier + benchmark scores joined in, recent news mentions, and agent traffic. Costs 1 credit ($0.02). Strict premium, no free trial. Aggregation IS the value; doing this from free endpoints would take 4 round-trips.', {
679
832
  provider: z.string().describe('Provider id or display name (case-insensitive). Examples: anthropic, openai, google, mistral, cohere'),
680
833
  }, async ({ provider }) => {
681
834
  const data = (await fetchJSON(`/premium/providers/${encodeURIComponent(provider)}`, { auth: true }));
@@ -705,7 +858,7 @@ registerTool('provider_deepdive', 'Everything about an AI provider in one call:
705
858
  };
706
859
  });
707
860
  // ── Tool: cost_projection (1 credit) ────────────────────────────────
708
- registerTool('cost_projection', 'Project the cost of a token-usage workload across 1-10 AI models. Returns daily/weekly/monthly/yearly totals per model and a ranking by cheapest monthly. Costs 1 credit.', {
861
+ registerTool('cost_projection', 'Project the cost of a token-usage workload across 1-10 AI models. Returns daily/weekly/monthly/yearly totals per model and a ranking by cheapest monthly. Costs 1 credit ($0.02).', {
709
862
  models: z.string().describe('One model or comma-separated list of up to 10 (e.g. "Claude Opus 4.7,GPT-5.5,Gemini 3"). Names or ids both work.'),
710
863
  input_tokens_per_day: z.number().min(0).describe('Expected daily input token volume'),
711
864
  output_tokens_per_day: z.number().min(0).describe('Expected daily output token volume'),
@@ -740,7 +893,7 @@ registerTool('cost_projection', 'Project the cost of a token-usage workload acro
740
893
  };
741
894
  });
742
895
  // ── Tool: news_search (1 credit) ────────────────────────────────────
743
- registerTool('news_search', 'Full-text search over the TensorFeed news article corpus with optional date range, provider, and category filters. Relevance scoring with recency boost. Costs 1 credit.', {
896
+ registerTool('news_search', 'Full-text search over the TensorFeed news article corpus with optional date range, provider, and category filters. Relevance scoring with recency boost. Costs 1 credit ($0.02).', {
744
897
  q: z.string().optional().describe('Free-text query, e.g. "claude opus pricing". Omit to browse latest filtered articles.'),
745
898
  from: z.string().optional().describe('Start date YYYY-MM-DD UTC (inclusive)'),
746
899
  to: z.string().optional().describe('End date YYYY-MM-DD UTC (inclusive end-of-day)'),
@@ -794,7 +947,7 @@ registerTool('list_watches', 'List the active webhook watches owned by the confi
794
947
  return { content: [{ type: 'text', text: `${data.count} active watches:\n\n${rows}` }] };
795
948
  });
796
949
  // ── Tool: create_price_watch (1 credit) ─────────────────────────────
797
- registerTool('create_price_watch', 'Register a webhook watch on a model price change. Costs 1 credit. Watch lives 90 days. Each fire is an HMAC-signed POST to callback_url.', {
950
+ registerTool('create_price_watch', 'Register a webhook watch on a model price change. Costs 1 credit ($0.02). Watch lives 90 days. Each fire is an HMAC-signed POST to callback_url.', {
798
951
  model: z.string().describe('Model name (e.g. "Claude Opus 4.7")'),
799
952
  field: z.enum(['inputPrice', 'outputPrice', 'blended']).describe('Which price field to watch'),
800
953
  op: z.enum(['lt', 'gt', 'changes']).describe('Trigger: lt = below threshold, gt = above, changes = any change'),
@@ -819,7 +972,7 @@ registerTool('create_price_watch', 'Register a webhook watch on a model price ch
819
972
  };
820
973
  }, CREATE_TOOL);
821
974
  // ── Tool: create_status_watch (1 credit) ────────────────────────────
822
- registerTool('create_status_watch', 'Register a webhook watch on a service status transition (e.g. anthropic becomes down). Costs 1 credit. Watch lives 90 days.', {
975
+ registerTool('create_status_watch', 'Register a webhook watch on a service status transition (e.g. anthropic becomes down). Costs 1 credit ($0.02). Watch lives 90 days.', {
823
976
  provider: z.string().describe('Provider name (e.g. anthropic, openai)'),
824
977
  op: z.enum(['becomes', 'changes']).describe('becomes = transitions to a specific value; changes = any transition'),
825
978
  value: z.enum(['operational', 'degraded', 'down']).optional().describe('Required when op is becomes'),
@@ -843,7 +996,7 @@ registerTool('create_status_watch', 'Register a webhook watch on a service statu
843
996
  };
844
997
  }, CREATE_TOOL);
845
998
  // ── Tool: create_digest_watch (1 credit) ────────────────────────────
846
- registerTool('create_digest_watch', 'Register a scheduled digest webhook that fires daily or weekly with a curated summary of pricing changes (regardless of whether anything dramatic happened). Costs 1 credit. Watch lives 90 days. Set-and-forget for agents that want a periodic snapshot without subscribing to realtime transitions.', {
999
+ registerTool('create_digest_watch', 'Register a scheduled digest webhook that fires daily or weekly with a curated summary of pricing changes (regardless of whether anything dramatic happened). Costs 1 credit ($0.02). Watch lives 90 days. Set-and-forget for agents that want a periodic snapshot without subscribing to realtime transitions.', {
847
1000
  cadence: z.enum(['daily', 'weekly']).describe('How often the digest fires'),
848
1001
  callback_url: z.string().describe('HTTPS URL to POST to when the digest fires'),
849
1002
  secret: z.string().optional().describe('Optional HMAC shared secret'),
@@ -865,7 +1018,7 @@ registerTool('create_digest_watch', 'Register a scheduled digest webhook that fi
865
1018
  };
866
1019
  }, CREATE_TOOL);
867
1020
  // ── Tool: create_leaderboard_rank_watch (1 credit) ──────────────────
868
- registerTool('create_leaderboard_rank_watch', 'Register a webhook that fires when a provider crosses a rank threshold on the cross-provider 7-day uptime leaderboard. Rank 1 = best (highest uptime). drops_below: was rank<=N, now rank>N (got worse). rises_above: was rank>=N, now rank<N (got better). changes: any rank movement. Costs 1 credit at registration. Watch lives 90 days.', {
1021
+ registerTool('create_leaderboard_rank_watch', 'Register a webhook that fires when a provider crosses a rank threshold on the cross-provider 7-day uptime leaderboard. Rank 1 = best (highest uptime). drops_below: was rank<=N, now rank>N (got worse). rises_above: was rank>=N, now rank<N (got better). changes: any rank movement. Costs 1 credit ($0.02) at registration. Watch lives 90 days.', {
869
1022
  provider: z.string().describe('Provider name or slug (case-insensitive). e.g. claude, openai, gemini, bedrock, azure'),
870
1023
  op: z.enum(['drops_below', 'rises_above', 'changes']).describe('Trigger condition'),
871
1024
  threshold: z.number().int().min(1).optional().describe('Required for drops_below / rises_above. Integer rank position.'),
@@ -925,7 +1078,7 @@ registerTool('probe_latest', 'Last 24 hours of measured LLM endpoint latency and
925
1078
  };
926
1079
  });
927
1080
  // ── Tool: probe_series (1 credit) ───────────────────────────────────
928
- registerTool('probe_series', 'Daily SLA series for one LLM provider, measured by TensorFeed. Returns per-day count, success rate, ttfb p50/p95/p99, total p50/p95/p99, and incident-hour count across the requested window. Provider status pages are politically managed; this is the measured truth. Pairs naturally with premium_routing for picking a model whose SLA you can verify. Costs 1 credit.', {
1081
+ registerTool('probe_series', 'Daily SLA series for one LLM provider, measured by TensorFeed. Returns per-day count, success rate, ttfb p50/p95/p99, total p50/p95/p99, and incident-hour count across the requested window. Provider status pages are politically managed; this is the measured truth. Pairs naturally with premium_routing for picking a model whose SLA you can verify. Costs 1 credit ($0.02). Strict premium, no free trial.', {
929
1082
  provider: z.enum(['anthropic', 'openai', 'google', 'mistral', 'cohere']).describe('LLM provider key'),
930
1083
  from: z.string().optional().describe('Inclusive start YYYY-MM-DD (default: 30 days before to)'),
931
1084
  to: z.string().optional().describe('Inclusive end YYYY-MM-DD (default: today UTC)'),
@@ -987,7 +1140,7 @@ registerTool('mcp_registry_snapshot', 'Today\'s summary of the official Model Co
987
1140
  };
988
1141
  });
989
1142
  // ── Tool: mcp_registry_series (1 credit) ────────────────────────────
990
- registerTool('mcp_registry_series', 'Multi-day series of MCP server registry growth and churn. Returns per-day total servers, active count, and daily added/removed counts across the requested window. The registry itself is open data, but a 30/90-day trend requires daily capture started weeks ago, which TensorFeed has been running. Costs 1 credit.', {
1143
+ registerTool('mcp_registry_series', 'Multi-day series of MCP server registry growth and churn. Returns per-day total servers, active count, and daily added/removed counts across the requested window. The registry itself is open data, but a 30/90-day trend requires daily capture started weeks ago, which TensorFeed has been running. Costs 1 credit ($0.02).', {
991
1144
  from: z.string().optional().describe('Inclusive start date YYYY-MM-DD (default: 30 days before to)'),
992
1145
  to: z.string().optional().describe('Inclusive end date YYYY-MM-DD (default: today UTC)'),
993
1146
  }, async ({ from, to }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tensorfeed/mcp-server",
3
- "version": "1.28.0",
3
+ "version": "1.30.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",